Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences in the initialization of the EAX register when calling a function in C and C++

Tags:

There is a curious difference between assemblies of a small program, when compiled as a C-program or as a C++-program (for Linux x86-64).

The code in question:

int fun(); int main(){     return fun(); } 

Compiling it as a C-program (with gcc -O2) yields:

main:     xorl    %eax, %eax     jmp fun 

But compiling it as a C++-program (with g++ -02) yields:

main:     jmp _Z3funv 

I find it puzzling, that the C-version initializes the return value of the main-function with 0 (xorl %eax, %eax).

Which feature of the C-language is responsible for this necessity?

Edit: It is true that, for int fun(void); the is no initialization of the eax-register.

If there is no prototype of fun at all, i.e.:

int main(){     return fun(); } 

then the C-compiler zeros the eax-register once again.

like image 792
ead Avatar asked Jan 30 '17 21:01

ead


People also ask

What is the value of EAX register?

I know when add integers in assembly, it stores result in the destination. and when we multiply it stores in eax. so if I call the function multiply( 3 , 8 ), the value of eax register after that line should be 120.

Is EAX the return register?

eax (or rax) is the return value register. edi (or rdi) is the first function argument. esi (or rsi) is the second function argument.

Does EAX return value?

EAX: The accumulator. This register typically stores return values from functions.


2 Answers

In C int fun(); can take any number of arguments, so it may even be a varargs function. In C++ however it means it takes no arguments.

The x86-64 sysv abi convention demands that the register AL must contain the number of SSE registers used when invoking a varargs function. You of course pass no argument, so it is zeroed. For convenience the compiler decided to zero the whole eax. Declare your prototype as int fun(void); and the xor shall disappear.

like image 63
Jester Avatar answered Oct 07 '22 15:10

Jester


Apparently it is a defensive measure, designed for situations when prototype-less fun function happens to actually be a variadic function, as explained by @Jester's answer.

Note though that this explanation does not hold any water from the point of view of standard C language.

Since the beginning of standardized times (C89/90) C language explicitly required all variadic functions to be declared with prototype before the point of the call. Calling a non-prototyped variadic function triggers undefined behavior in standard C. So, formally, compilers do not have to accommodate the possibility of fun being variadic - if it is, the behavior would be undefined anyway.

Moreover, as @John Bollinger noted in the comments, according to the C standard, a non-prototype int fun() declaration actually precludes further variadic prototype declarations of fun. I.e. a variadic function cannot be legally pre-declared as a () function. That would be another reason why the above non-prototype declaration is sufficient for the compiler to assume that fun cannot possibly be variadic.

This could actually be a legacy feature, designed to support pre-standard C code, where pre-declaring variadic functions with prototype was not required.

like image 31
AnT Avatar answered Oct 07 '22 14:10

AnT