C 是否需要为共享库文件专门编译32位x86代码?

C 是否需要为共享库文件专门编译32位x86代码?,c,linux,shared-libraries,elf,pic,C,Linux,Shared Libraries,Elf,Pic,如果目标文件打算作为共享库加载(.so),则需要独立于位置将代码编译到目标文件,因为在不同的进程中加载到的共享目标文件的基虚拟地址可能不同 现在,我在32位x86计算机上加载.so文件时,没有使用-fpicGCC选项进行编译和链接,但在64位x86计算机上却失败了 我发现的随机网站说我不需要32位的-fpic,因为没有-fpic编译的代码在以位置独立的方式使用时,根据X86 32位ABI也会巧合地工作。但我仍然发现软件在32位版本中附带了不同版本的库:一个用于PIC,一个用于非PIC。例如,“英

如果目标文件打算作为共享库加载(
.so
),则需要独立于位置将代码编译到目标文件,因为在不同的进程中加载到的共享目标文件的基虚拟地址可能不同

现在,我在32位x86计算机上加载
.so
文件时,没有使用
-fpic
GCC选项进行编译和链接,但在64位x86计算机上却失败了

我发现的随机网站说我不需要32位的
-fpic
,因为没有
-fpic
编译的代码在以位置独立的方式使用时,根据X86 32位ABI也会巧合地工作。但我仍然发现软件在32位版本中附带了不同版本的库:一个用于PIC,一个用于非PIC。例如,“英特尔编译器”附带了
libirc.a
libirc\u pic.a
,后者被编译为位置独立模式(如果要将
.a
文件链接到
.so
文件)

我想知道在32位代码中使用fpic和不使用fpic的确切区别是什么,为什么有些软件包,比如英特尔编译器,仍然附带不同版本的库

在不同进程中加载共享对象文件的基本虚拟地址可能不同

由于共享对象通常在其首选地址加载,因此它们可能看起来工作正常。但是对于所有共享代码来说,
fPIC
是个好主意


我相信这个库不经常有两个版本的原因是许多发行版使用
fPIC
作为所有代码的默认值。

并非非PIC代码在x86(32位)上“巧合”工作。这是因为x86的动态链接器支持使其工作所需的“textrels”。这在内存消耗和启动时间方面代价非常高,因为基本上整个代码段都必须在加载时修补(因此成为不可共享内存)


动态链接器维护者声称,x86_64上不支持非PIC共享库,因为体系结构中存在根本性问题(即时地址位移不能大于32位),但只要始终在第一个4gb虚拟地址空间中加载库,就可以轻松解决此问题。当然,PIC代码在x86_64上非常便宜(PIC不像在32位x86上那样是性能杀手),所以他们可能正确地保持它不受支持,并防止傻瓜创建非PIC库…

您是否尝试过使用TLS进行非fpic代码编译?或者加载几个具有重叠内存范围的非pic库?单独的静态库是静态链接到程序(libirc.a;没有pic会快一点)和静态链接到.so库(_pic.a版本)。谢谢,这很有意义。如果我在x86上编译PIC库,我怀疑它不再使用textrels了?为什么它仍然是一个性能杀手呢?PIC代码需要一个额外的寄存器来保存基址。在x86上,您的寄存器已经太少,因此多保留一个寄存器会使编译器更频繁地将寄存器溢出到内存中。PIC在x86上是一个性能杀手,因为加载GOT寄存器非常昂贵。它需要一个函数调用并从堆栈中读取/保存返回地址。x86_64具有
eip
-相对寻址,因此不需要GET寄存器。这也是俄罗斯人所说的。@Alek:非PIC库仍将消耗更多内存,它们将比PIC快,但不如静态链接快,因为函数调用将通过PLT。对于性能非常重要的库(如
libavcodec
libx264
),我只会避免生成
。因此
库总是静态链接到主程序二进制文件中。共享对象通常链接到地址0处的加载,而且它们肯定不会在那里加载。另外,您可以
prelink
库以使它们具有首选的基址,但这是正常链接的一个单独(可选)步骤。@俄语:这是linux独有的吗?其他操作系统不会愚蠢到默认为100%的基址冲突。@Ben这到底是什么操作系统?似乎不可能协调随机程序用于使用非重叠地址范围的库的所有作者。作者是如何了解彼此的图书馆的?@Johannes:Keyword“通常”。与代码段大小相比,地址空间相当大,因此冲突很少。这基本上是一个生日问题,但是如果你不考虑JIT编译(它可以动态地选择一个未使用的地址)和操作系统供应商提供的库(可以小心地打包以避免冲突),很少有程序加载超过六个第三方库。