当前位置:  开发笔记 > 运维 > 正文

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

如何解决《为什么在调用printf时会覆盖EDX的值?》经验,为你挑选了2个好方法。

我写了一个简单的汇编程序:

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, EDX
PUSH EAX
PUSH str_out
CALL printf
SUB ESP, 8 ; cleanup stack
CALL exit

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

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

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

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



1> MikeCAT..:

按照86 ABI,EBX,ESI,EDI,和EBP被调用函数保存寄存器EAX,ECX并且EDX是调用者保存寄存器.

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

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


因为EBX,ESI,EDI,和EBP被调用函数保存寄存器,功能有恢复值到原来的任何那些他们返回之前修改的.

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


`EBP`也是callee-save!

2> Peter Cordes..:

目标平台的ABI(例如32位x86 Linux)定义了功能可以使用哪些寄存器而不保存.(也就是说,如果你想在电话中保留它们,你必须自己做).

指向Windows和非Window,32和64位的ABI文档的链接,网址为/sf/ask/17360801/

有一些寄存器没有保留在调用之间(可用作临时寄存器)意味着函数可以更小.简单的功能通常可以避免进行任何push/pop保存/恢复.这减少了指令的数量,从而导致更快的代码.

拥有一些内容非常重要:不得不跨越调用将所有状态溢出到内存中会使非叶函数的代码膨胀,并减慢速度.在被调用函数没有触及所有寄存器的情况下.

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有