0

我是汇编语言的新手,并试图理解一个简单的程序,它将添加两个数字并显示结果。

section .data

message1 db "value=%d%d",10,0

 section .text
   global main
   extern printf
   main:
   mov eax, 55
   mov ebx, 45
   add eax,ebx
   push eax
   push ebx
   push message1
   call printf
   add esp, 8
   ret

现在输出是 45 100

添加 eax 后,ebx 指令的结果将存储在 eax 寄存器中。

但是现在接下来的几行会发生什么

 push eax   // push 100 on to stack

 push ebx   // push 45 on to stack

 push message1  // push "value=%d" on to stack // I m bit doubtful here 

我想知道的是执行“call printf”时会发生什么?

“添加esp,8”的目的是什么?

4

1 回答 1

1

该库printf可能以多种方式实现,因此断言ALL printf例程将以THIS printf行为方式执行是危险的。

序列

push eax // 将 100 推入堆栈 push ebx // 将 45 推入堆栈 push message1 // 将消息“value=%d”的地址推入堆栈 call printf // 将返回地址推入堆栈

进入printf例程,从 BOTTOM 读取堆栈

  1. 退货地址
  2. 指向消息的指针
  3. 一些参数值

所以,PRINTF很可能

  1. POP返回地址并保存
  2. POP指向消息的指针
  3. MOVeSTACK POINTER到寄存器或保存

然后它可以开始它的任务 - 使用指向消息的指针,写出每个字符,直到它遇到一个键字符串,比如%d“打印一些小数”。所以它POP是堆栈中的下一个值(45,如推入ebx),将其格式化为十进制并打印它,然后继续使用printf字符串。

另一个%d- 从 100 推送eax,然后继续 - 直到找到0指示字符串结尾的字节。

现在printf返回所需要做的就是stack pointer从存储它的任何地方恢复它,然后返回到返回地址——无论它被存储在哪里。

当它返回时,堆栈将恢复到printf调用时的状态 - 并且在那个时候,EBX并且EAX已经被PUSH编辑。每个都是4个字节,所以stack pointer需要调整8个字节才能去掉这两PUSH条指令存储的数据。

那么 - 为什么这样做 - 为什么不简单PRINTF地调整堆栈 - 它可以,因为它知道它已经删除了 8 个字节用于显示(2*%d)?

好吧,从本质上讲,它可以——但假设消息只包含一个 %d——或 3——或者消耗了 8 个字节以外的东西?返回时,stack-pointer将包含一个意外值 - 这取决于如何PRINTF解释字符串。非常难以使用汇编程序技巧,例如在不特别小心的情况下覆盖部分消息。正如它所写的那样,该printf函数始终以可预测的方式运行,返回弹出消息地址,而不考虑任何其他考虑。由程序员正确处理堆栈内容。

于 2013-06-06T13:26:52.520 回答