Sunday, February 18, 2007

New style system call in linux x86 [ref]

We all know that the x86 architecture is bizarre. An interesting event that has happened in the last 10 years was the change of the "system call" instruction. We all remember from DOS time that we issued "int 0x21" to make a DOS system call, and "int 0x10" to make a ROM BIOS system call. Some of us has even played with the linux system call, "int 0x80". However, five years ago, when using a Pentium IV, someone has found out that the "int" instruction could be half as fast in a Pentium IV than in a Pentium III. Why was that ?

Intel introduced a new system call instruction in Pentium Pro/Pentium II, called "sysenter" that was supposed to be a "Fast System Call" facility. This meant that Pentium processors had two system call instructions: one that was the fast system call and the other, the slow system call (currently, there are three system calls: "int", "sysenter" and "syscall" !!!). Linux has since migrated to new style system calls. Instead of changing the "int" logic to the "sysenter" logic, another approach was taken. A new virtual shared library was created, called "linux gate". This library is virtual because it's like a standard .so library, but only exists in memory (do a "ldd /bin/ls" and see if your system is using "linux-gate.so.1"). Applications (like libc or other system call front-ends) may then call a simple function exported from this library, __kernel_vsyscall, and this function will "systenter", "int" or issue whatever instruction is necessary for completing the system call. So here we have quick dirty "hello world" written in NASM assembly for linux (should work on linux 2.6 for x86). The code assumes that __kernel_vsyscall is always mapped at 0xffffe400 in memory (which is wrong ;-)).

; hello.asm
bits 32
global _start
section .data
   linux_gate: equ 0xffffe400
   hello: db 'Hello World !', 10
   hello_len: equ $-hello
section .text
_start:
   mov eax, 4 ; write()
   mov ebx, 1
   mov ecx, hello
   mov edx, hello_len
   call linux_gate

   mov eax, 1 ; _exit()
   mov ebx, 42
   call linux_gate

Notice that we are making a procedure call, not a system call. Compile it with "nasm -f elf hello.asm; ld -o hello hello.o" . :-)

No comments: