嵌入式Linux应用开发完全手册
3 嵌入式Linux基础知识
3.1 交叉编译工具
- 编译工具链,编译工作由几个步骤完成,分别用到了不同的工具
- PC端应用
- gcc
- ld
- objcopy
- objdump
- 交叉编译(编译和运行在不同的环境下),arm平台工具链
- arm-linux-gcc
- arm-linux-ld
- PC端应用
3.1.1 arm-linux-gcc
- C/C++文件的编译步骤
- 预处理 preprocessing
- 编译 compilation
- 汇编 assembly
- 连接 linking
预处理
根据预处理命令(#开头的命令)修改源文件,形成.i文件
用到额工具是arm-linux-cpp
编译
将经过预处理的.i文件,翻译成汇编代码,.s文件
用到的工具是ccl
汇编
将汇编代码文件.s翻译成目标文件 .o文件
用到的工具是arm-linux-as
连接
将汇编生成的目标文件和库文件连接起来,最终生成特定平台的可执行文件
用到的工具是arm-linux-ld
连接器处理的文件一般包括
- .o 目标文件
- .a 库文件
arm-linux-gcc的重要选项
- -E 预处理 :预处理结果通过stdout显示出来
- -S 预处理、编译 :可以生成.s文件
- -c 预处理、编译、汇编 :可以生成.o文件
- -o 指定输出文件名 :如果不指定名成,按照一般规则生成
- -v 显示制作gcc命令时的配置 :显示更详细的编译信息
- -Wall 显示所有警告信息 :Warning ALL
- -g 产生调试信息,以便GDB使用 : 要使用gdb调试,必须有这个选项
- -O 优化选项 :O0,O1,O2,O3 4级优化,一般应用选O2
- [object-file-name] :连接的时候,目标文件和库文件名称
- -l[library] :连接名为l[library]的库文件,库文件名称规则lib[libraay].so
- -nostartfiles :不连接系统标准启动文件,编译bootloader, 内核的时候会用到
- -nostdlib :不连接系统标准启动文件和标准库文件,编译bootloader和内核会用到
- -static :组织连接共享库,打开这个选线柜让编译出来的结果很大,不动态连接一些库,那么就都包含在编译结果里边了
- -shared :生成库文件
- -I[dir] :头文件搜索路径中添加 dir
- -L[dir] :-l指定某个库需要连接的时候,这个参数增加搜索路径,比如-L. 当前目录作为搜索目录添加进来
/ * 举例 */
[main.c] - include [sub.h]
[sub.h]
[sub.c] - include [sub.h]
--------
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
--------
gcc -S -o main.s main.c
gcc -E main.c | less
--------
gcc -shared -o sub.a sub.o sub2.o sub3.o
arm-linux-ld选项
- -T :指定代码段、数据段、bss段的起始位置,或者指定一个连接脚本
:-T参数只用于连接bootloader,内核等“没有底层软件支持”的软件
:连接运行于操作系统之上的软件时,不需要-T,使用默认连接即可
-Ttext startaddr
-Tdata startaddr
-Tbss startaddr
/* 代码段运行地址0x00000000,没有定义数据段,bss段,它们依次放在代码段后面 */
arm-linux-ld -Ttext 0x00000000 -g ledon.o -o ledon_elf
/* 使用连接脚本设置地址 */
arm-linux-ld -Ttimer.lds -o timer_elf head.o init.o interrupt.o main.o
[timer.lds]
--------
SECTIONs {
. = 0x30000000;
.text : {*(.text)}
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : {*(.data)}
.bss ALIGN(4) : {*(.bss) *(COMMON)}
}
--------
arm-linux-objcopy选项
- arm-linux-objcopy用来赋值一个目标文件到另一个文件中,可以使用不同于源文件的格式来输出目的文件,也就是可以用来进行格式转换
- 可以用它来将elf文件转换位二进制可执行文件
/* 编译bootloader和内核的时候,用此命令将elf文件转换为二进制文件 */
arm-linux-objcopy -O binary -S elf_file bin_file
arm-linux-objdump选项
- 用于显示二进制文件信息,常用来查看反汇编代码
/* 将elf格式文件反汇编 */
arm-linux_objdump -D elffile > dis_file
/* 将二进制文件反汇编 */
arm-linux-objdump -D binary -m arm bin_file > dis_file
3.2 Makefile
Makefile就是一个名字是Makefile的文件。内部缩进必须使用Tab,不能转换为空格。
这里介绍Makefile的最基本的规则。
格式
[Makefile]
--------
target : prerequiries
<Tab>command
--------
- 目标 通常是要生成的文件名称,可以是可执行文件或者obj文件。也可以是一个需要执行的动作名称,比如“clean”
- 依赖 用来产生目标的原料(比如源文件),一个目标通常有几个依赖。
- 命令 生成目标时进行的动作。可以有若干命令,一个命令一行
- 依赖发生改变,目标就需要通过命令重新生成。如果依赖没有改变,目标不需要重新生成。
/* 一个最简单的例子 */
hello: hello.c
gcc -o hello hello.c
clean:
rm -f hello
变量及赋值
- 延时变量
- =
- ?=
- 立即变量
- :=
- 扩展变量
- +=
src := $(shel ls *.c) /* 立即变量的赋值 */
obj := $(patsubst %.c, %.o, $(src))
obj += temp.o /* 扩展变量 */
test: $(obj) /* 变量使用 */
gcc ...
Makefile函数
Makefile里边可以使用一些函数,格式如下
$(function arguments)
字符串替换和分析函数
- $(subst from, to, text)
- $(patsubst pattern, replacement, text)
- $(strip string)
- $(findstring find, in)
- $(filter pattern…, text)
- $(filterout pattern…, text)
- $(sort list)
$(subst ee, EE, feet on the street)
fEEt on the strEEt
$(patsubst %.c, %.o, x.c.c bar.c)
x.c.o ar.o
$(strip a b c
a b c
$(findstring a, a b c)
a
$(findstring a, b c)
$(filter %.c %.s, foo.c bar.c baz.s ugh.h)
foo.c bar.c baz.s
$(filterout %.c %.s, foo.c bar.c baz.s ugh.h)
ugh.h
$(sort foo bar lose)
bar foo lose
文件名函数
- $(dir names)
- $(notdir names)
- $(suffix names)
- $(basename names)
- $(addsuffix suffix, names)
- $(addprefix prefix, names)
- $(wildcard pattern)
$(dir src/foo.c hacks)
src/ ./
$(notdir src/foo.c hacks)
foo.c hacks
$(suffix src/foo.c src-1.0/bar.c hacks)
.c .c
$(basename src/foo.c src-1.0/bar.c hacks)
src/foo src-1.0/bar hacks
$(addsuffix .c, foo bar)
foo.c bar.c
$(addprefix src/, foo bar)
src/foo, src/bar
$(wildcard *.c)/* 等于 $(shell ls *.c)*/
1.c 2.c
其他
- $(foreach var, list, text)
- $(if condition, then-part, else-part)
- $(origin var)
- $(shell cmd)
dirs := a b c d
files := $(foreach dir, $(dir), $(wildcard $(dir)/*))
/* files 变量赋值为 a/ b/ c/ d/ 下边的所有文件*/
自动变量
- $@ 规则的目标文件
- $^ 所有依赖名字,名字之间空格分开
- $< 第一个依赖的名字
一个稍微复杂点的例子
src := $(shell *.c)
objs := $(patsbust %.c, %.o, $(src))
test: $(objs)
gcc -o $@ $^
%.o: %.c
gcc -c -o $@ $<
clean:
rm -f test *.o
3.3 常用ARM汇编指令和ATPCS规则
ARM汇编指令
- 相对跳转b, bl
- 数据传送指令mov
- 地址读取伪指令ldr
- 内存访问指令 ldr str ldm stm
- 加减 add sub
- 程序转台寄存器访问指令 msr mrs
- 其他 .extern .text .global
ATPCS
ARM程序和Thumb程序中子程序调用规则。
- 寄存器
- r0 - r3 传递参数
- r4 - r11 保存局部变量
- r12 子程序间scratch寄存器,别名ip
- r13 数据栈指针,别名sp
- r14 连接寄存器,别名lr
- r15 程序计数器,别名pc
- 堆栈
- FD 满减堆栈
- 8字节对齐
- stmdb ldmia 指令访问,db: Decending before, ia:increase after
- 参数传递规则
- 参数不超过4个,用r0 - r3,超过4个用堆栈
- 返回结果用r0 - r3
4 Windows,Linux环境下的工具、命令介绍
4.1 windows
Source insight
Cuteftp, SecureCRT, tftp
- 这几个工具用Xshell就全搞定了,不用书上介绍的这几个 *
4.2 Linux
KScope
CKermit
Vi
- 这几个工具暂时用不到,就用windows环境 Samba连接Linux即可,Vi可以通过Vimtutor熟悉下 *
grep find 命令
grep
- grep [operation] PATTERN [FILE…]
grep "request_irq" * -R // *表示当前目录下所有文件,-R 表示递归查找所有子目录
grep "request_irq" kernel -R // 在当前目录的kernel目录下查找,包含所有子目录
find -name "*.[ch]" | xargs grep "request_irq" // 在当前目录下所有的.c .h 文件中查找关键字
find
- find [-H] [-L] [-P] [path…] [expression]
find -name "*fb*"
find drivers/net -name "*fb*"
man
- man [section] name
- 需要注意的是[section] 部分,因为同一个关键字,可以出现在不同的地方,比如uname 可以是shell命令,也可以是系统格调用,单纯根据关键字是无法定位的。*
- section的定义
- 1 命令,比如ls grep find
- 2 系统调用,比如open read socket
- 3 库调用,比如fopen, fread
- 4 特殊文件 比如 /dev 目录下的文件
- 5 文件格式和惯例,比如/etc/passwd
- 6 游戏
- 7 其他
- 8 系统管理命令,如mount
- 9 内核例程
man uname
man 2 uname
tar
- tar有打包、解包、压缩、解压缩 4种功能
- 压缩格式
- gzip .gz .z
- bzip2 .bz2
- tar命令常用选项
- c 创建
- x 提取
- z 使用gzip方式处理
- j 使用bzip2方式处理
- f 文件,接文件名
- 最常用的选项组合套路
- czf, cjf 创建压缩包
- xzf, xjf 解压缩
tar czf dirA.tar.gz dirA
tar cjf dirA.tar.bz2 dirA
tar xzf dirA.tar.gz
tar xjf dirA.tar.bz2
diff patch
- diff是UNIX标准的文件差异对比工具和格式
- diff常用选项
- -u 显示上下文中一些相同的行
- -r 递归比较各目录下的文件
- -N 不存在的文件当做空文件
- -w 忽略空格
- -B 忽略空行
- 用diff命令制作补丁文件
diff -urNwB linux-2.6.22.6 linux-2.6.22.6_ok > linux-2.6.22.6_ok.diff
- patch的用法,patch用来把一个补丁文件(.diff)合并到工程中
cd linux-2.6.22.6
patch -p1 < ../linux-2.6.22.6_ok.diff
其中参数-p1 中是数字1,不是子母l。含义是忽略补丁文件中所有路径相关字段的的第一级目录,具体应该忽略几级,需要打开diff文件查看一下,跟patch执行的当前路径对比一下。