我正在用汇编语言编写一个目标文件,并将其包含在共享对象中。我使用GNU工具链,目标是x86_64-pc-linux-gnu
。请考虑以下(示例)源:
.text
.globl f
f: leaq g(%rip),%rax
ret
.data
.globl g
.protected g
g: .quad 8
字符串
关键部分是全局保护符号g
和f
中对g
的引用。当我使用gcc -c -o example.o --shared -fpic example.s
组装源代码时,objdump -x
告诉我,gas
为本地引用插入了一个重定位(一些重定位条目显然是必要的):
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000003 R_X86_64_PC32 g-0x0000000000000004
型
当我尝试链接文件时,问题出现了:
$ gcc -o example.so --shared -fpic example.s
/usr/bin/ld: /tmp/ccQ6BcLl.o: relocation R_X86_64_PC32 against protected symbol `g' can not be used when making a shared object
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
型
就我认为我已经通过阅读Ian Lance Taylor's blog理解(但我可能是错误的),这是由于这样一个事实,即链接器不能保证指针平等时,符号插入发生(在其他一些目标文件)。
由于这永远不会是我共享对象中的符号g
的实际问题,我想静默ld
。Linux ABI 0.1似乎说我应该向包含GNU_PROPERTY_NO_COPY_ON_PROTECTED
设置的源代码添加.note.gnu.property
部分。实际上我如何做到这一点?
如果可能的话,我不想在汇编器和链接器的调用中添加额外的标志,所以我正在寻找一种解决方案,其中必要的修改只是源文件的一部分。
1条答案
按热度按时间juzqafwq1#
你不能像这样引用符号,因为虽然它现在被保护不受插入,但它仍然受到从共享库导出的任何对象类型符号的相同行为的影响。要解决这个问题,请像任何其他导出的对象类型符号一样通过GOT。
背景
ELF ABI的设计者希望共享对象对主程序是透明的。ELF ABI程序(但不是共享对象)完全不知道共享对象的存在,并且被写得好像程序使用的所有符号都被静态地链接到程序中。这包括对象类型符号,允许直接访问这些符号。例如,主程序可以做
字符串
并获取共享库使用的同一变量
g
的值。其工作方式是,对于主程序引用但由共享库提供的所有对象类型符号,链接器在链接时查找符号的大小,并在可执行文件的BSS段中分配相应的空间。符号(在本例中为g
)被解析为指向该空间。在加载时,动态加载器找到定义
g
的共享库,并将g
的数据从共享对象的数据段复制到主可执行文件的BSS段中保留的空间中,并将g
的GOT条目解析到该地址。这称为复制重定位。因此,共享库,当访问g
时,将访问主程序可以访问的相同变量。(如果主程序不访问g
,则不会发生复制重定位,并且g
将解析为其在共享库的数据/BSS段中的定义)但是,此方案仅适用于共享库通过GOT访问符号的情况,因为符号不会随共享库重新定位。因此,您必须通过GOT访问符号。
即做
型
g
的地址在程序运行时不会改变,所以在代码开始时只需要这样做一次就足够了。开销应该很低。解决方法
解决方法包括:
-Bsymbolic
让共享库始终使用它自己的符号副本,即使它受到副本重定位。请注意,这实际上禁用了共享库和主可执行文件之间共享变量的能力。您也无法正确比较函数指针的相等性。因此,不应在生产环境中使用此选项。型
在主二进制文件中,声明
g
为持有指向变量的指针,并解引用它来访问变量。考虑将它声明为const
,这样它就不会被意外地解引用。注意,这种方法比通过GOT从其他共享对象访问符号的性能更差。-mno-direct-extern-access
来避免复制重定位(你可能还需要用-Wz,nocopyreloc
链接所有部分。注意,这样编译的共享库与没有这个选项编译的主程序是ABI不兼容的,不能链接到它们。反过来也可以。然而,最好的选择是只通过GOT与任何其他全球符号。