gccのコンパイルオプション-fPICと-fpicの違いは?

提供: C言語入門
2016年2月28日 (日) 00:33時点におけるDaemon (トーク | 投稿記録)による版

(差分) ←前の版 | 最新版 (差分) | 次の版→ (差分)
移動: 案内検索
スポンサーリンク

Cコンパイラgccコンパイラやg++(C++のコンパイラ)でよく使われる -fPIC オプションと -fpic オプションの違いについてまとめました。また、-fPIC オプションをつけた場合、どのようなアセンブリ言語が生成されるのか、確認しました。

読み方

pic
ぴっく
position-independent code
ぽじしょん いんでぃぺんでんと こーど

概要

Unixでプログラムをビルドしている最中に流れてくるメッセージをみていると、cc(gcc)のコンパイルオプションに -fPIC というオプションがついていることに気がつくと思います。

gccには、-fPIC のほかに -fpic というオプションもあります。小文字か大文字かの違いがありますが、一体何が違うのでしょうか?

まず、 -fPIC について解説し、そのあと -fPIC と -fpic の違いについて説明します。

-fPIC は何のためのオプションか?

gcc でプログラムをコンパイルする場合に、-fPIC がよく使われます。

-fPIC は、UNIX(Linux)で共有ライブラリ(shared object, シェアードオブジェクト)を作成するときに使われるオプションです。

PIC とは Position-Independent Code の略です。日本語では、位置独立コードとなります。または、位置独立実行形式です。 PIC でコンパイルされたコードは、メインメモリのどこに配置されても、絶対アドレスに関わらず、正しく実行できます。

-fpicと-fPICの違い

-fpicと-fPICの違い は、以下の通りです。

  • -fpic は、オーバーヘッドが小さく、実効速度が比較的速い。しかし、OSによってサイズの制限がある場合がある。
  • -fPIC は、サイズの制限を回避できる。

gccの-fpicオプション

ターゲットマシンをサポートしている場合、共有ライブラリのために、position-independent code(PIC)を生成します。 global offset table(GOT, グローバルオフセットテーブル)を通じて、コードは、すべての定数アドレスにアクセスします。ダイナミックローダは、プログラムがスタートしたときに、GOTエントリを解決します。ダイナミックローダは、GCCではありません。ダイナミックローダは、オペレーティングシステムです。リンクされた実行ファイルのGOTサイズがマシン固有の最大サイズを超えたとき、 -fpic が機能しないことを示す リンカのエラーメッセージが出ます。そのような場合には、 -FPIC で再コンパイルしてください。 SPARC は 8k 、 m68kやRS/6000では、32k、が最大サイズです。 x86 は、制限はありません。

このフラグが設定されたとき、 マクロ __pic_____PIC___ が 1 と定義されます。

gccの-fPICオプション

ターゲットマシンをサポートしている場合、ダイナミック均キングに適し、 global offset table のサイズ制限を避けた、 position-independent code を生成します。

このフラグが設定されたとき、 マクロ __pic_____PIC___ が 1 と定義されます。

-fPIC有りとなしの違い

-fPICオプションをつけた場合と、つけない場合で、生成されるコードを比較してみました。 以下の簡単なプログラムを gcc でアセンブリ言語のコードに変換します。

/*
 * printf.c
 * Copyright (C) 2016 kaoru <kaoru@localhost>
 */
#include <stdio.h>
 
int
main(int argc, char *argv[])
{
        printf("hoge");
        return 0;
}
$ gcc5 -Wl,-rpath=/usr/local/lib/gcc5/ -O2 -o printf-no-fpic.S -S printf.c
$ gcc5 -Wl,-rpath=/usr/local/lib/gcc5/ -O2 -fPIC -o printf-fpic.S -S printf.c
$ diff printf-fpic.S printf-no-fpic.S
15d14
<       leaq    .LC0(%rip), %rdi
17a17
>       movl    $.LC0, %edi
19c19
<       call    printf@PLT
---
>       call    printf

アセンブラの場合、 printf 関数の呼び出しが以下のように @PLT となっています。 PLT は、 Procedure Linkage Table の略です。

        call    printf@PLT

printf-pic.S のコードの全体は、以下の通りです。

        .file   "printf.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "hoge"
        .section        .text.unlikely,"ax",@progbits
.LCOLDB1:
        .section        .text.startup,"ax",@progbits
.LHOTB1:
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        leaq    .LC0(%rip), %rdi
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        xorl    %eax, %eax
        call    printf@PLT
        xorl    %eax, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .section        .text.unlikely
.LCOLDE1:
        .section        .text.startup
.LHOTE1:
        .ident  "GCC: (FreeBSD Ports Collection) 5.3.0"
        .section        .note.GNU-stack,"",@progbits

関連項目




スポンサーリンク