System calls and inline assembler
Michal Sojka [email protected] ČVUT, FEL
License: CC-BY-SA
System calls
● A way for “normal” applications to invoke operating system (OS) kernel's services. ● Applications run in unprivileged CPU mode (user space, user mode) ● OS kernel runs in privileged CPU mode (kernel mode) ● System call is a way how to securely switch from user to kernel mode.
What is a system call technically?
● A machine instruction that: – Increases the CPU privilege level and – Passes the control to a predefined place in the kernel. ● Arguments are (typically) passed in CPU registers.
● Instructions: – x86: int 0x80, sysenter, syscall – MIPS: syscall – ARM: swi
x86 user execution environment (32 bit)
Basic Program Execution Registers Address Space* 2^32 -1 Eight 32-bit General-Purpose Registers Registers General-Purpose Registers 31 16 15 8 07 16-bit 32-bit AH AL AX EAX Six 16-bit Segment Registers Registers BH BL BX EBX General-Purpose Registers 32-bits EFLAGS Register CH CL 031 CX ECX DH DL DX 32-bits EIP (Instruction Pointer Register) EAX EDX BP EBX EBP FPU Registers SI ECX ESI EDX Eight 80-bit Floating-Point DI EDI ESI Registers Data Registers 0 SP EDI ESP *The address space can be flat or segmented. Using EBP 16 bits Control Register the physical address ESP 16 bits Status Register extension mechanism, a physical address space of 16 bits Tag Register 2^36 - 1 canbeaddressed. Segment Registers Opcode Register (11-bits) 15 0 48 bits FPU Instruction Pointer Register CS 48 bits FPU Data (Operand) Pointer Register DS SS MMX Registers ES FS Eight 64-bit Registers MMX Registers GS
Program Status and Control Register 31 0 EFLAGS XMM Registers Instruction Pointer 31 0 Eight 128-bit XMM Registers Registers EIP
32-bits MXCSR Register Source: Intel Linux system call ABI (x86, 32-bit)
● Application Binary Interface ● int 0x80 – System call number in EAX
● /usr/include/sys/syscall.h ● /usr/include/asm/unistd_32.h – Arguments
● 1st in EBX, 2nd in ECX, 3rd in EDX, 4th in ESI, 5th in EDI, 6th in EBP – Return value: EAX
● Zero or positive: success ● Negative: error (see /usr/include/asm-generic/errno.h, errno-base) ● sysenter – faster, slightly more complicated
Hello world Hello app main() #include
● _startup symbol Hello world It's simpler in assembler AT&T assembler syntax: hello: label: .ascii "Hello world\n" instruction src,dst .directive .global _start ● _start: immediate operands preceded by '$' ● register operands preceded by '%' mov $4,%eax # write mov $1,%ebx # stdout mov $hello,%ecx # ptr to data mov $12,%edx # length of the data int $0x80
● Compile: gcc -m32 -nostdlib -o hello1 hello1.S
● Run: ./hello1 ● Why it ends with segmentation fault?
● Disassemble the binary: objdump -d hello1 Geting rid of the fault
hello: .ascii "Hello world\n" .global _start _start: mov $4,%eax # write mov $1,%ebx # stdout ● We need to tell the mov $hello,%ecx # ptr to data OS that we are about mov $12,%edx # length of the data to finish – with exit int $0x80 syscall. mov $1,%eax # exit mov $0,%ebx # exit code ● Inspect the syscalls int $0x80 invoked by the program: strace ./hello > /dev/null
Hello world in C with inline assembler
char *hello = "Hello world\n";
void _start() Compilation: { gcc -m32 -nostdlib -nostdinc -static -O2 hello2.c -o hello2 asm volatile ( "mov $4,%eax;" "mov $1,%ebx;" "mov hello,%ecx;" "mov $12,%edx;" "int $0x80" ); }
● But C compiler allows us to do better than that! – Assembler Instructions with C Expression
Operands Hello world in C with extended assembler
Extended assembler syntax: asm ( assembler template char hello[] = "Hello world\n"; : output operands /* optional */ : input operands /* optional */ void _start() : list of clobbered registers /* optional */ { ); The syntax after “:” is: int retval;
● Disassemble: objdump -d hello3 C wrappers around system calls static inline long syscall1(long syscall, long arg1) { long ret; asm volatile ("int $0x80" : "=a" (ret) : "a" (syscall), "b" (arg1) : “memory”); return ret; } static inline long syscall3(long syscall, long arg1, long arg2, long arg3) { long ret; asm volatile ("int $0x80" : "=a" (ret) : "a" (syscall), "b" (arg1), "c" (arg2), "d" (arg3) : “memory”); return ret; } int write(int fd, const void *buf, int count) { return syscall3(4, fd, (long)buf, count); } void exit(int status) { syscall1(1, status); } void _start() { int retval; retval = write(1, "Hello world\n", 12); exit(0); } Assignment
● Write a program that: – Opens file “file.txt” (open()) – Reads the first 100 bytes of the file (read()) – Writes the first line (or 100 bytes if the line is linger) of the read data to standard output (write()) – Executes program /bin/date (execve()) ● The program must be compiled without libc i.e. with gcc -nostdlib -nostdinc ...