linux 无法分析nasm中的单词

nqwrtyyt  于 2023-10-16  发布在  Linux
关注(0)|答案(1)|浏览(98)

我得到了一个函数描述:接受缓冲区地址和大小作为参数。从标准输入中读取下一个单词(跳过空白进入缓冲区)。如果字对于指定的缓冲区来说太大,则停止并返回0;否则返回缓冲区地址。此函数应将接受的字符串空终止。但是我写的代码不工作,当我试图输入像echo -n "" | ./read_wordecho -n "\t " | ./read_word。输出是

[3]    40467 done                              echo -n "\t   " | 
       40468 segmentation fault (core dumped)  ./read_word

我的代码中可能有什么错误?
这就是:

read_word:
    xor r8, r8
    xor r9, r9
    mov r9, rdi
    push rdi
    push rsi
    .whitespace_reader:
        call read_char
        
        mov rdi, rax
        push rax
        call .whitespace_checker
        cmp rax, 2
        jz .fail
        
        cmp rax, 0
        pop rax

        jz .whitespace_reader
    
    pop rsi
    pop rdi
         
    .word_reader:
        inc r8
        cmp r8, rsi
        jnb .fail              ; если >=, фейлим, иначе пишем в буфер
        mov byte [rdi], al
        inc rdi

        push rdi
        push rsi
        
        call read_char

        pop rsi
        pop rdi
        push rdi
        mov rdi, rax
        
        push rax
        call .whitespace_checker
        cmp rax, 0
        pop rax
        pop rdi
        jz .success

        jmp .word_reader

    ; возвращает 0, если символ пробельный, 1 если непробельный и 2, если нуль-терминатор
    .whitespace_checker:
        mov rax, rdi
        cmp rax, 0x20
        jz .ret_0
        cmp rax, 0x9
        jz .ret_0
        cmp rax, 0xA
        jz .ret_0
        cmp rax, 0
        jz .ret_2
        
        mov rax, 1
        ret 

        .ret_0:
            mov rax, 0
            ret

        .ret_2:
            mov rax, 2
            ret
    
    .fail:
        xor rax, rax
        ret
        
    .success:
        inc rdi
        mov byte [rdi], 0
        mov rax, r9
        mov rdx, r8
        ret`
ajsxfq5m

ajsxfq5m1#

你的两个例子都只包含空格和一个终止零。.whitespace_reader 将循环遍历空白,然后偶然发现终止零,它将跳转到 .fail。这里的ret指令将使用不平衡堆栈,因为返回地址被您压入的RDI、RSI和RAX寄存器阻塞。在您当前的代码中没有快速解决这个问题的方法,因为它包含其他问题。

  • 由于r12r13r14r15rbxrsprbp是保留调用的寄存器,因此不能保证R8和R9寄存器在调用 read_char 后仍然有效。
  • 当调用 .whitespace_checker 时,您试图遵循调用约定将第一个参数存储在RDI中,这会使事情变得复杂。这个 .whitespace_checker 是您自己的子例程,并且在本地使用,所以使用RAX中已有的参数调用它完全没有问题。在任何您觉得方便的寄存器中返回结果也是可以的,比如本例中的RDX。
  • 由于 .whitespace_checker 返回一个三态结果{0,1,2},因此您只需要一条cmp dl, 1指令就可以覆盖所有可能的结果。
  • .success 中的inc rdi会在缓冲区中留下一个未使用的字节,并可能将新的终止零放在缓冲区之外!

这是我的重写,包含了以上所有内容:

; IN (rdi,rsi) OUT (rax,rdx)
read_word:
    push rbx                  ; Save call-preserved registers
    push r12
    push r13
    xor  ebx, ebx             ; WordLength = 0
    mov  r12, rdi             ; BufferAddress
    mov  r13, rsi             ; BufferSize

.whitespace_reader:           ; Skip leading whitespace
    call read_char            ; -> RAX
    call .whitespace_checker  ; -> RDX={0,1,2}
    cmp  dl, 1
    jb   .whitespace_reader   ; 0 == Whitespace
    ja   .fail                ; 2 == Terminating zero
                              ; 1 == Normal character
.word_reader:
    inc  rbx
    cmp  rbx, r13
    jnb  .fail 
    mov  [r12], al
    inc  r12
    call read_char            ; -> RAX
    call .whitespace_checker  ; -> RDX={0,1,2}
    cmp  dl, 1
    je   .word_reader         ; 1 == Normal character
.success:
    mov  byte [r12], 0        ; New terminating-zero
    mov  rax, r12             ; WordAddress to RAX
    sub  rax, rbx
    mov  rdx, rbx             ; WordLength to RDX
    jmp  .done

; IN (rax) OUT (rdx)
.whitespace_checker:
    xor  edx, edx             ; 0 == Whitespace
    cmp  al, 32
    je   .ret
    cmp  al, 9
    je   .ret
    cmp  al, 10
    je   .ret
    inc  edx                  ; 1 == Normal character
    test al, al
    jnz  .ret
    inc  edx                  ; 2 == Terminating zero
.ret:
    ret
    
.fail:
    xor  eax, eax
.done:
    pop  r13                  ; Restore call-preserved registers
    pop  r12
    pop  rbx
    ret

相关问题