Why is the value of EDX overwritten when making call to printf?

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

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.

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.

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 is also callee-saved, but you can't mess this up unless you copy the return address somewhere.

The ABI for the target platform (e.g. 32bit x86 Linux) defines which registers can be used by functions without saving. (i.e., if you want them preserved across a call, you have to do it yourself).

Links to ABI docs for Windows and non-Window, 32 and 64bit, at https://stackoverflow.com/tags/x86/info

Having some registers that aren't preserved across calls (available as scratch registers) means functions can be smaller. Simple functions can often avoid doing any push/pop save/restores. This cuts down on the number of instructions, leading to faster code.

It's important to have some of each: having to spill all state to memory across calls would bloat the code of non-leaf functions, and slow things down esp. in cases where the called function didn't touch all the registers.