从内联汇编调用函数时,针对符号的重定位R_X86_64_PC32

6

我正在尝试构建包含一些汇编函数调用其他函数的共享库。当我使用以下代码构建liba.so时:

void aFunc1()
{
}

asm(
    ".globl aFunc2\n\t"
    ".type aFunc2, @function\n\t"
    "aFunc2:\n\t"
    ".cfi_startproc\n\t"
    "call aFunc1\n\t" /* note here*/
    "ret\n\t"
    ".cfi_endproc\n\t"
);

命令行操作

gcc -o liba.so a.c -shared -fPIC

我遇到了一个错误。
/usr/bin/ld: /tmp/ccdGBiQv.o: relocation R_X86_64_PC32 against symbol `aFunc1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

它告诉我要使用选项-fPIC,但我已经使用了该选项!然而,我发现使用选项-Wl,-Bsymbolic可以成功编译。

gcc -o liba.so a.c -shared -fPIC -Wl,-Bsymbolic

很抱歉,当我尝试构建第二个库libb.so并使用汇编函数来调用第一个库中的函数时,问题再次出现。正在编译代码。
#include <a.h>

asm(
    ".globl bFunc2\n\t"
    ".type bFunc2, @function\n\t"
    "bFunc2:\n\t"
    ".cfi_startproc\n\t"
    "call aFunc1\n\t" /* note here*/
    "ret\n\t"
    ".cfi_endproc\n\t"
);

使用命令

gcc -o libb.so b.c liba.so -shared -fPIC -Wl,-Bsymbolic

出现错误

/usr/bin/ld: /tmp/ccaGvn5d.o: relocation R_X86_64_PC32 against symbol `aFunc1' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

因为可以看到,选项-Wl,Bsymbolic没有起到作用。

我想知道如何构建第二个库以及为什么第一个库需要-Wl,Bsymbolic选项。我不是汇编大师,因此不知道是否正确 - 我正在尝试构建别人的库。也许我应该使用其他选项?


"ld"的“重新使用'-fPIC'编译”的消息是一个建议,如果汇编代码来自编译器输出而不是手写,则可以采纳。 - Peter Cordes
跨共享对象边界的函数调用(从一个库或可执行文件到另一个库)需要使用某种间接方式:共享对象可以在虚拟地址空间中相隔超过2GiB,因此call rel32并不总是有效。此外,ASLR和其他因素意味着位移不能是链接时常量,并且链接器告诉您运行时修复(编辑机器代码以在动态链接时调整位移)不可用。这就是为什么Linux动态链接不设计允许这样做的2GiB限制。 - Peter Cordes
1个回答

3

由于函数可能被拦截(使调用不再是直接的,具有固定偏移量),因此您需要调用PLT存根之一:

call aFunc1@plt

通过使用-Bsymbolic,链接器将把这个调用转换成对aFunc1的直接调用(避免了双重间接性)。
也可以通过GOT调用函数,类似于PLT存根的方式:
jmp *aFunc1@GOTPCREL(%rip)

或者您可以将函数隐藏起来:

.hidden aFunc1
jmp aFunc1

请注意,这将隐藏定义,因此该函数不再被导出。要模拟单个符号的-Bsymbolic行为,您可以使用隐藏别名:
.set aFunc1Alias, aFunc1
.hidden aFunc1Alias
jmp aFunc1Alias

使用隐藏函数和隐藏别名,调用将始终转到同一个函数,即不再可能在此特定调用站点插入符号。

@plt 添加到函数调用中是可行的,但如果可能的话,我不想更改汇编代码。我注意到我正在尝试构建的库通过令人讨厌的宏在 libb.so 中添加了 __attribute__((visibility ("hidden")))aFunc1 声明,并且在 liba.so 中没有添加任何内容。是否可以通过这种方式实现?当我在我的最小示例中这样做时,它仍然在 libb.so 中存在重定位问题。 - Mateusz Drost
__attribute__((visibility ("hidden"))) 会导致GCC发出一个.hidden指令,因此它非常相似。但是,如果它只包含在头文件中并且函数没有从C中调用,则在汇编器级别可能无效。 - Florian Weimer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接