我有一些使用scanf和printf的汇编代码,我遇到了一些问题。当在同一代码中使用这两个函数时,寄存器中的值似乎丢失了。该程序基本上加载一个数字并打印出来。我们使用
运行它nasm -f elf64 file.asm && gcc -o file file.o && ./file
在linux上
这是我们的代码:
extern printf
extern scanf
section .data
a db "set: ", 0
b db "not set: ", 0
reading db "Please enter a number: ", 0
message db "\n", 0
printsent db "%s", 10, 0
printint db "%d", 10, 0
printchar db "%c", 10, 0
readInt db "%d", 0
input db "%d", 0
section .text
global main
main:
hatta:
push rbp,
mov rbp, rsp,
push rbx,
xor rax, rax,
mov rdi, printsent,
mov rsi, reading
call printf,
pop rbx,
xor rax, rax,
mov rdi, readInt,
call scanf,
mov rbx, rdi
push rbx,
xor rax, rax,
mov rdi, printint,
mov rsi, rbx,
call printf,
pop rbx,
pop rbp,
ret
奇怪的是,如果删除了行mov rdi, printint,
,我们会获得正确的值。但是,如果我们使用printsentence执行相同的操作,则会出现分段错误。谁能告诉我们这个的原因?
谢谢!
答案 0 :(得分:1)
scanf
用法中有两个错误,可能基于一个错误的假设:您似乎意味着scanf
返回rdi
中加载的数字,并且不需要进一步的参数格式为"%d"
。实际上,数字(如果成功扫描)将在第二个参数指向的内存中返回。因此,以下更改使代码工作。
pop rbx, | ;delete
=
xor rax, rax, = xor rax, rax,
mov rdi, readInt, = mov rdi, readInt,
> mov rsi, rsp
call scanf, = call scanf,
mov rbx, rdi | pop rbx,
关于如果删除了行mov rdi,printint,我们会获得正确的值 - 我对此表示怀疑。
答案 1 :(得分:0)
我不明白为什么你这里有C标志,没有涉及C代码,但问题是:
据我记得,printf(format, argument)
的Linux glibc x64中的调用约定是format in rdi
,argument in rsi
。
如果您移除mov rdi, printsent,
,则表示您正在呼叫printf(undefined,"Please enter a number: ")
。您没有在rdi
中提供格式参数,但是printf
不知道那个并且在那个时刻使用rdi
中的任何东西。很可能是一个无效的内存地址,从而调用SIGSEGV
。
默认情况下,x86中的函数调用应该对参数是非破坏性的,尽管这不是必需的。标准库函数通常是。他们通过将参数推送到堆栈并在完成后重新加载它来实现此目的。
因此,当您致电scanf(readInt, ...)
时,它会恢复指向readInt
的指针,该指针与printint
中rdi
的内容相同。因此删除行mov rdi, printint,
无效,因为rdi
包含指向您所需格式的有效指针。