在汇编语言段错误(Segmentation Fault in Assembly Language)

2019-09-23 01:08发布

我学习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

问题是我得到段错误。 问题出在哪儿?

Answer 1:

我认为这里的代码失败:

_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 。 然后你弹出的价值eipebx和价值$24eax

然后,在最后,如果jg end分支end: ,那么栈将不会持有有效的返回地址和ret将失败。 你可能需要pushl %ebx有太多。

    cmpl %ecx, %esi
    jg end
    pushl %ebx
    ret

end:
    mov %ecx, %eax
    ; `pushl %ebx` is needed here!
    ret


Answer 2:

你似乎并没有被正确地做函数调用。 你需要阅读和理解的x86 ABI( 32位 , 64位 ),特别是“调用约定”部分。

此外,这是不是你的眼前问题,但:不要写_start ,写main仿佛这是一个C程序。 当你开始做更复杂的东西,你会希望C库可用,这意味着你必须让它初始化。 与此相关的, 不要让自己的系统调用; 调用C库的包装。 那你隔离在内核接口的低层次的变化,保证了errno是可用的,等等。



Answer 3:

  1. 你用ecx而没有明确地初始化它(我不知道如果Linux将保证状态ecx过程开始时-看起来像它的0在实践中,如果没有规则)
  2. 当程序需要的jg end接近过程结束跳跃,返回地址是在栈上不再,所以ret将控制转移到一些垃圾地址。


Answer 4:

你的问题是,你弹出的返回地址从堆栈中的,当你转移到结束不还原它。 一个快速的解决办法是增加push %ebx那里。

你应该做的是修改你的过程,以便正确地使用调用约定。 在Linux中,调用函数预计清理从堆栈中的参数,所以你的程序应该让他们身在何处。

而不是这样做是为了获取参数,然后再恢复的返回地址

popl %ebx
popl %eax

你应该这样做,并留下寄信人地址和论据他们在哪里

movl 4(%esp), %eax

和摆脱将返回地址返回到堆栈中的代码。 然后,您应该添加

subl $4, %esp

在调用程序后,删除从堆栈的说法。 它如果您希望能够从其他语言调用你的组装程序正确地遵循这个惯例是很重要的。



Answer 5:

它看起来像你对我有一个pushl你打电话的利润,然后第一件事,利润确实是做两个popl等指令之前。 我希望这会弹出你压入堆栈的值以及返回代码,使您的RET是行不通的。

push和pop应该是相同的次数。

通话将返回地址压入堆栈。



文章来源: Segmentation Fault in Assembly Language