rules.mk文件内容
_depend: $(obj).depend
$(obj).depend: $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
@rm -f $@
@for f in $(SRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
我觉得,要看懂这些,需要在一开始把所有的变量都记录下来,后面再把所有的变量给展开,就好懂
很多了。
上面文件内容最难懂的一句是:
g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
这里大概就是说,把basename $$f 得到的结果送到sed执行。
而sed的作用是把$$f的后缀(.xxx)改成(.o)
所以,rules.mk的内容就是:
为了生成_depend,需要依赖$(obj).depend
而为了生成$(obj).depend,需要依赖$(src)Makefile $(TOPDIR)/config.mk $(SRCS)
首先删除已有的目标
然后遍历SRCS(估计是每一个文件夹)
然后执行
把所有的x.x改成x.o
然后赋值给g
最后运行
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(OBJ)$$g $$f >> $@
我这里不是很明白$$是啥意思,从执行来看,就是个双指针,但是为什么这么做呢?
难道跟for语句有关?分析一下shell中的for语句先。
果然是这样,因为是数组,所以要用两个$$才能拿到变量值
所以就是说
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(OBJ)xxx.o xxx.x >> $@
这一句有2两个详细关键字,分别是
basename 和sed
关于命令跳到下面看,这里只看上面一句的意思,所以,首先把$$f变量的目录去掉,剩下文件名
传送给sed命令
分解一下$$f,首先f是一个变量,第一步获取$f,然后这个$f再作为一个变量,有点双指针的意思。
然后是:
sed -e 's/\(.*\)\.\w/\1.o/'basename 命令:返回一个字符串参数的基本文件名称
再terminal中测试一下 basename 命令得到一下结果
$ basename //hello/hello.c
hello.c
$ basename --help
Usage: basename NAME [SUFFIX]
or: basename OPTION... NAME...
Print NAME with any leading directory components removed.
If specified, also remove a trailing SUFFIX.
Mandatory arguments to long options are mandatory for short options too.
-a, --multiple support multiple arguments and treat each as a NAME
-s, --suffix=SUFFIX remove a trailing SUFFIX; implies -a
-z, --zero end each output line with NUL, not newline
--help display this help and exit
--version output version information and exit
Examples:
basename /usr/bin/sort -> "sort"
basename include/stdio.h .h -> "stdio"
basename -s .h include/stdio.h -> "stdio"
basename -a any/str1 any/str2 -> "str1" followed by "str2"
GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/basename>
or available locally via: info '(coreutils) basename invocation'
翻译一下上面的help意思
首先是语法:
basename NAME [SUFFIX]
或者是
basename OPTION... NAME...
意思:把NAME中的前导目录组件移除后再打印,如果如果有规定SUFFIX部分,同样需要移除掉
简单来说就是移除掉目录和SUFFIX指定的部分得到剩下的东西并返回
具体怎么用可以看Examples,简单直接sed命令:
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
-n, --quiet, --silent
suppress automatic printing of pattern space
-e script, --expression=script
add the script to the commands to be executed
-f script-file, --file=script-file
add the contents of script-file to the commands to be executed
--follow-symlinks
follow symlinks when processing in place
-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)
-l N, --line-length=N
specify the desired line-wrap length for the `l' command
--posix
disable all GNU extensions.
-r, --regexp-extended
use extended regular expressions in the script.
-s, --separate
consider files as separate rather than as a single continuous
long stream.
-u, --unbuffered
load minimal amounts of data from the input files and flush
the output buffers more often
-z, --null-data
separate lines by NUL characters
--help display this help and exit
--version output version information and exit
If no -e, --expression, -f, or --file option is given, then the first
non-option argument is taken as the sed script to interpret. All
remaining arguments are names of input files; if no input files are
specified, then the standard input is read.
GNU sed home page: <http://www.gnu.org/software/sed/>.
General help using GNU software: <http://www.gnu.org/gethelp/>.
首先sed是一种流编辑器,可以搜索,可以替换,用这个命令前需要掌握一点正则表达式的基础。
用法:sed [OPTION]... {script-only-if-no-other-script} [input-file]...
以分析目标举例
sed -e 's/\(.*\)\.\w/\1.o/'
-e就是script
add the script to the commands to be executed
把脚本添加到命令中去并执行
大概就是说,按照这个正则表达式去匹配输入内容得到匹配的结果把。
s表示替换
所以把输入的字符串中符合‘\(.*\)\.\w’的替换成\1.o
首先用正则表达式来分解‘\(.*\)\.\w’
首先,把sed的中的字符集给移除掉。
sed中,\(..\)表示匹配字串,保存匹配的字符
所以,把\(xxx\)中的xxx保存到\1中
分为3部分
.*
开头以'('结尾以')'的任意除了换行符以外的所有字符
\.
\w
\. 表示匹配一个'.'字符
\w 匹配任何单个 "单词" 字符, 即字母, 数字或下划线. 这等同于 [a-zA-Z0-9_].
相反地, 大写的 \W 表示 "任何 非单词字符".
所以整个句子的意思就是,就是把a.c中的.c换成.o
例如main.c 变成 main.o
/
/
分析Makefile
编译uboot时,第一步是配置
make 100ask24x0_config
在Makefile中,可以查到这个目标:
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
SRCTREE := $(CURDIR)
化简就是:
100ask24x0_config : unconfig
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
mkconfig文件注释如下
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
-gt 表示 大于
现在有6个,OK
$1 = 100ask24x0
没有--
没有-a
没有-n
所以跳过
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
||表示条件如果为假则执行后面的语句,引申&&表示条件如果为真则执行后面的语句
由于BOARD_NAME = NULL,所以执行后面的语句
BOARD_NAME = 100ask24x0
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
参数个数如果小于4或者大于6都退出,现在刚好6个,跳过
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1
打印 "Configuring for ${BOARD_NAME} board..."
echo "Configuring for 100ask24x0 board..."
#
# Create link to architecture specific headers
#
如果SRCTREE不等于OBJTREE,执行then后面,否则执行else后面语句
分析Makefile开头时,
SRCTREE := $(CURDIR)
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
OBJTREE表示如果BUILD_DIR存在则是BUILD_DIR,否则是CURDIR
截取Makefile中的BUILD_DIR语句
如果定义了O,则行下面语句,所以跳过
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
如果BUILD_DIR != NULL,执行下面的语句,所以也跳过
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)
所以SRCTREE和OBJTREE都是NULL
由于SRCTREE和OBJTREE都是NULL,所以执行else后面的语句
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
#进入include文件夹,删除asm,然后建立一个连接文件
cd ./include
rm -f asm
ln -s asm-$2 asm
#上面表示:ln -s asm-arm asm
fi
删除 asm-arm/arch文件夹
rm -f asm-$2/arch
$6长度为0或者%6为NULL执行then后面语句,否则则行else后面语句
$6 = s3c24x0,所以执行else后面语句
if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi
if [ "$2" = "arm" ] ; then
#删除 asm-arm/proc
rm -f asm-$2/proc
#LNPREFIX为NULL所以下面简化为:ln -s proc-armv asm-arm/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
输入以上参数到config.mk, >表示新建, >>表示追加,所以config的内容是:
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
$5不为空且$5不为NULL执行后面,但是$5为NULL,所以跳过
同样,$6=s3c24x0,所以追加,最后config.mk的内容就是
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0
APPEND为NO,执行else,新建一个config.h文件
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
然后追加下面内容到config.h文件里
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
所以config.h的内容是:
/* Automatically generated - do not edit */
#include <configs/100ask24x0.h>
最后完成退出
exit 0
//
配置完,执行make
all: $(ALL)
all依赖ALL
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
以下是Makefile的$(obj)内容,所以obj为空
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
如果CONFIG_NAND_U_BOOT == yes
但是Makefile中没有,所以为空所以上面命令化简为:
all: u-boot.srec u-boot.bin System.map $(U_BOOT_NAND)
u-boot.srec: u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
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
System.map: u-boot
@$(NM) $< | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
sort > $(obj)System.map
上面把变量删除得到的
所以我们的目标得到的u-boot.bin是通过u-boot得到的
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
所以make主要执行了这个
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 /home/inc/work/uboot_s3c2440/u-boot-1.1.6 && arm-linux-ld -Bstatic -T \
/home/inc/work/uboot_s3c2440/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 /home/inc/work/tool_s3c2440/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5 -lgcc \
-Map u-boot.map -o u-boot
从上面命令可以看出,怎么组织代码,主要看连接脚本 u-boot.lds
在这里,后面存在一个:-Ttext = 0x33F80000
从连接文件可以得到,第一个文件是start.o,然后是boot_init.o
{
第一个文件是start.o,然后是100ask24x0/boot_init.o
cpu/arm920t/start.o (.text)
board/100ask24x0/boot_init.o (.text)
*(.text)
}
所以分析源码先从这两个文件开始分析起来