首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GCC提出了奇怪的重定位R_X86_64_32S错误,但不是手动链接

GCC提出了奇怪的重定位R_X86_64_32S错误,但不是手动链接
EN

Stack Overflow用户
提问于 2021-05-15 19:02:11
回答 2查看 170关注 0票数 1

我正在尝试通过Linux环境下的syscall指令在一台x86_64机器上调用sys_write,其中包含C文件中的内联汇编。为此,我编写了以下代码:

代码语言:javascript
复制
[tjm@ArchPad poc]$ cat poc.c 
const char S[] = "abcd\n";

int main() {
    __asm __volatile(" \
            movq $1, %%rax; \
            movq $1, %%rdi; \
            movq %0, %%rsi; \
            movq $5, %%rdx; \
            syscall;"
            :
            : "i"(S)
            );
    return 0;
}

当我和GCC一起编译的时候,出现了以下错误:

代码语言:javascript
复制
[tjm@ArchPad poc]$ gcc -O0 poc.c -o poc
/usr/bin/ld: /tmp/ccWYxmSb.o: relocation R_X86_64_32S against symbol `S' can not be used when making a PIE object; recompile with -fPIE
collect2: error: ld returned 1 exit status

然后,我将-fPIE添加到编译器命令中。不幸的是,一切都没有改变:

代码语言:javascript
复制
[tjm@ArchPad poc]$ gcc -fPIE -O0 poc.c -o poc 
/usr/bin/ld: /tmp/ccdK36lx.o: relocation R_X86_64_32S against symbol `S' can not be used when making a PIE object; recompile with -fPIE
collect2: error: ld returned 1 exit status

这非常奇怪,这让我好奇地想手动编译和链接它:

代码语言:javascript
复制
[tjm@ArchPad poc]$ gcc -fPIE -O0 poc.c -S
[tjm@ArchPad poc]$ as -O0 poc.s -o poc.o
[tjm@ArchPad poc]$ ld -O0 -melf_x86_64 --dynamic-linker /usr/lib/ld-linux-x86-64.so.2 /usr/lib/crt{1,i,n}.o -lc poc.o -o poc

令人惊讶的是,在上面的尝试过程中没有发生错误。然后我测试了可执行文件的功能,这似乎是有效的:

代码语言:javascript
复制
[tjm@ArchPad poc]$ ./poc 
abcd

有没有想过为什么会发生这种情况,以及如何防止GCC推广上面的错误?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-05-16 00:55:27

立即移动指令movq $S, %rsi尽管采用64位寄存器,但仅采用32位带符号立即数。在构建与位置无关的可执行文件并使用ASLR运行时(这是大多数Linux系统上的默认设置),您的程序通常不位于虚拟内存的低或高31位中,因此像S这样的全局或静态对象的地址不适合有符号的32位立即数。

一种修复方法是使用movabs,它需要完整的64位立即数。用movabs %0, %%rsi替换movq %0, %%rsi可以构建代码。但是,更好的方法是使用RIP-relative addressing lea S(%rip), %rsi,这是一条较短的指令,避免了在加载时重新定位的需要。在内联asm中做这件事有点笨拙,所以你可以让编译器用"S" (S)这样的输入操作数为你加载地址到寄存器中(令人困惑的是,rsi寄存器的约束是S,它恰好与你为数组变量选择的名称一致)。

当您手动链接时,您构建了一个非位置独立的可执行文件,这意味着将地址视为常量是有效的。通过将-no-pie传递给gcc,也可以获得相同的效果。

您的代码还有另一个严重的问题,因为它没有声明它遇到的寄存器--不仅是显式mov到的寄存器,还包括syscall隐式修改的rcxr11。你应该看看How to invoke a system call via syscall or sysenter in inline assembly?,它有一个正确的例子。仔细研究这些例子是明智的-- GCC内联asm功能强大,但也很容易以编译器无法帮助你检测的微妙方式出错,可能在简单的程序中看起来有效,但在更复杂的程序中却无法预测地失败。

票数 1
EN

Stack Overflow用户

发布于 2021-05-15 22:50:15

对于ASM指令,GCC支持多种方言,默认值可能会有所不同。点击此处阅读更多信息:

代码语言:javascript
复制
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm

..。当然,这些方言彼此并不兼容。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67545995

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档