ILD

无c库支持的hello world程序
作者:Herbert Yuan 邮箱:yuanjp89@163.com
发布时间:2017-7-14 站点:Inside Linux Development

libc提供系统调用接口和程序的入口支持等。为了实现一个独立的程序,需要实现入口和系统调用。

1 nostd.h

先看源代码,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会自动为函数添加。

2 main.c

和普通的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)也是可以的。

3 编译运行

编译命令:cc -m32 -nostdlib main.c

运行结果:

1
2
herbert@Lenovo:/work/link/nostdlib$ ./a.out
hello


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.