概述
常用gcc/makefile/arm汇编指令
这3个工具平时用的比较少,基本上是看了忘,忘了看,我们只记住几个常用的命令,不懂的可以用到再查。
推荐资料:<>,<<跟我一起写makefile>>,<<汇编语言程序设计+基于ARM体系结构>>
(1)arm-linux-gcc 选项
-c
预处理、编译和汇编源文件,但是不作连接,编译器根据源文件生成 OBJ 文件。缺省情
况下,GCC 通过用`.o'替换源文件名的后缀`.c',`.i',`.s'等,产生 OBJ 文件名。可以使
用-o 选项选择其他名字。GCC 忽略-c 选项后面任何无法识别的输入文件。
-S
编译后即停止,不进行汇编。对于每个输入的非汇编语言文件,输出结果是汇编语言文
件。缺省情况下,GCC 通过用`.s'替换源文件名后缀`.c',`.i'等等,产生汇编文件名。可
以使用-o 选项选择其他名字。GCC 忽略任何不需要汇编的输入文件。
-E
预处理后即停止,不进行编译。预处理后的代码送往标准输出。GCC 忽略任何不需要预
处理的输入文件。
-o file
指定输出文件为 file。无论是预处理、编译、汇编还是连接,这个选项都可以使用。如
果没有使用`-o'选项,默认的输出结果是:可执行文件为`a.out';修改输入文件的名称是
`source.suffix',则它的 OBJ 文件是`source.o',汇编文件是 `source.s',而预处理后的
C 源代码送往标准输出。
-v
显示制作 GCC 工具自身时的配置命令;同时显示编译器驱动程序、预处理器、编译器的
版本号
-Idir
在头文件的搜索路径列表中添加 dir 目录
举例如下:
$ gcc -c -o main.o main.c
$ gcc -c -o sub.o sub.c
$ gcc -o test main.o sub.o
(2)arm-linux-ld 选项
直接指定代码段、数据段、bss段的起始地址
-Ttext startaddr
-Tdata startaddr
-Tbss startaddr
举例如下:
arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elf
使用连接脚本设置地址
arm-linux-ld -Ttimer.lds -o timer_elf head.o init.o interrupt.o main.o
timer.lds:
SECTIONS {
. = 0x30000000; //设置“当前运行地址”为 0x30000000
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
(3) arm-linux-objdump 选项
-b bfdname 或--target=bfdname
指定目标码格式
--disassemble 或-d
反汇编可执行段(executable sections)。
--disassemble-all 或-D
与-d 类似,反汇编所有段。
--architecture=machine 或-m machine
指定反汇编目标文件时使用的架构,
arm-linux-objdump -D elf_file > dis_file //将ELF格式的文件转换为反汇编文件
arm-linux-objdump -D -b binary -m arm bin_file > dis_file //将二进制文件转换为反汇编文件
(4) arm-linux-objcopy 选项
-O bfdname 或--output-target= bfdname
使用指定的格式来输出文件,bfdname 是 BFD 库中描述的标准格式名。
-R sectionname 或--remove-section= sectionname
从输出文件中删掉所有名为 sectionname 的段。这个选项可以多次使用。
注意:不恰当地使用这个选项可能会导致输出文件不可用。
-S 或--strip-all(strip,剥去、剥)
不从源文件中拷贝重定位信息和符号信息到目标文件中去。
-g 或--strip-debug
不从源文件中拷贝调试符号到目标文件中去。
arm-linux-objcopy -O binary -S elf_file bin_file //将 ELF 格式的生成结果转换为二进制文件
Makefile 规则
目标(target)„: 依赖(prerequiries)„
命令(command)
Makefile 常用函数
(1)$(subst from,to,text)
在文本`text’中使用`to’替换每一处`from’。
比如:
$(subst ee,EE,feet on the street)
结果为‘fEEt on the strEEt’
(2)$(patsubst pattern,replacement,text)
寻找`text’中符合格式`pattern’的字,用`replacement’替换它们。`pattern’和
`replacement’中可以使用通配符。
比如:
$(patsubst %.c,%.o,x.c.c bar.c)
结果为:`x.c.o bar.o’。
(3)$(strip string)
去掉前导和结尾空格,并将中间的多个空格压缩为单个空格。
比如:
$(strip a b c )
结果为`a b c’。
(4)$(findstring find,in)
在字符串`in’中搜寻`find’,如果找到,则返回值是`find’,否则返回值为空。
比如:
$(findstring a,a b c)
$(findstring a,b c)
将分别产生值`a’和`’(空字符串)。
(5)$(filter pattern...,text)
返回在`text’中由空格隔开且匹配格式`pattern...’的字 , 去除不符合格式`pattern...’的字。
比如:
$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
结果为`foo.c bar.c baz.s’。
(6)$(filter-out pattern...,text)
返回在`text’中由空格隔开且不匹配格式`pattern...’的字,去除符合格式`pattern...’的字。它是函数 filter 的反函数。
比如:
$(filter %.c %.s,foo.c bar.c baz.s ugh.h)
结果为`ugh.h’。
(7)$(sort list)
将‘list’中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开的字的列表。
比如:
$(sort foo bar lose)
返回值是‘bar foo lose’。
(1)$(dir names...)
抽取‘names...’中每一个文件名的路径部分,文件名的路径部分包括从文件名的首字
符到最后一个斜杠(含斜杠)之前的一切字符。
比如:
$(dir src/foo.c hacks)
结果为‘src/ ./’。
(2)$(notdir names...)
抽取‘names...’中每一个文件名中除路径部分外一切字符(真正的文件名)
比如:
$(notdir src/foo.c hacks)
结果为‘foo.c hacks’。
(3)$(suffix names...)
抽取‘names...’中每一个文件名的后缀。
比如:
$(suffix src/foo.c src-1.0/bar.c hacks)
结果为‘.c .c’。
(4)$(basename names...)
抽取‘names...’中每一个文件名中除后缀外一切字符。
比如:
$(basename src/foo.c src-1.0/bar hacks)
结果为‘src/foo src-1.0/bar hacks’。
(5)$(addsuffix suffix,names...)
参数‘names...’是一系列的文件名,文件名之间用空格隔开;suffix 是一个后缀名。
将 suffix(后缀)的值附加在每一个独立文件名的后面,完成后将文件名串联起来,它们之间
用单个空格隔开。
比如:
$(addsuffix .c,foo bar)
结果为‘foo.c bar.c’。
(6)$(addprefix prefix,names...)
参数‘names’是一系列的文件名,文件名之间用空格隔开;prefix 是一个前缀名。将preffix(前缀)的值附加在每一个独立文件名的前面,
完成后将文件名串联起来,它们之间用单个空格隔开。
比如:
$(addprefix src/,foo bar)
结果为‘src/foo src/bar’。
(7)$(wildcard pattern)
参数‘pattern’是一个文件名格式,包含有通配符(通配符和 shell 中的用法一样)。函
数 wildcard 的结果是一列和格式匹配的且真实存在的文件的名称,
文件名之间用一个空格隔开。比如若当前目录下有文件 1.c、2.c、1.h、2.h,则:
c_src := $(wildcard *.c)
结果为‘1.c 2.c’。
3.
其他函数
(1)$(foreach var,list,text)
前两个参数,‘var’和‘list’将首先扩展,注意最后一个参数‘text’此时不扩展;
接着,‘list’扩展所得的每个字,都赋给‘var’变量;然后‘text’引用该变量进行扩展,
因此‘text’每次扩展都不相同。函数的结果是由空格隔开的‘text’ 在‘list’中多次扩展后,得到的新‘list’
,就是说:‘text’多次扩展的字串联起来,字与字之间由空格隔开,如此就产生了函数 foreach的返回值。
下面是一个简单的例子,将变量‘files’的值设置为 ‘dirs’中的所有目录下的所有
文件的列表:
dirs := a b c d
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
这里‘text’是‘$(wildcard $(dir)/*)’,它的扩展过程如下:
1 第一个赋给变量 dir 的值是`a’,扩展结果为‘$(wildcard a/*)’;
2 第二个赋给变量 dir 的值是`b’,扩展结果为‘$(wildcard b/*)’;
3 第三个赋给变量 dir 的值是`c’,扩展结果为‘$(wildcard c/*)’;
4 如此继续扩展。
这个例子和下面的例有共同的结果:
files := $(wildcard a/* b/* c/* d/*)
(2)$(if condition,then-part[,else-part])
首先把第一个参数‘condition’的前导空格、结尾空格去掉,然后扩展。如果扩展为非
空字符串,则条件‘condition’为 ‘真’ 如果扩展为空字符串,则条件‘condition’ ‘为假’
如果条件‘condition’为‘真’,那么计算第二个参数‘then-part’的值,并将该值作为整个函数 if 的值。
如果条件‘condition’ ‘假’,并且第三个参数存在,
则计算第三个参数‘else-part’的值,并将该值作为整个函数 if 的值;如果第三个参数不存在,函数 if 将什么也不计算,
返回空值。注意:仅能计算‘then-part’和‘else-part’二者之一,不能同时计算。这样有可能产生副作用(例如函数 shell 的调用)
。
(3)$(origin variable)
变量‘variable’是一个查询变量的名称,不是对该变量的引用。所以,不能采用‘$’
和圆括号的格式书写该变量,当然,如果需要使用非常量的文件名,可以在文件名中使用变
量引用。
函数 origin 的结果是一个字符串,该字符串变量是这样定义的:
‘undefined':如果变量‘variable’从没有定义;
‘default':变量‘variable’是缺省定义;
‘environment':变量‘variable’作为环境变量定义,选项‘-e’没有打开;
‘environment override' :变量‘variable’作为环境变量定义,选项‘-e’已打开;
‘file':变量‘variable’在 Makefile 中定义;
‘command line':变量‘variable’在命令行中定义;
‘override':变量‘variable’在 Makefile 中用 override 指令定义;
‘automatic':变量‘variable’是自动变量
(4)$(shell command arguments)
函数 shell 是 make 与外部环境的通讯工具。函数 shell 的执行结果和在控制台上执行
‘command arguments’的结果相似。不过如果‘command arguments’的结果含有换行符(和
回车符) 则在函数 shell 的返回结果中将把它们处理为单个空格,
,
若返回结果最后是换行符
(和回车符)则被去掉。
比如当前目录下有文件 1.c、2.c、1.h、2.h,则:
c_src := $(shell ls *.c)
结果为‘1.c 2.c’。
举例如下:
src := $(shell ls *.c)
objs := $(patsubst %.c,%.o,$(src))
test: $(objs)
gcc -o $@ $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm -f test *.o
上述 Makefile 中$@、$^、$<称为自动变量。$@表示规则的目标文件名;$^表示所有依赖的名字,名字之间用空格隔开;$<表示第一个依赖的文件名。
‘%’是通配符,它和一个字符串中任意个数的字符相匹配
常用ARM汇编指令:
1. 相对跳转指令:b、bl
这两条指令的不同之处在于 bl 指令除了跳转之外,还将返回地址(bl 的下一条指令的地
址)保存在 lr 寄存器中。
这两条指令的可跳转范围是当前指令的前后 32M:-32M~+32M。
它们是位置无关的指令。
使用示例:
b fun1
„„
fun1:
bl fun2
„„
fun2:
„„
2. 数据传送指令 mov,地址读取伪指令 ldr
mov 指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋给寄存器。例子
如下:
mov r1, r2 /* r1=r2 */
mov r1, #4096 /* r1=4096 */
mov 指令传送的常数必须能用“立即数”来表示,立即数的格式请参考 4.1.5 节。
当不知道一个数能否用“立即数”来表示时,可以使用 ldr 命令来赋值。ldr 是伪指令,
它不是真实存在的指令,编译器会把它扩展成真正的指令:如果该常数能用“立即数”来表
示,则使用 mov 指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
例子如下:
ldr r1, =4097 /* r1=4097 */
ldr 本意为“大范围的地址读取伪指令”,上面的例子使用它来将常数赋给寄存器 r1。下
面的例子是获得代码的绝对地址:
ldr r1, =label
label:
„„
3. 内存访问指令:ldr、str、ldm、stm
注意: “ldr”指令既可能是前面所述的“大范围的地址读取伪指令”, 也可能是内存访问指令。
当它的第二个参数前面有“=”号时,表示伪指令,否则表示内存访问指令。
ldr 指令从内存中读取数据到寄存器,str 指令把寄存器的值存储到内存中,它们操作的
数据都是 32位的。示例如下:
ldr r1, [r2, #4] /* 将地址为 r2+4 的内存单元数据读取到 r1 中 */
ldr r1, [r2] /* 将地址为 r2 的内存单元位数据读取到 r1 中 */
ldr r1, [r2], #4 /* 将地址为 r2 的内存单元数据读取到 r1 中,然后 r2=r2+4 */
str r1, [r2, #4] /* 将r1 的数据保存到地址为 r2+4 的内存单元中 */
str r1, [r2] /* 将r1 的数据保存到地址为 r2 的内存单元中 */
str r1, [r2], #4 /* 将r1 的数据保存到地址为 r2 的内存单元中,然后 r2=r2+4 */
ldm 和 stm 属于批量内存访问指令,只用一条指令就可以读写多个数据。它们的格式如
下:
ldm{cond} {!} {^}
stm{cond} {!} {^}
其中{cond}表示指令的执行条件,请参考后面表 4.2。
表示地址变化模式,有以下 4 种方式:
① ia(Increment After) 事后递增方式
② ib(Increment Before) 事先递增方式
③ da(Decrement After) 事后递减方式
④ db(Decrement Before) 事先递减方式
中保存内存的地址,如果后面加上了感叹号,指令执行后,rn 的值会更新:等于下
一个内存单元的地址。
表示寄存器列表,对于 ldm 指令,从所对应的内存块中取出数据,
写入这些寄存器;对于 stm 指令,把这些寄存器的值,写入所对应的内存块中。
{^}有两种含义:如果中有 pc 寄存器,它表示指令执行后,spsr 寄存
器的值将自动复制到 cpsr 寄存器中──这常用于从中断处理函数中返回;如果list>中没有pc 寄存器, {^}表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存
器。
指令中寄存器列表和内存单元的对应关系为:编号低的寄存器对应内存中的低地址单元,
编号高的寄存器对应内存中的高地址单元。
ldm 和 stm 指令示例如下:
01 HandleIRQ: @ 中断入口函数
02 sub lr, lr, #4 @ 计算返回地址
03 stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
04 @ r0-r12,lr 被保存在 sp 表示的内存中,
05 @ “!”使得指令执行后,sp=sp-14*4
06
07 ldr lr, =int_return @ 设置调用 IRQ_Handle 函数后的返回地址
08 ldr pc, =IRQ_Handle @ 调用中断分发函数
09 int_return:
10 ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将 spsr 的值复制到 cpsr
11 @ 于是从 irq 模式返回被中断的工作模式
4. 加减指令:add、sub
例子如下:
add r1, r2, #1 /* 表示 r1=r2+1,即寄存器 r1 的值等于寄存器 r2 的值加上 1 */
sub r1, r2, #1 /* 表示 r1=r2-1 */
5. 程序状态寄存器的访问指令:msr、mrs
ARM 处理器有一个程序状态寄存器(cpsr),它用来控制处理器的工作模式、设置中断的
总开关。示例如下:
msr cpsr, r0 /* 复制 r0 到 cpsr 中 */
mrs r0, cpsr /* 复制 cpsr到 r0 中 */
在第 9 章会描述 cpsr 寄存器的格式。
6. 其他伪指令
在本书的汇编程序中,常常见到如下语句:
.extern main
.text
.global _start
_start:
“.extern”定义一个外部符号(可以是变量也可以是函数), 上面的代码表示本文件中引
用的 main 是一个外部函数。
“.text”表示下面的语句都属于代码段。
“.global”将本文件中的某个程序标号定义为全局的,比如上面的代码表示_start 个
全局函数。
最后
以上就是感性母鸡为你收集整理的4412裸机程序之常用gcc/makefile/arm汇编指令的全部内容,希望文章能够帮你解决4412裸机程序之常用gcc/makefile/arm汇编指令所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复