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

9lowa7mx  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(155)

我现在正在学习Gnu汇编,试图读字符串,但是是错误的。什么问题?

.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
of1yzvn4

of1yzvn41#

我看到的错误:

  • 堆栈管理。你不能对程序入口时已经在堆栈上的数据做任何假设,也不能假设有多少空间可用。而且你不能写在%esp中当前地址的下面;例如,信号处理程序可能会在任何时候意外地覆盖它,所以你需要从%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寄存器推断出来的,但由于您在其余的cod中使用了后缀,因此最好保持一致。)
  • 您在这里写的是32位代码,所以一定要在32位模式下构建程序。例如,如果使用gcc,您需要-m32标志。从长远来看,您可能更愿意学习64位x86汇编; 32位x86代码已经过时了。
  • 实际上,通过搜索换行符来计算输入的长度(0xa)首先并不合适。如果输入中根本不包含换行符(这在一行长度超过30个字节的情况下是可能的),那么您的循环将运行到缓冲区的末尾。要了解读取了多少个字符,您应该使用read的返回值。在read系统调用返回后,它留在%eax中。(如果它为零,则到达文件结尾;如果是负数,则表示有错误。)

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

相关问题