Why is the EDX value overwritten when printf is called?

I wrote a simple build 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 

I am an assembler of NASM and GCC to associate an object file with an executable file on linux.

Essentially, this program first places the value of the stack pointer into the EDX register, and then prints the contents of that register twice. However, after the second call to printf, the value output to standard output does not match the first.

This behavior seems strange. When I replace any use of EDX in this program with EBX, the calculated integers are identical to those expected. I can only indicate that EDX is being overwritten at some point during the call to the printf function.

Why is this so? And how can I make sure that the registers that I use in the future do not conflict with C lib functions?

+6
source share
2 answers

According to x86, ABI , EBX , ESI , EDI and EBP are savings account registers and EAX , ECX and EDX are call save registers.

This means that functions are free to use and destroy previous EAX , ECX and EDX values. For this reason, save the values ​​of EAX , ECX , EDX before calling functions, if you do not want their values ​​to change. This is what the call-save means.

Or better, use other registers for the values ​​that you will need after calling the function. push / pop EBX at the beginning / end of a function is much better than push / pop EDX inside a loop that calls a function call. Whenever possible, use blurred call registers for temporary ones that are not needed after the call. Values ​​that are already in memory, so they do not need to be written before re-reading, are also cheaper to spill.


Since EBX , ESI , EDI and EBP are saving registers, functions must return values ​​for the original for any of those that they change before returning.

ESP also saved, but you cannot mess it up unless you copy the return address somewhere. A failed / ret call is terrible for performance because modern processors use a return address predictor.

+11
source

The ABI for the target platform (for example, 32-bit x86 Linux) determines which registers can be used by functions without saving. (i.e. if you want them to be kept during the conversation, you have to do it yourself).

Links to ABI documents for Windows and non-Window, 32 and 64 bit, at fooobar.com/tags/x86 / ...

The presence of some registers that are not stored in all calls (available as zero registers) means that functions may be smaller. Simple functions can often avoid doing push/pop save / restore. This reduces the number of instructions, resulting in faster code.

It is important to have some of them: the need to pour all the state into memory on calls will inflate the code of non-leaf functions and slow down the work. in cases when the called function did not touch all registers.

+5
source

All Articles