我使用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
若行
;cmp eax,0
;jz exit
没有注释代码运行正常,没有任何seg错误。但是,当我对它进行注释并在调用者中包含这些行以便在调用者中进行相同的比较而不是在这里时,此过程会出错。
gdb backtrace给出
#0 0x00000000 in ?? ()
任何想法为什么会这样发生?
答案 0 :(得分:1)
您正在使用NASM但是您没有指定您是否使用英特尔风格的语法或AT& T风格的语法。但是,看看你的示例代码,我猜它是英特尔式的。
在英特尔风格的语法中,mov
等操作的工作原理如下:
mov <destination>, <source>
换句话说,他们试图模仿&#34; destination = source&#34;思维方式。在AT&amp; T语法中,它采用另一种方式:
mov <source>, <destination>
他们认为,换句话说,好像你正在阅读&#34;将来源移动到目的地&#34;。
现在看看这行代码:
mov ebp, eax
如果你正在使用英特尔风格的语法(我认为你是因为AT&amp; T风格的语法是mov %ebp, %eax
),那么你移动寄存器{{1}的内容进入eax
。 ebp
传统上用作&#34;基指针&#34; ...注意单词&#34;指针&#34;那里......并且经常被用作那样。当您在eax中获得0时,您将使用空指针覆盖现有的基指针。随之而来的是古怪的人。
ebp
我在您发布的代码中的任何位置都没有看到退出标签,因此您在某个程序之外会跳过某个地方(否则汇编程序会发出抱怨)。在此过程中,您将通过堆栈清理代码,使堆栈处于未知状态。你基本上把三个寄存器的内容压到了堆栈上,然后把它们放在那些其他例程没有预料到的地方。
问题在于您跳过了清理代码。在您的程序开始时,您需要推送jz exit
,ebx
和edx
。在您的过程结束时,您可以按相反的顺序正确弹出它们(eax
,eax
和edx
)。这使得堆栈在退出时处于与进入时相同的状态,并且依赖于此的代码将设置为按预期运行。
然而,ebx
跳过这一点,所以无论你在哪里,堆栈上都有三个值不应该存在。你必须跳过到你的清理代码,而不是过去。
一般规则总是弹出您在程序中推送的内容。这个规则存在(极少数)例外情况,但它们不会经常发生,足以让你现在分散注意力。