【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

作者 : 韩曙亮

博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137

转载请著名出处


本博客相关文档下载

-- ARM 汇编手册http://download.csdn.net/detail/han1202012/8328375

-- ARM 手册 : http://download.csdn.net/detail/han1202012/8324641

-- ARM 9 芯片文档 : http://download.csdn.net/detail/han1202012/8332389

-- ARM 11 芯片文档 : http://download.csdn.net/detail/han1202012/8332403

一. ARM 汇编概述

1. 汇编使用位置

汇编位置 :

-- 启动代码 : Bootloader 初始化时对 CPU 和 协处理器 等进行初始化, 此时没有建立起 C 语言运行环境, 这个时候使用汇编语言执行初始化操作;

-- 效率要求 : 汇编效率高, Linux 内核中, 对效率有特殊要求的地方需要汇编;

2. 汇编分类

(1) ARM 标准汇编

ARM 标准汇编简介 :

-- 使用场景 : 适用于ARM公司的汇编器, 适合在 Windows 平台使用, 如ADS;

(2) GNU汇编

GNU 汇编简介 :

-- 使用场景 : 适用于 Linux 平台交叉编译工具链的汇编器;

3. ARM 汇编程序框架

ARM 汇编框架 :

-- ARM 汇编框架示例 :

.section .data
	< 初始化的数据>
.section .bss
	< 未初始化的数据>
.section .text
.global _start
_start:
	<汇编代码>

-- 程序入口 : "_start:" 是汇编程序的入口, 相当于 main();

-- 标注入口 : 使用 ".global _start" 标注程序入口, 外部才可以识别这是程序入口;

-- 标明代码段 : ".section .text" 标明这是一个代码段;

-- 标明 bss 段 : 使用 ".section .bss" 标明bss段, 如果没有 bss 段 和 数据段, 直接从 .text 开始;

4. 搭建汇编开发调试环境

(1) 汇编程序准备

程序代码 :

-- 定义代码段 : .text ;

-- 定义程序入口 : .globl _start;

-- 代码示例 :

.text
.globl _start
_start:
	mov r1,#1
	mov r2,#2
	mov r3,#3

Makefile 代码 :

-- 链接 elf 格式文件 : 设置程序起始位置 6410板子是 0x50008000 地址;

-- 在 arm-linux-ld 指定程序起始地址 : 在 -Ttext 50008000 即可;

-- 如果使用链接器脚本指定地址 : 注意第三行指定程序起始地址;

SECTIONS
{
	. = 0x50008000;

	. = ALIGN(4);
	.text :
	{
		led.o	(.text)
		*(.text)
	}

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
}

-- 代码示例 :

all:start.o
	arm-linux-ld -Ttext 0x50008000 -o start.elf $^

%.o:%.S
	arm-linux-gcc -g -o $@ $^ -c

clean:
	rm -rf *.o *.elf

(2) 启动 JLink 调试

JLink 调试启动 :

-- 确保驱动安装 : 注意 要安装 Windows 驱动;

-- 连接 JLink : 虚拟机右下角连接 JLink;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 启动 JLinkGDBServer :

[root@localhost JLink_Linux_V434a]# ./JLinkGDBServer
SEGGER J-Link GDB Server V4.34a

JLinkARM.dll V4.34a (DLL compiled Aug 31 2011 11:51:40)

Listening on TCP/IP port 2331

J-Link connected
Firmware: J-Link ARM V8 compiled Aug 24 2011 17:23:32
Hardware: V8.00
S/N: 17935099
Feature(s): RDI,FlashDL,FlashBP,JFlash

J-Link found 2 JTAG devices, Total IRLen = 5
JTAG ID: 0x07B76F0F (ARM11)

(2) eclipse 调试环境

搭建 eclipse 调试环境 :

-- 导入工程 : 选择 Makefile Project With Existing Code;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 选择导入的代码位置 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- clean 代码 : 选择 "Project" --> "Clean";

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- build 工程 : 选择 "菜单" --> Project --> Build All 选项即可;

-- 配置 Debug 调试参数 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 执行调试 : F6 单步调试走两步, 可以再 Register 视图中查看寄存器的值, 可以看到 r1 和 r2 被赋值为 1 和 2 了;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

二. ARM 指令分类

ARM 汇编手册 :

-- CSDN 下载地址 :http://download.csdn.net/detail/han1202012/8328375.

转载请著名出处

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

GNU 汇编 与 ARM 标准汇编区别 : 上面的手册是 ARM 标准汇编手册, 我们写的是 GNU 汇编手册, 有一定区别;

-- 大小写区别 : ARM 标准汇编 都是大写的, GNU 汇编可以是小写字母;

1. 算术和逻辑指令

(1) MOV 指令

MOV 指令简介 : 赋值操作;

-- 语法格式 : MOV <dest>, <op1>;

-- 语法解析 : dest 是目的寄存器, op1 可以是立即数, 也可以是寄存器, 地址等, 等价于 dest = op1;

汇编程序注释 : 汇编中使用 "@" 符号添加注释;

示例代码 :

.text
.global _start
_start:

@mov 指令范例
mov r1, #8	@将 8 赋值给 r1
mov r2, r1	@将 r1 中的值赋值给 r2
mov r3, #10	@将 10 赋值给 r3 寄存器

(2) MVN 指令

MVN 指令简介 : 取反赋值操作;

-- 语法格式 : MVN <dest>, <op1>;

-- 语法解析 : 将操作数 op1 取反后 赋值给 dest;

指令示例 :

-- 代码 :

.text
.global _start
_start:

@mvn 指令范例
mvn r1, #0b10	@0b10 二进制数取反, 赋值给 r1
mvn r2, #5	@5 十进制数取反, 赋值给 r2
mvn r3, r1	@将 r1 寄存器的值, 赋值给 r3

(3) SUB 指令

SUB 指令简介 : 减法操作;

-- 语法格式 : SUB <dest>, <op1>, <op2>;

-- 语法解析 : dest 存放减法结果, op1 是减数, op2 是被减数, dest = op1 - op2;

-- 注意 : dest op1 都不能使用立即数, op2 可以使用立即数;

代码示例 :

.text
.global _start
_start:

@sub 指令范例
@sub r1, #4, #2 错误示例, 减数不能是立即数, 必须是寄存器
mov r2, #4
sub r1, r2, #4
mov r0, #1
sub r3, r1, r0

(4) ADD 指令

ADD 指令简介 : 加法操作;

-- 语法格式 : ADD <dest>, <op1>, <op2>;

-- 语法解析 : dest 存放加法结果, op1 和 op2 是相加的两个数, dest = op1 + op2;

-- 注意 dest op1 都不能使用立即数, op2 可以使用立即数;

代码示例 :

@add 指令范例
mov r2, #1
add r1, r2, #3

(5) AND 指令

AND 指令简介 : 逻辑与操作;

-- 语法格式 : AND <dest>, <op1>, <op2>;

-- 语法解析 : dest 存放逻辑与结果, op1 和 op2 是相与的两个数, dest = op1 & op2;

-- 注意 dest op1 都不能使用立即数, 必须使用寄存器, op2 可以使用立即数;

代码示例 :

.text
.global _start
_start:

@and 指令范例
mov r1, #5
and r2, r1, #0

mov r1, #5
mov r2, r1, #1

(6) BIC 指令

BIC 指令简介 : 位清除指令操作;

-- 语法格式 : AND <dest>, <op1>, <op2>;

-- 语法解析 : dest 存放位清除结果, op1 是被清除的对象, op2 是掩码;

-- 示例 : "bic r0, r0, #0b1011", 清除 r0 中的 第0, 1, 3 位, 其余位保持不变, 结果放入 r0 中;

-- 注意 dest op1 都不能使用立即数, 必须使用寄存器, op2 可以使用立即数; 

-- 二进制表示 : 掩码中 % 在标准汇编中表示二进制, 但是在 GNU 汇编中无法使用, GNU 汇编中使用 0b 代表二进制;

代码示例 :

.text
.global _start
_start:

@bic 指令范例
mov r1, #0b101011
bic r2, r1, #0b101	@将r1 的 0, 2 位清除

2. 比较指令

(1) CMP 指令

CMP 指令简介 : 比较指令;

-- 语法格式 : CMP <op1>, <op2>;

-- 语法解析 : 比较结果有三种 op1 > op2 (CPSR N = 0), op1 = op2 (CPSR Z = 1), op1 < op2 (CPSR N = 1), 结果放入 CPSR 寄存器;

代码示例 :

.text
.global _start
_start:

@cmp 指令范例
mov r1, #2
cmp r1, #1

mov r1, #2
cmp r1, #3

mov r1, #2
cmp r1, #2

(2) TST 指令

TST 指令简介 : 比较指令;

-- 语法格式 : TST <op1>, <op2>;

-- 语法解析 : op1 和 op2 按位与操作, 结果影响 CPSR 寄存器, 如果结果 不为 0, CPSR 的 Z = 0, 如果结果为0, Z = 1;

代码示例 :

.text
.global _start
_start:

@cmp 指令范例
mov r1, #0b101
tst r1, #0b001	@按位与结果是 0b1, 结果不为0, CPSR Z = 0

mov r1, #0b101
tst r1, #0b10	@按位与结果是 0, 结果不为

3. 分支指令

(1) B 指令

B 指令简介 : 分支指令;

-- 语法格式 : B{条件} 地址;

-- 语法解析 : 如果满足条件, 就跳转到 地址 位置, 如果不满足条件, 就执行下面的语句, 如果没有条件, 就是 100% 执行;;

代码示例 :

-- 条件分析 : gt 是大于条件, 如果 r1 > r2 就走条件分支, 否则就继续执行下一条;

.text
.global _start
_start:

@b 分支指令范例
mov r1, #6
mov r2, #5
cmp r1, r2	@比较 r1 和 r2 中的值
@b 后可以跟一个条件, {条件} 在 {} 中就是可加可不加, 如果没有条件就是无条件100%执行
@gt 是大于条件指令, 如果条件满足会跳转到 branch1, 如果不满足就执行下面的指令
bgt branch1
add r3, r1, r2
b end	@这里为了不执行 branch1 操作, 直接跳转到 end 执行

branch1:
sub r3, r1, r2

end:
nop

(2) BL 指令

BL 指令简介 : 带连接的分支指令;

-- 语法格式 : BL{条件} 地址;

-- 语法解析 如果满足条件, 就跳转到 地址 位置, 如果不满足条件, 就执行下面的语句, 如果没有条件, 就是 100% 执行;;

代码示例 :

.text
.global _start
_start:

@bl 带连接的分支指令范例
mov r1, #2
cmp r1, #1
@此时跳转到 func1, func1 执行完程序无法返回, 如果 使用 bl 跳转, 程序会返回
@b func1
@此时使用 bl 跳转到 func1 执行, func1 执行完毕后会返回执行下面的语句
bl func1		

mov r1, #2
cmp r1, #3

func1:
mov r1, #2
cmp r1, #2

mov r1, #4
cmp r1, #6

4. 移位指令

(1) LSL 指令

LSL 指令简介 : 逻辑左移指令;

-- 语法格式 : Rx, LSL#2;

-- 语法解析 : 将 Rx 寄存器中的值, 左移2 位;

代码示例 :

.text
.global _start
_start:

@lsl 左移指令范例
mov r1, #0b1
@将 r1 中的值, 左移 2 位, 放入 r1 寄存器中
mov r1, r1, lsl#2

(2) ROR 指令

ROR 指令简介 : 循环右移指令;

-- 语法格式 : Rx, ROR#2;

-- 语法解析 : 将 Rx 寄存器中的值 循环右移 2 位;

代码示例 :

.text
.global _start
_start:

@ror 循环右移指令范例
mov r1, #0b11
@结果是 ob1000...0001
mov r1, r1, ror#1

5. 程序状态字访问指令

程序状态字 : CPSRSPSR;

-- 注意 : 程序状态字 不能使用 通用寄存器的语句 如 MOV 等访问, 必须使用 程序状态寄存器的 专用指令 读写;

转载请著名出处

代码示例 :

.text
.global _start
_start:

@mrs 指令范例
@rs 是 将 s -> r, sr 是 r -> s
mrs r0, cpsr	@将 cpsr 中的数据搬移到 r0 中
orr r0, #0b100	@将 cpsr 中的第三位置为1
msr cprs, r0

6. 存储器访问指令

(1) STR 指令


STR 指令简介 : 将 寄存器中的值 保存到 内存中;

-- 语法格式 : str r0, 地址;

-- 语法解析 : 将 R0 寄存器中的值 保存到 内存地址中;;

代码示例 :

.text
.global _start
_start:

@str 指令范例
mov r0, #0xff
@将 r1 值改为 50000000 (OK-6410)
str r0, [r1]

-- 调试 : 添加地址监控, 在 Memory 视图中进行监控;

(2) LDR 指令

LDR 指令简介 : 将 寄存器中的值 保存到 内存中;

-- 语法格式 : ldr r0, 地址;

-- 语法解析 : 将 内存地址中 存放的值 加载入 r0 中;

代码示例 :

@ldr 指令范例
mov r0, #0xff
@将 r1 值改为 50000000 (OK-6410)
str r0, [r1]
ldr r0, [r1]

7. 以上所有代码示例

以上所有代码示例 : 便于调试学习;

.text
.global _start
_start:

@ldr 指令范例
mov r0, #0xff
@将 r1 值改为 50000000 (OK-6410)
str r0, [r1]
ldr r0, [r1]

@str 指令范例
mov r0, #0xff
@将 r1 值改为 50000000 (OK-6410)
str r0, [r1]

@mrs msr 指令范例
@rs 是 将 s -> r, sr 是 r -> s
mrs r0, cpsr	@将 cpsr 中的数据搬移到 r0 中
orr r0, #0b100	 程序入口, 用法 ".globol _start", 注意前面加上点;@将 cpsr 中的第三位置为1
msr cprs, r0

@ror 循环右移指令范例
mov r1, #0b11
@结果是 ob1000...0001
mov r1, r1, ror#1

@lsl 左移指令范例
mov r1, #0b1
@将 r1 中的值, 左移 2 位, 放入 r1 寄存器中
mov r1, r1, lsl#2

@bl 带连接的分支指令范例
mov r1, #2
cmp r1, #1
@此时跳转到 func1, func1 执行完程序无法返回, 如果 使用 bl 跳转, 程序会返回
@b func1
@此时使用 bl 跳转到 func1 执行, func1 执行完毕后会返回执行下面的语句
bl func1		

mov r1, #2
cmp r1, #3

func1:
mov r1, #2
cmp r1, #2

mov r1, #4
cmp r1, #6

@b 分支指令范例
mov r1, #6
mov r2, #5
cmp r1, r2	@比较 r1 和 r2 中的值
@b 后可以跟一个条件, {条件} 在 {} 中就是可加可不加, 如果没有条件就是无条件100%执行
@gt 是大于条件指令, 如果条件满足会跳转到 branch1, 如果不满足就执行下面的指令
bgt branch1
add r3, r1, r2
b end	@这里为了不执行 branch1 操作, 直接跳转到 end 执行

branch1:
sub r3, r1, r2

end:
nop

@cmp 指令范例
mov r1, #0b101
tst r1, #0b001	@按位与结果是 0b1, 结果不为0, CPSR Z = 0

mov r1, #0b101
tst r1, #0b10	@按位与结果是 0, 结果不为

@cmp 指令范例
mov r1, #2
cmp r1, #1

mov r1, #2
cmp r1, #3

mov r1, #2
cmp r1, #2

@bic 指令范例
mov r1, #0b101011
bic r2, r1, #0b101	@将r1 的 0, 2 位清除

@and 指令范例
mov r1, #5
and r2, r1, #0

mov r1, #5
mov r2, r1, #1

@add 指令范例
mov r2, #1
add r1, r2, #3

@mov 指令范例
mov r1, #8	@将 8 赋值给 r1
mov r2, r1	@将 r1 中的值赋值给 r2
mov r3, #10	@将 10 赋值给 r3 寄存器

@mvn 指令范例
mvn r1, #0b10	@0b10 二进制数取反, 赋值给 r1
mvn r2, #5	@5 十进制数取反, 赋值给 r2
mvn r3, r1	@将 r1 寄存器的值, 赋值给 r3

@sub 指令范例
@sub r1, #4, #2 错误示例, 减数不能是立即数, 必须是寄存器
mov r2, #4
sub r1, r2, #4
mov r0, #1
sub r3, r1, r0

三. ARM 伪指令

参考文档 : ARM 文档 Page 110, 上面有提供下载.

1. ARM 机器码

(1) 机器码反汇编示例

汇编程序执行流程 : 汇编代码 --> 汇编器 --> 机器码 --> CPU 运行;

反汇编示例 : 找到一个 elf 文件, 使用 arm-linux-objdump 反汇编;

-- 命令 : 使用 arm-linux-objdump -S -D start.elf 命令进行反汇编, 其中 "50008000: e3a01001 mov r1, #1 ; 0x1" 中的 "e3a01001" 就是机器码, 如下图标注部分;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 反汇编部分结果 :

[root@localhost 04_assembly]# arm-linux-objdump -S -D start.elf 

start.elf:     file format elf32-littlearm

Disassembly of section .text:

50008000 <_start>:
.text
.globl _start
_start:
	mov r1,#1
50008000:	e3a01001 	mov	r1, #1	; 0x1
	mov r2,#2
50008004:	e3a02002 	mov	r2, #2	; 0x2
	mov r3,#3
50008008:	e3a03003 	mov	r3, #3	; 0x3
Disassembly of section .debug_aranges:

(2) 机器码格式

机器码格式 : 截图自 arm 文档 P110;

-- ARM 机器码位数 : 32位;

-- 机器码分段 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

(3) 解析 MOV 指令机器码

代码准备 :

-- 汇编代码 :

.text
.globl _start
_start:

	mov r0, r1
	moveq r0, #0xff

-- Makefile 脚本 :

all:start.o
	arm-linux-ld -Ttext 0x50008000 -o start.elf $^

%.o:%.S
	arm-linux-gcc -g -o $@ $^ -c

clean:
	rm -rf *.o *.elf

反汇编 elf 文件 :

-- 反汇编内容 : 省略下面的大部分;

[root@localhost 04_assembly]# arm-linux-objdump -S -D start.elf 

start.elf:     file format elf32-littlearm

Disassembly of section .text:

50008000 <_start>:
.text
.globl _start
_start:

	mov r0, r1
50008000:	e1a00001 	mov	r0, r1
	moveq r0, #0xff
50008004:	03a000ff 	moveq	r0, #255	; 0xff
Disassembly of section .debug_aranges:

汇编对应机器码 :

-- "mov r0, r1" : 十六进制 0xe1a00001, 二进制      11100001101000000000000000000001;

-- "moveq r0, #0xff" : 十六进制 0x03a000ff, 二进制 00000011101000000000000011111111;

机器码解析 :

第一条 : 1110 00 0 1101 0 0000 0000 000000000001

第二条 : 0000 00 1 1101 0 0000 0000 000011111111

-- 条件位对比 (第一段 31 ~ 28) :  第一条是 1110 对应 AL 总是执行,  第二条是 0000 对应 EQ;

-- 保留位对比 (第二段 27 ~ 26) : 第一条 00, 第二条 00, 明显都一样;

-- I 操作数类型标识位 (第三段 25) : 标志最后一个存立即数 还是寄存器, 如果是 0 表示寄存器, 如果是 1 表示立即数;

-- 操作码位 (第四段 24 ~ 21) : 区分不同指令, 1101 是 MOV 指令;

-- S 状态寄存器改变标识 (第五段 20) : 是否影响 CPSR 寄存器, 如果 S = 0 不影响, 如果 S = 1 影响;

-- Rn 源操作寄存器 (第六段 19 ~ 16) : MOV 和 MVN 不使用 Rn 位, 寄存器编号;

-- Rd 目的操作寄存器 (第七段 15 ~ 12) : 寄存器编号;

-- shifter_operand 源操作书 (第八段 11 ~ 0) : 源操作数, 这个与 I 位结合起来, 如果 I = 0, 该位表示寄存器编号, 如果 I = 1, 该位表示 立即数大小, 立即数是有范围的, 如果超出会报错, 这里就需要使用伪指令了;

(4) 机器码相关文档

相关文档 :

-- 位数文档 : P116, The ARM Instruction Set, A3.4.1 Instruction encoding;

-- MOV 和 MVN 指令 : 机器码格式 "<opcode1>{<cond>}{S} <Rd>, <shifter_operand>", 没有 Rn 字段, 该字段没用;

转载请著名出处

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 条件位文档 : Page 112, The ARM Instruction Set, A3.2.1 Condition code 0b1111;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

2. 伪指令

伪指令简介 : 伪指令没有对应的机器码, 这种指令只在编译时起作用, 伪指令需要转化成 其它汇编指令运行, 如 定义 宏, 不会产生机器码;

(1) globol 伪指令

globol 伪指令介绍 :

-- 伪指令作用 : 用于定义 程序入口, 用法 ".globol _start", 注意前面加上点;

-- 代码示例 :

.text
.global _start
_start:

@lsl 左移指令范例
mov r1, #0b1
@将 r1 中的值, 左移 2 位, 放入 r1 寄存器中
mov r1, r1, lsl#2

(2) data acsii byte word 伪指令

伪指令介绍 :

-- 伪指令作用 : data 用于定义 数据段, 标明后面的数据存放到数据段中; acsii 标明字符串变量类型, byte 标明 byte 类型变量, word  标明 word 类型变量;

代码示例 :

-- 汇编代码 : start.S ;

.data	@定义数据变量
hello:	@标明变量地址, 字符串变量
.ascii "Hello World !"
bh:	@标明变量地址, byte 变量
.byte 0x1
ADD:	@标明变量地址, word 变量
.word 0xff

.text
.global _start
_start:

mov r0, #0xff

-- make 脚本 : Makefile;

all: start.o
	arm-linux-ld -Ttext 0x50008000 -o start.elf start.o

start.o : start.S
	arm-linux-gcc -g -o start.o -c start.S

.PHONY: clean
clean:
	rm *.o *.elf *.bin

分析 elf 文件 : 使用 arm-linux-readelf -a start.elf 命令分析 start.elf 文件;

-- .data 段地址 : 注意 [2] 中 .data 地址为 0x50010004;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 数据变量 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- elf 文件分析全文 :

octopus@octopus:~/arm/demo$ arm-linux-readelf -a start.elf
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x50008000
  Start of program headers:          52 (bytes into file)
  Start of section headers:          33100 (bytes into file)
  Flags:                             0x5000002, has entry point, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         11
  Section header string table index: 8

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        50008000 008000 000004 00  AX  0   0  4
  [ 2] .data             PROGBITS        50010004 008004 000012 00  WA  0   0  1
  [ 3] .debug_aranges    PROGBITS        00000000 008018 000020 00      0   0  8
  [ 4] .debug_info       PROGBITS        00000000 008038 000048 00      0   0  1
  [ 5] .debug_abbrev     PROGBITS        00000000 008080 000014 00      0   0  1
  [ 6] .debug_line       PROGBITS        00000000 008094 000037 00      0   0  1
  [ 7] .ARM.attributes   ARM_ATTRIBUTES  00000000 0080cb 000014 00      0   0  1
  [ 8] .shstrtab         STRTAB          00000000 0080df 00006c 00      0   0  1
  [ 9] .symtab           SYMTAB          00000000 008304 000180 10     10  13  4
  [10] .strtab           STRTAB          00000000 008484 000087 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x008000 0x50008000 0x50008000 0x00004 0x00004 R E 0x8000
  LOAD           0x008004 0x50010004 0x50010004 0x00012 0x00012 RW  0x8000

 Section to Segment mapping:
  Segment Sections...
   00     .text
   01     .data 

There is no dynamic section in this file.

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.symtab' contains 24 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 50008000     0 SECTION LOCAL  DEFAULT    1
     2: 50010004     0 SECTION LOCAL  DEFAULT    2
     3: 00000000     0 SECTION LOCAL  DEFAULT    3
     4: 00000000     0 SECTION LOCAL  DEFAULT    4
     5: 00000000     0 SECTION LOCAL  DEFAULT    5
     6: 00000000     0 SECTION LOCAL  DEFAULT    6
     7: 00000000     0 SECTION LOCAL  DEFAULT    7
     8: 50010004     0 NOTYPE  LOCAL  DEFAULT    2 hello
     9: 50010011     0 NOTYPE  LOCAL  DEFAULT    2 bh
    10: 50010011     0 NOTYPE  LOCAL  DEFAULT    2 $d
    11: 50010012     0 NOTYPE  LOCAL  DEFAULT    2 ADD
    12: 50008000     0 NOTYPE  LOCAL  DEFAULT    1 $a
    13: 50008004     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_end
    14: 50010016     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
    15: 50010016     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start__
    16: 50008004     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start
    17: 50010016     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__
    18: 50008000     0 NOTYPE  GLOBAL DEFAULT    1 _start
    19: 50010016     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    20: 50010018     0 NOTYPE  GLOBAL DEFAULT  ABS __end__
    21: 50010016     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    22: 50010018     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    23: 50010004     0 NOTYPE  GLOBAL DEFAULT    2 __data_start

No version information found in this file.
Attribute Section: aeabi
File Attributes
  Tag_CPU_arch: v4
  Tag_ARM_ISA_use: Yes

(3) equ 伪指令

equ 伪指令介绍 :

-- 伪指令作用 : 该指定作用是定义常量;

-- 代码示例 :

.text
.global _start
_start:

@定义一个宏变量
.equ DA, 0x68

@将 DA 值赋值给 r0 寄存器
mov r0, #DA

(4) align 伪指令

align 伪指令介绍 :

-- 伪指令作用 : 标明数据对齐;

对齐代码示例 :

-- 含有对齐的代码 :

.data	@定义数据变量
hello:	@标明变量地址, 字符串变量
.ascii "Hello World !"
bh:	@标明变量地址, byte 变量
.byte 0x1
ADD:	@标明变量地址, word 变量
.word 0xff

.text
.global _start
_start:

@定义一个宏变量
.equ DA, 0x68

@将 DA 值赋值给 r0 寄存器
mov r0, #DA

-- 不含对齐的代码 :

.data	@定义数据变量
hello:	@标明变量地址, 字符串变量
.ascii "Hello World !"
.align 4
bh:	@标明变量地址, byte 变量
.byte 0x1
ADD:	@标明变量地址, word 变量
.word 0xff

.text
.global _start
_start:

@定义一个宏变量
.equ DA, 0x68

@将 DA 值赋值给 r0 寄存器
mov r0, #DA

代码 elf 内容对比 : 这里省略大部分, 只给出内存对应地址, 查看对齐内容;

-- 没有对齐的代码 : 0x50010011 明显不能被 4 整除;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- 对齐的代码 : 0x50010020 可以被4整除, 此时已经进行了对齐;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

3. 操作类伪指令

(1) ldr 伪指令

机器码 shifter_operand 段解析 :

-- 段解析 : 其中 4 位存放位移值, 8 位存放数值, 因此 立即数不能超过 8位, 最大 0xFF;

-- 缺陷 : 无法使用 大的数字;

-- 示例 :

.text
.global _start
_start:

mov r0, #0xFFF

-- 编译错误

octopus@octopus:~/arm/demo$ make
arm-linux-gcc -g -o start.o -c start.S
start.S: Assembler messages:
start.S:5: Error: invalid constant (fff) after fixup
make: *** [start.o] 错误 1

ldr 伪指令 :

-- 作用 : 可以 向寄存器中赋值 大立即数;

-- 语法格式 : "ldr r0, =0xFFF", 注意 不使用 # , 使用 = 后面加上立即数;

-- 代码示例 : 此时能编译成功, 0xfff 被赋值给 r0 寄存器;

.text
.global _start
_start:

ldr r0, =0xFFF

-- 反汇编 elf 代码

octopus@octopus:~/arm/demo$ arm-linux-objdump -S -D start.elf 

start.elf:     file format elf32-littlearm

Disassembly of section .text:

50008000 <_start>:
.text
.global _start
_start:

ldr r0, =0xFFF
50008000:	e51f0004 	ldr	r0, [pc, #-4]	; 50008004 <_start+0x4>
50008004:	00000fff 	.word	0x00000fff
Disassembly of section .debug_aranges:

... ...

-- 分析反汇编代码 : "50008000: e51f0004 ldr r0, [pc, #-4] ; 50008004 <_start+0x4>" 代码表明 ldr r0, =0xFFF 是使用 ldr 读取内存指令, 从 pc - 4 地址上读取该地址存储的值, "50008004: 00000fff .word 0x00000fff"  表明 系统将 0xFFF 定义在了 pc -4 内存地址中;

(2) nop 伪指令

nop 伪指令 :

-- 作用 : 进行延时, 在一些对时序要求较高的程序中, 使用该指令进行一个时钟的延时;

-- 代码示例 :

.text
.global _start
_start:

nop

-- 反汇编 : nop 伪指令执行了 "mov r0, r0" 这个无意义的操作;

octopus@octopus:~/arm/demo$ arm-linux-objdump -S -D start.elf 

start.elf:     file format elf32-littlearm

Disassembly of section .text:

50008000 <_start>:
.text
.global _start
_start:

nop
50008000:	e1a00000 	nop			(mov r0,r0)
Disassembly of section .debug_aranges:

... ...

三. 协处理器访问指令

1. 协处理器简介

协处理器简介 :

-- 作用 : 执行特定处理任务, 减轻处理器负担;

-- 数学协处理器 : 主要进行数字处理;

-- 协处理器支持 : ARM 芯片最多支持 16 个协处理器, 最重要的协处理器 是 CP15;

CP15 协处理器作用 : CP15 是系统控制寄存器, 通过这些寄存器, 配置与控制 缓存, MMU, 保护系统, 时钟模式 和 其它系统参数;

-- 如何访问 CP15 : 通过访问 CP15 中的寄存器控制上面的参数, CP15 提供了 16 组寄存器;

-- 文档 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

2. 协处理器访问指令

mcr 指令解析 : 详情见 ARM11 文档, P145, 3.2;

-- 作用 : 将本地寄存器中的数据 赋值给 CP15 的寄存器;

-- 语法格式 : "MCR{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>";

-- 语法解析 : CRn 表示 CP15 寄存器属于哪一组, CRm 也是组名;

-- 代码示例 :

.text
.global _start
_start:

@"MCR{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>"
@读取 MainID 寄存器
mcr p15, 0, r0, c0, c0, 0

-- 文档截图 :

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

-- CP15 寄存器访问 : 如果读取 MainID 寄存器, 就取前面的哪些 CRn Op1 CRm Op2 等参数;

【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

作者 : 韩曙亮

博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137

转载请著名出处


本博客相关文档下载 

-- ARM 汇编手册 : http://download.csdn.net/detail/han1202012/8328375

-- ARM 手册 : http://download.csdn.net/detail/han1202012/8324641

-- ARM 9 芯片文档 : http://download.csdn.net/detail/han1202012/8332389

-- ARM 11 芯片文档 : http://download.csdn.net/detail/han1202012/8332403

上一篇:ARM汇编指令特点


下一篇:vc++远程调试工具