我有一个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