我得到了一个函数描述:接受缓冲区地址和大小作为参数。从标准输入读取下一个单词(将空格跳过到缓冲区)。如果单词太大,则停止并返回 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`
您的两个示例都只包含空格加上终止零。 .whitespace_reader 将循环遍历空白,然后偶然发现终止零,它将跳转到 .fail。那里的
ret
指令将使用不平衡的堆栈,因为返回地址被您压入的 RDI、RSI 和 RAX 寄存器的存在所阻塞。在您当前的代码中没有快速解决此问题的方法,因为它包含其他问题。
r12
、r13
、r14
、r15
、rbx
、rsp
和 rbp
是调用保留寄存器,因此无法保证您的 R8 和 R9 寄存器将继续存在对 read_char 的调用。cmp dl, 1
指令即可涵盖所有可能的结果。中的
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