Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is EAX being cleared before calling a function if I don't include the header?

Tags:

In the following C code:

#include <stdio.h> int main(void){getchar();} 

It produces the following asm:

main:         push    rbp         mov     rbp, rsp                  # no extra instruction here when header is included         call    getchar         mov     eax, 0         pop     rbp         ret 

However, if I don't include stdio.h in the file, then it still compiles, but adds in what looks like a random mov eax, 0 instruction:

enter image description here

Here is Compiler Explorer: https://godbolt.org/z/3fTcss. Is this just part of "undefined behavior", or is there a particular reason that the instruction before call getchar is added there?

like image 907
carl.hiass Avatar asked Mar 10 '21 20:03

carl.hiass


People also ask

Does Borland fastcall/Register use EAX or EDX?

AFAIK Borland fastcall/register is using EAX and EDX, but also ECX, which is not the case here. Can I tell IDA somehow about this calling convention? How would I do this? Can you paste some code which uses EAX, EDX and stack without ECX?

Does Delphi use only the EAX and EDX registers for passing arguments?

I have some code (I assume Delphi) which uses only the EAX and EDX register for passing the arguments (and of course the stack if more are required). I looked which calling conventions would match, but I haven't found one which uses only EAX and EDX. AFAIK Borland fastcall/register is using EAX and EDX, but also ECX, which is not the case here.

Do functions with more than two arguments use ECX?

After looking into this in more detail, it seems that ECX is indeed used, and that the code usually doesn't need more than two arguments. When I looked at functions with more arguments, I realized that it was the standard with EAX/EDX/ECX/PUSH...


1 Answers

Without the header, gcc provides an implicit declaration of getchar when you use it, as if you had previously declared

int getchar(); 

(This behavior was guaranteed by older versions of the C standard. Current versions make it undefined behavior to use a function which was not previously declared, but gcc still provides the old behavior as an extension.)

This declaration provides no information about the types of arguments which getchar expects. (Remember that unlike in C++, a declaration with () doesn't declare a function as taking no arguments, but as taking unspecified arguments, leaving it up to the programmer to know what the function expects and pass the proper number and type of arguments.) For all the compiler knows, it could even be variadic, and per the x86-64 SysV ABI, variadic functions expect in al the number of vector registers which are being used to pass in arguments. Here no vector registers are being used, so the compiler sets al to 0 before the call. (It's slightly more efficient to actually zero all of rax so it does that instead.)

like image 56
Nate Eldredge Avatar answered Sep 23 '22 04:09

Nate Eldredge