为什么调用 printf 时会覆盖 EDX 的值? [英] Why is the value of EDX overwritten when making call to printf?

查看:17
本文介绍了为什么调用 printf 时会覆盖 EDX 的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've written a simple assembly program:

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 上的可执行文件.

I am the NASM assembler and the GCC to link the object file to an executable on linux.

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

Essentially, this program is first putting the value of the stack pointer into register EDX, it is then printing the contents of this register twice. However, after the second printf call, the value printed to the stdout does not match the first.

这种行为看起来很奇怪.当我用 EBX 替换这个程序中 EDX 的每个用法时,输出的整数与预期的一样.我只能推断 EDX 在 printf 函数调用期间的某个时刻被覆盖.

This behaviour seems strange. When I replace every usage of EDX in this program with EBX, the outputted integers are identical as expected. I can only infer that EDX is overwritten at some point during the printf function call.

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

Why is this the case? And how can I make sure that the registers I use in future don't conflict with C lib functions?

推荐答案

根据 x86 ABIEBXESIEDIEBP是被调用者保存寄存器,EAXECXEDX 是调用者保存寄存器.

According to the x86 ABI, EBX, ESI, EDI, and EBP are callee-save registers and EAX, ECX and EDX are caller-save registers.

这意味着函数可以自由地使用和破坏以前的值EAXECXEDX.出于这个原因,如果你不想改变它们的值,请在调用函数之前保存 EAXECXEDX 的值.这就是呼叫者保存".意思是.

It means that functions can freely use and destroy previous values EAX, ECX, and EDX. For that reason, save values of EAX, ECX, EDX before calling functions if you don't want their values to change. It is what "caller-save" mean.

或者更好的是,将其他寄存器用于函数调用后仍然需要的值.EBX 在函数开始/结束时的 push/pop 比在进行函数调用的循环中的 EDX 的 push/pop 好得多.如果可能,请为调用后不需要的临时寄存器使用调用破坏寄存器.已经在内存中的值,因此它们在重新读取之前不需要写入,也更容易溢出.

Or better, use other registers for values that you're still going to need after a function call. push/pop of EBX at the start/end of a function is much better than push/pop of EDX inside a loop that makes a function call. When possible, use call-clobbered registers for temporaries that aren't needed after the call. Values that are already in memory, so they don't need to written before being re-read, are also cheaper to spill.

由于EBXESIEDIEBP是被调用者保存寄存器,函数必须恢复在返回之前,它们修改的任何值的原始值.

Since EBX, ESI, EDI, and EBP are callee-save registers, functions have to restore the values to the original for any of those they modify, before returning.

ESP 也是被调用者保存的,但除非你将返回地址复制到某处,否则你不能搞砸.

ESP is also callee-saved, but you can't mess this up unless you copy the return address somewhere.

这篇关于为什么调用 printf 时会覆盖 EDX 的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆