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

Posted

技术标签:

【中文标题】了解汇编、nasm、x86 中的 printf 函数。我不知道为啥这段代码没有打印出任何东西【英文标题】:Understanding the printf function in assembly, nasm, x86. I don't know why this code isn't printing anything out了解汇编、nasm、x86 中的 printf 函数。我不知道为什么这段代码没有打印出任何东西 【发布时间】:2021-02-21 01:10:17 【问题描述】:
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

【问题讨论】:

请注意,push qword 300 再次未对齐堆栈,您甚至没有从堆栈中删除该参数。可能是也可能不是您的打印问题的原因,但它肯定会导致您的程序崩溃。 add rsp, 24 也不是分配空间,而是释放空间。你想要一个sub。再说一次,您不需要任何堆栈空间,那有什么意义呢? mov rbx, [rbp] 也不是参数。 另外,sum 是一团糟,从 add rsp,24 开始(不是子),所以它后来推送覆盖 main 的堆栈帧。以及它自己的,递归的,因为它调用自己。而[rbp] 不是它的堆栈 arg 所在的位置。 (通常你会在寄存器中传递参数)。我假设这个段错误,但你还没有确切地说会发生什么(minimal reproducible example 的必要部分)。使用调试器单步执行它,并为自己找到一个工作递归阶乘或其他微不足道的示例,因为有很多误解需要澄清。如果你用谷歌搜索,Stack Overflow 上应该有一些。 【参考方案1】:

让我们一点一点来。

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 个字节(返回地址),然后!堆栈已对齐。

我最后的建议是使用 AMD64 调用约定而不是使用堆栈。将参数放入rdi,获取rax中的返回值。

如果不知道您要做什么,我无法提供更多帮助。

【讨论】:

同意,从mov rbx, [rbp] ;rbx = param 的评论来看,他们正在尝试加载他们在 main 中推送的堆栈 arg,但显然他们没有理解或考虑过任何指令在做什么。他们正在重新加载保存的 RBX 值(因为他们在 mov rbp, rsp 将 RBP 设置为帧指针之前推送了 RBX,该帧指针没有指向堆栈帧中的旧帧指针的通常位置)。我还想知道add rsp, 24 是否与应该是[rbp + 24] 混淆了。但如此混乱,它应该被抛弃并用新的理解代替:/

以上是关于了解汇编、nasm、x86 中的 printf 函数。我不知道为啥这段代码没有打印出任何东西的主要内容,如果未能解决你的问题,请参考以下文章

x86 NASM更改未作为参数传递的地址的值

汇编器的NASM

VS2015使用NASM编译汇编文件

直接在x86硬件上显示图片(无os)

执行汇编代码时控制台上的垃圾字符

动手开发操作系统- 0x1