无法解析 nasm 中的单词

Unable to parse a word in nasm

提问人:MC_Vovi 提问时间:9/24/2023 最后编辑:AndrewMC_Vovi 更新时间:9/25/2023 访问量:69

问:

我得到了一个函数描述:接受缓冲区地址和大小作为参数。从 stdin 读取下一个单词(将空格跳过到缓冲区中)。如果单词太大,则停止并返回 0 指定的缓冲区;否则返回缓冲区地址。此函数应以 null 结尾接受的字符串。但是当我尝试输入类似 or 的内容时,我编写的代码不起作用。输出为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`
Linux 调试 程序集 NASM

评论

1赞 Nassau 9/24/2023
嗯,可能是错误的操作。之前,在堆栈上。如果 ,程序跳转到并且有指令。 应该在之后,应该应用于 并且与有相同,弹出堆栈上的任何内容。push...popcall .whitespace_checkerraxrax=2.failretpop raxjz .whitespace_readerjzcmp rax,0jz .successret
0赞 MC_Vovi 9/24/2023
@Nassau,所以代码应该是这样的:“read_word:xor r8, r8 xor r9, r9 mov r9, rdi push rdi push rsi .whitespace_reader: call read_char mov rdi, rax push rdi call .whitespace_checker cmp rax, 2 pop rdi jz .fail cmp rax, 0 mov rax, rdi jz .whitespace_reader pop rsi pop rdi'?
2赞 Nassau 9/24/2023
如果你在堆栈上值,你应该这样做。如果您使用您应该在过程结束时使用。如果之后呢?程序将跳转到,并且有指令从堆栈中弹出返回地址,但在堆栈上没有地址,有.在其他情况下,嗯,应该没问题,我想。所以也许这就是这个错误的原因。我不知道该再添加什么,也许其他人会看到我看不到的东西。pushpopcallretcall .whitespace_checkerrax = 2.failretrsirax = 0 or 1segmentation fault (core dumped)

答:

1赞 Sep Roland 9/25/2023 #1

您的两个示例都只包含空格和终止零。.whitespace_reader将遍历空格,然后偶然发现终止零,它将跳转到 .fail。那里的指令将使用不平衡堆栈,因为返回地址被您推送的 RDI、RSI 和 RAX 寄存器的存在所阻塞。在您当前的代码中没有快速修复此问题,因为它包含其他问题。ret

  • 由于它是 、 、 、 、 和 ,并且是调用保留的寄存器,因此无法保证 R8 和 R9 寄存器在调用read_char后仍然有效。r12r13r14r15rbxrsprbp
  • 调用.whitespace_checker时,尝试遵循调用约定将第一个参数存储在 RDI 中会使问题复杂化。此.whitespace_checker是您自己的子例程,无论如何都可以在本地使用,因此使用 RAX 中已有的参数调用它是完全可以的。而且,也可以将结果返回到您认为方便的任何寄存器中,例如本例中的 RDX。
  • 由于.whitespace_checker返回三态结果 {0,1,2},因此您只需要一条指令即可涵盖所有可能的结果。cmp dl, 1
  • in .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

评论

0赞 MC_Vovi 9/25/2023
请在 .success 中将 dh 更改为 dl。完善!谢谢!