提问人:Marc 提问时间:11/16/2023 最后编辑:Peter CordesMarc 更新时间:11/16/2023 访问量:124
在程序集中写入共享对象时,如何处理“针对受保护符号的重新定位R_X86_64_PC32”?
How to handle the "relocation R_X86_64_PC32 against protected symbol" when writing shared objects in assembly?
问:
我正在用汇编语言编写一个对象文件,以包含在共享对象中。我正在使用 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 的博客(但我可能弄错了),这是因为链接器在发生符号插入时(在其他对象文件中)无法保证指针相等。
由于这永远不会成为我共享对象中的符号的实际问题,因此我想保持沉默。Linux ABI 0.1 似乎说我应该在我的源代码中添加一个包含设置 .在实践中如何做到这一点?g
ld
.note.gnu.property
GNU_PROPERTY_NO_COPY_ON_PROTECTED
如果可能的话,我不想在汇编程序和链接器的调用中添加额外的标志,因此我正在寻找一种解决方案,其中必要的修改只是源文件的一部分。
答:
您不能像这样引用符号,因为虽然它现在受到保护,不会入,但它仍然受到与从共享库导出的任何对象类型符号相同的行为的约束。要解决此问题,请像使用任何其他导出的对象类型符号一样浏览 GOT。
背景
ELF ABI 的设计者希望共享对象对主程序是透明的。ELF ABI 程序(但不是共享对象)完全不知道共享对象的存在,并且编写得好像程序使用的所有符号都静态链接到程序中。这包括允许直接访问的对象类型符号。例如,主程序可以做
movq g(%rip), %rax
并获取共享库使用的同一变量的值。其工作方式是,对于主程序引用但由共享库提供的所有对象类型符号,链接器在链接时查找符号的大小,并在可执行文件的 BSS 段中分配该空间。符号(在本例中为)被解析为指向该空间。g
g
在加载时,动态加载器会查找共享库,该库定义数据并将其从共享对象的数据段复制到主可执行文件的 BSS 段中保留的空间中,并将 的 GOT 条目解析为该地址。这称为副本重定位。因此,共享库在访问时将访问主程序可以访问的相同变量。(如果主程序不访问 ,则不会发生副本重定位,并在共享库的数据/BSS 段中解析为其定义)g
g
g
g
g
g
但是,仅当共享库通过 GOT 访问符号时,此方案才有效,因为符号不会与共享库一起重新定位。因此,您必须通过 GOT 才能访问该符号。
即做
movq g@GOTPCREL(%rip), %rax # find the address of g
movq (%rax), %rax # load the value of g
在程序运行时,地址不会更改,因此在代码开头执行一次就足够了。开销应该很低。g
解决方法
解决方法包括:
请考虑隐藏符号,并仅通过访问器函数将其公开给主可执行文件,并返回其地址。您可以使用映射文件(版本脚本)在一个位置设置库中所有函数的可见性,这可能比在源文件中注释符号更容易。
如果主可执行文件和库是否看到相同的符号地址(例如,如果它是一个常量)并不重要,则可以为符号提供隐藏的别名,并将其用于内部引用
您可以使用使共享库始终使用其自己的符号副本,即使它需要重新定位副本。请注意,这实际上禁用了在共享库和主可执行文件之间共享变量的功能。您也无法正确比较函数指针的相等性。因此,不应在生产中使用此选项。
-Bsymbolic
如果由于某种原因无法使用访问器函数,则可以通过指针绕行导出的符号,只允许在指针上进行复制重定位:
.bss .globl local_g .hidden local_g local_g: .space 8 .data .globl g g: .quad local_g
在主二进制文件中,声明为持有指向变量的指针,并取消引用它以访问变量。请考虑声明它,以便它不会被意外取消引用。请注意,此方法的性能比通过 GOT 从其他共享对象访问符号的性能更差。
g
const
您可以在编译所有程序部件和链接(共享库和主可执行文件)期间使用,以避免复制重新定位(您可能还需要将所有部件与 .请注意,此类编译的共享库与未使用此选项编译的主程序不兼容,并且不得链接到它们。反之亦然。
-mno-direct-extern-access
-Wz,nocopyreloc
然而,最好的选择是像使用任何其他全局符号一样通过 GOT。
评论
g
g
g
-mcmodel=medium
__attribute__((section(".largedata")))
.h
-z nocopyreloc
评论
.global
.hidden
.protected