新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM標(biāo)準(zhǔn)匯編與GNU匯編

ARM標(biāo)準(zhǔn)匯編與GNU匯編

作者: 時(shí)間:2016-11-21 來源:網(wǎng)絡(luò) 收藏
前段時(shí)間看arm的匯編,發(fā)現(xiàn)很多有一個(gè)小點(diǎn),但是借來的書上的語(yǔ)法卻沒有,問同學(xué)也不知道,于是在網(wǎng)上查了一番才發(fā)現(xiàn)我書上看到的是arm的標(biāo)準(zhǔn)匯編,而有小點(diǎn)的gnu的匯編,于是將收集到的資料整理后放到這里來。


GNU匯編語(yǔ)言結(jié)構(gòu)
主要包括三個(gè)常用的段:
data 數(shù)據(jù)段 聲明帶有初始值的元素
bss 數(shù)據(jù)段 聲明使用0或者null初始化的元素
text 正文段 包含的指令, 每個(gè)匯編程序都必須包含此段

使用.section 指令定義段, 如:
.section .data
.section .bss
.section .text

起始點(diǎn):
gnu匯編器使用_start標(biāo)簽表示默認(rèn)的起始點(diǎn), 此外如果想要匯編內(nèi)部的標(biāo)簽?zāi)軌虮煌獠砍绦蛟L問,
需要使用.globl 指令, 如:.globl _start


使用通用庫(kù)函數(shù)時(shí)可以使用:
ld -dynamic-linker /lib/ld-linux.so.2

本文引用地址:http://butianyuan.cn/article/201611/319125.htm

################################################################################################
# 四, 數(shù)據(jù)傳遞
################################################################################################
1, 數(shù)據(jù)段
使用.data聲明數(shù)據(jù)段, 這個(gè)段中聲明的任何數(shù)據(jù)元素都保留在內(nèi)存中并可以被匯編程序的指令讀取,
此外還可以使用.rodata聲明只讀的數(shù)據(jù)段, 在聲明一個(gè)數(shù)據(jù)元素時(shí), 需要使用標(biāo)簽和命令:

標(biāo)簽:用做引用數(shù)據(jù)元素所使用的標(biāo)記, 它和c語(yǔ)言的變量很相似, 它對(duì)于處理器是沒有意義的, 它只是用做匯編器試圖訪問內(nèi)存位置時(shí)用做引用指針的一個(gè)位置。

指令:指示匯編器為通過標(biāo)簽引用的數(shù)據(jù)元素保留特定數(shù)量的內(nèi)存, 聲明命令之后必須給出一個(gè)或多個(gè)默認(rèn)值。

聲明指令:
.ascii 文本字符串
.asciz 以空字符結(jié)尾的字符串
.byte 字節(jié)值
.double 雙精度浮點(diǎn)值
.float 單精度浮點(diǎn)值
.int 32位整數(shù)
.long 32位整數(shù), 和int相同
.octa 16字節(jié)整數(shù)
.quad 8字節(jié)整數(shù)
.short 16位整數(shù)
.single 單精度浮點(diǎn)數(shù)(和float相同)


例子:
output:
.ascii "hello world."

pi:
.float 2.14

聲明可以在一行中定義多個(gè)值, 如:
ages:
.int 20, 10, 30, 40


定義靜態(tài)符號(hào):
使用.equ命令把常量值定義為可以在文本段中使用的符號(hào),如:
.section .data
.equ LINUX_SYS_CALL, 0x80
.section .text
movl $LINUX_SYS_CALL, ?x



2, bss段
和data段不同, 無需聲明特定的數(shù)據(jù)類型, 只需聲明為所需目的保留的原始內(nèi)存部分即可。
GNU匯編器使用以下兩個(gè)命令聲明內(nèi)存區(qū)域:
.comm 聲明為未初始化的通用內(nèi)存區(qū)域
.lcomm 聲明為未初始化的本地內(nèi)存區(qū)域

兩種聲明很相似,但.lcomm是為不會(huì)從本地匯編代碼之外進(jìn)行訪問的數(shù)據(jù)保留的, 格式為:
.comm/.lcomm symbol, length

例子:
.section .bss
.lcomm buffer, 1000
該語(yǔ)句把1000字節(jié)的內(nèi)存地址賦予標(biāo)簽buffer, 在聲明本地通用內(nèi)存區(qū)域的程序之外的函數(shù)是不能訪問他們的.(不能在.globl命令中使用他們)

在bss段聲明的好處是, 數(shù)據(jù)不包含在可執(zhí)行文件中。在數(shù)據(jù)段中定義數(shù)據(jù)時(shí), 它必須被包含在可執(zhí)行程序中, 因?yàn)楸仨毷褂锰囟ㄖ党跏蓟R驗(yàn)椴皇褂脭?shù)據(jù)初始化bss段中聲明的數(shù)據(jù)區(qū)域,所以內(nèi)存區(qū)域被保留在運(yùn)行時(shí)使用, 并且不必包含在最終的程序中。

3, 傳送數(shù)據(jù)
move 指令:
格式 movex 源操作數(shù), 目的操作數(shù)。 其中x為要傳送數(shù)據(jù)的長(zhǎng)度, 取值有:
l 用于32位的長(zhǎng)字節(jié)
w 用于16位的字
b 用于8位的字節(jié)值

立即數(shù)前面要加一個(gè)$符號(hào), 寄存器前面要加%符號(hào)。

8個(gè)通用的寄存器是用于保存數(shù)據(jù)的最常用的寄存器, 這些寄存器的內(nèi)容可以傳遞
給其他的任何可用的寄存器。 和通用寄存器不同, 專用寄存器(控制, 調(diào)試, 段)
的內(nèi)容只能傳送給通用寄存器, 或者接收從通用寄存器傳過來的內(nèi)容。


在對(duì)標(biāo)簽進(jìn)行引用時(shí):
例:
.section .data
value:
.int 100
_start:
movl value, ?x
movl $value, ?x
movl ?x, (?i)
movl ?x, 4(?i)

其中:movl value, ?x 只是把標(biāo)簽value當(dāng)前引用的內(nèi)存值傳遞給eax
movl $value, ?x 把標(biāo)簽value當(dāng)前引用的內(nèi)存地址指針傳遞給eax
movl ?x, (?i) 如果edi外面沒有括號(hào)那么這個(gè)指令只是把ebx中的
值加載到edi中, 如果有了括號(hào)就表示把ebx中的內(nèi)容
傳送給edi中包含的內(nèi)存位置。
movl ?x, 4(?i) 表示把edi中的值放在edi指向的位置之后的4字節(jié)內(nèi)存位置中
movl ?x, -4(?i) 表示把edi中的值放在edi指向的位置之前的4字節(jié)內(nèi)存位置中



cmove 指令(條件轉(zhuǎn)移):
cmovex 源操作數(shù), 目的操作數(shù). x的取值為:
無符號(hào)數(shù):
a/nbe 大于/不小于或者等于
ae/nb 大于或者等于/不小于
nc 無進(jìn)位
b/nae 小于/不大于等于
c 進(jìn)位
be/na 小于或等于/不大于
e/z 等于/零
ne/nz 不等于/不為零
p/pe 奇偶校驗(yàn)/偶校驗(yàn)
np/po 非奇偶校驗(yàn)/奇校驗(yàn)

有符號(hào)數(shù):
ge/nl 大于或者等于/不小于
l/nge 小于/不大于或者等于
le/ng 小于或者等于/不大于
o 溢出
no 未溢出
s 帶符號(hào)(負(fù))
ns 無符號(hào)(非負(fù))

交換數(shù)據(jù):
xchg 在兩個(gè)寄存器之間或者寄存器和內(nèi)存間交換值
如:
xchg 操作數(shù), 操作數(shù), 要求兩個(gè)操作數(shù)必須長(zhǎng)度相同且不能同時(shí)都是內(nèi)存位置
其中寄存器可以是32,16,8位的

bswap 反轉(zhuǎn)一個(gè)32位寄存器的字節(jié)順序

如: bswap ?x

xadd 交換兩個(gè)值并把兩個(gè)值只和存儲(chǔ)在目標(biāo)操作數(shù)中

如: xadd 源操作數(shù),目標(biāo)操作數(shù)
其中源操作數(shù)必須是寄存器, 目標(biāo)操作數(shù)可以是內(nèi)存位置也可以是寄存器
其中寄存器可以是32,16,8位的

cmpxchg
cmpxchg source, destination
其中source必須是寄存器, destination可以是內(nèi)存或者寄存器, 用來比較
兩者的值, 如果相等,就把源操作數(shù)的值加載到目標(biāo)操作數(shù)中, 如果不等就把
目標(biāo)操作數(shù)加載到源操作數(shù)中,其中寄存器可以是32,16,8位的, 其中源操作
數(shù)是EAX,AX或者AL寄存器中的值


cmpxchg8b 同cmpxchg, 但是它處理8字節(jié)值, 同時(shí)它只有一個(gè)操作數(shù)
cmpxchg8b destination
其中destination引用一個(gè)內(nèi)存位置, 其中的8字節(jié)值會(huì)與EDX和EAX寄存器中
包含的值(EDX高位寄存器, EAX低位寄存器)進(jìn)行比較, 如果目標(biāo)值和EDX:EAX
對(duì)中的值相等, 就把EDX:EAX對(duì)中的64位值傳遞給內(nèi)存位置, 如果不匹配就把
內(nèi)存地址中的值加載到EDX:EAX對(duì)中



4, 堆棧
ESP 寄存器保存了當(dāng)前堆棧的起始位置, 當(dāng)一個(gè)數(shù)據(jù)壓入棧時(shí), 它就會(huì)自動(dòng)遞減,
反之其自動(dòng)遞增

壓入堆棧操作:
pushx source, x取值為:
l 32位長(zhǎng)字
w 16位字

彈出堆棧操作:
popx source
其中source必須是16或32位寄存器或者內(nèi)存位置, 當(dāng)pop最后一個(gè)元素時(shí)ESP值應(yīng)該
和以前的相等


5,壓入和彈出所有寄存器
pusha/popa 壓入或者彈出所有16位通用寄存器
pushad/popad 壓入或者彈出所有32位通用寄存器
pushf/popf 壓入或者彈出EFLAGS寄存器的低16位
pushfd/popfd 壓入或者彈出EFLAGS寄存器的全部32位

6,數(shù)據(jù)地址對(duì)齊
gas 匯編器支持.align 命令, 它用于在特定的內(nèi)存邊界對(duì)準(zhǔn)定義的數(shù)據(jù)元素, 在數(shù)據(jù)段中.align命令緊貼在數(shù)據(jù)定義的前面

比較:
cmp operend1, operend2

進(jìn)位標(biāo)志修改指令:
CLC 清空進(jìn)位標(biāo)志(設(shè)置為0)
CMC 對(duì)進(jìn)位標(biāo)志求反(把它改變?yōu)橄喾吹闹?
STC 設(shè)置進(jìn)位標(biāo)志(設(shè)置為1)


循環(huán):
loop 循環(huán)直到ECX寄存器為0
loope/loopz 循環(huán)直到ecx寄存器為0 或者沒有設(shè)置ZF標(biāo)志
loopne/loopnz 循環(huán)直到ecx為0或者設(shè)置了ZF標(biāo)志

指令格式為: loopxx address 注意循環(huán)指令只支持8位偏移地址

另有一個(gè)比較篇的如下:

ARM匯編和Gnu匯編的轉(zhuǎn)換

將ARM ADS下的匯編碼移植到GCC for ARM編譯器時(shí),有如下規(guī)則:
1, 注釋行以"@"或""代替";"

2, GET或INCLUDE => .INCLUDE
如:get option.a => .include "option.a"

3, EQU => .equ
TCLK2 EQU PB25 => .equ TCLK2, PB25
SETA ==> .equ
SETL ==> .equ
BUSWIDTH SETA 16 => .equ BUSWIDTH, 16

4, EXPORT => .global
IMPORT => .extern
GBLL => .global
GBLA => .global

5, DCD => .long

6, IF :DEF: => .IFDEF
ELSE => .ELSE
ENDIF => .ENDIF
:OR: => |
:SHL: => <<

7, END =>.end
NOTE:在被include的頭文件中,如"option.a"中,不再需要.end,否則會(huì)導(dǎo)致主匯編程序結(jié)束。

8, 符號(hào)定義加":"號(hào)
Entry => Entry:
AREA Word, CODE, READONLY ==> .text
AREA Block, DATA, READWRITE ==> .data
CODE32 ==> .arm
CODE16 ==> .thumb

9, MACRO ==> .macro
MEND ==> .endm

開始看start.s中的代碼,又一句.balignl 16,0xdeadbeef,不知什么意思,網(wǎng)上搜了一下了解到這條


命令的作用如下:



.balign[wl] abs-expr, abs-expr, abs-expr



增加位置計(jì)數(shù)器(在當(dāng)前子段)使它指向規(guī)定的存儲(chǔ)邊界。第一個(gè)表達(dá)式參數(shù)(結(jié)果必須是純粹的數(shù)字)是必需參數(shù):邊界基準(zhǔn),單位為字節(jié)。例如,‘.balign 8’向后移動(dòng)位置計(jì)數(shù)器直至計(jì)數(shù)器的值等于8的倍數(shù)。如果位置計(jì)數(shù)器已經(jīng)是8的倍數(shù),則無需移動(dòng)。第2個(gè)表達(dá)式參數(shù)(結(jié)果必須是純粹的數(shù)字)給出填充字節(jié)的值,用這個(gè)值填充位置計(jì)數(shù)器越過的地方。第2個(gè)參數(shù)(和逗點(diǎn))可以省略。如果省略它,填充字節(jié)的值通常是0。但在某些系統(tǒng)上,如果本段標(biāo)識(shí)為包含代碼,而填充值被省略,則使用no-op指令填充空白區(qū)。第3個(gè)參數(shù)的結(jié)果也必須是純粹的數(shù)字,這個(gè)參數(shù)是可選的。如果存在第3個(gè)參數(shù),它代表本對(duì)齊命令允許跳過字節(jié)數(shù)的最大值。如果完成這個(gè)對(duì)齊需要跳過的字節(jié)數(shù)比規(guī)定的最大值還多,則根本無法完成對(duì)齊。您可以在邊界基準(zhǔn)參數(shù)后簡(jiǎn)單地使用兩個(gè)逗號(hào),以省略填充值參數(shù)(第二參數(shù));如果您在想在適當(dāng)?shù)臅r(shí)候,對(duì)齊操作自動(dòng)使用no-op指令填充,本方法將非常奏效。.balignw和.balignl是.balign命令的變化形式。.balignw使用2個(gè)字節(jié)來填充空白區(qū)。.balignl使用4字節(jié)來填充。例如,.balignw 4,0x368d將地址對(duì)齊到4的倍數(shù),如果它跳過2個(gè)字節(jié),GAS將使用0x368d填充這2個(gè)字節(jié)(字節(jié)的確切存放位置視處理器的存儲(chǔ)方式而定)。

如果它跳過1或3個(gè)字節(jié),則填充值不明確。

GNU中的.word的另一種作用
標(biāo)簽: word 2011-04-13 18:13


不管是ARM的匯編還是GNU的匯編,都有DCD或者.word命令,它是用來開辟一個(gè)字空間。

如:標(biāo)識(shí)1 .word 標(biāo)識(shí)2 它表示將標(biāo)識(shí)2的數(shù)據(jù)存放在以標(biāo)識(shí)1的地址上去。這個(gè).word和DCD等指令,相當(dāng)于C語(yǔ)言的指針(如 char * p)。那么在匯編中用以上的代碼聲明的標(biāo)識(shí)1不需要在該文件中用extern的字段來表明是可以在外部引用的,它是內(nèi)存空間,可以在每個(gè)文件中使用這個(gè)標(biāo)識(shí)1.而ldr pc,內(nèi)存地址 它表示將內(nèi)存地址中的數(shù)據(jù)送入pc寄存器中去,而ldr pc,=內(nèi)存地址它表示將內(nèi)存地址放入pc寄存器中去。

這些是在分析代碼時(shí)候遇到的不明白的地方,經(jīng)過查找資料的出來的。



評(píng)論


技術(shù)專區(qū)

關(guān)閉