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) ● 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: – : 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 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 printf() int main (int argc, char *argv[]) _start { libc printf(“Hello world\n”); write() } Kernel ● Let's look how to do it without libc ● write system call – Documentation: man 2 write – ssize_t write(int fd, const void *buf, size_t count); – Three arguments

● _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 - 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 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 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; (), .... asm volatile ("int $0x80" : "=a" (retval) : "a" (4), "b" (1), "c" (hello), "d" (sizeof(hello)-1) : “memory”); asm volatile ("int $0x80" : : "a" (1), "b" (0)); } ● Compile: gcc -m32 -nostdlib -nostdinc -static -O2 hello3.c -o hello3

● 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 ...