汇编中的C调用约定(64位) - scanf

时间:2012-12-11 11:40:53

标签: c assembly 64-bit printf scanf

我有一些使用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执行相同的操作,则会出现分段错误。谁能告诉我们这个的原因?

谢谢!

2 个答案:

答案 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 rdiargument in rsi

如果您移除mov rdi, printsent,,则表示您正在呼叫printf(undefined,"Please enter a number: ")。您没有在rdi中提供格式参数,但是printf  不知道那个并且在那个时刻使用rdi中的任何东西。很可能是一个无效的内存地址,从而调用SIGSEGV

默认情况下,x86中的函数调用应该对参数是非破坏性的,尽管这不是必需的。标准库函数通常是。他们通过将参数推送到堆栈并在完成后重新加载它来实现此目的。

因此,当您致电scanf(readInt, ...)时,它会恢复指向readInt的指针,该指针与printintrdi的内容相同。因此删除行mov rdi, printint,无效,因为rdi包含指向您所需格式的有效指针。