嵌入式爱好者

查看: 8866|回复: 13

[技术分享] 嵌入式Linux开发实用教程(连载)

  [复制链接]

0

主题

312

帖子

285

积分

扫一扫,手机访问本帖
发表于 2013-12-20 11:27:40 | 显示全部楼层 |阅读模式
本帖最后由 zzq宁静致远 于 2014-4-10 18:30 编辑

    《嵌入式linux开发实用教程》一书在大家的期待中终于面世,本书由人民邮电出版社出版:


    《嵌入式linux开发实用教程》一书以OK6410为载体,从嵌入式Linux基础到u-boot-2013.04移植,再到linux-3.8.3移植、再到linux驱动程序设计、再到Qt-4.8.4移植以及Qt应用程序设计,最终以一个实战项目收尾。全书定位一个从未接触过嵌入式的初学者,将其引导进入嵌入式linux。由浅入深、深入浅出。
2014年4月8日:
     《嵌入式Linux开发实用教程》一书已经出版,本帖将会持续更新,后续更会有裸板视频和项目视频更新。敬请关注。
    《嵌入式Linux开发实用教程》购买地址:
http://item.taobao.com/item.htm?spm=a1z10.1.w6545579-4546600052.3.8xcC8H&id=36731267737

    1.本书及视频QQ群:
嵌入式Linux开发实用教程1:284013595  (1000人)
嵌入式Linux开发实用教程2:271641475   (1000人,已满)
嵌入式Linux开发实用教程3:301012138    (500人)
深圳市馒头科技有限公司官方百度网盘(C语言、单片机、嵌入式视频):
http://pan.baidu.com/share/link?shareid=3562495290&uk=3996269986

2.《嵌入式Linux开发实用教程》视频清单:
第1课:Linux基本操作指令
第2课:Makefile
第3课:Linux常用软件
第4课:U-Boot-2013.04搭建适合OK6410模板
第5课:初步编译U-Boot-2013.04
第6课:U-Boot-2013.04启动分析1
第7课:U-Boot-2013.04启动分析2
第8课:SD卡启动U-Boot原理
第9课:SD卡启动U-Boot-2013.04移植1(解开众多商家SD卡启动机密)
第10课:SD卡启动U-Boot-2013.04移植2(解开众多商家SD卡启动机密)
第11课:SD卡启动
第12课:Signal # 8 caught错误
第13课:MMC驱动移植
第14课:FAT文件系统
第15课:U-Boot命令
第16课:NAND Flash移植(1)
第17课:NAND Flash移植(2)
第18课:NAND Flash移植(3)
第19课:DM9000网卡移植
第20课:Linux-3.8.3内核介绍
第21课:初步测试内核(1)
第22课:初步测试内核(2)
第23课:下载地址和入口地址
第24课: MTD分区
第25课:NAND Flash移植
第26课:DM9000网卡移植
第27课:使内核支持YAFFS2文件系统
第28课:制作YAFFS2文件系统
第29课:LCD移植
第30课:字符设备驱动之LED
第31课:字符设备驱动之ADC
第32课:块驱动
第33课:tslib安装
第34课:安装Linux和embedded版本Qt-4.8.4
第35课:安装QtCreator编译环境
第36课:Qt初体验之Hello
第37课:Qt之LED
第38课:Qt之ADC
第39课:项目拓展学习(1)
第40课:项目拓展学习(2)
第41—50课:裸板程序设计
3. 嵌入式Linux实用教程软件:
Ubuntu10.04.4镜像、VMware-7.0.1虚拟机、Source Insight3.5+注册码、SecureCRT_6.6.1_PiaoXu.net、SD_Writer、UltraEdit10c、USB转串口驱动、等等嵌入式Linux常用软件。
4. 嵌入式Linux实用教程资料
S3c6410相关手册、Linux常用书籍、嵌入式Linux实用手册初稿、等等。
5. 嵌入式Linux开发实用教程源码
linux-3.8.3、u-boot-2013.04-rc1、自制编译器、等等相关源码
6. 嵌入式Linux实用教程程序
《嵌入式Linux开发实用教程》一书中涉及的U-Boot添加程序、Linux程序、Linux设备驱动程序、Qt程序等等
下载说明:
百度网盘:

[url=]http://pan.baidu.com/share/link?shareid=3562495290&uk=3996269986[/url]

    别再为嵌入式Linux太难无法入门而困扰,从今天起,跟随朱老师每天学习一讲,一步一步学习嵌入式linux,相信这个难关不再难。曾经太多人问我,怎么学习,其实很简单,克服自己的心理障碍,马上投入学习,你就走出了第一步。


第二章第一节  U-Boot-2013.04分析与移植之BootLoader概述
http://bbs.witech.com.cn/forum.p ... 3&fromuid=14255



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2013-12-20 11:29:51 | 显示全部楼层
    第一章第一讲    Linux基本命令
    在进行嵌入式Linux学习开发的过程中,将经常使用到Linux的操作命令。实际上,Linux系统中的命令也是为实现特定的功能而编写的程序,而且绝大数的命令是用C语言编写的。有些实用性强的程序被广泛使用和传播,逐渐地演变成Linux的标准命令。但是Linux的操作命令繁多,本节将在U-Boot、Linux移植过程中常用到的Linux操作命令罗列出来进行讲解,为后续的学习做好良好的铺垫。读者不要认为这是Linux简单命令则不屑一顾,嵌入式Linux学习是一个漫长的过程,循序渐进方能有所成就,这个过程是由每一小步累加而成的。天下难事,必作于易;天下大事,必作于细。所以读者务必要对待学习的每一个细节。
1.1.1   文件属性查询与修改1.   文件属性查询
“ls”命令在Linux目录中占据着重要地位,主要用于查看文件属性、查看目录下所包含的文件等。
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/busybox-1.20.2/_install$ ls
bin              dev  home  linuxrc  proc  sbin  tmp  var
creat_yaffs2.sh  etc  lib   mnt      root  sys   usr
通过“ls”命令即查看_install目录下有哪些东西。如果要进一步查看文件属性,则使用“ll”命令或者“ls -al”命令,这两个命令是等效的。
zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/busybox-1.20.2/_install$ ll
总用量 64
drwxr-xr-x 15 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 .
drwxr-xr-x 35 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 15:34 ..
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 15:34 bin
-rw-r--r--  1 zhuzhaoqi zhuzhaoqi  393 2013-03-17 16:32 creat_yaffs2.sh
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 dev
drwxr-xr-x  3 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 21:01 etc
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 home
drwxr-xr-x  3 zhuzhaoqi zhuzhaoqi 4096 2013-03-18 09:57 lib
lrwxrwxrwx  1 zhuzhaoqi zhuzhaoqi   11 2013-03-17 15:34 linuxrc -> bin/busybox
drwxr-xr-x  5 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 mnt
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 proc
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 root
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 15:34 sbin
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 sys
drwxrwxrwx  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 tmp
drwxr-xr-x  7 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 usr
drwxr-xr-x  2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 16:33 var
这样每一个文件的属性将一目了然。而属性中的每一个数据都有特定的含义。如表1. 1所示。
drwxr-xr-x 2 zhuzhaoqi zhuzhaoqi 4096 2013-03-17 15:34 bin
文件权限 连接数 文件所有者 文件所属用户组 文件大小 文件最后一次被修改的时间 文件名称

而其中文件权限的10个字符含义如表1. 2所示。
文件类型 文件所有者的权限 文件所属用户组的权限 其他人对此文件的权限
d r w x r - x r - x
目录 可读 可写 可执行 可读 无权限 可执行 可读 无权限 可执行

因此/bin目录的文件权限是:文件所有者对/bin目录可读可写可执行,文件所属用户组对/bin目录可读不可写可执行,其他人对/bin目录可读不可写可执行。
当对某个文件进行操作,要特别注意这个文件是否具有将要进行操作的权限。如果我们所在的用户组没有操作权限而又得进行操作,此时就得修改文件的权限。
1.   文件权限修改
“chmod”命令是使得一个文件变更权限。
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/include$ ll
总用量 8
drwxr-xr-x 2 zhuzhaoqi zhuzhaoqi 4096 2013-03-18 22:02 ./
drwxr-xr-x 3 zhuzhaoqi zhuzhaoqi 4096 2013-03-18 22:07 ../
-rw-r--r-- 1 zhuzhaoqi zhuzhaoqi    0 2013-03-18 22:02 s3c6410.h
从上一小节可知,“drwxr-xr-x”除了“d”是文件类型,剩下9个字符划分成3组,表示3个用户组的使用权限。而在Linux系统中,每一个用户组的3个字母分别可用数字进行描述其权限,r:4、w:2、x:1、-:0,将每一组的数字进行相加,即得到这组用户的权限。例如上面s3c6410.h的权限是:rw-r--r--,那么每一用户组权限分别是:6、4、4,那么组合起来即为:644。每个文件的最高权限为:777。
给予s3c6410.h最高权限,如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/include$ chmod 777 s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/kernel/include$ ll
总用量 8
drwxr-xr-x 2 zhuzhaoqi zhuzhaoqi 4096 2013-03-18 22:02 ./
drwxr-xr-x 3 zhuzhaoqi zhuzhaoqi 4096 2013-03-18 22:07 ../
-rwxrwxrwx 1 zhuzhaoqi zhuzhaoqi    0 2013-03-18 22:02 s3c6410.h*
通过“chmod”更改权限命令可以看到s3c6410.h的权限是最高权限。
1.1.2   目录与路径处理命令1.   切换目录
“cd”命令的作用是从当前目录切换到另一个目录下。如从用户根目录进入/linux目录下,如下操作:
zhuzhaoqi@zhuzhaoqi-desktop:~$ cd linux/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$
2.   创建新目录
“mkdir”命令的作用是创建一个新的目录,如在/linux目录下再创建一个/linux-3.8.3子目录,如下操作:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ mkdir linux-3.8.3
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
linux-3.8.3
mkdir的用法很多,可以从过输入mkdir –help查看,如下:
zhuzhaoqi@zhuzhaoqi-desktop:~$ mkdir --help
用法:mkdir [选项]... 目录...
若指定目录不存在则创建目录。
长选项必须使用的参数对于短选项时也是必需使用的。
  -m, --mode=模式    设置权限模式(类似chmod),而不是rwxrwxrwx 减umask
  -p, --parents     需要时创建目标目录的上层目录,但即使这些目录已存在也不当作错误处理
  -v, --verbose     每次创建新目录都显示信息
-Z, --context=CTX  将每个创建的目录的SELinux 安全环境设置为CTX
--help      显示此帮助信息并退出
--version       显示版朩信并退出
mkdir –p这个指令在U-Boot和Linux内核源码中的Makefile中的使用是相当频繁的。
3.   删除目录
如果是删除一个空目录,则使用“rmdir”命令即可;如果该目录下有东西,则不能使用“rmdir”命令删除。
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/linux-3.6.7$ ls
arch
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/linux-3.6.7$ cd ..
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
linux-3.6.7  linux-3.8.3
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ cd linux-3.8.3/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/linux-3.8.3$ ls
zhuzhaoqi@zhuzhaoqi-desktop:~/linux/linux-3.8.3$ cd ..
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
linux-3.6.7  linux-3.8.3
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ rmdir linux-3.8.3/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
linux-3.6.7
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ rmdir linux-3.6.7/
rmdir: 删除 "linux-3.6.7/" 失败:目录非空
上面操作可知,由于/linux-3.8.3目录为空,则可使用“rmdir”删除;但是/ linux-3.6.7目录下有一个子目录/arch,则不能使用“rmdir”删除。此时则应该使用“rm -r”命令删除。
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
linux-3.6.7
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls linux-3.6.7/
arch
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ rm -r linux-3.6.7/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$
通过“ls”命令可知,linux目录下的linux-3.6.7/目录以及被删除。
1.1.3   文件操作1.   新建文件
新建一个文件可以使用“vim”命令,但是使用“vim”命令退出打开的文件时需要保存退出,否则会视为没有创建文件。
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ vim s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
s3c6410.h
2.   复制文件
复制文件命令为“cp”。如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ cp s3c6410.h include/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls include/
s3c6410.h
如果要复制并且重命名,如下操作:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  kernel  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ cp s3c6410.c include/s3c6400.c
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls include/
s3c6400.c  s3c6410.h
当复制目录时,使用“cp -r”命令。如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  kernel  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls kernel/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ cp -r include/ kernel/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  kernel  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls kernel/
include
3.   移动文件
移动一个文件则使用“mv”命令,如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  kernel  s3c6410.c  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ mv s3c6410.c kernel/
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls
include  kernel  s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ ls kernel/
include  s3c6410.c
编辑一个文件,作者提倡使用“gedit”命令或者“vim”命令。
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ gedit s3c6410.h
zhuzhaoqi@zhuzhaoqi-desktop:~/linux$ vim s3c6410.c
1.1.4   打包与解包、压缩与解压缩
熟悉打包与解包、压缩与解压缩的操作命令是能很好在Linux操作文件的必备技能,而Linux下的打包与解包、压缩与解压缩的操作命令也是种类繁多,本节截取常用的8个格式进行讲解。本节中,FileName是指打包、压缩之后的文件名,DirName是指待打包、压缩的文件名。
1)      .tar格式
单纯的tar功能其实仅仅是打包而已,也就是说将很多文件集结成一个文件,并没有进行压缩。
解包:tar xvf FileName.tar
打包:tar cvf FileName.tar DirName
2)      .gz格式
GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。在Linux中经常会碰到后缀名为.gz的文件,它们的原型即是GZIP格式。
解压1:gunzip FileName.gz
解压2:gzip -d FileName.gz
压缩:gzip FileName
3)      .tar.gz格式和 .tgz格式
以.tar.gz和.tgz为后缀名的压缩文件在在Linux和OSX下是非常常见的,Linux和OSX都可以直接解压使用这种压缩文件。
解压:tar zxvf FileName.tar.gz
压缩:tar zcvf FileName.tar.gz DirName
4)      .bz2格式
压缩生成后缀名为.bz2的压缩算法使用的是“Burrows-Wheeler block sorting text”,这类算法压缩比率比较高。
解压1:bzip2 -d FileName.bz2
解压2:bunzip2 FileName.bz2
压缩: bzip2 -z DirName
这里需要注意的是,当执行压缩指令之后,将会生成FileName.bz2压缩文件,同时DirName文件将会自动删除。
5)      .tar.bz2格式
bzip2是一个压缩能力非常强的压缩程序,以.bz2和.tar.bz2为后缀名的压缩文件都是bzip2压缩的结果。
解压:tar jxvf FileName.tar.bz2
压缩:tar jcvf FileName.tar.bz2 DirName
6)      .Z格式
compress 是一个相当古老的 unix 压缩指令,压缩后的文件是以.Z 作为后缀名。
解压:uncompress FileName.Z
压缩:compress DirName
7)      .tar.Z格式
解压:tar Zxvf FileName.tar.Z
压缩:tar Zcvf FileName.tar.Z DirName
8)      .zip格式
ZIP因为格式开放而且免费,越来越多的软件支持打开Zip文件。
解压:unzip FileName.zip
压缩:zip FileName.zip DirName
以上8种打包压缩算法都有所区别,最终导致的结果是压缩时间和压缩大小的不一样。每一种压缩格式都有其优势和不足,在何种场应该使用何种压缩格式就得视实际情况而定了。
在程序设计当中,空间换取时间、时间换取空间的现象是非常常见的一种方法。比如在单片机中LED跑马灯中,经常使用数组中取出想要的花样,这就是空间换取时间。
回复 支持 反对

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2013-12-22 19:17:28 | 显示全部楼层
第一章第二节  Makefile基本知识
Makefile如今能得以广泛应用,这还得归功于它被包含在Unix系统中。在make诞生之前,Unix系统的编译系统主要由“make”“install”shell脚本程序和程序的源代码组成。它可以把不同目标的命令组成一个文件,而且可以抽象化依赖关系的检查和存档。这是向现代编译环境发展的重要一步。1977年,斯图亚特·费尔德曼在贝尔实验室里制作了这个软件。2003年,斯图亚特·费尔德曼因发明了这样一个重要的工具而接受了美国计算机协会(ACM)颁发的软件系统奖。
Makefile文件是可以实现自动化编译,只需要一个“make”命令,整个工程就能完全自动编译,极大的提高了软件开发的效率。目前虽有众多依赖关系检查工具,但是make是应用最广泛的一个。一个程序员会不会写makefile,从一个侧面说明了这个程序员是否具备完成大型工程的能力。
1.1.1   Makefile规则
一个简单的Makefile语句由目标、依赖条件、指令组成。
smdk6400_config :   unconfig
    @mkdir -p $(obj)include $(obj)board/samsung/smdk6400
smdk6400_config:目标;
unconfig:先决条件;
@mkdir -p $(obj)include $(obj)board/samsung/smdk6400:指令。这里特别注意,“@”前面是Tab键,并且必须是Tab键,而不能是空格。
目标和先决条件是依赖关系,目标是依赖于先决条件生成的。
1.1.2   Makefile变量1.   变量的引用方式
使用“$(OBJTREE)”或者“${ OBJTREE }”来引用OBJTREE这个变量的定义。这个引用方式似乎很像C语言中的指针变量,使用*p来取存放在指针p中的值。
obj := $(OBJTREE)/
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
export BUILD_DIR=/tmp/build
$(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))的含义:如果“BUILD_DIR”变量值不为空,则将变量“BUILD_DIR”指定的目录作为一个子目录;否则将目录“CURDIR”作为一个子目录。
2.   递归展开式变量
这类变量的定义是通过“=”和“define”来定义的。
student = lilei
CLASS = $(student) $(teacher)
teacher = yang
all:
    @echo $(CLASS)
其优点是:这种类型递归展开式的变量在定义时,可以引用其它的之前没有定义的变量,这个变量可能在后续部分定义,或者是通过make的命令行选项传递的变量。
其缺点是:其一,使用此风格的变量定义,可能会由于出现变量的递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败。
x = $(y)
y = $(z)
z = $(x)
这样的话会使得Makefile出错,因为都最终引用了自己。
其二,这种风格的变量定义中如果使用了函数,那么包含在变量值中的函数总会在变量被引用的地方执行。
3.   直接展开式变量
为了避免递归展开式变量存在的问题和不方便。GNU make支持另外一种风格的变量,称为直接展开式变量。这种风格的变量使用“:=”定义。在使用“:=”定义变量时,变量值中对其他量或者函数的引用在定义变量时被展开,也就是对变量进行替换。
X := student
Y := $(X)
X := teacher
all:
    @echo $(X) $(Y)
这里的输出是:teacher  student
这个直接展开式变量在定义时就完成了对所引用变量和函数的展开,因此不能实现对其后定义变量的引用。
4.   条件赋值
在对变量进行赋值之前,会对其进行判断,只有在这个变量之前没有进行赋值的情况下才会对这个变量进行赋值。
X := student
X ?= teacher
all:
    @echo $(X)
由于X在之前被赋值了,所以这里的输出是student
5.   变量的替换引用
对于一个已经定义的变量,可以使用变量的替换引用将变量中的后缀字符使用指定的字符替换。格式为“$(X:a=b)”(或者“${X:a=b}”),即是将变量“X”中所有“a”字符结尾的字替换为“b”结尾的字。
X := fun.o main.o
Y := $(X: .o=.c)
all:
    @echo $(X) $(Y)
特别注意的是$(X: .o=.c)的“=”两边不能有空格。输出是:fun.o main.o  fun.c main.c
6.   追加变量值
追加变量值是指一个通用变量在定义之后的其他一个地方,可以对其值进行追加。也就是说可以在定义时(也可以不定义而直接追加)给它赋一个基本值,后续根据需要可随时对它的值进行追加(增加它的值)。在Makefile中使用“+=”(追加方式)来实现对一个变量值的追加操作。
X = fun.o main.o
X += sub.o
all:
    @echo $(x)
这里输出是:fun.o main.o sub.o
1.1.3   Makfile常用关键字1.   ifneq关键字
这个关键字是用来判断两个参数是否不相等。格式为:
ifneq “Value1”“Value2”
ifneq (Value1,Value2)
在判断之前先要将Value1Value2的值进行展开和替换,如在U-Boot-2013.04的顶层目录Makefile中,对U-Boot的版本参数就使用了ifneq关键字进行判断。
VERSION      = 2013
PATCHLEVEL   = 04
SUBLEVEL     =
EXTRAVERSION =
ifneq "$(SUBLEVEL)" ""
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
else
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
endif
先将SUBLEVEL使用$()展开和替换,如果SUBLEVEL的值不是空,则执行:
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
也就是说,如果$(SUBLEVEL) = 1的话,那么U_BOOT_VERSION = 2013.04.1
如果SUBLEVEL的值是空,则是执行:
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
那么此时U_BOOT_VERSION = 2013.04
2.   ifeq关键字
ifeq关键是和ifneq关键字相对而言,用来判断两个参数是否相等。格式为:
ifeq “Value1”“Value2”
ifeq (Value1,Value2)
ifneq一样,先要将Value1Value2展开替换之后,再进行比较。
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE=/usr/local/arm/4.4.1/bin/arm-linux-
endif
如果HOSTARCH展开替换之后和ARCH展开替换之后相等,则:
CROSS_COMPILE=/usr/local/arm/4.4.1/bin/arm-linux-
否则CROSS_COMPILE不等于/usr/local/arm/4.4.1/bin/arm-linux-
3.   ifndef关键字
ifndef关键字用来判断一个变量是否没有进行定义。格式:
ifndef Value
由于在Makefile中,没有定义的变量的值为空。
ifndef CONFIG_SANDBOX
SUBDIRS += $(SUBDIR_EXAMPLES)
endif
如果CONFIG_SANDBOX值为空,条件成立,执行如下语句:
SUBDIRS += $(SUBDIR_EXAMPLES)
否则不执行。
4.   ifdef关键字
ifdef关键字用来判断一个变量是否已经进行定义过。格式:
ifdef Value
如:
ifdef CONFIG_SYS_LDSCRIPT
        # need to strip off double quotes
        LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
endif
如果CONFIG_SYS_LDSCRIPT定义过,则执行:
LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
否则不执行。
1.1.4   Makefile常用函数1.   Makefile函数语法
在Makefile中,函数的调用和变量的调用类似,都是使用“$”进行标识。语法如下:
$(函数名 函数的参数)
${函数名 函数的参数}
函数名与函数的参数之间使用空格隔开,而函数的参数间使用逗号进行分隔。以上两种写法都是可以的,但是为了风格统一,请不要两者进行混合使用。
2.   shell函数
make可以使用shell函数和外部通信。shell函数本身的返回值是其参数的执行结果,没有进行任何处理,对结果的处理是由make进行。当对函数的引用出现在规则的命令行中,命令行在执行时函数才被展开。展开时函数参数(shell命令)的执行是在另外一个shell进程中完成的,因此需要对出现在规则命令行的多级“shell”函数引用需要谨慎处理,否则会影响效率(每一级的“shell”函数的参数都会有各自的shell进程)。
建立一个测试程序,Makefile的内容:
zhu := $(shell cat func)
all:
        @ echo $(zhu)
Func文件中的内容:
juxst zhuzhaoqi
执行完成Makefile之后:
zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/Makefile/shellfunction$ make
juxst zhuzhaoqi
U-BootLinux内核源码中将会大量使用到shell函数。
3.   subst函数
subst函数是字符串替换函数,语法为:
$(subst 被替换字串 替换字串 替换操作字符串)
执行subst函数之后,返回的是执行替换操作之后的字符串。如下:
name := zhu zhaoqi
Alphabet_befor := z
Alphabet_after := Z
Name := $(subst $(Alphabet_befor), $(Alphabet_after), $(name))
all:
        echo $(Name)
执行上面Makefile,输出结果为:
echo   Zhu  Zhaoqi
Zhu Zhaoqi
即是将“z”替换成“Z”.
4.   dir函数
dir函数作用为取出该文件的目录,其语法为:
$(dir 文件名称)
执行该函数之后返回文件目录部分。
Makefile中常用函数较多,笔者就不一一例举,读者可参考相关文献进行深入了解。

回复 支持 反对

使用道具 举报

jbh15385476 该用户已被删除
发表于 2014-1-9 15:40:11 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2014-2-22 10:39:49 | 显示全部楼层
jbh15385476 发表于 2014-1-9 15:40
不错,开始学习
新书来了要第一个买

现在已经可以开始购买。
http://item.taobao.com/item.htm? ... &id=36731267737
回复 支持 反对

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2014-2-22 10:41:59 | 显示全部楼层
第一章第三节  arm-linux交叉编译链
平常我们做的编译叫本地编译,也就是在当前平台编译,编译得到的程序也是在本地执行。相对而言的交叉编译指的是在一个平台上生成另一个平台的可执行代码。
常见的交叉编译有以下三种:
Windows PC上,利用ADSARM 开发环境),使用armcc编译器,编译出针对ARM CPU可执行代码
Linux PC上,利用arm-linux-gcc编译器,编译出针对Linux ARM平台的可执行代码
Windows PC上,利用cygwin环境,运行arm-elf-gcc编译器,编译出针对ARM CPU可执行代码
1.1.1   arm-linux交叉编译工具链的制作方法
由于一般嵌入式开发系统存储大小是有限的,通常都要在性能优越的PC上建立一个用于目标机的交叉编译工具链,用该交叉编译工具链在PC上编译目标机上要运行的程序,比如在PC平台(X86 CPU)上编译出能运行在以ARM为内核的CPU平台上的程序。要生成在目标机上运行的程序,必须要用交叉编译工具链完成。交叉编译工具链是一个由编译器、连接器和解释器组成的综合开发环境,交叉编译工具链主要由binutilsgccglibc 3个部分组成。有时出于减小libc 库大小的考虑,也可以用别的 c 库来代替 glibc,例如 uClibcdietlibc newlib。建立交叉编译工具链是一个相当复杂的过程,如果不想自己经历复杂繁琐的编译过程,网上有一些编译好的可用的交叉编译工具链可以下载,但就以学习为目的来说读者有必要学习自己制作一个交叉编译工具链。本节通过具体的实例讲述基于ARM的嵌入式Linux交叉编译工具链的制作过程。
制作arm-linux交叉编译工具链的一般通过crosstool工具或者crosstool_NG,前者使用方便,但是制作会受到一些限制,使用crosstool最多只能编译gcc 4.1.1glibc 2.x的版本。crosstool-NG是新的用来建立交叉工具链的工具,它是crosstool的替换者,而crosstool_NG则有更好的定制性,并且一直保持着更新,对新版本的编译工具链的支持比较好,当然也带来了一些麻烦,它并不是下载下来就可以使用,必须先配置安装。我们这里选用crosstool_NG来制作我们的编译链。
1.   安装crosstool_NG
在crosstool_NG官网上下载最新版本,官网链接:http://crosstool-ng.org/
zhuzhaoqi@zhuzhaoqi-desktop:~$ mkdir arm-linux-tools
zhuzhaoqi@zhuzhaoqi-desktop:~$ cd arm-linux-tools/
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ ls
获取源码操作命令:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ wget http://crosstool-ng.org/download ... l-ng-1.18.0.tar.bz2
正在解析主机 crosstool-ng.org... 140.211.15.107
正在连接 crosstool-ng.org|140.211.15.107|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度: 1884219 (1.8M) [application/x-bzip]
正在保存至: crosstool-ng-1.18.0.tar.bz2
100%[======================================>] 1,884,219    223K/s   花时 8.8s
下载源码成功之后解压源码:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ tar jxvf crosstool-ng-1.18.0.tar.bz2
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ ls
crosstool-ng-1.18.0  crosstool-ng-1.18.0.tar.bz2
考虑到后续将要使用到的各种目录,在这里先建立号后续所需目录。
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ mkdir crosstool-build crosstool-install src  
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ ls
crosstool-build    crosstool-ng-1.18.0          src
crosstool-install  crosstool-ng-1.18.0.tar.bz2
由于ubuntu操作系统很多开发软件都没有安装,因此要先安装一些制作交叉编译链必备的软件。在ubuntu下安装软件的命令为: sudo apt-get install ***
注:笔者建议arm-linux交叉编译工具链的制作最好在CentOS系统中完成,因为CentOS系统自带较为完善的开发软件,对于初学者不会造成不必要的麻烦。
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools$ sudo apt-get install sed bash cut dpkg-dev patch texinfom4 libtool statwebsvn tar gzip bzip2 lzmabison flex texinfo automake libtool patchcvs cvsd gawk–y
配置整个工程并且进行依赖检测:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ./configure --prefix /home/zhuzhaoqi/arm-linux-tools/crosstool-install
在安装过程中,提示如下错误:
……
checking how to run the C preprocessor... gcc -E
checking for ranlib... ranlib
checking for objcopy... objcopy
checking for absolute path to objcopy... /usr/bin/objcopy
checking for objdump... objdump
checking for absolute path to objdump... /usr/bin/objdump
checking for readelf... readelf
checking for absolute path to readelf... /usr/bin/readelf
checking for bison... no
configure: error: missing required tool: bison
输出错误提示确实bison这个软件,安装:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo apt-get install bison
安装完成之后,再次进行配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ./configure --prefix /home/zhuzhaoqi/arm-linux-tools/crosstool-install
又一次输出错误:
……
checking for bison... bison
checking for flex... no
configure: error: missing required tool: flex
提示确实flex这个软件,进行安装:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo apt-get install flex
安装完成之后,再一次进行配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ./configure --prefix /home/zhuzhaoqi/arm-linux-tools/crosstool-install
又一次提示错误:
checking for bison... bison
checking for flex... flex
checking for gperf... no
configure: error: missing required tool: gperf
提示缺失gperf这个软件,进行安装:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo apt-get install gperf
安装完成之后,再一次进行配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ./configure --prefix /home/zhuzhaoqi/arm-linux-tools/crosstool-install
再一次提示出错:
……
checking for bison... bison
checking for flex... flex
checking for gperf... gperf
checking for makeinfo... no
configure: error: missing required tool: makeinfo
缺失makeinfo软件,进行安装,如果安装的是makeinfo,则会有如下提示:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo apt-get install makeinfo
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
E: 无法找到软件包makeinfo
此时应该安装texinfo软件:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo apt-get install makeinfo
安装完成之后,再一次进行配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ./configure --prefix /home/zhuzhaoqi/arm-linux-tools/crosstool-install
这次成功的配置成功,如果读者操作还会报错的话,依照上面方法找出其根源进行改正即可。成功配置之后会自动创建了我们需要的Makefile文件。
checking for library containing initscr... -lncursesw
configure: creating ./config.status
config.status: creating Makefile
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ ls
bootstrap      configure     ct-ng.comp  LICENSES     patches  steps.mk
config         configure.ac  ct-ng.in    licenses.d   README   TODO
config.log     contrib       docs        Makefile     samples
config.status  COPYING       kconfig     Makefile.in  scripts
执行Makefile文件:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ make
  SED    'ct-ng'
  SED    'scripts/crosstool-NG.sh'
  SED    'scripts/saveSample.sh'
  SED    'scripts/showTuple.sh'
  GEN    'config/configure.in'
  GEN    'paths.mk'
  GEN    'paths.sh'
  DEP    'nconf.gui.dep'
  DEP    'nconf.dep'
  DEP    'lxdialog/yesno.dep'
  DEP    'lxdialog/util.dep'
  DEP    'lxdialog/textbox.dep'
  DEP    'lxdialog/menubox.dep'
  DEP    'lxdialog/inputbox.dep'
  DEP    'lxdialog/checklist.dep'
  DEP    'mconf.dep'
  DEP    'conf.dep'
  BISON  'zconf.tab.c'
  GPERF  'zconf.hash.c'
  LEX    'lex.zconf.c'
  DEP    'zconf.tab.dep'
  CC     'zconf.tab.o'
  CC     'conf.o'
  LD     'conf'
  CC     'lxdialog/checklist.o'
  CC     'lxdialog/inputbox.o'
  CC     'lxdialog/menubox.o'
  CC     'lxdialog/textbox.o'
  CC     'lxdialog/util.o'
  CC     'lxdialog/yesno.o'
  CC     'mconf.o'
  LD     'mconf'
  CC     'nconf.o'
  CC     'nconf.gui.o'
  LD     'nconf'
  SED    'docs/ct-ng.1'
  GZIP   'docs/ct-ng.1.gz'
编译成功之后进行安装:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ make install
成功安装之后,可以看到已经安装到我们指定的目录下,最后输出有这么一句话:
……
For auto-completion, do not forget to install 'ct-ng.comp' into
your bash completion directory (usually /etc/bash_completion.d)
这是在提醒我们不要忘记了配置环境变量,多么人性化的提示。接下来配置环境变量。
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0$ sudo echo "PATH=$PATH:/home/zhuzhaoqi/arm-linux-tools/crosstool-install/bin" >> ~/.bashrc
执行使其生效:
zhuzhaoqi@zhuzhaoqi-desktop:~$ source /home/zhuzhaoqi/.bashrc
使用ct-ng –v命令查看安装结果:
zhuzhaoqi@zhuzhaoqi-desktop:~$ ct-ng -v
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
这个程序创建为 i486-pc-linux-gnu
OKct-ng环境变量添加成功,也就意味着整个crosstool-ng安装成功。
2.   配置交叉编译链
现在需要去做的就是配置要编译的交叉编译工具链,在crosstool-ng已很多已经做好的默认配置(位于crosstool-ng- X.Y.Z(crosstool-ng-1.18.0)/samples目录下),这里只要针对其进行修改就好了。对于编译器组件部分的版本最好不要修改,因为那个配搭应该是经过测试后的最高本版了,但内核版本可以修改。
可以看到samples目录下的一些默认配置如下所示:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0/samples$ ls
alphaev56-unknown-linux-gnu           mips64el-n64-linux-uclibc
alphaev67-unknown-linux-gnu           mips-ar2315-linux-gnu
arm-bare_newlib_cortex_m3_nommu-eabi  mipsel-sde-elf
arm-cortex_a15-linux-gnueabi          mipsel-unknown-linux-gnu
arm-cortex_a8-linux-gnueabi           mips-malta-linux-gnu
arm-davinci-linux-gnueabi             mips-unknown-elf
armeb-unknown-eabi                    mips-unknown-linux-uclibc
armeb-unknown-linux-gnueabi           powerpc-405-linux-gnu
armeb-unknown-linux-uclibcgnueabi     powerpc64-unknown-linux-gnu
arm-unknown-eabi                      powerpc-860-linux-gnu
arm-unknown-linux-gnueabi             powerpc-e300c3-linux-gnu
arm-unknown-linux-uclibcgnueabi       powerpc-e500v2-linux-gnuspe
armv6-rpi-linux-gnueabi               powerpc-unknown-linux-gnu
avr32-unknown-none                    powerpc-unknown-linux-uclibc
bfin-unknown-linux-uclibc   powerpc-unknown_nofpu-linux-gnu
i586-geode-linux-uclibc               s390-ibm-linux-gnu
i586-mingw32msvc,i686-none-linux-gnu  s390x-ibm-linux-gnu
i686-nptl-linux-gnu                   samples.mk
i686-unknown-mingw32                  sh4-unknown-linux-gnu
m68k-unknown-elf                      x86_64-unknown-linux-gnu
m68k-unknown-uclinux-uclibc           x86_64-unknown-linux-uclibc
mips64el-n32-linux-uclibc             x86_64-unknown-mingw32
里面有很多默认配置,有armavr32mipspowerpc等硬件平台。而arm平台有如下:
arm-unknown-eabi是基于裸板,也就是无操作系统。
arm-unknown-linux-gnueabi 是基于linux
arm-unknown-linux-uclibcgnueabi 这个应该能看出来了,是为uclinux用的。
arm-cortex_a15-linux-gnueabi可从名字上看是为cortex-a15用的。
arm-cortex_a8-linux-gnueabi 这个也可从名字上看是为cortex-a8用的。
arm-**$&#*&还有几个,这些暂且不去理会。
这里是制作arm-linux交叉编译链,因此,我们选择arm-unknown-linux-gnueabi进行配置。将arm-unknown-linux-gnueabi文件夹复制到crosstool-build/目录下:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-ng-1.18.0/samples$ cp -r arm-unknown-linux-gnueabi/ ../../crosstool-build/
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-build$ ls
arm-unknown-linux-gnueabi
将默认配置文件拷贝到工作目录(crosstool-build)下并改名为.config,因为默认的配置文件为.config这个名字,完成之后可以加载需要的配置。
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-build$ cp arm-unknown-linux-gnueabi/crosstool.config .config
执行ct-ng menuconfig进入配置界面进行配置:
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-build$ ct-ng menuconfig
  LN    config
  MKDIR config.gen
  IN    config.gen/arch.in
  IN    config.gen/kernel.in
  IN    config.gen/cc.in
  IN    config.gen/binutils.in
  IN    config.gen/libc.in
  IN    config.gen/debug.in
  CONF  config/config.in
#
# configuration saved
#
进入配置界面如图1. 1所示。
下面就是设置源码目录和安装目录,这需要按照读者依据自己实际设定的情况来进行配置。
第一步,设定源码包路径和交叉编译器的安装路径。
Paths and misc options --->
   (/home/zhuzhaoqi/arm-linux-tools/src) Local tarballs directory 保存源码包路径
     (/home/zhuzhaoqi/arm-linux-tools/tools) Prefix directory交叉编译器的安装路径
配置之后的结构如图1. 2所示。

第二步,修改交叉编译器针对的构架。
因为本次是针对OK6410制作编译链,那就依据s3c6410的硬件特性来制作。
Target options这是重点要修改的地方。(以下配置均是基于已拷贝过来的配置。)
        Target Architecture(arm) 这个不用管,已经是arm了。
        Default instruction set mode (arm) 这个也不管,也已经是arm了。
Architecture level() 这个需要进行修改。
通过查找资料,这个应该是指令集的架构,对于S3C6410 ARM1176JZF-S核心使用的是armv6zk架构,就选armv6zk。那么,具体都支持哪些架构呢?可以用man gcc来查询,搜索arm,再搜索-march=就可以找到本gcc支持的处理器核心列表了:
-march=name
This specifies the name of the target ARM architecture.  GCC uses
this name to determine what kind of instructions it can emit when
generating assembly code.  This option can be used in conjunction
with or instead of the -mcpu= option.  Permissible names are:
armv2, armv2a, armv3, armv3m, armv4, armv4t, armv5, armv5t, armv5e,
armv5te, armv6, armv6j, armv6t2, armv6z, armv6zk, armv6-m, armv7,
armv7-a, armv7-r, armv7-m, iwmmxt, iwmmxt2, ep9312.     
Emit assembly for CPU() 这个需要进行修改。
这个对应的是CPU的核心类型。同样,也和上面的选项一样,对应一个GCC选项。GCC中这样描述。
-mcpu=name
This specifies the name of the target ARM processor.  GCC uses this
name to determine what kind of instructions it can emit when
generating assembly code.  Permissible names are: arm2, arm250,
arm3, arm6, arm60, arm600, arm610, arm620, arm7, arm7m, arm7d,
arm7dm, arm7di, arm7dmi, arm70, arm700, arm700i, arm710, arm710c,
arm7100, arm720, arm7500, arm7500fe, arm7tdmi,arm7tdmi-s, arm710t,
arm720t, arm740t, strongarm, strongarm110, strongarm1100,
strongarm1110, arm8, arm810, arm9, arm9e, arm920, arm920t, arm922t,
arm946e-s, arm966e-s, arm968e-s, arm926ej-s, arm940t, arm9tdmi,
arm10tdmi, arm1020t, arm1026ej-s, arm10e, arm1020e, arm1022e,
arm1136j-s, arm1136jf-s, mpcore, mpcorenovfp, arm1156t2-s,
arm1176jz-s, arm1176jzf-s, cortex-a8, cortex-a9, cortex-r4,
cortex-r4f, cortex-m3, cortex-m1, xscale, iwmmxt, iwmmxt2, ep9312.
这样看简单一些了如果是 S3C2410/S3C2440 就选 arm920t  如果是s3c6410就选arm1176jzf-s
Tune for CPU() ,对应的GCC描述是这样的:
-mtune=name
This option is very similar to the -mcpu= option, except that
instead of specifying the actual target processor type, and hence
restricting which instructions can be used, it specifies that GCC
should tune the performance of the code as if the target were of
the type specified in this option, but still choosing the
instructions that it will generate based on the cpu specified by a
-mcpu= option.  For some ARM implementations better performance can
be obtained by using this option.
意思是说这个选项和-mcpu 很类似,这里是指定真实的CPU型号。不过有读者是编译2440的工具链,这里选择的是arm9tdmi,如果不是,那就空着。这里的作用是如果arm920t处理不了,就用arm9tdmi的方式来编译。
Floating point() 浮点相关的选项s3c6410 有硬件VFP,所以这里选的 hardware FPU。这个是给有硬浮点的处理器强行选软浮点用的。
Use specific FPU() 是跟浮点有关,这里不选任何内容。至于怎么组合,读者可以跟据自己的CPU的实际情况相应的进行配置。
C compiler  --->
      *** Additional supported languages: ***  
      [ ] Java //不用这个编译器来编译java
当然如果读者需要用它来编译java那就不用去除。
其它选项不动Save an Alternate Configuration File存盘Exit 退出,OK,配置完了。
zhuzhaoqi@zhuzhaoqi-desktop:~/arm-linux-tools/crosstool-build$ ct-ng build
开始编译,此编译过程需要花费大约两个小时,最终编译出arm-linux-gcc-4.4.1编译链。
1.1.1   交叉编译链在宿主机上安装
交叉编译链版本:arm-linux-gcc 4.4.1。交叉编译链的版本很多,读者可以自行安装版本更高的编译链。
Linux系统环境:Ubuntu10.04.4。Ubuntu10.04.4发布于2012年2月17日,作为Ubuntu 10.04 LTS第四个也是最后一个版本,Ubuntu 10.04.4 修复了大量错误,提高了稳定性与兼容性。
1.         在/usr/local下面创建一个文件夹:mkdir arm,将arm-linux-gcc 4.4.1放在arm文件夹里面。然后解压缩,命令根据压缩包的后缀不同而不同。
2.         添加环境变量,vim /etc/profile。
3.         在最后一行添加:export PATH=$PATH:/usr/local/arm/4.4.1/bin。
4.         退出执行命令:source /etc/profile。使其生效。
5.         检测安装是否成功:arm-linux-gcc -v ;如果成功,输出最后一行则会提示:gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67)。如下:
zhuzhaoqi@zhuzhaoqi-desktop:~/u-boot/Makefile/shellfunction$ arm-linux-gcc -v
Using built-in specs.
Target: arm-none-linux-gnueabi
Configured with: /scratch/julian/2009q3-respin-linux-lite/src/gcc-4.4/configure --build=i686-pc-linux-gnu --host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi --enable-threads --disable-libmudflap --disable-libssp --disable-libstdcxx-pch --enable-extra-sgxxlite-multilibs --with-arch=armv5te --with-gnu-as --with-gnu-ld --with-specs='%{funwind-tables|fno-unwind-tables|mabi=*|ffreestanding|nostdlib:;:-funwind-tables} %{O2:%{!fno-remove-local-statics: -fremove-local-statics}} %{O*:%{O|O0|O1|O2|Os:;:%{!fno-remove-local-statics: -fremove-local-statics}}}' --enable-languages=c,c++ --enable-shared --disable-lto --enable-symvers=gnu --enable-__cxa_atexit --with-pkgversion='Sourcery G++ Lite 2009q3-67' --with-bugurl=https://support.codesourcery.com/GNUToolchain/ --disable-nls --prefix=/opt/codesourcery --with-sysroot=/opt/codesourcery/arm-none-linux-gnueabi/libc --with-build-sysroot=/scratch/julian/2009q3-respin-linux-lite/install/arm-none-linux-gnueabi/libc --with-gmp=/scratch/julian/2009q3-respin-linux-lite/obj/host-libs-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu/usr --with-mpfr=/scratch/julian/2009q3-respin-linux-lite/obj/host-libs-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu/usr --with-ppl=/scratch/julian/2009q3-respin-linux-lite/obj/host-libs-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu/usr --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --with-cloog=/scratch/julian/2009q3-respin-linux-lite/obj/host-libs-2009q3-67-arm-none-linux-gnueabi-i686-pc-linux-gnu/usr --disable-libgomp --enable-poison-system-directories --with-build-time-tools=/scratch/julian/2009q3-respin-linux-lite/install/arm-none-linux-gnueabi/bin --with-build-time-tools=/scratch/julian/2009q3-respin-linux-lite/install/arm-none-linux-gnueabi/bin
Thread model: posix
gcc version 4.4.1 (Sourcery G++ Lite 2009q3-67)
笔者建议安装最好不要在root用户下进行安装,否则使用交叉编译链可能会存在权限限制。
回复 支持 反对

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2014-2-23 10:23:05 | 显示全部楼层
第一章第四节  映像文件的生产和运行
德国罕见的科学大师莱布尼茨,在他的手迹里留下这么一句话:“1与0,一切数字的神奇渊源。这是造物的秘密美妙的典范,因为,一切无非都来自上帝。”二进制0和1两个简单的数字,构造了神奇的计算机世界,对人类的生产活动和社会活动产生了极其重要的影响,并以强大的生命力飞速发展。在嵌入式系统移植过程中,不管文件数量多么庞大的工程,经过编译工具的层层处理后,最终生成一个可以加载到存储器内执行的二进制映像文件(.bin)。本节内容将会探讨映像文件的生成过程,以及它在存储设备的不同位置对程序运行产生的影响,为本书后文嵌入式系统的移植打下坚定的基础。
1.1.1   编译过程
GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、链接器ld、二进制转换工具objcopy和反汇编的工具objdump等。它们被称作GNU编译器集合,支持多种计算机体系类型。基于ARM平台的工具分别为arm-linux-gcc、arm-linux-g++、arm-linux-ld、arm-linux-objcopy和arm-linux-objdump。arm-linux交叉编译编译工具链的制作方法已经详细介绍过,编译程序直接使用前面制作好的工具链。
GNU编译器的功能非常强大,程序可以用C文件、汇编文件编写,甚至是二者的混合。如1. 3所示是程序编译的大体流程,源文件经过预处理器、汇编器、编译器、链接器处理后生成可执行文件,再由二进制转换工具转换为可用于烧写到Flash的二进制文件,同时为了调试的方便还可以用反汇编工具生成反汇编文件。图中双向箭头的含义是,当gcc增加一些参数时可以相互调用汇编器和链接器进行工作。例如输入命令行“gcc –O  main.c”后,直接就得到可执行文件a.out(elf)
程序编译大体上可以分为编译和链接两个步骤:把源文件处理成中间目标文件.o(linux)、obj(windows)的动作称为编译;把编译形成的中间目标文件以及它们所需要的库函数.a(linux) 、lib(windows)链接在一起的动作称为链接。现用一个简单的test工程来分析程序的编译流程,麻雀虽小五脏俱全,它由启动程序start.S、应用程序main.c、链接脚本test.lds和Makefile四个文件构成。test工程中的程序通过操作单板上的LED灯的状态来判定程序的运行结果,它除了用于理论研究之外,没有其它的实用价值。
1.   编译
在编译阶段,编译器会检查程序的语法、函数与变量的声明情况等。如果检查到程序的语法有错误,编译器立即停止编译,并给出错误提示。如果程序调用的函数、变量没有声明原型,编译器只会抛出一个警告,继续编译生成中间目标文件,待到链接阶段进一步确定调用的变量、函数是否存在。
程序清单1. 1start.S中汇编代码
/*
*      This is a part of the test project
*      Author: LiQiang Date: 2013/04/01
*      Licensed under the GPL-2 or later.
*/
.globl _start
_start:
    #define REG32 0x70000000
    ldr r0, =REG32
    orr r0, r0, #0x13
    mcr p15,0,r0,c15,c2,4      
    /*关闭看门狗*/
    #define WATCHDOG 0x7E004000
    ldr r0, =WATCHDOG
    mov r1, #0
    str r1, [r0]
clean_bss:
    ldr r0, =bss_start
    ldr r1, =bss_end
    mov r3, #0
    cmp r0, r1
    beq clean_done
clean_loop:
    str r3, [r0], #4
    cmp r0, r1  
    bne clean_loop      
clean_done:
     /* 初始化栈 S3C6410 8KSRAM映射到0地址处*/
     ldr sp, =8*1024
     bl main
halt:
    b halt  
start.S文件的内容如程序清单1. 1,文件中的_start函数为C语言运行环境做最低限度的初始化:将S3C6410处理外设端口的地址范围告知ARM内核,关闭看门狗,清除bss段,初始化栈。初始化工作完毕后,跳转到main()。start.S是用汇编语言编写的代码文件,文件中定义了一个WATCHDOG宏,用于寄存器的赋值。在汇编文件中出现#define宏定义语句,对于初学者可能会有些迷惑。
事实上,汇编文件有“.S”和“.s”两种后缀,在以“.s”为后缀的汇编文件中,程序完全是由纯粹的汇编代码编写。所谓的纯粹是相对以“.S”为后缀的汇编文件而言的,由于现代汇编工具引入了预处理的概念,允许在汇编代码(.S)中使用预处理命令。预处理命令以符号“#”开头,包括宏定义、文件包含和条件编译。在U-Boot和Linux内核源码中,这种编程方式运用非常广泛
程序清单1. 2  main.c文件内容
/*
*  This is a part of the test project
* Author: LiQiang Date: 2013/04/01
* Licensed under the GPL-2 or later.
*/
#define GPMCON  *((volatile unsigned long*)0x7F008820)
#define GPMDAT  *((volatile unsigned long*)0x7F008824)
#define GPMPUD  *((volatile unsigned long*)0x7F008828)
int main()
{
    static int flag = 12;
    GPMCON = 0x1111; /* 输出模式 */
    GPMPUD = 0x55;   /* 使能下拉 */
    GPMDAT = 0x0f;   /* 关闭LED */
    if(12 == flag)
        GPMDAT = 0x00;
    else
        GPMDAT = 0x0f;
    while(1);
    return 0;
}
main.c文件内容如程序清单1. 2所示,main.c中的main函数是运行完_start函数的跳转点。main()中首先定义了一个静态局部变量初值为12,然后配置S3C6410处理器的GPM端口为输出、下拉模式,并将GPM低四位管脚的设为高电平(单板上LED在管脚为高电平的熄灭)。最后判断是flag是否等于12,如果等于点亮LED,否则不点亮。从程序上看,这个判断语句好像多此一举、莫名其妙,因为flag期间并没有作任何改变。其实,这个变量是为讲解程序的运行地址和加载地址的概念而定义的,它与程序运行的位置有关。
将上面两个源码文件处理成中间目标文件,分别输入如下命令行:
arm-linux-gcc -o mian.o main.c –c
arm-linux-gcc -o start.o start.S –c
得到main.o  Start.o两个中间目标文件,供链接器使用。
2.   链接
链接是汇编阶段生成的中间目标文件,相互查找自己所需要的函数与变量,重定向数据,完成符号解析的过程。包括对所有目标文件进行重定位、建立符号引用规则,同时为变量、函数等分配运行地地址。函数与变量可能来源与其它中间文件或者库文件,如果没有找到所需的实现,链接器立即停止链接,给处错误提示。
利用一个链接脚本(.lds后缀)来指导链接链接器工作。控制输出节在映像文件中的布局。fortest.lds是一个简单的链接脚本,指示了程序的运行地址(又称链接地址)为0x5000_0000以及text段、data段和bss段在映像文件中的空间排布顺序。fortest.lds文件的内容如下:
ENTRY(_start)
SECTIONS
{
        . = 0x50000000;
        . = ALIGN(4);
        .text : {
                start.o (.text)
                * (.text)
        }
        .data : {
                * (.data)
        }
        bss_start = .;
        .bss : {
                * (.bss)
        }
        bss_end  = .;
}
1)        text段代码段(text segment),通常是用来存放程序执行代码的内存区域。这块区域的大小在程序编译时就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量。
2)        data段数据段(data segment),数据段是存放已经初始化不为0的静态变量的内存区域,静态变量包括全局变量和静态局部变量,它们与程序有着相同的生存期。
3)        bss段bss segment,bbs段与data段类似,也是存放的静态变量的内存区域。与data段不同的是,bbs段存放是没有初始化或者初始化为0的静态变量,并且bbs段不在生成的可执行二进制文件内。bss_start表示这块内存区域的起始地址,bss_end表示结束地址,它们由编译系统计算得到。未初始化的静态变量默认为0,因此程序开始执行的时候,在bss_start到bss_end内存中存储的数据都必须是0。
4)        其他段,上面三个段是编译系统预定义的段名,用户还能通过.section伪操作自定义段,在后面的移植过程我们会发现,Linux内核源码中为了合理地排布数据实现特定的功能,定义了各种各样的段。
在宿主机上输入以下命令行,完成中间的目标文件的链接和可执行二进制文件的格式转换。
arm-linux-ld –T test.lds -o test.elf start.o main.o
arm-linux-objcopy -O binary test.elf test.bin
arm-linux-objdump -D test.elf > test.dis
如图1. 4所示是使用arm-linux-objcopy格式转换工具得到的二进制文件test.bin的内容,这些内容是处理器能够识别的机器码,我们往往难以直接阅读、理解它们的含义。使用arm-linux-objdump工具生成便以我们阅读的反汇编文件test.dis。对比二进制文件test.bin的内容,耐心细致地分析反汇编文件,如程序清单1. 3所示,可以提炼出大量的信息。
程序清单1. 3text.dis文件内容
50000000 <_start>:  /* 代码段起始位置程序的运行地址为0x5000_0000*/
50000000:   e3a00207    mov r0, #1879048192 ; 0x70000000
50000004:   e3800013    orr r0, r0, #19 ; 0x13
50000008:   ee0f0f92    mcr 15, 0, r0, cr15, cr2, {4}
5000000c:   e59f0030    ldr r0, [pc, #48]   ; 50000044 <halt+0x4>
50000010:   e3a01000    mov r1, #0  ; 0x0
50000014:   e5801000    str r1, [r0]
50000018 <clean_bss>: /* 清除bss */
50000018:   e59f0028    ldr r0, [pc, #40]   ; 50000048 <halt+0x8>
5000001c:   e59f1028    ldr r1, [pc, #40]   ; 5000004c <halt+0xc>
50000020:   e3a03000    mov r3, #0  ; 0x0
50000024:   e1500001    cmp r0, r1
50000028:   0a000002    beq 50000038 <clean_done>
5000002c <clean_loop>:
5000002c:   e4803004    str r3, [r0], #4
50000030:   e1500001    cmp r0, r1
50000034:   1afffffc    bne 5000002c <clean_loop>
50000038 <clean_done>:
50000038:   e3a0da02    mov sp, #8192   ; 0x2000 /* 初始化sp */
5000003c:   eb000003    bl  50000050 <main> /* 跳转至mian() */
50000040 <halt>:
50000040:   eafffffe    b   50000040 <halt>
50000044:   7e004000    .word   0x7e004000
50000048:   500000e0    .word   0x500000e0
5000004c:   500000e0    .word   0x500000e0
50000050 <main>: /* main()*/
50000050:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
50000054:   e28db000    add fp, sp, #0  ; 0x0
50000058:   e3a0247f    mov r2, #2130706432 ; 0x7f000000
5000005c:   e2822b22    add r2, r2, #34816  ; 0x8800
50000060:   e2822020    add r2, r2, #32 ; 0x20
50000064:   e3a03c11    mov r3, #4352   ; 0x1100
50000068:   e2833011    add r3, r3, #17 ; 0x11
5000006c:   e5823000    str r3, [r2]  /*  GPMCON = 0x1111 */
50000070:   e3a0347f    mov r3, #2130706432 ; 0x7f000000
50000074:   e2833b22    add r3, r3, #34816  ; 0x8800
50000078:   e2833028    add r3, r3, #40 ; 0x28
5000007c:   e3a02055    mov r2, #85 ; 0x55
50000080:   e5832000    str r2, [r3]  /*  GPMPUD = 0x55 */
50000084:   e3a0347f    mov r3, #2130706432 ; 0x7f000000
50000088:   e2833b22    add r3, r3, #34816  ; 0x8800
5000008c:   e2833024    add r3, r3, #36 ; 0x24
50000090:   e3a0200f    mov r2, #15 ; 0xf
50000094:   e5832000    str r2, [r3]  /* GPMDAT = 0x0f */
50000098:   e59f3038    ldr r3, [pc, #56]   ; 500000d8 <main+0x88>  /* 读取flag变量存储地址 */
5000009c:   e5933000    ldr r3, [r3]/* 读取flag变量的值 */
500000a0:   e353000c    cmp r3, #12 ; 0xc
500000a4:   1a000005    bne 500000c0 <main+0x70>
500000a8:   e3a0347f    mov r3, #2130706432 ; 0x7f000000
500000ac:   e2833b22    add r3, r3, #34816  ; 0x8800
500000b0:   e2833024    add r3, r3, #36 ; 0x24
500000b4:   e3a02000    mov r2, #0  ; 0x0
500000b8:   e5832000    str r2, [r3]
500000bc:   ea000004    b   500000d4 <main+0x84>
500000c0:   e3a0347f    mov r3, #2130706432 ; 0x7f000000
500000c4:   e2833b22    add r3, r3, #34816  ; 0x8800
500000c8:   e2833024    add r3, r3, #36 ; 0x24
500000cc:   e3a0200f    mov r2, #15 ; 0xf
500000d0:   e5832000    str r2, [r3]
500000d4:   eafffffe    b   500000d4 <main+0x84>
500000d8:   500000dc    .word   0x500000dc
Disassembly of section .data:
500000dc <flag.1245>: /* flag变量的地址为0x5000_00dc,值为12 */
500000dc:   0000000c    .word   0x0000000c
从test.dis反汇编文件中可知,test.bin包含了代码段和数据段,并没有包含bss段。我们知道,bbs内存区域的数据初始值全部为零,区域的起始位置和结束位置在程序编译的时候预知。很容易想到在程序开始运行时,执行一小段代码将这个区域的数据全部清零即可,没必要在test.bin包含全为0的bss段。编译器的这种机制有效地减小了镜像文件的大小、节约了磁盘容量。
main()函数的核心功能是验证flag变量是否等于12,现在追踪下这个操作的实现过程。要想读取flag的值,必须知道它的存储位置,首先执行指令“ldrr3, [pc, #56]”得到flag变量的地址(指针)。pc与56相加合成一个地址,它是相对pc偏移56产生的。pc+56地址处存放了flag变量的指针0x5000_00dc,读取出来存放到r3寄存器。然后执行指令“ldrr3, [r3]”将内存0x5000_00dc地址处的值读出,这个值就是flag,并覆盖r3寄存器。最后,判断r3寄存器是否等于12。flag变量的地址在链接阶段已经被分配好了,固定在0x5000_00dc处,但是从代码中,我们没有找到对flag变量赋初值的语句,尽管在main函数已经用C语句“flag = 12”对它赋初值。
现提供一个验证程序效果的简单方法:将S3C6410处理器设置为SD卡启动方式,使用SD_Writer软件将test.bin烧写至SD卡中,然后将SD卡插入单板的卡槽,复位启动即可。实际上,启动的时候test.bin被加载到内部SRAM中,SRAM映射到0地址处。这个简单方法可以用来验证一些裸板程序,方法实现的原理和SD_Writer软件用法现在不展开讨论,目前只要会使用即可。复位后,LED并没有点亮。
如果每次编译都要重复输入编译命令,操作起来很麻烦,为此test工程中建立了一个Makefile文件,内容如下:
test.bin: start.o main.o
    arm-linux-ld -T fortest.lds -o test.elf start.o main.o
    arm-linux-objcopy -O binary test.elf test.bin
    arm-linux-objdump -D test.elf > test.dis
start.o : start.S
    arm-linux-gcc -o start.o start.S -c
main.o : main.c
    arm-linux-gcc -o main.o main.c -c
clean:
    rm *.o test.*
当将链接脚本中的运行地址修改为0时,进入test目录,输入“make clean”命令清除旧的文件,再输入“make”重新编译程序,验证新生成的test.bin文件的效果,发现LED全部点亮,产生这个现象的原因在下一个小节讲述。
1.1.1   代码搬运
当程序执行时,必须把代码搬运到链接时所指定的运行地址空间,以保证程序在执行过程中对变量、函数等符号的正确引用。在带有操作系统中,这个过程由操作系统负责完成。而在裸机环境下,镜像文件的运行地址由程序员根据具体平台指定,加载地址又与处理器的设计密切相关。通常情况下,启动代码最先执行一段位置无关码,这段代码实现程序从加载地址到运行地址的重定位,或者将程序从外部存储介质直接拷贝至其运行地址。
1.   位置无关码
位置无关码必须具有位置无关的跳转、位置无关的常量访问等特点,不能访问静态变量,都是相对pc的偏移量来函数的跳转或者常量的访问。在ARM 体系中,使用相对跳转指令b/bl实现程序跳转。指令中所跳转的目标地址用基于当前PC的偏移量来表示,与链接时分配给地址标号的绝对地址值无关,因而代码可以在任何位置正确的跳转,实现位置无关性。
使用ldr伪指令将一个常量读取到非pc的其他通用寄存器中,可实现位置无关的常量访问。例如:
ldr r0, =WATCHDOG
如果使用ldr伪指令将一个函数标号读取到pc,这是一条与位置有关的跳转指令,执行的结果是跳转到函数的运行地址处。
2.   运行地址与加载地址
试想一下,当系统上电复位的时候,如果test.bin刚好位于在0x5000_0000地址(flag的初值12位于0x5000_00dc),PC指向0x5000_0000地址,那么这段代码按照上述flag变量的读取步骤,能够准确无误的得到结果。但是,如果test.bin位于0地址(flag的初值12位于0xdc,LED不亮时的情况),PC指向0地址,程序依然从0x5000_00dc地址读取flag变量,实际上它的初值位于0xdc。这时从C语言的角度看,出现一个flag不等于它的初值的现象(期间没有改变flag)。出现错误的原因是在程序中使用了位置相关的变量,但运行地址与加载地址不一致(加载地址为0,运行地址为0x5000_0000)。由此,能够容易理解运行地址和加载地址的含义:
加载地址是系统上电启动时,程序被加载到可直接执行的存储器的地址,也就是程序在RAM或者Flash ROM中的地址。因为有些存储介质只能用来存储数据不能执行程序,例如SD卡和NAND Flash等,必须把程序从这些存储介质加载到可以执行的地址处。运行地址就是程序在链接时候确定的地址,比如fortest.lds链接脚本指定了程序的运行地址为0x5000_0000,那么链接器在为变量、函数等分配地址的时候就会以0x5000_0000作为参考。当加载地址和运行地址不相等时,必须使用与位置无关码把程序代码从它的加载地址搬运至运行地址,然后使用“ldr pc, =label”指令跳转到运行地址处执行。

1.1.2   混合编程
在嵌入式系统底层编程中,C语言和汇编两种编程语言的使用最广泛。C语言开发的程序具有可读性高,容易修改、移植和开发周期短等特点。但是,C语言在一些场合很难或无法实现特定的功能:底层程序需要直接与CPU内核打交道,一些特殊的指令在C语言中并没有对应的成分,例如关闭看门狗、中断的使能等;被系统频繁调用的代码段,对代码的执行效率要求严格的时候。事实上,CPU体系结构并不一致,没有对内部寄存器操作的通用指令。汇编语言与CPU的类型密切相关,提供的助记符指令能够方便直接地访问硬件,但要求开发人员对CPU的体系结构十分熟悉。在早期的微处理器中,由于处理器速度、存储空间等硬件条件的限制,开发人员不得不选用汇编语言开发程序。随着微处理器的发展,这些问题已经得到很好的解决。如果依然完全使用汇编语言编写程序,工作量会非常大,系统很难维护升级。大多数情况下,充分结合两种语言的特点,彼此相互调用,以约定规则传递参数,共享数据。
1.   汇编函数与C语言函数相互调用
C程序函数与汇编函数相互调用时必须严格遵循ATPCS(ARMThumb Procedure Call Standard)。函数间约定R0、R1和R2为传入参数,函数的返回值放在R0中。GNU ARM编译环境中,在汇编程序中要使用.global伪操作声明改汇编程序为全局的函数,可被外部函数调用。在C程序中要被汇编程序调用的C函数,同样需要用关键字extern声明。
程序清单1. 4代码重定位函数
.globl  relocate_code
relocate_code:
    mov r4, r0  /* save addr_sp */
    mov r5, r1  /* save addr of gd */
    mov r6, r2  /* save addr of destination */
    /* Set up the stack */
stack_setup:
    mov sp, r4
    adr r0, _start
    cmp r0, r6
    beq clear_bss       /* skip relocation */
    mov r1, r6          /* r1 <- scratch for copy_loop */
    ldr r3, _bss_start_ofs
    add r2, r0, r3      /* r2 <- source end address     */
……
程序清单1. 4是从arch\arm\cpu\arm1176\start.S文件(U-Boot)中截取的代码片段,relocate_code函数用于重定位代码。它在C程序中,通过relocate_code(addr_sp, id, addr)被调用。变量addr_sp、id和addr分别通过寄存器R0、R1和R3传递给汇编程序,实现了C函数和汇编函数数据的共享。
2.   C语言内嵌汇编
当需要在C语言程序中内嵌汇编代码时,可以使用gcc提供的asm语句功能。
程序清单1. 5整数原子加操作的实现
/*
* ARMv6 UP and SMP safe atomic ops.  We use load exclusive and
* store exclusive to ensure that these are atomic.  We may loop
* to ensure that the update happens.
*/
static inline void atomic_add(int i, atomic_t *v)
{
    unsigned long tmp;
    int result;
    __asm__ __volatile__("@ atomic_add\n"
"1: ldrex   %0, [%3]\n"
"   add %0, %0, %4\n"
"   strex   %1, %0, [%3]\n"
"   teq %1, #0\n"
"   bne 1b"
    : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
    : "r" (&v->counter), "Ir" (i)
    : "cc");
}
程序清单1. 5是从Linux源码文件arch/arm/include/asm/atomic.h截取的一段代码,本节内容不分析函数的具体实现。对于初学者,这段代码看起来晦涩难懂,因为这不是标准C所定义的形式,而是gcc对C语言扩充的asm功能语句,用以在C语言程序中嵌入汇编代码。asm语句最常用的格式为:
__asm __  __volatile__(inst1 op1, op2, . \n
inst2 op1, op2, . \n”         /* 指令部分必选*/
...
instN op1, op2, . \n
: output_operands   /* 输出操作数可选 */
: input_operands        /* 输入操作数可选 */
: clobbered_operands    /* 损坏描述部分可选*/
);
它由四个部分组成:指令部分,输出部分,输入部分,损坏描述部分。各部分使用“:”格开,指令部分必不可少,其他三部分可选,但是如果使用了后面的部分,而前面部分为空,也需要用“:”分隔,相应部分内容为空。__asm__表示汇编语句的起始,__volatile__是一个可选项,加上它可以防止编译器优化时对汇编语句删除、移动。
指令部分,指令之间使用“\n”(也可以使用“;”或者“\n\t”)分隔。嵌入汇编指令的格式与标准汇编指令的格式大体相同,嵌入汇编指令的操作数使用占位符“%”预留位置,用以引用C语言程序中的变量。操作数占位符的数量取决于CPU中通用寄存器的总数量,占位符的格式为%0,%1,……,%n。
输出、输入部分,这两部分用于描述操作数,不同的操作数描述语句之间用逗号分隔,每个操作数描述符由限定字符串和C语言表达式组成,当限定字符串中带有“=”时表示该操作数为输出操作数。限定字符串用于指示编译器如何处理C语言表达式与指令操作数之间的关系,限定字符串中的限定字母有很多种,有些是通用的,有些跟特定的体系相关。在程序清单1. 5中:result、tmp和v->counter是输出操作数,分别赋给%0、%1和%2;v->counter和i是输入操作数,分别赋给%3和%4。其中,“r”:表示把变量放入通用寄存器中;“I”:表示0-31之间的常数。
回复 支持 反对

使用道具 举报

0

主题

312

帖子

285

积分

 楼主| 发表于 2014-4-10 18:27:22 | 显示全部楼层
第二章第一节  U-Boot-2013.04分析与移植之BootLoader概述
朱兆祺从本节开始,就带领大家进入U-Boot-2013.04的移植,这是2013年4月份发布的U-Boot源码,是U-Boot版本中的重要分水岭。从本节开始,如果大家要跟随我的步伐,那就得准备好一块OK6410开发板,因为我讲解嵌入式Linux的学习是以OK6410为载体进行的。
    从最终用户的角度看,BootLoader(即启动代码)是处理器复位后进入操作系统之前执行的一段代码,用以完成由硬件启动到操作系统启动的过渡,为操作系统的运行提供基本的环境,如关闭看门狗、初始化时钟和配置存储器等。启动代码的最终目的是引导操作系统的启动,但从开发人员的角度看,为了开发和调试的方便,还会增加串口控制、以太网络等功能。
    嵌入式系统与应用密切结合,它具有很强的专用性。实际系统的需求往往千差万别,BootLoader代码与CPU的类型、应用系统的配置及使用的操作系统等因素密切相关,这就注定了不可能有完全通用的BootLoader,实际运用时必须根据具体情况对启动代码进行移植。
表2. 1开发板配置
类别
型号
规格
CPU
S3C6410
-
NAND Flash
K9GAG08U0D
2G
DRAM
K4X1G163PC
128M*2
Ethernet
DM9000A
-
LCD
WXCAT43
4.3寸

    本文所写的内容都是基于表2. 1所示配置的单板(board):NAND芯片K9GAG08U0D共4096块,每块的包含128页,每页由4096字节的数据区和218字节的空闲区组成。两片64 M×16 bit的Mobile DDR芯片K4X1G163PC,组合构成共256 MB的内存。尽管每个人持有的单板配置各异,但分析、移植的原理相通。
    U-Boot,全称为Universal Boot Loader(通用bootloader),是遵循GPL条款的开放源码项目。由德国DENX小组开发和维护的,其高超的技术使得U-Boot能够非常容易地被移植到多种嵌入式CPU中,支持多种嵌入式操作系统内核的引导。不少U-Boot源码就是linux内核源码的简化,特别是一些设备的驱动程序,使得它在引导Linux Kernel方面尤为出色。本文选用当前最新版本的U-Boot,结合S3C6410处理器自身的特点,分析和探讨它的移植要点。笔者认为移植工作必须在充分理解源码的组织方式、处理器特点、单板外围器件原理等基础上进行。充分利用源码已经实现的功能,最小限度地破坏源码的结构。

回复 支持 反对

使用道具 举报

不能佚佉祢 该用户已被删除
发表于 2014-10-25 18:47:52 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

3

帖子

3

积分

发表于 2015-11-13 14:54:25 | 显示全部楼层
这绝对是很好的入门资料呀
回复 支持 反对

使用道具 举报

0

主题

1

帖子

3

积分

发表于 2015-12-8 21:19:52 | 显示全部楼层
谢谢,很需要这东西
回复 支持 反对

使用道具 举报

3

主题

8

帖子

17

积分

发表于 2016-10-6 23:34:05 | 显示全部楼层
还有技术支持吗?为什么用SD Writer,按照书的操作,根本启动不了u-boot
回复 支持 反对

使用道具 举报

0

主题

10

帖子

12

积分

发表于 2017-4-11 01:29:40 | 显示全部楼层
谢谢,很需要这东西
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|飞凌嵌入式 ( 冀ICP备12004394号-1

GMT+8, 2017-6-29 10:00

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表