Three Ways to Get Something Done Use the C library functions (libc.a static library or libc.so dynamic shared object library) cis-lclient03:~/2012/sys2>more hello1.c #include main() { printf("Hello world\n"); } cis-lclient03:~/2012/sys2>gcc -O1 -S hello1.c cis-lclient03:~/2012/sys2>more hello1.s .file "hello1.c" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "Hello world" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $.LC0, (%esp) call puts leave ret .size main, .-main .ident "GCC: (GNU) 4.4.5 20101112 (Red Hat 4.4.5-2)" .section .note.GNU-stack,"",@progbits cis-lclient03:~/2012/sys2>gcc hello1.s cis-lclient03:~/2012/sys2>./a.out Hello world cis-lclient03:~/2012/sys2> Use the unix library functions (ls /usr/share/man/man2) and then (man 2 functioname) cis-lclient19:~/class/nov08>more hello2.c int write(int fd, const void *buf, int count); main() { int result; result = write(1, "Hello world\n", 13); } cis-lclient19:~/class/nov08>more hello2.s .section .rodata .LC0: .string "Hello world\n" .text .globl main main: pushl %ebp movl %esp, %ebp pushl $13 pushl $.LC0 pushl $1 call write addl $12, %esp leave ret cis-lclient19:~/class/nov08>gcc hello2.s cis-lclient19:~/class/nov08>./a.out Hello world cis-lclient19:~/class/nov08> Call the Linux operating system directly with the "int" instruction cis-lclient19:~/class/nov08>more hello.s .section .text .globl _start _start: movl $4, %eax # sys_write function code movl $1, %ebx # file descriptor (sysout) movl $str, %ecx # starting address of string movl $length, %edx # length of string int $0x80 # transfer to kernel with code 0x80 (system call) movl $1, %eax # sys_exit function code movl $0, %ebx # return code 0 (everything is great) int $0x80 # transfer to kernel (and never return) str: .string "Hello World\n" strend: length = strend - str cis-lclient19:~/class/nov08>as -o hello.o hello.s cis-lclient19:~/class/nov08>ld -o hello hello.o cis-lclient19:~/class/nov08>hello Hello World cis-lclient19:~/class/nov08> Interrupts The Big Picture. User vs Kernel or Supervisor mode User mode - Certain instructions illegal (eg. any instructions that control I/O devices). Kernel or Supervisor mode - all instruction legal. An interrupt is "a friendly tap on the sholder" that causes the processor to temporarily stop executing the current program so that it can take care of a higher priority event. Higher priority events include: a hardware interrupt (a user presses a keyboard key) a software interrupt (an "int" instruction is executed) a processor exception occured (the program attempted to divide by 0 or the program attempted to access an illegal memory address) At boot-up, the operating system creates the "interrupt vector" table (this description is for very old Intel processors). The interrupt vector table is an array of 256 addresses (e.g. 1024 bytes) that is stored in an area of kernel memory that user-mode programs can't access or modify). Each of the addresses in the array points to a function in the operating system (called an interrupt routine) that handles a specific type of hardware interrupt, software interrupt, or processor exception. When an interrupt occurs, the interrupt provides an "interrupt number" between 0x00 and 0xff to tell the processor which interrupt routine to execute. When an interrupt occurs (and the interrupt is not "masked"), the processor finishes executing the current instruction. Then the processor: Saves %eip on the stack (so the program can be resumed) Switches from user to kernel mode Places the starting address of the requested interrupt handler in %eip by using the interrupt table and the interrupt number. When the processor reaches the end of the interrupt routing and executes an intr (interrupt return) instruction, the processor: Switches from kernel mode back to user mode (actually, to whatever mode theprocessor was in when the interrupt occured). Restores %eip using the value staved on the stack to resume execution of the original program. The interrupted program does not notice that it has been interrupted (unless of course it examines 4(%esp) or counts machine cycles with rtdsc). This discription is oversimplified. For example, when the interrupt occurs, we need to save a bunch of other things such as the interrupt programs condition code bits (CF, SF, OF, and ZF), processor priority, mode (user or kernel), etc. Linux programs use interrupt vector 0x80 to request services (open, read, exit, fork, exec, etc) from the operating system by executing the "int 0x80" instruction. Intel reserves vectors 0 to 31 (0x00 to 0x20) although only 0x00 to 0x13 are currently used. INT_NUM Short Description PM 0x00 Division by zero 0x01 Debugger 0x02 NMI 0x03 Breakpoint 0x04 Overflow 0x05 Bounds 0x06 Invalid Opcode 0x07 Coprocessor not available 0x08 Double fault 0x09 Coprocessor Segment Overrun (386 or earlier only) 0x0A Invalid Task State Segment 0x0B Segment not present 0x0C Stack Fault 0x0D General Protection 0x0E Page Fault 0x0F reserved 0x10 Math Fault 0x11 Alignment Check 0x12 Machine Check 0x13 SIMD Floating-Point Exception Printing using system calls directly "Hello World" using C formatted output library functions your c code -> printf() -> int $0x80 -> Linux and printer driver -> printer or screen "Hello World" using Linux System Calls your assembly code -> int $0x80 -> Linux and printer driver -> printer or screen CIS-Linux2:~/nov30>cat hello.s /* ************************************************************************ */ .section .text .globl _start _start: movl $4, %eax # sys_write function code movl $1, %ebx # file descriptor (sysout) movl $str, %ecx # starting address of string movl $length, %edx # length of string int $0x80 # transfer to kernel with code 0x80 (system call) movl $1, %eax # sys_exit function code movl $0, %ebx # return code 0 (everything is great) int $0x80 # transfer to kernel (and never return) str: .string "Hello World\n" strend: length = strend - str /* ************************************************************************ */ CIS-Linux2:~/nov30>as -o hello.o hello.s CIS-Linux2:~/nov30>dir hello.o hello.s CIS-Linux2:~/nov30>ld -o hello hello.o CIS-Linux2:~/nov30>dir hello hello.o hello.s CIS-Linux2:~/nov30>hello Hello World CIS-Linux2:~/nov30> System Calls from C CIS-Linux2:~/nov30>cat csyscall.c #include #include #include #include int main(void) { long ID1, ID2; /*-----------------------------*/ /* direct system call */ /* SYS_getpid (func no. is 20) */ /*-----------------------------*/ ID1 = syscall(SYS_getpid); printf ("syscall(SYS_getpid)=%ld\n", ID1); /*-----------------------------*/ /* "libc" wrapped system call */ /* SYS_getpid (Func No. is 20) */ /*-----------------------------*/ ID2 = getpid(); printf ("getpid()=%ld\n", ID2); return(0); } CIS-Linux2:~/nov30>gcc -m32 csyscall.c CIS-Linux2:~/nov30>a.out syscall(SYS_getpid)=32594 getpid()=32594 CIS-Linux2:~/nov30> System Calls from C, version 2 CIS-Linux2:~/nov30>cat mysyscall.c #include #include #include #include int main(void) { long ID1, ID2, ID3, ID4; /*-----------------------------*/ /* direct system call */ /* SYS_getpid (func no. is 20) */ /*-----------------------------*/ ID1 = syscall(SYS_getpid); printf ("syscall(SYS_getpid)=%ld\n", ID1); /*-----------------------------*/ /* "libc" wrapped system call */ /* SYS_getpid (Func No. is 20) */ /*-----------------------------*/ ID2 = getpid(); printf ("getpid()=%ld\n", ID2); /*-----------------------------*/ /* direct system call */ /* SYS_write (func no. is 4, STDOUT = 1) */ /*-----------------------------*/ char string1[] = "Direct call to SYS_write\n"; ID3 = syscall(SYS_write, 1, string1, sizeof(string1)); printf("Characters printed = %d\n", ID3); /*-----------------------------*/ /* "libc" wrapped system call */ /* SYS_write (Func No. is 4, STDOUT = 1) */ /*-----------------------------*/ char string2[] = "libc wrapped system call\n"; ID4 = write(1, string2, sizeof(string2)); printf("Characters printed = %d\n", ID4); return(0); } CIS-Linux2:~/nov30>gcc mysyscall.c CIS-Linux2:~/nov30>a.out syscall(SYS_getpid)=2237 getpid()=2237 Direct call to SYS_write Characters printed = 26 libc wrapped system call Characters printed = 26 CIS-Linux2:~/nov30> Linux System Calls Quick reference - http://www.digilife.be/quickreferences/QRC/LINUX%20System%20Call%20Quick%20Reference.pdf Common system calls - http://www2.cs.uregina.ca/~hamilton/courses/330/notes/unix/SystemCallsDoc.htm Linux system call table - http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html The fork() System Call CIS-Linux2:~/nov30>more fork.c #include #include int main(void) { int pid1 = fork(); int pid2 = fork(); int pid3 = fork(); int pid4 = fork(); int pid5 = fork(); printf("%d, %d, %d, %d, %d, %d\n", getpid(), pid1, pid2, pid3, pid4, pid5); } CIS-Linux2:~/nov30>gcc fork.c CIS-Linux2:~/nov30>./a.out 2605, 0, 2593, 2597, 0, 0 2604, 0, 2593, 2597, 0, 2605 2607, 0, 2593, 2597, 2604, 0 2608, 2592, 0, 2599, 2601, 0 2592, 0, 2593, 2597, 2604, 2607 2610, 2592, 2598, 2602, 0, 0 2598, 2592, 0, 2599, 2601, 2608 2609, 2592, 2598, 2602, 0, 2610 2611, 2592, 2598, 2602, 2609, 0 2613, 2592, 0, 0, 2600, 0 2591, 2592, 2598, 2602, 2609, 2611 2599, 2592, 0, 0, 2600, 2613 CIS-Linux2:~/nov30>2615, 0, 0, 2594, 2596, 0 2593, 0, 0, 2594, 2596, 2615 2617, 0, 0, 0, 2595, 0 2594, 0, 0, 0, 2595, 2617 2620, 2592, 2598, 0, 2606, 0 2602, 2592, 2598, 0, 2606, 2620 2622, 0, 2593, 0, 2603, 0 2614, 2592, 0, 0, 0, 0 2600, 2592, 0, 0, 0, 2614 2597, 0, 2593, 0, 2603, 2622 2612, 2592, 0, 2599, 0, 0 2616, 0, 0, 0, 0, 0 2601, 2592, 0, 2599, 0, 2612 2603, 0, 2593, 0, 0, 2618 2595, 0, 0, 0, 0, 2616 2606, 2592, 2598, 0, 0, 2619 2619, 2592, 2598, 0, 0, 0 2621, 0, 0, 2594, 0, 0 2596, 0, 0, 2594, 0, 2621 2618, 0, 2593, 0, 0, 0 CIS-Linux2:~/nov30> The execl() system call CIS-Linux2:~/nov30>more fork2.c #include #include #include #include int main(void) { execl("one", (char *) NULL); printf("We will never get here\n"); return(0); } CIS-Linux2:~/nov30>more one.c #include int main(void) { printf("really dumb program one\n"); } CIS-Linux2:~/nov30>gcc -o one one.c CIS-Linux2:~/nov30>gcc -o fork fork2.c CIS-Linux2:~/nov30>fork really dumb program one CIS-Linux2:~/nov30> The fork() and execl() system calls CIS-Linux2:~/nov30>more fork3.c #include #include #include #include int main(void) { int pid = fork(); if (pid == 0) { execl("one", (char *) NULL); printf("The child will never get here\n"); } printf("But the parent will get here\n"); return(0); } CIS-Linux2:~/nov30>gcc -o fork3 fork3.c CIS-Linux2:~/nov30>fork3 really dumb program one But the parent will get here CIS-Linux2:~/nov30>fork3 But the parent will get here really dumb program one CIS-Linux2:~/nov30>fork3 But the parent will get here really dumb program one CIS-Linux2:~/nov30>fork3 But the parent will get here CIS-Linux2:~/nov30>really dumb program one Microsoft Windows Windows API (Application Programming Interface) at http://msdn.microsoft.com/en-us/library/ff818516(VS.85).aspx Some Windows DLL (Dynamic Link Library) files c:/Windows/System32/kernel32.dll provides access to file systems, devices, processes and threads, and error handling. c:/Windows/System32/gdi32.dll provides the graphical device interface (output to printers and screens). c:/Windows/System32/user32.dll provides the GUI (windows, buttons, input from the keyboard and mouse, etc). c:/Windows/System32/Ntdll.dll provides a lower-level interface used to implement the Windows API (1 to 3 above), a Unix API, or other API. Listing entry points in kernel32.dll with objdump.