无法通过汇编(yasm)代码在64位Linux上调用C标准库函数 [英] Can't call C standard library function on 64-bit Linux from assembly (yasm) code
问题描述
我有一个用汇编语言编写的函数foo
,并在yasm和GCC(在Linux(Ubuntu)64位)上进行了编译.它只是使用puts()
将一条消息打印到stdout,如下所示:
I have a function foo
written in assembly and compiled with yasm and GCC on Linux (Ubuntu) 64-bit. It simply prints a message to stdout using puts()
, here is how it looks:
bits 64
extern puts
global foo
section .data
message:
db 'foo() called', 0
section .text
foo:
push rbp
mov rbp, rsp
lea rdi, [rel message]
call puts
pop rbp
ret
由使用GCC编译的C程序调用:
It is called by a C program compiled with GCC:
extern void foo();
int main() {
foo();
return 0;
}
构建命令:
yasm -f elf64 foo_64_unix.asm
gcc -c foo_main.c -o foo_main.o
gcc foo_64_unix.o foo_main.o -o foo
./foo
问题出在这里
在运行程序时,它会显示一条错误消息,并在调用puts
期间立即出现段错误:
When running the program it prints an error message and immediately segfaults during the call to puts
:
./foo: Symbol `puts' causes overflow in R_X86_64_PC32 relocation
Segmentation fault
在与objdump拆卸后,我看到调用是用错误的地址进行的:
After disassembling with objdump I see that the call is made with the wrong address:
0000000000000660 <foo>:
660: 90 nop
661: 55 push %rbp
662: 48 89 e5 mov %rsp,%rbp
665: 48 8d 3d a4 09 20 00 lea 0x2009a4(%rip),%rdi
66c: e8 00 00 00 00 callq 671 <foo+0x11> <-- here
671: 5d pop %rbp
672: c3 retq
(671是下一条指令的地址,而不是puts
的地址)
(671 is the address of the next instruction, not address of puts
)
但是,如果我用C重写相同的代码,则调用将以不同的方式完成:
However, if I rewrite the same code in C the call is done differently:
645: e8 c6 fe ff ff callq 510 <puts@plt>
即它引用了PLT中的puts
.
i.e. it references puts
from the PLT.
是否可以告诉yasm生成类似的代码?
Is it possible to tell yasm to generate similar code?
推荐答案
0xe8
操作码后跟一个带符号的偏移量,该偏移量将应用于PC(到那时已前进到下一条指令)以计算分支目标.因此,objdump
将分支目标解释为0x671
.
The 0xe8
opcode is followed by a signed offset to be applied to the PC (which has advanced to the next instruction by that time) to compute the branch target. Hence objdump
is interpreting the branch target as 0x671
.
YASM正在渲染零,因为它可能已在该偏移量上进行了重定位,这就是它要求加载程序在加载期间为puts
填充正确的偏移量的方式.加载程序在计算重定位时遇到溢出,这可能表明puts
与您的调用之间的偏移量比32位带符号偏移量所代表的偏移量还大.因此,加载程序无法修复此指令,您会崩溃.
YASM is rendering zeros because it has likely put a relocation on that offset, which is how it asks the loader to populate the correct offset for puts
during loading. The loader is encountering an overflow when computing the relocation, which may indicate that puts
is at a further offset from your call than can be represented in a 32-bit signed offset. Hence the loader fails to fix this instruction, and you get a crash.
66c: e8 00 00 00 00
显示未填充的地址.如果查看重定位表,则应该在0x66d
上看到重定位.汇编器通常使用全零的重定位来填充地址/偏移量,这种情况并不罕见.
66c: e8 00 00 00 00
shows the unpopulated address. If you look in your relocation table, you should see a relocation on 0x66d
. It is not uncommon for the assembler to populate addresses/offsets with relocations as all zeros.
此页面建议YASM具有一个WRT
指令,可以控制.got
,.plt
等的使用.
This page suggests that YASM has a WRT
directive that can control use of .got
, .plt
, etc.
根据 NASM文档上的S9.2.5,您似乎可以使用CALL puts WRT ..plt
(假设YASM具有相同的语法).
Per S9.2.5 on the NASM documentation, it looks like you can use CALL puts WRT ..plt
(presuming YASM has the same syntax).
这篇关于无法通过汇编(yasm)代码在64位Linux上调用C标准库函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!