C++ 在使用GCC编译时是否仍然需要使用-fPIC?

C++ 在使用GCC编译时是否仍然需要使用-fPIC?,c++,c,gcc,C++,C,Gcc,在gcc目标机器上,当您想要编译一个共享库时,需要指定-fpic或-fpic才能正确运行。这是因为默认情况下使用了绝对寻址,这适用于完全控制自己地址空间的可执行文件,但不适用于可以加载到可执行文件地址空间中任何位置的共享库 然而,现代内核现在正在实现地址空间随机化,许多现代体系结构支持PC相对寻址。这一切似乎使绝对寻址要么不可用(地址空间随机化),要么不需要(PC相对寻址) 我还注意到,clang没有一个-fPIC选项,这让我觉得它不再必要了 那么-fPIC现在是多余的还是需要生成单独的.o文件

在gcc目标机器上,当您想要编译一个共享库时,需要指定-fpic或-fpic才能正确运行。这是因为默认情况下使用了绝对寻址,这适用于完全控制自己地址空间的可执行文件,但不适用于可以加载到可执行文件地址空间中任何位置的共享库

然而,现代内核现在正在实现地址空间随机化,许多现代体系结构支持PC相对寻址。这一切似乎使绝对寻址要么不可用(地址空间随机化),要么不需要(PC相对寻址)

我还注意到,clang没有一个-fPIC选项,这让我觉得它不再必要了


那么-fPIC现在是多余的还是需要生成单独的.o文件,一个用于静态库使用,一个用于共享库使用?

您永远不需要生成单独的
.o
文件。始终指定编译器选项以生成可移植代码(通常为
-fPIC

在某些系统上,编译器可能被配置为强制启用此选项,或默认设置此选项。但无论如何,指定它也没什么坏处


注:我们希望在支持PC相对寻址且性能良好的情况下,
-fPIC
使用该模式,而不是专用一个额外的寄存器。

我同意您的看法:在许多情况下,-fPIC/-fPIC选项几乎是冗余的,但是我使用它们来确保:

  • 可移植性(永远不确定哪些特定操作系统/内核可用)
  • 向后兼容性:通过这些选项,它可以确保在旧内核上的行为
  • 习惯-很难打破:)
  • 遵守可能需要它的旧代码库

    • 您仍然需要使用-fPIC进行编译。使用pc相对寻址无法解决此问题。问题在于如何解析外部符号。在动态链接程序中,解析遵循不同的规则,尤其是地址空间随机化,在链接时间内无法解析

      和gcc一样,clang也有-fPIC标志

      $ cat > foo.c
      void foo(void);
      void bar(void) { foo(); }
      $ gcc -S foo.c && grep call.*foo foo.s
          call    foo
      $ gcc -fPIC -S foo.c && grep call.*foo foo.s
          call    foo@PLT
      $ clang -S foo.c && grep call.*foo foo.s
          callq   foo
      $ clang -fPIC -S foo.c && grep call.*foo foo.s
          callq   foo@PLT
      $
      

      gcc
      针对许多平台和体系结构,但并非所有平台和体系结构都像x86体系结构那样支持本机PIC。在某些情况下,创建PIC意味着额外的开销,这可能是不希望的,您想要还是需要这取决于您的项目和目标平台。

      这取决于目标。默认情况下,某些目标(如x86_64)与位置无关,sp
      -fpic
      是noop,对生成的代码没有影响。因此,在这些情况下,您可以省略它,而不会发生任何更改。默认情况下,其他目标(如x86 32位)不是位置独立的,因此在这些机器上,如果省略可执行文件的
      -fpic
      ,它将禁用该图像文件的ASLR(但不用于它使用的共享库).

      PIC代码在大多数情况下速度较慢,因为通过PLT的额外跳转在许多函数调用中需要额外的间接寻址。在没有PIC的情况下构建静态库是个好主意。@Art:可能会慢一些,但不会慢很多。我也不同意在没有任何限制的情况下构建静态库是有用的。静态库不仅链接到可执行文件中,还链接到共享库中。而且,正如前面提到的问题,对于ASLR,可执行文件也需要是位置独立的。因此,最安全的做法是始终指定
      -fPIC
      @Art英特尔上的问题不是函数调用或跳转(无论如何都与PC相关),而是访问全局数据。(在其他处理器上,问题会有所不同。)如果要将静态库链接到动态库,还应该构建静态库的pic版本。或者最好是一个部分链接的PIC对象文件(这是我在工作的构建系统中所做的)。我见过的系统实际上有PIE(我想这就是你所说的aslr可执行文件)默认编译器总是生成PIC,而你不能生成非PIC代码,所以这不是问题。@JamesKanze当您不知道函数相对于正在调用的函数的偏移量时,您不能进行pc相对函数调用,这在动态链接时是个问题。这就是为什么在PIC模式下,我知道的所有编译器都会生成PLT调用。在没有PIC的情况下进行构建时,您可以避免PLT调用,因为这减少了一个间接过程。@doron我想是的(但可能不是在2013年):