【问题标题】:Does one still need to use -fPIC when compiling with GCC?使用 GCC 编译时还需要使用 -fPIC 吗?
【发布时间】:2014-01-05 09:38:09
【问题描述】:

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

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

我还注意到 clang 没有 -fPIC 选项,这让我认为它不再需要。

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

【问题讨论】:

    标签: c++ c gcc


    【解决方案1】:

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

    clang 确实有 -fPIC 标志,就像 gcc 一样。

    $ 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
    $
    

    【讨论】:

    【解决方案2】:

    我同意你的观点:在许多情况下,-fpic/-fPIC 选项几乎是多余的,但我确实使用它们来确保:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

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

        gcc 针对很多平台和架构,并不是所有的平台和架构都像 x86 架构那样支持原生 PIC。在某些情况下,创建 PIC 意味着额外的开销,这可能是不受欢迎的,您是否想要或需要这取决于您的项目和您的目标平台。

        【讨论】:

          猜你喜欢
          • 2011-04-17
          • 2013-08-25
          • 2015-01-18
          • 2022-11-20
          • 2011-11-30
          • 2016-09-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多