您的位置:首页 > 其它

4412裸机程序之常用gcc/makefile/arm汇编指令

2015-11-14 15:02 309 查看
常用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裸机程序