bor*_*ree 5 x86 segmentation-fault fault
我已经使用nasm编译器和ld链接器用汇编语言编写了一个hexdump实用程序.程序应该转储任何输入文件的十六进制值.但是它在一个特定的过程"LoadBuff"中进行了段错误.loadbuff的功能是读取输入到一个16字节的缓冲区.代码是
LoadBuff:
push ebx
push edx
push eax
mov eax,3 ;sys_read call
mov ebx,0 ;read from standard input
mov ecx,Buff ;pass the buffer adress
mov edx,BuffLen ;pass the number of bytes to be read at a time
int 80h ;call the linux kernel
mov ebp,eax
;cmp eax,0 ;number of characters read is returned in eax
;jz exit ;if zero character is returned i.e end of iinput file
;jump to exit
xor ecx,ecx
pop eax
pop edx
pop ebx
ret
Run Code Online (Sandbox Code Playgroud)
如果行
;cmp eax,0
;jz exit
Run Code Online (Sandbox Code Playgroud)
取消注释代码运行正常,没有任何seg错误.但是,当我评论它并在调用者中包含这些行以便在调用者中进行相同的比较而不是在这里时,此过程会出错.
gdb backtrace给出
#0 0x00000000 in ?? ()
Run Code Online (Sandbox Code Playgroud)
知道为什么会这样吗?
您正在使用 NASM,但未指定您使用的是 Intel 风格语法还是 AT&T 风格语法。然而,看看你的示例代码,我猜它是英特尔风格的。
在 Intel 风格的语法中,类似的操作mov
如下所示:
mov <destination>, <source>
Run Code Online (Sandbox Code Playgroud)
换句话说,他们试图模仿“目的地=来源”的思维方式。在 AT&T 语法中,它是相反的:
mov <source>, <destination>
Run Code Online (Sandbox Code Playgroud)
换句话说,他们认为您正在阅读“将源移动到目的地”。
现在看看这行代码:
mov ebp, eax
Run Code Online (Sandbox Code Playgroud)
如果您使用的是 Intel 风格的语法(我认为您是因为 AT&T 风格的语法是mov %ebp, %eax
),那么您会将寄存器的内容eax
移至ebp
. ebp
传统上用作“基指针”...请注意那里的“指针”一词...并且通常完全如此使用。当您在 eax 中得到 0 时,您将用空指针覆盖现有的基指针。古怪的狂欢随之而来。
然而,这并不是唯一的问题。进一步的问题是:
jz exit
Run Code Online (Sandbox Code Playgroud)
我在您发布的代码中的任何地方都没有看到退出标签,因此您正在跳转到程序之外的某个地方(否则汇编器会发出呜呜声)。在此过程中,您将传递堆栈清理代码,使堆栈处于未知状态。您基本上已将三个寄存器的内容压入堆栈,并将它们留在其他例程不需要的地方。
问题是您跳过了清理代码。在程序开始时,您将推动ebx
、edx
和eax
。在程序结束时,您可以正确地以相反的顺序弹出它们(eax
、edx
和ebx
)。这使得堆栈在退出时处于与进入时相同的状态,并且依赖于此的代码被设置为按预期运行。
然而,这jz
会跳过该点,因此无论您走到哪里,堆栈上都会有三个不应该存在的值。你必须跳到你的清理代码,而不是过去它。
一般规则始终是在程序中弹出您推入的内容。这条规则也有(极少数)例外情况,但它们出现的频率还不足以让你现在分心。