Date created: Sunday, April 21, 2019 11:19:15 AM. Last modified: Monday, June 3, 2019 8:04:58 AM
RET vs. SYSCALL vs. C exit()
Return
The need for return()/RET is when ending a thread or process and returning the status to the parent thread or OS/Kernel. A function call may also use return()/RET if it returns a result. Using return() in C translates to the RET assembly instruction which can return one or more values (at least one, the status code, is required, in both languages).
Compile and run the C code:
$ cat c_return.c
int main () {
return 0;
}
$ gcc -g -O0 -o c_return c_return.c
$ file c_return
c_return: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=7cb093ecac606c7792f1a3f5a9bc757afbb801b7, with debug_info, not stripped
$ ./c_return
$ echo $?
0
Compile the C code to assembly:
# Produces object file c_return.o
$ gcc -g -c c_return.c
$ file c_return.o
c_return.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
$ objdump -d -M intel -S c_return.o
c_return.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
int main () {
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
return 0;
4: b8 00 00 00 00 mov eax,0x0
}
9: 5d pop rbp
a: c3 ret
Compile the C to assembly then compile and execute the assembly:
# Generates assembly code from C code as c_return.s
$ gcc -g -S c_return.c
# Compile the assembly into an object file c_return.o
$ gcc -g -c c_return.s
$ objdump -d -M intel -S c_return.o
c_return.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: b8 00 00 00 00 mov eax,0x0
9: 5d pop rbp
a: c3 ret
# Link and compile the object file into an executable c_return
$ gcc -g c_return.o -o c_return
$ file c_return
c_return: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0cdede0af9ffd055c1d344f435b7fd392fa06a2b, with debug_info, not stripped
$ ./c_return
$ echo $?
0
SYSCALL
The SYSCALL instruction can be used to terminate a program that is either return a value by placing it somewhere in memory (like on the stack) and not via the RET instruction, or when the program is not written in C (i.e. a raw assembly program) that doesn't use the standard C libraries with functions like exit().
$ cat exit_syscall.s
.intel_syntax noprefix
.section .data
.section .text
.globl _start
_start:
mov rax, 60 # Linux exit syscall
mov rdi, 0 # Return code, 0 == exit successfully
syscall
# Compile assembly code into an object (.o) file of machine code:
$ as exit_syscall.s -o exit_syscall.o
$ file exit_syscall.o
exit_syscall.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
# Optionally dump the opcodes in the compiled assembly:
$ objdump -d -M intel -S exit_syscall.o
exit_syscall.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: 48 c7 c0 3c 00 00 00 mov rax,0x3c
7: 48 c7 c7 00 00 00 00 mov rdi,0x0
e: 0f 05 syscall
# Link the assembly into an executable binary:
$ ld -o exit_syscall exit_syscall.o
$ file exit_syscall
exit_syscall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$ ./exit_syscall
$ echo $?
0
Exit
The C function exit() is calling into the standard library glibc on Linux which works with the Kernel to call various clean-up function as the process (or thread) is terminated. Using exit() in C compiles in assembly to a link to the library function for exit().
Compile and run the C code:
$ cat c_exit.c
#include <stdlib.h>
int main () {
exit(0);
}
$ gcc -g -O0 -o c_exit c_exit.c
$ file c_exit
c_exit: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=5ceaaca6c7f7d95d79fff01af3b2dc973ea45535, with debug_info, not stripped
$ ./c_exit
$ echo $?
0
Compile the C code to assembly:
# Produces object file c_exit.o
$ gcc -g -c c_exit.c
$ file c_exit.o
c_exit.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
$ objdump -d -M intel -S c_exit.o
c_exit.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
#include <stdlib.h>
int main () {
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
exit(0);
4: bf 00 00 00 00 mov edi,0x0
9: e8 00 00 00 00 call e <main+0xe>
Compile the C to assembly then compile and execute the assembly:
# Generates assembly code from C code as c_exit.s
$ gcc -g -S c_exit.c
# Compile the assembly into an object file c_exit.o
$ gcc -g -c c_exit.s
$ objdump -d -M intel -S c_exit.o
c_exit.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: bf 00 00 00 00 mov edi,0x0
9: e8 00 00 00 00 call e <main+0xe>
# Link and compile the object file into an executable c_exit
$ gcc -g c_exit.o -o c_exit
$ file c_exit
c_exit: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a1f86b81ead50f59e7b1d925dfdb979a1470204e, with debug_info, not stripped
$ ./c_exit
$ echo $?
0
Previous page: Preprocessor, Compiler, Assembler, Linker, Loader (gcc)
Next page: Syscalls On Linux