前言:
我们分析一个工程的时候,想知道它的结构,它是如何链接的,最好的方法就是分析它的Makefile。
U-boot编译步骤:
1、配置:make xxx_config
2、编译:make
注:先配置再编译的由来,是u-boot README说明文档得来。
在分析Makefile之前,我们发现U-boot目录下这么多的Makefile,这么多的文件,我们应该如何进行查找呢。
顺藤摸瓜法:
我们以make 100ask24x0_config 配置为例:
1、首先编译U-boot ,拷贝到ubuntu 的/mnt 共享目录里 ,打开主目录下的Makefile,搜索100ask24x0_config 。
得到如下代码:
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
2、想弄明白上诉代码,我们需要先知道:MKCONFIG所代表的含义,所以我们在主Makefile中再查找MKCONFIG所在的定义,得到如下代码:
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
这句话由语义看出,MKCONFIG 是由原码树mkconfig文件得到。
3、我们查看主目录下是否有mkconfig这个文件。查看得到确实有这个文件,所以
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0 这句话翻译过来就是:
mkconfig 100ask24x0_config arm arm920t 100ask24x0 NULL s3c24x0
$0 $1 $2 $3 $4 $5 $6 注:下文计算参数需用。
4、分析mkconfig文件
-----------------------------------------------------------------------------------------------------------
while [ $# -gt 0 ] ; do #分析上述命令传入的参数
case "$1" indone 注:我们发现并没有这些参数,该段可省略不看。
---------------------------------------------------------------------------------------------------------------
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
#如果BOARD_NAME已经定义,不会执行BOARD_NAME="$1",否则执行BOARD_NAME="$1"
[ $# -lt 4 ] && exit 1 #如果参数个数小于4个--退出echo "Configuring for ${BOARD_NAME} board..." #参数个数合法,打印这句话
---------------------------------------------------------------------------------------------------------------
if [ "$SRCTREE" != "$OBJTREE" ] ; then #如果SRCTREE变量不等于OBJTREE变量,则执行下列创建目录、删除等一系列操作
mkdir -p ${OBJTREE}/include 注:在主Makefile中查找这两个定义,它们是相等的,所以执行else分支。mkdir -p ${OBJTREE}/include2 主Makefile :
cd ${OBJTREE}/include2 OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))fi 注:asm 是指向架构的链接,这样头文件会自动选择链接指向的构架
---------------------------------------------------------------------------------------------------------------
rm -f asm-$2/arch #rm -f asm-asm/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then #我们有第六个参数,所以执行else分支
ln -s ${LNPREFIX}arch-$3 asm-$2/arch #LNPREFIX 无定义
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch #ln -s arch-s3c24x0 asm-asm/arch
fi
f [ "$2" = "arm" ] ; then #第二个参数是arm 成立
rm -f asm-$2/proc # rm -f asm-arm/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc #ln -s proc-armv asm-arm/proc
fi
---------------------------------------------------------------------------------------------------------------
echo "ARCH = $2" > config.mk #新建config.mk 文件,里面填写ARCH = arm
echo "CPU = $3" >> config.mk CPU = arm920t
echo "BOARD = $4" >> config.mk BOARD = 100ask24x0
SOC = s3c24x0
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
---------------------------------------------------------------------------------------------------------------
创建单板相关的头文件
if [ "$APPEND" = "yes" ] # Append 定义为no 执行else分支
then
echo >> config.h
else
> config.h # 创建config.h
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include
该文件分析得到:100ask24x0_config.h 为我们的配置文件。
配置过程总结:
就是根据mkconfig这个脚本文件,来判断解析参数,生成cpu构架指向链接,生成含有ARCH、CPU、BOARD 、SOC等信息的 config.mk文件以供主Makefile使用及创建单板相关的头文件。
5、分析编译过程
#前面版本号之类的省略
include $(OBJTREE)/include/config.mk #此处包含了配置过程中生成的config.mk文件,替换主Makefile的变量
export ARCH CPU BOARD VENDOR SOC 选择交叉编译工具链、库等
OBJS = cpu/$(CPU)/start.o #此处根据config.mk文件选择 使用哪一个Start.o
LIBS = lib_generic/libgeneric.a #此处根据config.mk文件选择库
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)
---------------------------------------------------------------------------------------------------------------
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all: $(ALL) #主编译依赖关系
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
$(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=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd /work/system/u-boot-1.1.6 && arm-linux-ld -Bstatic -T /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o \
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group -L /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5 -lgcc \
-Map u-boot.map -o u-boot
arm-linux-objcopy --gap-fill=0xff -O srec u-boot u-boot.srec
arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin
上文蓝色部分,得到了链接文件u-boot.lds 代码段的基地址:0x33F80000 及文件Start.o。
然后就是所有用到的库。
6、链接脚本分析:u-boot.lds
路径:根据上文蓝色字体,从编译信息中即可得到链接脚本所在目录
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; /* -Ttext 0x33F80000 指定排放开始地址为:0x00000000+0x33F80000 */
. = ALIGN(4);
.text : /* 第一个排放的内容,cpu/arm920t/start.o,由此我们得到,U-boot第一个文件为Start.S */
{
cpu/arm920t/start.o (.text) /* start.o的代码段 */
board/100ask24x0/boot_init.o (.text) /* boot_init.o的代码段 */
*(.text) /* 所有文件的代码段 */
}
. = ALIGN(4);
.rodata : { *(.rodata) } /* 所有文件的只读数据段 */
. = ALIGN(4);
.data : { *(.data) } /* 所有文件的数据段 */
. = ALIGN(4); /* 全局变量地址段 */
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .; /* u-boot 命令段 ,只有U-boot自己有*/
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4); /* bss 段 */
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}