I have the following code (caller.c):
#include <stdio.h>
extern int callee(int);
int main(int argc, char *argv[]){
callee(4);
return 1;
}
and (callee.s):
.globl callee
callee:
pop %eax
add $4, %eax
ret
I compile with: gcc -m32 caller.c callee.s
and run:
./a.out
Segmentation fault (core dumped)
I am wondering what my mistake(s) is/are as I believed that main should now push a 32 bite number of the stack. I havent changed stack so that callee should now be able to pop that number from the same stack. Maybe I should add (add $4, %esp) before the pop (if the address of callee is in the "way"/actually been popped). I have tried that too with no success. callee should now get the number from the stack and add 4 to it. The eax-register should be where the return value from callee to caller should be kept (calling convention), but here I ignore the return value.
Could someone assist me?
related question: calling assembly function from c
calling convention: https://en.wikipedia.org/wiki/X86_calling_conventions
(With the x86-32 calling convention,) the arguments to a function are pushed on the stack first, and then the return address. Thus, your pop
instruction popped the return address, and the subsequent ret
tried to return to address 0x00000004, which is unmapped memory, causing a crash.
Also, in this convention the callee is not supposed to pop its arguments. The caller will do that.
The code you should have written is
callee:
movl 4(%esp), %eax
addl $4, %eax
ret
You can confirm this for yourself by compiling
unsigned int callee(unsigned int x) { return x + 4; }
with the options -m32 -O2 -S -fomit-frame-pointer
and inspecting the .s
file produced; you should get the same assembly code as above.