为什么在调用printf时会覆盖EDX的值?

前端之家收集整理的这篇文章主要介绍了为什么在调用printf时会覆盖EDX的值?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我写了一个简单的汇编程序:
section .data
str_out db "%d ",10,0
section .text
extern printf
extern exit
global main
main:

MOV EDX,ESP
MOV EAX,EDX
PUSH EAX
PUSH str_out
CALL printf
SUB ESP,8 ; cleanup stack
MOV EAX,8 ; cleanup stack
CALL exit

我是NASM汇编程序和GCC,用于将目标文件链接到linux上的可执行文件.

本质上,该程序首先将堆栈指针的值放入寄存器EDX,然后将该寄存器的内容打印两次.但是,在第二次printf调用之后,打印到stdout的值与第一个不匹配.

这种行为似乎很奇怪.当我用EBX替换该程序中每次使用EDX时,输出的整数与预期的完全相同.我只能推断在printf函数调用期间某些时候EDX会被覆盖.

为什么会这样?如何确保我将来使用的寄存器与C lib函数不冲突?

解决方法

根据 x86 ABI,EBX,ESI,EDI和EBP是被调用者保存寄存器,EAX,ECX和EDX是调用者保存寄存器.

这意味着函数可以自由使用和销毁以前的值EAX,ECX和EDX.
因此,如果您不希望更改其值,请在调用函数之前保存EAX,ECX,EDX的值.这就是“来电保存”的意思.

或者更好的是,将其他寄存器用于函数调用后仍需要的值.在函数的开始/结束处推送/弹出EBX比在进行函数调用的循环中推送/弹出EDX要好得多.如果可能,请使用call-clobbered寄存器作为调用后不需要的临时寄存器.已经在内存中的值,因此在重新读取之前不需要写入,溢出也更便宜.

由于EBX,因此函数必须在返回之前将值恢复为他们修改的任何值.

ESP也被callee保存,但除非你在某处复制返回地址,否则你不能搞砸了.不匹配的call / ret对于性能来说很糟糕,因为现代cpu使用返回地址预测器.

猜你在找的C&C++相关文章