提问人:MC_Vovi 提问时间:9/24/2023 最后编辑:AndrewMC_Vovi 更新时间:9/25/2023 访问量:69
无法解析 nasm 中的单词
Unable to parse a word in nasm
问:
我得到了一个函数描述:接受缓冲区地址和大小作为参数。从 stdin 读取下一个单词(将空格跳过到缓冲区中)。如果单词太大,则停止并返回 0
指定的缓冲区;否则返回缓冲区地址。此函数应以 null 结尾接受的字符串。但是当我尝试输入类似 or 的内容时,我编写的代码不起作用。输出为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`
答:
1赞
Sep Roland
9/25/2023
#1
您的两个示例都只包含空格和终止零。.whitespace_reader将遍历空格,然后偶然发现终止零,它将跳转到 .fail。那里的指令将使用不平衡堆栈,因为返回地址被您推送的 RDI、RSI 和 RAX 寄存器的存在所阻塞。在您当前的代码中没有快速修复此问题,因为它包含其他问题。ret
- 由于它是 、 、 、 、 和 ,并且是调用保留的寄存器,因此无法保证 R8 和 R9 寄存器在调用read_char后仍然有效。
r12
r13
r14
r15
rbx
rsp
rbp
- 调用.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。完善!谢谢!
评论
push...pop
call .whitespace_checker
rax
rax=2
.fail
ret
pop rax
jz .whitespace_reader
jz
cmp rax,0
jz .success
ret
push
pop
call
ret
call .whitespace_checker
rax = 2
.fail
ret
rsi
rax = 0 or 1
segmentation fault (core dumped)