3

我有一些使用 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 做同样的事情,我们会得到一个分段错误。谁能告诉我们这样做的原因?

谢谢!

4

2 回答 2

2

您的使用中有两个错误scanf,可能基于一个错误的假设:您似乎的意思是scanf返回加载的数字,rdi并且不需要进一步的参数 format "%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 行,我们获得了正确的值——我对此表示怀疑。

于 2014-05-07T09:03:07.133 回答
0

我不明白你为什么在这里有 C 标志,没有涉及 C 代码,但是对于你的问题:

据我记得 Linux glibc x64 中的调用约定printf(format, argument)format in rdi, argument in rsi.

如果您删除mov rdi, printsent,,那么您正在调用printf(undefined,"Please enter a number: "). 您没有在 中提供格式参数rdi,但printf 不知道并使用当时的任何内容rdi。很可能是一个无效的内存地址,因此调用了SIGSEGV.

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

因此,当您调用它时,它将恢复与返回时具有相同内容的scanf(readInt, ...)指针。因此,删除该行无效,因为包含指向您需要的格式的有效指针。readIntprintintrdimov rdi, printint,rdi

于 2012-12-11T12:01:25.150 回答