繁体   English   中英

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

[英]Segmentation fault (core dumped) in x86 assembly

我编写了一个 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?

欢迎任何帮助。

我看到的错误:

  • 堆栈管理。 您不能假设程序入口时堆栈中已有的数据,也不能假设有多少可用空间。 而且你不能在%esp的当前地址下面写; 例如,信号处理程序可以随时意外地覆盖它。 所以你需要从%esp中减去为你的缓冲区分配空间,然后在完成后加回去。

  • 此外, %esp应始终保持 4 字节对齐。 这不是严格的架构要求,但打破这个规则会导致执行效率低下和很多混乱。 因此,要为 30 字节缓冲区创建空间,请向上取整并从%esp中减去 32。

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

  • 由于上述两个原因,不要在循环中使用%esp作为指针变量:不要管它,选择其他寄存器。

  • 操作数大小。 x86-32 指令通常可以在 8、16 或 32 位上运行。 l后缀和/或 32 位寄存器(eax、ebx 等)的使用表示 32 位指令。 所以mov (%esp), %ebx从 memory 加载 4 个字节,并且cmp $0xa, %ebx将它们与 32 位值0x0000000a进行比较。 因此,比较将是错误的,除非 memory 中接下来的三个字节恰好全为零。 要获得 8 位操作,请使用 8 位寄存器(al、bl、ah、bh 等),但要注意它们与相应的 16 位和 32 位寄存器重叠; 所以不要尝试同时使用%ebx%bl做不同的事情。 试试movb (%reg), %bl (如上所述, %reg不应该是%esp而是你使用的任何寄存器)和cmpb $0xa, %bl b后缀是可选的,因为大小是从 8 位bl寄存器推断出来的,但是由于您在 cod 的大部分 rest 中使用后缀,所以最好保持一致。)

  • 您在这里编写的是 32 位代码,所以一定要在 32 位模式下构建您的程序。 例如,如果使用 gcc,则需要-m32标志。 从长远来看,您可能更愿意学习 64 位 x86 汇编; 32 位 x86 代码几乎已过时。

  • 实际上,首先通过搜索换行符 (0xa) 来计算输入的长度并不合适。 如果输入根本不包含换行符(如果该行的长度超过 30 个字节,这是可能的),那么您的循环将从缓冲区的末尾运行。 要找出读取了多少个字符,您应该使用read的返回值,它在 read 系统调用返回后留在%eax中。 (如果为零,则到达文件末尾;如果为负,则出现错误。)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM