我正在用汇编语言编写一个目标文件以包含在共享对象中。我正在使用 GNU 工具链,我的目标是
x86_64-pc-linux-gnu
。请考虑以下(示例)来源:
.text
.globl f
f: leaq g(%rip),%rax
ret
.data
.globl g
.protected g
g: .quad 8
关键部分是全局受保护符号
g
以及g
中对f
的引用。当我使用 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的博客理解了(但我可能是错的),这是因为当符号插入发生时(在其他一些目标文件中)链接器无法保证指针相等。
由于这永远不会成为我的共享对象中的符号
g
的实际问题,因此我想保持沉默ld
。 Linux ABI 0.1 似乎说我应该在我的源代码中添加一个 .note.gnu.property
部分,其中包含 GNU_PROPERTY_NO_COPY_ON_PROTECTED
设置。在实践中我该如何做到这一点?
如果可能,我不想在汇编器和链接器的调用中添加额外的标志,因此我正在寻找一种解决方案,其中必要的修改只是源文件的一部分。
您不能像这样引用该符号,因为虽然它现在受到保护而不会被插入,但它仍然受到与从共享库导出的任何对象类型符号相同的行为的影响。要解决此问题,请像处理任何其他导出的对象类型符号一样检查 GOT。让我解释一下。
ELF ABI 的设计者希望共享对象对主程序是透明的。 ELF ABI 程序(但不是共享对象)完全不知道共享对象的存在,并且编写时就好像程序使用的所有符号都静态链接到程序中一样。这包括允许直接访问的对象类型符号。例如。主程序可以做什么
movq g(%rip), %rax
并获取共享库使用的同一变量
g
的值。其工作方式是,对于主程序引用但由共享库提供的所有对象类型符号,链接器在链接时查找符号的大小,并在可执行文件的 BSS 段中分配那么多空间。符号(在本例中为g
)被解析为指向该空间。
在加载时,动态加载器找到定义
g
的共享库,并将g
的数据从共享对象的数据段复制到主可执行文件的BSS段中保留的空间中,并解析GOT条目g
到该地址。这称为“复制重定位”。因此,共享库在访问 g
时将访问主程序可以访问的相同变量。 (如果主程序不访问 g
,则不会发生复制重定位,并且 g
会解析为其在共享库的数据/BSS 段中的定义)但是,此方案仅在共享库通过 GOT 访问符号时才有效,因为符号不会随共享库一起重定位。因此,您必须通过 GOT 才能访问该符号。
解决方法包括:
考虑隐藏该符号,仅通过访问器函数将其暴露给主可执行文件,并返回其地址。您可以使用映射文件(版本脚本)在一个位置设置库中所有函数的可见性,这可能比在源文件中注释符号更容易。
-Bsymbolic