六、编译过程分析
作者:解琛
时间:2020 年 12 月 18 日
基于一个基本的 helloWorld 工程来分析其编译过程。
6.1 终端输出分析
使用 make
进行编译,终端输出如下。
moocos-> make
+ cc kern/init/init.c
+ ld bin/kernel
+ cc boot/bootasm.S
+ cc boot/bootmain.c
+ cc tools/sign.c
+ ld bin/bootblock
'obj/bootblock.out' size: 484 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0393024 s, 130 MB/s
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000188811 s, 2.7 MB/s
11+1 records in
11+1 records out
6138 bytes (6.1 kB) copied, 0.000236221 s, 26.0 MB/s
对终端的输出结果进行分析。
+ cc kern/init/init.c
+cc
表示编译了 kern/init
文件中的 init.c 文件。
+ ld bin/kernel
'obj/bootblock.out' size: 484 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
+ ld
表示将文件转化成可执行程序 kernel
,并放到 bin
目录下。这里转化成 bootblock.out
,可以理解成一个 Bootloader 的执行程序,大小为 484 字节。
再将 bootblock.out
转化为 512 字节对齐的、符合规范的主引导扇区文件 bootblock
,放到 bin
目录中。
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0393024 s, 130 MB/s
这里表示创建并生成了一个 5.1 MB 的虚拟硬盘,这个虚拟硬盘就会基于里面的数据来执行相应的代码,写入的速度为 130 MB/s。
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000188811 s, 2.7 MB/s
11+1 records in
11+1 records out
6138 bytes (6.1 kB) copied, 0.000236221 s, 26.0 MB/s
这里表示前面生成的 bootblock
引导程序和 kernel
内核程序被成功写入到了硬盘中。
6.2 编译详细过程分析
Gcc编译选项-fno-builtin -fno-builtin-function
用 make V=
命令查看 make
命令的详细执行过程。
moocos-> make V=
+ cc kern/init/init.c
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
+ ld bin/kernel
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o
+ cc boot/bootasm.S
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
+ ld bin/bootblock
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
'obj/bootblock.out' size: 484 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0369833 s, 138 MB/s
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000196741 s, 2.6 MB/s
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
11+1 records in
11+1 records out
6138 bytes (6.1 kB) copied, 0.000172118 s, 35.7 MB/s
现在对这段编译过程进行详细的分析。
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
使用 gcc 对 .c 文件进行预处理、编译和汇编,具体参数的含义如下。
参数 | 解释 |
---|---|
-Ikern/init/ | 从 kern/init/ 目录中寻找头文件; |
-fno-builtin | 不使用 C 语言的内建函数; |
-Wall | 编译后显示所有警告; |
-ggdb | 生成可供 gdb 使用的调试信息; |
-m32 | 生成适用于 32 位环境的代码; |
-gstabs | 生成 stabs 格式的调试信息; |
-nostdinc | 不在标准系统文件夹寻找头文件,只在 -I 等参数指定的文件夹中搜索头文件; |
-fno-stack-protector | 不生成用于检测缓冲区溢出的代码; |
-Ilibs/ | 从 libs/ 目录中寻找头文件; |
-Ikern/debug/ | 从 kern/debug/ 目录中寻找头文件; |
-Ikern/driver/ | 从 kern/driver/ 目录中寻找头文件; |
-Ikern/trap/ | 从 kern/trap/ 目录中寻找头文件; |
-Ikern/mm/ | 从 kern/mm/ 目录中寻找头文件; |
-c kern/init/init.c | 将 kern/init/ 目录中的 init.c 的 c 文件进行预处理、编译和汇编; |
-o obj/kern/init/init.o | 指定输出的文件到 obj/kern/init/ 目录中,并命名为 init.o。 |
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o
使用 ld 命令将指定文件链接为可执行程序,具体参数的含义如下。
参数 | 解释 |
---|---|
-m elf_i386 | 指定模拟仿真链接器为 elf_i386; |
-nostdlib | 只搜索命令行上显式指定的库目录,在链接器脚本中指定的库目录(包括在命令行中指定的链接器脚本)将被忽略; |
-T tools/kernel.ld | 指定链接器的脚本为 tools 目录下的 kernel.ld 文件; |
-o bin/kernel obj/kern/init/init.o | 将转化好的可执行文件放到 bin/ 目录下,并命名为 kernel,链接的文件为 obj/kern/init/ 目录中的 init.o。 |
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
参数 | 解释 |
---|---|
-Os | 为减小代码大小而进行优化; |
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
参数 | 解释 |
---|---|
-g | 编译时保留代码的文字信息,便于 gbk 调试; |
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
参数 | 解释 |
---|---|
-N | 将文本和数据部分设置为可读和可写,不对数据段进行页对齐,禁用对共享库的链接; |
-e start | 使用 start 作为程序开始执行的显式符号,而不是默认的入口点;如果没有名为 start 的符号,链接器将尝试将条目解析为一个数字,并使用该数字作为入口地址(该数字将以 10 进制进行解析;可以使用前缀 0x 表示 16 为基数,或使用前缀 0 表示 8 进制); |
-Ttext 0x7C00 | -Ttext 等同于 --section-start,即将 0x7C00 作为段的起始地址, -e start -Ttext 0x7C00,即使用 start 作为程序开始的显式符号,若不存在,则使用 0x7C00 作为段的起始地址; |
dd if=/dev/zero of=bin/ucore.img count=10000
使用以上 dd 命令,通过将 /dev/zero 文件拷贝的方式来创建一个 5.1 MB 的虚拟硬盘 ucore.img。
参数 | 解释 |
---|---|
if=/dev/zero | 从 /dev/zero 文件中读取内容来替代标准输入 stdin; |
of=bin/ucore.img | 将输出的内容写入到 bin 目录下的 ucore.img,替代标准的输出 stdout; |
count = 10000 | 复制 10000 个输入块; |
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
参数 | 解释 |
---|---|
conv=notrunc | 根据逗号分隔的符号列表转换文件,不截断输出文件; |
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
参数 | 解释 |
---|---|
seek=1 | 在输出开始时跳过 1 个单位大小的块; |