当前位置: 首页 > 面试题库 >

无法通过汇编(yasm)代码在64位Linux上调用C标准库函数

黄弘盛
2023-03-14
问题内容

我有一个foo用汇编语言编写的函数,并在64位Linux(Ubuntu)上使用yasm和GCC进行了编译。它只是使用来向stdout打印一条消息puts(),如下所示:

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程序调用:

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

./foo: Symbol `puts' causes overflow in R_X86_64_PC32 relocation
Segmentation fault

用objdump拆解后,我看到调用是用错误的地址进行的:

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

但是,如果我用C重写相同的代码,则调用将以不同的方式进行:

645:   e8 c6 fe ff ff          callq  510 <puts@plt>

即它puts来自PLT。

是否可以告诉yasm生成类似的代码?


问题答案:

0xe8操作码后面跟着一个符号偏移量被应用到PC(已经由时间推进到下一指令)来计算分支目标。因此objdump,将分支目标解释为0x671

YASM正在渲染零,因为它可能已在该偏移量上放置了重定位,这就是它要求加载程序puts在加载期间填充正确偏移量的方式。加载程序在计算重定位时遇到溢出,这可能表明它puts与您的调用之间的偏移量比32位带符号偏移量所表示的偏移量还大。因此,加载程序无法修复此指令,您将当机。

66c: e8 00 00 00 00显示未填充的地址。如果您在重定位表中查找,您应该在上看到重定位0x66d。汇编器使用全零的重定位填充地址/偏移量并不少见。

此页面提示YASM有一个WRT指令,可以控制使用.got.plt等等。

根据NASM文档上的
S9.2.5 ,看起来您可以使用CALL puts WRT ..plt(假定YASM具有相同的语法)。



 类似资料:
  • 问题内容: 我遇到了一个有趣的问题,我忘记了我正在使用64位计算机和操作系统,并编写了32位汇编代码。我不知道如何编写64位代码。 这是Linux上Gnu汇编程序(AT&T语法)的x86 32位汇编代码。 现在,此代码应该可以在32位处理器和32位操作系统上正常运行,对吗?众所周知,64位处理器与32位处理器向后兼容。因此,这也不是问题。由于在64位OS和32位OS中系统调用和调用机制存在差异,因

  • 在我的场景中,我使用CLANG/LLVM编译器和LLDB调试器。 由于某种未知的原因,我无法调试C++标准库。我可以单步执行应用程序中定义的符号,但无法执行标准符号,例如构造函数。 目前还不清楚这是不正确的配置还是这些工具的限制。在web上搜索,我注意到使用GNU工具链的C++标准库调试在Linux上确实工作得很好。 :

  • 就处理编译/链接错误而言,我是一个新手。 我正在使用一个很大的C++代码(还有一些C文件)。我已经成功地在Mac上运行了它,用G++编译。现在我需要在基于Linux的集群上运行它,因为它在我的Mac上太慢了。代码由我必须编译的几个库组成,加上我自己使用这些库的代码。 我可以使用默认的g++编译器编译集群上的所有代码。然而,不幸的是,我已经发现我需要用GCC/4.7.2进行编译,这样代码将与集群上的

  • 问题内容: 我有用32位汇编语言编写的程序…现在,我无法在64位OS上对其进行编译。在我们学校,它们是特定的,程序必须以32位版本编写。这是我的程序: 任何的想法?我尝试了很多方法来编译它。编译后输出错误: 输出: 问题答案: 首先将更改为并将符号更改为,然后使用链接目标文件,该文件将自动链接至该文件, 您需要这样做,因为AFAIK如果没有,就无法链接至libc。另外,在汇编时也应使用elf32而

  • 13.4. 通过cgo调用C代码 Go程序可能会遇到要访问C语言的某些硬件驱动函数的场景,或者是从一个C++语言实现的嵌入式数据库查询记录的场景,或者是使用Fortran语言实现的一些线性代数库的场景。C语言作为一个通用语言,很多库会选择提供一个C兼容的API,然后用其他不同的编程语言实现(译者:Go语言需要也应该拥抱这些巨大的代码遗产)。 在本节中,我们将构建一个简易的数据压缩程序,使用了一个G

  • 我正在使用VS代码来调试使用Windows Subsystem for Linux的'C'代码。我基本上想用一个基于linux的编译器来编译“C”代码,用于操作系统的课程。我已将Visual Studio代码上的默认终端设置为“wsl”。单击debug按钮时,我会得到以下错误“无法开始调试”。miDebuggerPath的值无效“我验证了'gdb'安装在Windows Subsystem for