ucore中Makefile 内核文件组织全解析,学习软件的组织方式
2016-02-05 15:46
417 查看
练习1:理解通过make生成执行文件的过程
操作系统镜像文件ucore.img是如何一步一步生成的,ucore 中MakeFile 解析
内核代码编译过程分析:1.内核代码的编译过程
工具函数的详细解释 tools/function.mk
BJPREFIX := __objs_ .SECONDEXPANSION: # -------------------- function begin -------------------- # list all files in some directories: (#directories, #types) #返回相应directories目录下所有 类型为(types)的文件 #example 输入为listf(libs, c s),输出为libs/a.c libs/a.s listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ $(wildcard $(addsuffix $(SLASH)*,$(1)))) # get .o obj files: (#files[, packet]) # 给出文件名列表files,和软件包名称packet,返回相应文件的目标文件名称 #example $(call toobj,libs/a.c libs/b.c,__obj_),生成相应的输出makefile代码为 obj/__obj_/libs/a.o obj/__obj_/libs/b.o toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),$(addsuffix .o,$(basename $(1)))) # get .d dependency files: (#files[, packet]) #输入为文件名列表,输出为相应代码文件的依赖文件名列表 #example $(call todep,libs/a.c libs/b.c,__obj__),对应相应的makefile代码为 __obj_/libs/a.d __obj_/libs/b.d todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) #输出最总的目标文件完整路径名, #example $(call totarget,kernel),则对应于makefile代码为输出的最总内核目标文件为bin/kernel totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) # change $(name) to $(OBJPREFIX)$(name): (#names) #给定名字加上前缀$(OBJPREFIX) packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) # cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) #内核各个模块编译的C代码模板,迎来为每一个.c活着.s文件生成编译后的目标文件 define cc_template #生成依目标文件的依赖文件。4个$$$$符号是因为该代码要被eval两次,并且最终生成的makefile文件继续保留对规则目标文件名的引用 $$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) @$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ #该模板就是生成目标文件的规则, $$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) @echo + cc $$< $(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ #用ALLOBJS保存所有的目标文件 ALLOBJS += $$(call toobj,$(1),$(4)) endef # compile file: (#files, cc[, flags, dir]) #用来生成最总的makefile中的所有目标文件的规则。 define do_cc_compile $$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) endef # add files to packet: (#files, cc[, flags, packet, dir]) #此模板,就是真正在makefile中用来编译所有的目表文件,并生成makefile规则的模板。 define do_add_files_to_packet #__temp_packet__用来记录所有的临时目标文件。 __temp_packet__ := $(call packetname,$(4)) ifeq ($$(origin $$(__temp_packet__)),undefined) $$(__temp_packet__) := endif __temp_objs__ := $(call toobj,$(1),$(5)) $$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) $$(__temp_packet__) += $$(__temp_objs__) endef # add objs to packet: (#objs, packet) define do_add_objs_to_packet __temp_packet__ := $(call packetname,$(2)) ifeq ($$(origin $$(__temp_packet__)),undefined) $$(__temp_packet__) := endif $$(__temp_packet__) += $(1) endef # add packets and objs to target (target, #packes, #objs[, cc, flags]) #用来生成最终的target,在内核代码中,也就是最终的kernel和bootloader的makefile规则,$$(__temp_objs__) | $$$$(dir $$$$@) 该语句表示依赖规则的目标文件,还需要有目录的支持,如果目录不存在则应该创建,见后面规则。 define do_create_target __temp_target__ = $(call totarget,$(1)) __temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) TARGETS += $$(__temp_target__) ifneq ($(4),) $$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) $(V)$(4) $(5) $$^ -o $$@ else $$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) endif endef # finish all define do_finish_all ALLDEPS = $$(ALLOBJS:.o=.d) $$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) #如果相应目录不存在则执行makedir -p 命令 $(OBJDIR)$(SLASH)): @$(MKDIR) $$@ endef # -------------------- function end -------------------- # compile file: (#files, cc[, flags, dir]) cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) # add files to packet: (#files, cc[, flags, packet, dir]) add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) # add objs to packet: (#objs, packet) add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) # add packets and objs to target (target, #packes, #objs, cc, [, flags]) create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) add_dependency = $(eval $(1): $(2)) finish_all = $(eval $(call do_finish_all))
makefile中内核文件的生成过程
#makefile中109-117 # include kernel/user INCLUDE += libs/ CFLAGS += $(addprefix -I,$(INCLUDE)) LIBDIR += libs $(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) #此段的含义就是把内核目录代码中所有的libs/*c,的文件进行编译,最总把编译后的目标文件完整路径名保存在__temp__packet变量中,并且生成目标文件新目录路径应该为obj/libs/*.o,*.d #源代码的120-153 # kernel KINCLUDE += kern/debug/ \ kern/driver/ \ kern/trap/ \ kern/mm/ KSRCDIR += kern/init \ kern/libs \ kern/debug \ kern/driver \ kern/trap \ kern/mm KCFLAGS += $(addprefix -I,$(KINCLUDE)) #该句同上,只是目录变为了$(KSRCDIR),编译所有内核文件 #最踪生成的路径应该obj/kern/init/*.o... 并追加保存路径在__temp__packet中。 $(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) #应为所有的编译后的目标文件路径都保存在__temp_packet中,则该函数直接引用,用来最后的链接工作 KOBJS = $(call read_packet,kernel libs) # create kernel target #最总的目标文件obj/kernel kernel = $(call totarget,kernel) $(kernel): tools/kernel.ld #最总的目标文件的规则 $(kernel): $(KOBJS) @echo + ld $@ #链接obj/libs/*和obj/kernel/init/*...所有的目标文件生成elf-i386的内核文件,并且使用kernel.ld链接器脚本 $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) #最终的内核文件应该去除符号表等信息,并输出符号表信息,汇编文件信息,和输出信息 @$(OBJDUMP) -S $@ > $(call asmfile,kernel) @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) # create bootblock #启动扇区的编译,过程与内核差不多唯一的区别是需要对编译后的启动扇区进行签名,即有效启动扇区,最后字节为0x55aa。 bootfiles = $(call listf_cc,boot) $(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc)) bootblock = $(call totarget,bootblock) $(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign) @echo + ld $@ $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock) @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) @$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) @$(call totarget,sign) $(call outfile,bootblock) $(bootblock) $(call create_target,bootblock) # create 'sign' tools #在内核工具目录中,sign.c,用来给扇区签名的小工具,为什么这而使用host呢,是因为该工具是在特定操作系统下的工具,所以编译过程跟内核编译过程完全不同,最显著的就是nostdlibc内核是必须的编译选项,而应用软件一般都是依赖C库,并且内核代码为了精简,也没有栈溢出保护 --no-stack-protector $(call add_files_host,tools/sign.c,sign,sign) $(call create_target_host,sign,sign) #最后把编译出的二进制文件和bootloader都写进一个大文件中,用来模拟硬盘。使用linux下dd块命令 UCOREIMG := $(call totarget,ucore.img) $(UCOREIMG): $(kernel) $(bootblock) $(V)dd if=/dev/zero of=$@ count=10000 $(V)dd if=$(bootblock) of=$@ conv=notrunc $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc $(call create_target,ucore.img)
相关文章推荐
- Linux 自检和 SystemTap
- 应用领航:盘点那些年我们一起追过的OS
- 无奇不有!盘点各国自己开发的操作系统
- 一张图看尽 Linux 内核运行原理
- 可自定义oem的萝卜家园 Ghost XP 新春装机版 V200801 下载
- C#实现判断操作系统是否为Win8以上版本
- js获取本机操作系统类型的两种方法
- Linux操作系统添加新硬盘方法
- Linux内核链表实现过程
- java如何获取本地操作系统进程列表
- Linux rdesktop操作系统下远程登录Windows XP桌面
- 32位操作系统认出超出4G内存的方法
- Linux rpm tar 操作系统下软件的安装与卸载方法
- Linux/Unix环境下的Make和Makefile详解
- JavaScript 获取用户客户端操作系统版本
- jsp 获取客户端的浏览器和操作系统信息
- Windows 操作系统的安全设置
- php判断当前操作系统类型
- PHP获取用户的浏览器与操作系统信息的代码
- Perl操作系统环境变量的脚本代码