gcc 编译选项汇总

-shared; Create a shared library /* 创建共享库时需要添加的编译选项 */
因为共享库的代码都是位置无关的,所以通常还需要添加编译选项 - fPIC。

-pic-fPIC 选项

PIC (position-independent code)

-pic-fPIC 都是编译时的选项,用于生成位置无关的代码 (Position-Independent-Code)。这两个选项都是可以使代码在加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表 (GOT) 来实现。

-pic-fPIC 区别在于:

  • -fpic 对 GOT 表的大小有限制。如果链接的可执行文件 GOT 表 size 超过了架构设定的 maximum size,链接器就会报错,这时候 -fpic 就不适用了。
  • -fPIC 对 GOT 表大小无限制,所以如果在不确定的情况下,建议使用 -fPIC

PIC 实现原理:

(1)GOT:在动态库的数据段增加 GOT(Global Offset Table),该表的每一项是符号到地址的绝对映射。由于代码段到数据段的偏移是固定的,因此可以在编译时确定代码段中的某个符号到 GOT 特定项之间的偏移。这样,代码段中的符号偏移就可以在编译时确定了,在加载时也无需修改代码段的内容,只需要填写位于数据段的 GOT 的所有项的符号的绝对地址就完成了。因为数据段本来就是进程间不共享,每个进程独立的一份,因此 GOT 的设计完全解决了以上两个问题,从而达到两个目的:1,代码段可以在多进程间共享;2,代码段是只读的。

(2)PLT:PLT 是 Program Linkage Table 的缩写,即程序链接表,PLT 的出现是为了延时定位的目的。一个动态库中的函数往往要远多于全局变量,并且被调用的函数往往少于定义的函数。GOT 中包含了该动态库中的所有的全局变量的映射,并且在连接器加载时解析所有的全局变量的地址。如果用同样的方式去处理函数调用符号,则开销会非常大。因此在代码段设计了一个 PLT 表,每一项其实是个代码段,用于执行如下逻辑:首次访问时,解析参数和向 GOT 填写函数地址,后续访问直接访问 GOT 中的函数地址。如此达到了延时定位的目的。

因此,一个 PIC 的动态库中,对全局变量使用 GOT 来映射,对函数调用使用 PLT+GOT 来映射,从而达到共享库代码段复用,代码段安全访问的目的。而这些就是 PIC 的意义。

GCC 官方手册解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC, 28k on AArch64 and 32k on the m68k and RS/6000. The x86 has no such limit.)

Position-independent code requires special support, and therefore works only on certain machines. For the x86, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent.

When this flag is set, the macros __pic__ and __PIC__ are defined to 1.

-fPIC
If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table. This option makes a difference on AArch64, m68k, PowerPC and SPARC.

Position-independent code requires special support, and therefore works only on certain machines.

When this flag is set, the macros __pic__ and __PIC__ are defined to 2.

-fpie-fPIE 选项

PIE (position-independent executable)

-fpie-fPIE 选项与 -pic-fPIC 是类似的,唯一区别是加上 -fpie-fPIE 这两个选项后编译生成的位置无关代码只能被用于链接成可执行文件。

即:-fPIC 用于生成动态库,-fPIE 用于生成可执行文件。

若想通过 gcc 编译出位置无关的可执行程序,必须同时使用 -fPIE-pie 选项。其中,-fPIE 选项用于编译器,使用该选项从.c 编译出来的.o 文件将是位置无关的目标文件;而 -pie 选项则用于链接器,使用该选项后,链接器能够把 -fPIE 选项下编译出来的.o 文件进一步链接成位置无关的可执行程序。

GCC 官方手册解释:

1
2
3
4
5
-fpie
-fPIE
These options are similar to -fpic and -fPIC, but the generated position-independent code can be only linked into executables. Usually these options are used to compile code that will be linked using the -pie GCC option.

-fpie and -fPIE both define the macros __pie__ and __PIE__. The macros have the value 1 for -fpie and 2 for -fPIE.

-pie-no-pie 选项

-pie-no-pie 属于链接选项,

  • -pie 表示生成动态链接的位置无关的可执行文件,前提是编译阶段使用了 -fpie-fPIE 选项。
  • -no-pie 表示不生成,和 -pie 意思相反。

GCC 官方手册解释:

1
2
3
4
5
-pie
Produce a dynamically linked position independent executable on targets that support it. For predictable results, you must also specify the same set of options used for compilation (-fpie, -fPIE, or model suboptions) when you specify this linker option.

-no-pie
Don’t produce a dynamically linked position independent executable.

-pthread 选项

-pthread 表示链接 POSIX 线程库。有些架构下,此选项还会为预处理器设置 flags,所以该选项会同时起作用于编译和链接阶段。

GCC 官方手册解释:

1
2
-pthread
Link with the POSIX threads library. This option is supported on GNU/Linux targets, most other Unix derivatives, and also on x86 Cygwin and MinGW targets. On some targets this option also sets flags for the preprocessor, so it should be used consistently for both compilation and linking.

-w/-W/-Wall 关闭编译警告

-w:关闭编译时所有的警告 warning,使用该选项后,编译界面非常清爽。

-Wall:编译时显示所有警告 warning。

-W:编译时显示编译器认为会导致报错的警告。