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 archstatic 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.outhello |