了解汇编中的 printf 函数,nasm,x86。我不知道为什么这段代码没有打印出任何东西

Understanding the printf function in assembly, nasm, x86. I don't know why this code isn't printing anything out

BITS 64;

          global    main
          extern    printf

          section   .text
main:                                       ; This is called by the C library startup code
          push      rbx                     ; required to make printf 16-byte aligned.
                                            ; For now we only do this once!
          
          push      qword 300
          call      sum 

          mov       rdi, dfmt               ; set 1st parameter (format)
          mov       rsi, rax                  ; set 2nd parameter (low byte)
          xor       rax, rax                ; because printf is varargs we must clear rax
          call      printf                  ; printf(format, current_number)

          pop       rbx                     ; must restore rbx for Linux
          ret                               ; Return from main back into C library wrapper

sum:
      push      rbp                     ; retrieve parameter
          push      rbx                     ; save previous 
          mov       rbp, rsp
          add       rsp, 24                 ; create room for integer
          mov       rbx, [rbp]              ; rbx = param
            
          cmp       rbx, 0                  ; if (n <= 0)
          je        base                 
          
          dec       rbx                     ; decrement rbx to place in stack
          push      rbx                     ; put (rbx - 1) in stack
          inc       rbx
          call      sum                     ; calculate sum(n - 1)
          add       rax, rbx     
          pop       rbx 
          
          jmp end
          base:
          mov       rax, 1
          
          end:
          pop       rbx
          pop       rbp
          ret
           
dfmt:
          db  "%ld", 10, 0

让我们一点一点地过一遍。

main 函数中你有 push qword 300。我假设这是为了将参数传递给 sum?如果是这样......那不是你在做什么。我建议改为遵循 AMD64 调用约定并使用 mov rdi, 300,然后使用 call sum,这将期望在 rdi.

中找到它的第一个参数

无论如何,在 sum 中,您要做的第一件事就是 push rbp,这是非常标准的。那你push rbx。那是一个被调用者保存的寄存器,很好。然后 mov rbp, rsp 这又是非常标准的。

现在你 释放 一些堆栈 add rsp,24,我不明白。堆栈向较低地址增长,因此要保留一些堆栈,您可以从 rsp 中减去 。但也许那不是你在做什么?

现在我真的很困惑。你做 mov rbx, [rbp],但由于 rbp 没有被 main 函数修改,它不会指向我们想要的任何地方。我只能假设这是在堆栈上获取参数的一些尝试?

老实说,在这一点上我很困惑。

通常使用以下样板启动您的函数。

push rbp
mov rbp, rsp

原因是为了保护堆栈。现在所有引用都可以针对 rbp 完成, 永远不会在您的代码中更改 (它始终指向 您的 堆栈框架) .这是一个被调用者保存的寄存器,所以无论如何我们都必须保存它,并在设置它之前在堆栈上进行保存。

通常使用以下样板文件结束函数。

mov rsp, rbp
pop rbp
ret

这会将堆栈指针和 rbp 恢复到它们在入口时的状态,这是 AMD64 调用约定所要求的。这个有一个同义词:leave。下面的代码是等价的。

leave
ret

现在,当您使用 printf(或任何可能使用 xmm 寄存器的函数)时,您还必须确保堆栈在您之前在 16 字节边界上对齐称呼。您可以假设堆栈在进入 main 时对齐。 push rbp 会把它搞砸(减去 8),但是当你执行 call printf 时,你会再压入 8 个字节(return 地址)并且很快!堆栈已对齐。

我最后的建议是使用 AMD64 调用约定而不使用堆栈。把参数放在rdi,得到return的值在rax.

在不知道你想要做什么的情况下,我无法提供更多帮助。