gccのコンパイルオプション-fPICと-fpicの違いは?
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
関連項目
ツイート