libc提供系统调用接口和程序的入口支持等。为了实现一个独立的程序,需要实现入口和系统调用。
先看源代码,nostd.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #ifndef __NOSTD_H__ #define __NOSTD_H__ __asm__ ( ".global _start\n\t" "_start:\n\t" "call main\n\t" "movl $1, %eax\n\t" "xorl %ebx, %ebx\n\t" "int $0x80\n\t" ); #define O_RDONLY 00 #define O_WRONLY 01 #define O_RDWR 02 #define O_CREAT 0100 #define O_EXCL 0200 #define O_NOCTTY 0400 #define O_TRUNC 01000 #define O_APPEND 02000 #define O_NONBLOCK 04000 #define O_SYNC 04010000 #define O_FSYNC O_SYNC #define O_ASYNC 020000 // note: `naked` attribute is not supported on x86 arch static int open( char *path, int flags, int mode) { __asm__ ( "pushl %ebx\n\t" "movl $0x05, %eax\n\t" "movl 8(%ebp), %ebx\n\t" "movl 12(%ebp), %ecx\n\t" "movl 16(%ebp), %edx\n\t" "int $0x80\n\t" "popl %ebx\n\t" ); } static int write( int fd, const void *buf, int count) { __asm__ ( "pushl %ebx\n\t" "movl $0x04, %eax\n\t" "movl 8(%ebp), %ebx\n\t" "movl 12(%ebp), %ecx\n\t" "movl 16(%ebp), %edx\n\t" "int $0x80\n\t" "popl %ebx\n\t" ); } static int read( int fd, void *buf, int count) { __asm__ ( "pushl %ebx\n\t" "movl $0x03, %eax\n\t" "movl 8(%ebp), %ebx\n\t" "movl 12(%ebp), %ecx\n\t" "movl 16(%ebp), %edx\n\t" "int $0x80\n\t" "popl %ebx\n\t" ); } static int close( int fd) { __asm__ ( "pushl %ebx\n\t" "movl $0x06, %eax\n\t" "movl 8(%ebp), %ebx\n\t" "int $0x80\n\t" "popl %ebx\n\t" ); } #endif |
这个头文件大部分是用汇编代码实现的,当然也可以用纯汇编文件,这里使用内联汇编。首先是函数外的汇编,如下:
1 2 3 4 5 6 7 8 | __asm__ ( ".global _start\n\t" "_start:\n\t" "call main\n\t" "movl $1, %eax\n\t" "xorl %ebx, %ebx\n\t" "int $0x80\n\t" ); |
只有简单汇编才能放到函数外,这里和直接写在汇编文件中是一样的。上述代码定义一个全局符号_start。默认的链接脚本会使用_start符号作为入口点。
对应的汇编指令是调用main函数,接着调用sys_exit系统调用退出程序。
接下来就是open/read/write/close的系统调用,有了这几个接口,我们就能输出/输入数据到文件、tty设备等。在linux中一切设备皆文件。
这里需要注意的是函数里面的汇编不需要入口处的压栈和退出的ret。gcc会自动为函数添加。
和普通的hello有点不同,我们打开/dev/tty(当前终端)设备、写字符串,然后关闭。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include "nostd.h" int main() { int ret; int fd = open( "/dev/tty" , O_WRONLY, 0); if (fd < 0) return 1; ret = write(fd, "hello\n" , 6); if (ret < 0) return 2; close(fd); return 0; } |
当然直接写入文件描述符1(stdout)或者2(stderr)也是可以的。
编译命令:cc -m32 -nostdlib main.c
运行结果:
1 2 | herbert@Lenovo:/work/link/nostdlib$ ./a.out hello |