无法解析nasm中的单词

问题描述 投票:0回答:1

我得到了一个函数描述:接受缓冲区地址和大小作为参数。从标准输入读取下一个单词(将空格跳过到缓冲区)。如果单词太大,则停止并返回 0 指定缓冲区;否则返回缓冲区地址。该函数应该以空值终止接受的字符串。但是当我尝试输入

echo -n "" | ./read_word
echo -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 debugging assembly nasm
1个回答
0
投票

您的两个示例都只包含空格加上终止零。 .whitespace_reader 将循环遍历空白,然后偶然发现终止零,它将跳转到 .fail。那里的

ret
指令将使用不平衡的堆栈,因为返回地址被您压入的 RDI、RSI 和 RAX 寄存器的存在所阻塞。在您当前的代码中没有快速解决此问题的方法,因为它包含其他问题。

  • 由于
    r12
    r13
    r14
    r15
    rbx
    rsp
    rbp
    是调用保留寄存器,因此无法保证您的 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 [r12], dh ; DH=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 rdx, rdx ; 0 == Whitespace cmp rax, 32 je .ret cmp rax, 9 je .ret cmp rax, 10 je .ret inc rdx ; 1 == Normal character test rax, rax jnz .ret inc rdx ; 2 == Terminating zero .ret: ret .fail: xor rax, rax .done: pop r13 ; Restore call-preserved registers pop r12 pop rbx ret
    
© www.soinside.com 2019 - 2024. All rights reserved.