ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

makefile

2021-04-03 03:32:09  阅读:255  来源: 互联网

标签:bin kernel obj bootblock makefile rule 生成


如何阅读makefile

作为使用者,makefile读起来真的是又干又硬。我读的第一篇makefile——清华大学ucore的makefile由两部分组成:提供一套生成rule的API并维护生成文件列表的function.mk,和Makefile。这篇makefile我读了三次。

第一遍读的时候秉承着自顶向下的原则,直接开始读Makefile,好像大概了解了他在干什么,但不知道他具体怎么做,在探究期间get到查阅了GNU文档的熟练度,总算了解到了rule,prerequsite,recipe,variable这些概念,顺着读下来也读了个七七八八,把语法给弄懂了(虽然现在还不知道多重dollar的意义是什么)

第二遍读的时候是对照着输出来看的。这个时候做了些后边的题,对需要生成的文件有了大致概念,再对照着Makefile看,发现了当时看被忽略掉的function.mk,饶有兴趣地看了会,但是Makefile的语法实在是。。。。不堪入目,函数形参名直接用数字表示,由实际调用去一层一层向上找被调用的函数试图理解,没多久就被绕晕了。。。

第三遍读是ddl前一晚,由于我发现把“ucore.img是如何一步步生成的”这一题给答成“如何写Makefile”了,赶紧重新看了一遍。虽然最终我满意的这个版本没有即时完成,但我重新学到了很多看Makefile的方法。

由于前面提到的Makefile自定义函数参数名很乱,于是我干脆从function.mk开始看起。还有一个原义是因为function.mk是作者自定义的函数库,它的每一个部分都会被用到,不存在白看的情况。由于Makefile的语法本身很复杂,所以我直接按照之前看的经验,将函数分成两类看,分为名字转换,编译规则生成两大类,并直接记下他们的名字和效果,省去了一个个分析语法的麻烦。然后很快就能发现,再Makefile中使用时,这些函数只不过是提前被填充了一些字段,或者直接是套娃,加个foreach而已。

对于生成的文件从何而来,我找到了生成相应路径名的函数,使用Vim的搜索功能很方便的就查找出了所有使用这些函数的Rule,那些Prerequisite大概率就是他们的编译源文件。

所以总结下来,看Makefile只需要:

  • 熟悉Makefile语法
  • 明确目标:了解最终有什么文件被生成,文件名从何处来,生成这些文件的Rule在哪里,这些文件的源文件是什么,这些文件的源文件就是原始文件吗?不是的话他们又是怎么被生成和记录的?
  • 简化函数,提取核心函数:这些函数叫什么名字,他们的作用是什么,记录下来

对于写报告,一定要抓住核心,不要写任何无关问题仅仅是为了展示努力程度的知识,这样只会显得报告冗长甚至走题。

以下就是我对ucore.img如何一步步生成的分析

ucore.img 如何一步步生成

编译参数解释

GCC
$gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs\
	-nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc \
	-c boot/bootasm.S -o obj/boot/bootasm.o
	
# [-Idir...]: 将dir添加到头文件搜索路径。
# -fno-builtin: 屏蔽不以__builtin_开头的内置函数
# -Wall:  打开所有warning选项,范围包括宏定义的conjunction
# -ggdb: 生成GDB使用的debug信息
# -m32: 以32bit为单位对齐
# -gstabs: 生成stabs格式的debug信息(不包含GDB扩展)
# -nostdinc: 改变system directories的搜索顺序
# -fno-stack-protector: 关闭stack-protector,避免编译器增加guard代码检查堆栈分配函数
# -Os: 以最小代码量为目标优化,具体是使用不会增加代码量的-O2优化
# -c: 编译或汇编原文件,但不链接。(输出.o结尾的object file)
# -o: 将输出放到指定的file中。
预处理器
define cc_template
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
	@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@">$$@
	
# Option that control the preprocessor
# -MM: 使用make描述依赖的rule替代preprocessor的输出  e.g.: filename.o : file1.c file2.c
# -MT "<string>": target就是你指定的string
ld
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel $^
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 -o obj/bootblock.o $^
# -m elf_i386:	仿真elf_i386 linker
# -nostdlib:	
# -T: 			用指定的linker script(tools/kernel.ld) 替换默认linker script
# -N --OMAGIC: 	将数据和代码区设为可读写,取消数据段的页对齐,禁止链接共享库,允许Unix magic number。
# -e : 			指明程序入口为start
# -Ttext: 		指明text段的地址为 0x7C00
# $^: 			makefile 自动变量,整个prerequisite列表 
objdump
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
# -S --source:	反汇编并显示源码(-d)
objcopy
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
# -S --strip-all: 不复制调试信息
# -O binary: output a binary file

Makefile解读

function.mk

名字转换

toobj:		obj/[dir/]filebasename.o
todep:		obj/[dir/]filebasename.d
totarget:	bin/filename
packetname:	__obj_[prefix]

包(文件列表)

listf: 将dir/*[.<type>]列出

do_add_files_to_packet -> cc_template: 编译files并将生成的objs并加入packet中
do_add_objs_to_packet: 将objs加入packet中

do_create_target: 使用taget,packet里的objs和指定的objs(可以指定是否链接)创建rule

do_finish_all: 创建ALLOBJS的文件夹, obj/,bin/

函数

do_cc_compile -> cc_template:
	1. 预处理为make生成文件obj/[dir/]filename.d, 内含target为.o .d的rule
	2. 编译生成目标文件.o
makefile_定制function

名字

listf_cc: 文件夹dir下.c文件列表
objfile: filebasename.o
asmfile: filebasename.asm
outfile: filebasename.out
symfile: filebasename.sym

生成rule

add_files_cc/hostcc -> add_files = do_add_files_to_packet: 用files生成编译型rule,并将目标文件加入到到指定的packet里,还可以选定obj下的子文件夹(这里并没有用到)
create_target_cc/hostcc -> create_target = 使用target,packet,和obj生成编译型rule
makefile_kernel
  1. 将所有lib文件夹下的文件都生成编译rule,并添加到packet lib中

  2. 将所有kernel依赖文件都生成编译rule,并将目标文件添加到packet kernel中

  3. 自定义bin/kernel的生成rule,prerequisite为lib包和kernel包中的目标文件

  4. bin/kernel加入TARGET中。

makefile_bootloader
  1. 对每个boot文件夹下的文件都创建编译rule

  2. 自定义bin/bootblock的生成rule

  3. bin/bootblock加入TARGET中

makefile_sign

使用本机选项编译tools/sign.c并将sign添加到TARGET中

makefile_ucore.img
  1. 使用字节拷贝工具dd将bootblock和kernel拷贝到一片空白文件中,制作磁盘映像

  2. bin/ucore.img加入到TARGET中

各文件的关系

理清了各个函数的作用和在makefile中的使用,我们应该已经能知道生成的文件是什么,由那些操作来,源文件是什么。

总共生成了两个文件夹:obj和bin。前缀bin从totarget中来,前缀obj从toobj中来。

只需查找toobj就能发现:使用toobj的rule是在cc_template中定义的,而cc_template只在cc_compile, add_files_to_packet中出现,调用cc_compile的只在编译各个boot文件夹下文件时出现过,因此确定:boot/*.c --> obj/boot*.o;而add_files_to_packet在Makefile中被重新封装成add_files_cc和add_files_host,使用搜索工具,发现在创建packet lib、kernel、sign, 编译lib、kernel子文件夹下的.c文件和tools/sign.c时,使用了add_files_cc和add_files_host,因此确定:lib/*.c --> obj/lib/*.okernel/*/*.c --> lib/kernel/*/*.otools/sign.c --> lib/tools/sign.c

再来查找totarget:在function.mk中只有create_target使用了totarget创建rule,该函数在Makefile中被定制成了create_target_host 和 create_target_cc,查找这些函数,发现创建了以下target:

  • bin/kernel:由obj/libs obj/kernel 的.o文件链接而成
  • bin/bootblock:由obj/boot 的 .o 文件链接而成
  • bin/sign:由bin/tools/sign/的.o文件链接而成
  • bin/ucore.img: 由dd工具将bin/bootblock和bin/kernel拼接生成

但是obj文件夹中,除了boot kernel libs sign文件夹下的文件,还有一些文件是从何而来呢?

其中bootblock.asm bootblock.o bootblock.out 来自于 bin/bootblock的生成rule:

  • bootblock.asm:由objdump反汇编bootblock而来
  • bootblock.o:由objcopy将调试信息剔除而来
  • bootblock.out:在bootblock.o的基础上,用bin/sign工具处理后的结果

对应的,kernel.asm和kernel.sym来自于 bin/kernel的生成rule:

  • kernel.asm:objdump工具打印的kernel的反汇编结果
  • kernel.sym:objdump工具打印的kernel的符号表入口,使用sed过滤后的结果

标签:bin,kernel,obj,bootblock,makefile,rule,生成
来源: https://www.cnblogs.com/laiyk/p/14613030.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有