我得到了一个函数描述:接受缓冲区地址和大小作为参数.从标准输入中读取下一个单词(跳过空格进入缓冲区).如果单词太大,则停止并返回0 指定的缓冲区;否则返回缓冲区地址.此函数应对接受的字符串进行空终止.但当我try 输入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`

推荐答案

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

  • 因为r12r13r14r15rbxrsprbp是调用保留寄存器,所以不能保证您的R8和R9寄存器在调用read_char之后仍然有效.
  • 当调用.whitespace_checker时,您试图遵循调用约定将第一个参数存储在RDI中,这会使问题复杂化.这.whitespace_checker是您自己的子 routine ,并且无论如何都是在本地使用的,所以只使用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

Linux相关问答推荐

Linux在所有多行中用新值替换整个列

如何查明第三方是否杀死或 destruct 了 C++ 中的程序

有没有办法确定什么代码使 linux 共享对象inflating ?

从父目录中删除目录而不删除父目录 (Linux)

操作系统信号处理循环 - 阻塞或非阻塞读取?

使用 bash 在包含模式的 java 文件中查找行,然后替换该行的另一部分

如何使用 sed debug调试正则表达式?

如何使用 GDB 和 QEMU 调试 Linux 内核?

每次来宾重新启动后 Vagrant 执行脚本或命令(vagrant up)

如何使用不同的出口 IP 一次运行多个 Tor 进程?

如何在 linux 'screen' 中搜索任何单词

模拟器在内核映像文件中找不到Linux 版本字符串

你如何在 C 中的 Linux 上进行非阻塞控制台 I/O?

bash 中的线程?

如何使用 Ansible 等待服务器重启?

在 mac 上通过 ssh 连接到 amazon aws linux 服务器

如何在 Linux 上取消关机?

根据日期范围过滤日志(log)文件条目

rename() 是原子的吗?

什么是 linux 脚本中的 start-stop-daemon?