tiny210(S5PV210)uboot的顶层Makefile的连接命令理解——记tiny210之uboot移植

贴上Makefile中的连接规则:

$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot

上面的连接规则看起来很复杂的样子,也让我纠结了好一阵子,后来查了很多资料终于理解了。下面来逐个击破:

       目标和依赖不难理解,就不解释了。

      接下来,看看UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`,注意了,这段命令是以TAB开头的,所以应该是shell里面执行命令。这命令应该是先执行“``”围起来的命令,然后将其得到的结果赋给shell变量UNDEF_SYM,强调一下,这是shell变量不是Makefile的变量。接下来拆分,各部分来理解。$(OBJDUMP) -x $(LIBS),展开来是:arm-linux-objdump -x $(LIBS),-x选项表示获取后面的库文件中所有可以获得的头部信息,包括符号表和重定位入口。

       这里介绍一下符号表,每个库文件目标文件都包含有未决符号表和导出符号表,未决符号表相当于记录了目标中引用的的外部函数或者变量,这些函数和变量就会被记录在未决符号表中,那未决符号表相当于告诉编译器我这里没有这些函数和变量的定义,你去别的目标文件中查找导出符号表查一下有没有。而导出符号表则记录了该目标中的全局变量和全局函数。故连接器根据这两个表就可以正确连接。更详细的相关解释看一下http://my.oschina.net/anyway/blog/11262

       $(OBJDUMP) -x $(LIBS)之后,其结果通过管道传送到sed作为其输入,sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p',sed是流编辑命令,-e表示将后面的's/.*\(__u_boot_cmd_.*\)/-u\1/p'添加到要执行的命令列表,也就是进行相关的编辑操作,-n表示不自动打印字符行,默认情况下,sed编辑器逐行处理文件(或输入),并将结果发送到屏幕,具体过程如下:首先sed把当前正在处理的行保存在一个临时缓存区中(sed也称为pattern space),然后处理临时缓冲区中的行,完成后把该行发送到屏幕上;sed一行一行地处理pattern space 中的副本,直至文本结束's/.*\(__u_boot_cmd_.*\)/-u\1/p',一看其符合字符串替换s/str1/str2,最后面的p表示打印出前面替换后的字符,中间使用的是正则匹配,意思是在前面匹配好的字符串前面加-u,如前面匹配了__u_boot_cmd_bootm,则输出为-u__u_boot_cmd_bootm.而uboot所有的命令都是以__u_boot_cmd_格式命名的,因此所有的命令都被加上了-u前缀,这很像是命令的选项参数对不对?没错,这就是在构造命令的选项,后面会再讲到。加了-u前缀后通过管道作为sort的输入,sort将其排序后再将结果通过管道传给uniq以去除相同的重复项,至于为什么要排序之后再调用uniq,是因为uniq只能删除排序文件中的重复行。

       到这里,先理一理这条命令的意思:找出库文件中uboot的所有命令并为其添加前缀-u,然后赋给shell变量UNDEF_SYM。

       接下来就是连接的重头戏了。

cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot

cd $(LNDIR) 表示先切换到连接目录,成功后执行后面的命令。这里就讲讲难以理解的两个选项参数

$$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group

$$表示转义为$,那么在shell环境中执行时就是$UNDEF_SYM,取出变量的内容则是很多-u__u_boot_cmd_bootm之类的加了前缀的-u命令,ln选项中-u代表什么意思呢?ld手册里面这样解释:-u sym 把  sym  作为  未定义(undefined)  的 符号 送入 输出文件.  这样做可以, 例如, 促使连接器 从 库中连接某个附加的模块。这句话怎么理解呢?考虑一下我们平时连接时是怎样的呢,如果目标文件显式引用了某个库中的函数,那么连接时连接器就会到库中把相应的函数连接进来,这里要强调的是没有被引用的库函数并不会被连接器连接进来。那么我们如果希望某些函数或模块没有被显示引用到也被连接器加入来进行连接时-u选项就派上用场了,利用-u强制把希望连接的函数或模块作为未定义的符号输入到连接器中,连接器发现有未定义符号就会在库中查找相应的函数作为输入文件,所以-u选项就相当于强制地将库中的某些函数作为输入文件以使生成的执行文件包含这些函数。比如,-u__u_boot_cmd_bootm就__u_boot_cmd_bootm函数强制连接到可执行文件中去了,那么$UNDEF_SYM就把迫使uboot所有的命令函数都连接到可执行文件中去了,所有uboot的命令都被连接了,也就相当于说明完整的uboot被完整连接了,因为所有的命令都实现了。

       那这里又会有一个疑问,为什么要强迫连接器去连接那些命令呢?就是因为uboot的命令架构问题,uboot的很多命令在代码中并未曾被显式地引用过,也就是相关的命令标号(即函数名)未曾在代码中引用过,这又是怎么做到的呢?这就得益于gcc拓展的特殊属性__attribute__ ((section ("")))与及连接脚本,他们将所有命令的相关信息如命令名字等信息集中在一片连续的空间中,相当于数组,结合连接脚本就可以做到不使用相关的命令函数名也可以检索出命令所在的地址,相关的知识,有兴趣的可自己去学学如何为uboot添加命令,应该就明白了。

      --start-group $(__LIBS) --end-group是什么意思呢?关于--start-group archives --end-group,手册的解释是The specified archives are searched repeatedly until no new undefinedreferences are created.也就说连接器会循环地在archives中解决未定义符号直到所有的未定义标号都被解决,为什么是循环呢?因为通常情况下库只会被查找一次,而--start-group archives --end-group中的archive文件有可能还会被后面命令行上指定的lib中的目标所依赖(即引用archive中的目标),这时就需要再次到--start-group archives --end-group中去找,使用--start-group archives --end-group就可以使archives被多次查找,否则连接器无法解决这种依赖关系。贴上手册的完整解释:

-( archives -) or --start-group archives --end-group

The archives should be a list of archive files. They may be either explicit file names, or -l options.

The specified archives are searched repeatedly until no new undefined references are created. Normally, an archive is searched only once in the order that it is specified on the command line. If a symbol in that archive is needed to resolve an undefined symbol referred to by an object in an archive that appears later on the command line, the linker would not be able to resolve that reference. By grouping the archives, they all be searched repeatedly until all possible references are resolved.

Using this option has a significant performance cost. It is best to use it only when there are unavoidable circular references between two or more archives.

-Map选项的意思是:把  连接映像  输出到  mapfile  文件  中  —  有关 ld 把 符号 映射到 何处 的 诊断信息, 以及全局公共存储器 的 分配 信息. -o则是生成可执行文件的名称。

到这里,uboot的连接规则算是勉强解释完了,水平有限,解释的逻辑很乱,以上都是个人的见解,如有不对的地方,恳请予以纠正,共同进步

转载请注明出处

你可能感兴趣的:(210学习)