I'm trying to link x86 assembly and C.
My C program:
extern int plus_10(int);
# include <stdio.h>
int main() {
int x = plus_10(40);
printf("%d\n", x);
return 0;
}
My assembly program:
[bits 32]
section .text
global plus_10
plus_10:
pop edx
mov eax, 10
add eax, edx
ret
I compile and link the two as follows:
gcc -c prog.c -o prog_c.o -m32
nasm -f elf32 prog.asm -o prog_asm.o
gcc prog_c.o prog_asm.o -m32
However, when I run the resulting file, I get a segmentation fault.
But when I replace
pop edx
with
mov edx, [esp+4]
the program works fine. Can someone please explain why this happens?
A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (for example, attempting to write to a read-only location, or to overwrite part of the operating system).
Check shell limits Usually it is the limit on stack size that causes this kind of problem. To check memory limits, use the ulimit command in bash or ksh , or the limit command in csh or tcsh . Try setting the stacksize higher, and then re-run your program to see if the segfault goes away.
No, memory leaks by themselves would not cause a segmentation fault. However, memory leaks usually indicate sloppy code, and in sloppy code other issues, which would cause a segmentation fault, are likely to be present.
1) Segmentation Fault (also known as SIGSEGV and is usually signal 11) occur when the program tries to write/read outside the memory allocated for it or when writing memory which can only be read.In other words when the program tries to access the memory to which it doesn't have access to.
This is a possible assembly code of int x = plus_10(40);
push 40 ; push argument
call plus_10 ; call function
retadd: add esp, 4 ; clean up stack (dummy pop)
; result of the function call is in EAX, per the calling convention
; if compiled without optimization, the caller might just store it:
mov DWORD PTR [ebp-x], eax ; store return value
; (in eax) in x
Now when you call plus_10
, the address retadd
is pushed on the stack by the call
instruction. It's effectively a push
+jmp
, and ret
is effectively pop eip
.
So your stack looks like this in the plus_10
function:
| ... |
+--------+
| 40 | <- ESP+4 points here (the function argument)
+--------+
| retadd | <- ESP points here
+--------+
ESP
points to a memory location that contains the return address.
Now if you use pop edx
the return address goes into edx
and the stack looks like this:
| ... |
+--------+
| 40 | <- ESP points here
+--------+
Now if you execute ret
at this point, the program will actually jump to address 40 and most likely segfault or behave in some other unpredictable way.
The actual assembly code generated by the compiler may be different, but this illustrates the problem.
BTW, a more efficient way to write your function is this: it's what most compilers would do with optimization enabled, for a non-inline version of this tiny function.
global plus_10
plus_10:
mov eax, [esp+4] ; retval = first arg
add eax, 10 ; retval += 10
ret
This is smaller and slightly more efficient than
mov eax, 10
add eax, [esp+4] ; decode to a load + add.
ret
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With