【嵌入式開發】 ARM 彙編 (指令分類 | 僞指令 | 協處理器訪問指令)

做者 : 韓曙亮html

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

轉載請著名出處編程


本博客相關文檔下載 緩存

-- ARM 彙編手冊http://download.csdn.net/detail/han1202012/8328375sass

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

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

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





一. 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;


-- 啓動 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;


-- 選擇導入的代碼位置


-- clean 代碼 : 選擇 "Project" --> "Clean";


-- build 工程 : 選擇 "菜單" --> Project --> Build All 選項便可;

-- 配置 Debug 調試參數




-- 執行調試 : F6 單步調試走兩步, 能夠再 Register 視圖中查看寄存器的值, 能夠看到 r1 和 r2 被賦值爲 1 和 2 了;





二. ARM 指令分類

 

ARM 彙編手冊

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

轉載請著名出處



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" 就是機器碼, 以下圖標註部分;


-- 反彙編部分結果

[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位;

-- 機器碼分段




(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 字段, 該字段沒用;

轉載請著名出處


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






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;


-- 數據變量


-- 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 整除;


-- 對齊的代碼 : 0x50010020 能夠被4整除, 此時已經進行了對齊;






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 組寄存器;

-- 文檔 : 


 



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

-- 文檔截圖


-- CP15 寄存器訪問 : 若是讀取 MainID 寄存器, 就取前面的哪些 CRn Op1 CRm Op2 等參數;





做者 : 韓曙亮

博客地址 : 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

相關文章
相關標籤/搜索