我学习AT&T x86汇编语言。 我试图写汇编程序它接受一个整数n,然后返回结果(N / 2 + N / 3 + N / 4)。 这是我做了什么:
.text
.global _start
_start:
pushl $24
call profit
movl %eax, %ebx
movl $1, %eax
int $0x80
profit:
popl %ebx
popl %eax
mov $0, %esi
movl $4, %ebp
div %ebp
addl %eax, %esi
movl %ecx, %eax
movl $3, %ebp
div %ebp
addl %eax, %esi
movl %ecx, %eax
movl $2, %ebp
div %ebp
addl %eax, %esi
movl %esi, %eax
cmpl %ecx, %esi
jg end
pushl %ebx
ret
end:
mov %ecx, %eax
ret
问题是我得到段错误。 问题出在哪儿?
我认为这里的代码失败:
_start:
pushl $24
call profit
movl %eax, %ebx
movl $1, %eax
int $0x80
profit:
popl %ebx
popl %eax
所以,你push $24
(4个字节),然后call profit
,这推动eip
,并跳转到profit
。 然后你弹出的价值eip
到ebx
和价值$24
到eax
。
然后,在最后,如果jg end
分支end:
,那么栈将不会持有有效的返回地址和ret
将失败。 你可能需要pushl %ebx
有太多。
cmpl %ecx, %esi
jg end
pushl %ebx
ret
end:
mov %ecx, %eax
; `pushl %ebx` is needed here!
ret
你似乎并没有被正确地做函数调用。 你需要阅读和理解的x86 ABI( 32位 , 64位 ),特别是“调用约定”部分。
此外,这是不是你的眼前问题,但:不要写_start
,写main
仿佛这是一个C程序。 当你开始做更复杂的东西,你会希望C库可用,这意味着你必须让它初始化。 与此相关的, 不要让自己的系统调用; 调用C库的包装。 那你隔离在内核接口的低层次的变化,保证了errno
是可用的,等等。
你的问题是,你弹出的返回地址从堆栈中的,当你转移到结束不还原它。 一个快速的解决办法是增加push %ebx
那里。
你应该做的是修改你的过程,以便正确地使用调用约定。 在Linux中,调用函数预计清理从堆栈中的参数,所以你的程序应该让他们身在何处。
而不是这样做是为了获取参数,然后再恢复的返回地址
popl %ebx
popl %eax
你应该这样做,并留下寄信人地址和论据他们在哪里
movl 4(%esp), %eax
和摆脱将返回地址返回到堆栈中的代码。 然后,您应该添加
subl $4, %esp
在调用程序后,删除从堆栈的说法。 它如果您希望能够从其他语言调用你的组装程序正确地遵循这个惯例是很重要的。
它看起来像你对我有一个pushl你打电话的利润,然后第一件事,利润确实是做两个popl等指令之前。 我希望这会弹出你压入堆栈的值以及返回代码,使您的RET是行不通的。
push和pop应该是相同的次数。
通话将返回地址压入堆栈。