无法通过汇编(yasm)代码在64位Linux上调用C标准库函数 [英] Can't call C standard library function on 64-bit Linux from assembly (yasm) code

查看:122
本文介绍了无法通过汇编(yasm)代码在64位Linux上调用C标准库函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用汇编语言编写的函数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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆