gcc provides the inline assembly statement feature to let programmers use assembly language from C, which was very useful to write the Linux system call interface as well as to use some machine-dependent instructions not used by gcc.
System calls under Linux are implemented via int $0x80. Typically, the syscall with three arguments looks like:
#include <sys/syscall.h> extern int errno; int read (int fd, void *buf, size count) { long ret; __asm__ __volatile__ ("int $0x80" : "=a" (ret) : "0" (SYS_read), "b" ((long) fd), "c" ((long) buf), "d" ((long) count): "bx"); if (ret >= 0) { return (int) ret; } errno = -ret; return -1; }
The asm statement above puts the system call number, SYS_read
,
into
eax, fd into ebx, buf into ecx,
count into edx and then puts eax in ret
upon return from int $0x80. This definition works fine without
-fPIC. Ideally, with -fPIC gcc should dectect that
ebx
will be clobbered and save/restore it around the asm statement. But
unfortunately, this is not the case. We have to support PIC in the
asm statement ourselves:
#include <sys/syscall.h> extern int errno; int read (int fd, void *buf, size count) { long ret; __asm__ __volatile__ ("pushl %%ebx\n\t" "movl %%esi,%%ebx\n\t" "int $0x80\n\t" "popl %%ebx" : "=a" (ret) : "0" (SYS_read), "S" ((long) fd), "c" ((long) buf) "d" ((long) count): "bx"); if (ret >= 0) { return (int) ret; } errno = -ret; return -1; }
Here we put fd into esi first, then we save ebx, move esi into ebx and restore ebx after int $0x80. This ensures ebx is not changed (except during int $0x80, but we don't care).
The same principle also applies to other inline assembly statements. ONE HAS TO SAVE AND RESTORE EBX ANYTIME WHEN IT MAY BE CHANGED.