assembly x86程序集中的分段错误(核心转储)

mpbci0fu  于 2023-01-02  发布在  其他
关注(0)|答案(1)|浏览(181)

我编写了一个x86(IA-32)汇编程序,该程序应该从标准输入读取字符串,但无法理解为什么会导致SEGFAULT。
我用GNU汇编器汇编了这个程序,使用了以下标志:

$ gcc (flags used) (file_name)

下面是该程序的代码:

.text

.globl _start

MAX_CHAR=30

_start:

    ## Start message ##
    movl $4, %eax
    movl $1, %ebx
    movl $msg, %ecx
    movl $len, %edx
    int $0x80

    ## READ ##
    movl $3, %eax       #sys_read (number 3)
    movl $0, %ebx       #stdin (number 0)
    movl %esp, %ecx     #starting point
    movl $MAX_CHAR, %edx    #max input
    int $0x80       #call

    ## Need the cycle to count input length ##  
    movl $1, %ecx       #counter
end_input:
    xor %ebx, %ebx
    mov (%esp), %ebx
    add $1, %esp        #get next char to compare 
    add $1, %ecx        #counter+=1
    cmp $0xa, %ebx      #compare with "\n" 
    jne end_input       #if not, continue 

    ## WRITE ##
    sub %ecx, %esp      #start from the first input char
    movl $4, %eax       #sys_write (number 4)
    movl $1, %ebx       #stdout (number 1)
    movl %ecx, %edx     #start pointer
    movl %esp, %ecx     #length
    int $0x80       #call
     

    ## EXIT ##
    movl $1, %eax
    int $0x80   

.data

msg: .ascii "Insert an input:\n"
len =.-msg

什么原因导致SEGFAULT?
任何帮助都是受欢迎的。

sqougxex

sqougxex1#

我看到的错误:

  • 栈管理。你不能对程序进入时栈上已有的数据做任何假设,也不能对栈上有多少空间做任何假设。而且你不能写在%esp中当前地址的下面;例如,信号处理程序可以在任何时候意外地覆盖它。2所以你需要从%esp中减去,为你的缓冲区分配空间,然后在完成后加回来。
  • 此外,%esp应该始终保持4字节对齐。这不是严格的体系结构要求,但违反此规则将导致低效执行和大量混乱。因此,要为30字节缓冲区创建空间,请向上舍入并从%esp中减去32。

当你想调用用C编写的函数时,还有额外的对齐要求,参见gcc x86-32 stack alignment and calling printf

  • 出于上述两个原因,不要在循环中使用%esp作为指针变量:不要管它,选择其他寄存器。
  • 操作数大小。x86-32指令通常可以在8、16或32位上操作。l后缀和/或使用32位寄存器(eax、ebx等)发出32位指令的信号,因此mov (%esp), %ebx从内存中加载4个字节,和cmp $0xa, %ebx将它们与32位值0x0000000a进行比较。因此,除非存储器中的下三个字节恰好都为零,否则比较将是错误的。要获得8位操作,请使用8位寄存器(a1、b1、ah、bh等),但要注意它们与相应的16位和32位寄存器重叠;所以不要尝试同时使用%ebx%bl来做不同的事情。(如上所述,%reg不应是%esp,而应是您使用的任何寄存器)和cmpb $0xa, %blb后缀是可选的,因为其大小是从8位bl寄存器推断出来的,但由于你在代码的其余部分都使用后缀,所以最好保持一致。)
  • 你在这里写的是32位代码,所以一定要在32位模式下构建你的程序。例如,如果使用gcc,你需要-m32标志。从长远来看,你可能更愿意学习64位x86汇编; 32位x86代码已经过时了。
  • 实际上,通过搜索换行符来计算输入的长度(0xa)首先就不合适,如果输入根本不包含换行符(如果行的长度超过30个字节就有可能出现这种情况),那么循环将运行到缓冲区的末尾,要了解读取了多少字符,应该使用read的返回值,在read系统调用返回之后,它被留在%eax中。(如果它是零,则到达文件结尾;如果为负数,则存在错误。)

此外,如果在默认模式下从终端阅读,通常一次最多只能读取一行,因此如果存在\n,它将对应于read返回的输入的结尾(但如果标准输入是从文件重定向的,则这不适用)。

相关问题