嵌入式Linux应用开发完全手册
3 嵌入式Linux基础知识
3.1 交叉编译工具
编译工具链,编译工作由几个步骤完成,分别用到了不同的工具
PC端应用
交叉编译(编译和运行在不同的环境下),arm平台工具链
arm-linux-gcc
arm-linux-ld
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
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文件转换位二进制可执行文件
arm-linux -objcopy -O binary -S elf_file bin_file
arm-linux-objdump选项
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
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 )
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)
自动变量
$@ 规则的目标文件
$^ 所有依赖名字,名字之间空格分开
$< 第一个依赖的名字
一个稍微复杂点的例子
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
grep "request_irq" kernel -R
find -name "*.[ch]" | xargs grep "request_irq"
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种功能
压缩格式
tar命令常用选项
c 创建
x 提取
z 使用gzip方式处理
j 使用bzip2方式处理
f 文件,接文件名
最常用的选项组合套路
czf, cjf 创建压缩包
xzf, xjf 解压缩
tar czf dirA.tar .gz dirA
tar cjf dirA.tar .bz 2 dirA
tar xzf dirA.tar .gz
tar xjf dirA.tar .bz 2
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执行的当前路径对比一下。