学习韦东山视频uboot之Makefile分析

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)
}
所以分析源码先从这两个文件开始分析起来


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
也可去华为网盘下载! http://dl.dbank.com/c0f2x98c6n# 第1课 环境搭建及工具、概念介绍 第2课 GPIO实验 第3课 存储管理器实验 第4课 MMU实验 第5课 NAND FLASH控制器 第6课 中断控制器 第7课 系统时钟和UART实验 第8课 LCD实验 第9课第1节 u-boot分析之编译体验 第9课第2节 u-boot分析Makefile结构分析 第9课第3节 u-boot分析之源码第1阶段 第9课第3节 u-boot分析之源码第2阶段 第9课第4节 u-boot分析之u-boot命令实现 第9课第5节 u-boot分析_uboot启动内核 第10课第1节 内核启动流程分析之编译体验 第10课第2节 内核启动流程分析之配置 第10课第3节 内核启动流程分析Makefile 第10课第4节 内核启动流程分析之内核启动 第11课第1节 构建根文件系统之启动第1个程序 第11课第2节 构建根文件系统之init进程分析 第11课第3节 构建根文件系统之busybox 第11课第4节 构建根文件系统之构建根文件系统 第12课第1节 字符设备驱动程序之概念介绍 第12课第2.1节 字符设备驱动程序之LED驱动程序_编写编译 第12课第2.2节 字符设备驱动程序之LED驱动程序_测试改进 第12课第2.3节 字符设备驱动程序之LED驱动程序_操作LED 第12课第3节 字符设备驱动程序之查询方式的按键驱动程序 第12课第4.1节 字符设备驱动程序之中断方式的按键驱动_Linux异常处理结构 第12课第4.2节 字符设备驱动程序之中断方式的按键驱动_Linux中断处理结构 第12课第4.3节 字符设备驱动程序之中断方式的按键驱动_编写代码 第12课第5节 字符设备驱动程序之poll机制 第12课第6节 字符设备驱动程序之异步通知 第12课第7节 字符设备驱动程序之同步互斥阻塞
韦东山老师为啥要录升级版嵌入式视频?200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。我记得我2005年进入中兴时,全部门的人正在努力学习Linux。在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。那时还没有这样讲解整个系统的书,芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。有全系统开发能力的人也很少。于是这书也就恰逢其时,变成了畅销书。我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。收获些许名声,带领很多人进入Linux世界。11年过去了,嵌入式Linux世界发生了翻天覆地的变化① 基本系统能用芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。② 基础驱动弱化;高级驱动专业化基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。很多所谓的驱动工程师,实际上就是“调参工程师”。我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。配置一下应用层工具就了事,能用就成。这些高级驱动,工作中需要专门的人来负责,非常专业。他们是某一块的专家,比如摄像头专家、音频专家。③ 项目为王你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。中小公司玩不起华为中兴的配置,需要的是全面手。大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。④ 调试很重要都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。基于上述4点,我录制的全新视频将有这些特点:1. 快速入门,2. 实战项目,3. 驱动大全,4. 专题,5. 授人以渔,6. 要做任务另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。1.快速入门入门讲究的是快速,入门之后再慢慢深入,特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快!再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。搞不好学了后面忘了前面。并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。讲哪些内容?怎么讲呢?混着讲比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。这样可以快速掌握嵌入式Linux的整套开发流程,不必像以前那样光学习裸机就花上1、2个月。而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。讲基础技能中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。这些技能是嵌入式Linux开发的基础。而这些驱动,只会涉及LED、按制、LCD等几个驱动。掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。讲配置我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它?需要懂shell和python等配置脚本。效果效率优先以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。这次,效果与效率统一考虑,不再追求所有东西都现场写。容易的地方可先写好代码文档,难的地方现场写。2.实战项目会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                   定位为:快速掌握项目开发经验,丰满简历。涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。3. 驱动大全包括基础驱动、高级驱动。这些驱动都是独立成章,深入讲解。虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。定位为:工具、字典,用到再学习。4. 专题想深入学习的任何内容,都可独立为专题。比如U-boot专题、内核内存管理专题、systemtap调试专题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值