Linux下ARM彙編語法(轉載學習)

第一部分 Linux下ARM彙編語法
儘管在Linux下使用C或C++編寫程序很方便,但彙編源程序用於系統最基本的初始化,如初始化堆棧指針、設置頁表、操作ARM的協處理器等。初始化完成後就可以跳轉到C代碼執行。需要注意的是,GNU的彙編器遵循AT&T的彙編語法,可以從GNU的站點(www.gnu.org)上下載有關規範。

一. Linux彙編行結構
任何彙編行都是如下結構:
[:] [} @ comment
[:] [} @ 註釋
Linux ARM 彙編中,任何以冒號結尾的標識符都被認爲是一個標號,而不一定非要在一行的開始。
【例1】定義一個"add"的函數,返回兩個參數的和。
.section .text, “x”
.global add @ give the symbol add external linkage
add:
ADD r0, r0, r1  @ add input arguments
MOV pc, lr @ return from subroutine
@ end of program

二. Linux 彙編程序中的標號
標號只能由a~z,A~Z,0~9,“.”,_等字符組成。當標號爲0~9的數字時爲局部標號,局部標號可以重複出現,使用方法如下:
 標號f: 在引用的地方向前的標號
 標號b: 在引用的地方向後的標號
【例2】使用局部符號的例子,一段循環程序
1:
    subs r0,r0,#1        @每次循環使r0=r0-1
    bne 1f         @跳轉到1標號去執行
局部標號代表它所在的地址,因此也可以當作變量或者函數來使用。

三. Linux彙編程序中的分段
(1).section僞操作
用戶可以通過.section僞操作來自定義一個段,格式如下:
 .section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一個段以段名爲開始, 以下一個段名或者文件結尾爲結束。這些段都有缺省的標誌(flags),連接器可以識別這些標誌。(與armasm中的AREA相同)。

下面是ELF格式允許的段標誌
<標誌> 含義
a 允許段
w 可寫段
x 執行段

【例3】定義段
 .section .mysection @自定義數據段,段名爲 “.mysection”
 .align  2
 strtemp:
 .ascii  "Temp string \n\0"


(2)彙編系統預定義的段名
.text  @代碼段
.data  @初始化數據段
.bss  @未初始化數據段
.sdata @
.sbss  @
需要注意的是,源程序中.bss段應該在.text之前。
四. 定義入口點
彙編程序的缺省入口是 start標號,用戶也可以在連接腳本文件中用ENTRY標誌指明其它入口點。
【例4】定義入口點
.section.data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:


五. Linux彙編程序中的宏定義
格式如下:
 .macro 宏名 參數名列表   @僞指令.macro定義一個宏
   宏體
 .endm  @.endm表示宏結束
如果宏使用參數,那麼在宏體中使用該參數時添加前綴“\”。宏定義時的參數還可以使用默認值。
可以使用.exitm僞指令來退出宏。
【例5】宏定義
.macro SHIFTLEFT a, b
.if \b < 0
MOV \a, \a, ASR #-\b
.exitm
.endif
MOV \a, \a, LSL #\b
.endm

六. Linux彙編程序中的常數
(1)十進制數以非0數字開頭,如:123和9876;
(2)二進制數以0b開頭,其中字母也可以爲大寫;
(3)八進制數以0開始,如:0456,0123;
(4)十六進制數以0x開頭,如:0xabcd,0X123f;
(5)字符串常量需要用引號括起來,中間也可以使用轉義字符,如: “You are welcome!\n”;
(6)當前地址以“.”表示,在彙編程序中可以使用這個符號代表當前指令的地址;
(7)表達式:在彙編程序中的表達式可以使用常數或者數值, “-”表示取負數, “~”表示取補,“<>”表示不相等,其他的符號如:+、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| 跟C語言中的用法相似。

七. Linux下ARM彙編的常用僞操作
在前面已經提到過了一些爲操作,還有下面一些爲操作:
>> 數據定義僞操作: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重複定義僞操作.rept,賦值語句.equ/.set ;
>> 函數的定義 ;
>> 對齊方式僞操作 .align;
>> 源文件結束僞操作.end;
>> .include僞操作;
>> if僞操作;
>> .global/ .globl 僞操作 ;
>> .type僞操作 ;
>> 列表控制語句 ;
>> 區別於gas彙編的通用僞操作,下面是ARM特有的僞操作 :.reg ,.unreq ,.code ,.thumb ,.thumb_func ,.thumb_set, .ltorg ,.pool
1. 數據定義僞操作
(1) .byte:單字節定義,如:.byte 1,2,0b01,0x34,072,'s' ;
(2) .short:定義雙字節數據,如:.short 0x1234,60000 ;
(3) .long:定義4字節數據,如:.long 0x12345678,23876565
(4) .quad:定義8字節,如:.quad 0x1234567890abcd
(5) .float:定義浮點數,如:
  .float 0f-314159265358979323846264338327\
    95028841971.693993751E-40                 @ - pi
(6) .string/.asciz/.ascii:定義多個字符串,如:
   .string "abcd", "efgh", "hello!"
   .asciz "qwer", "sun", "world!"
   .ascii "welcome\0"
需要注意的是:.ascii僞操作定義的字符串需要自行添加結尾字符'\0'。
(7) .rept:重複定義僞操作, 格式如下:
               .rept 重複次數
               數據定義
               .endr  @結束重複定義
     例如:
                .rept 3
                .byte 0x23
                .endr
(8) .equ/.set: 賦值語句, 格式如下:
                .equ(.set) 變量名,表達式
     例如:
                .equ abc 3  @讓abc=3

2.函數的定義僞操作
(1)函數的定義,格式如下:
         函數名:
         函數體
         返回語句
一般的,函數如果需要在其他文件中調用, 需要用到.global僞操作將函數聲明爲全局函數。爲了不至於在其他程序在調用某個C函數時發生混亂,對寄存器的使用我們需要遵循APCS準則。函數編譯器將處理爲函數代碼爲一段.global的彙編碼。
(2)函數的編寫應當遵循如下規則:
>> a1-a4寄存器(參數、結果或暫存寄存器,r0到r3 的同義字)以及浮點寄存器f0-f3(如果存在浮點協處理器)在函數中是不必保存的;
>> 如果函數返回一個不大於一個字大小的值,則在函數結束時應該把這個值送到 r0 中;
>> 如果函數返回一個浮點數,則在函數結束時把它放入浮點寄存器f0中;
>> 如果函數的過程改動了sp(堆棧指針,r13)、fp(框架指針,r11)、sl(堆棧限制,r10)、lr(連接寄存器,r14)、v1-v8(變量寄存器,r4 到 r11)和 f4-f7,那麼函數結束時這些寄存器應當被恢復爲包含在進入函數時它所持有的值。

3. .align .end .include .incbin僞操作
(1).align:用來指定數據的對齊方式,格式如下:
                .align [absexpr1, absexpr2]
     以某種對齊方式,在未使用的存儲區域填充值. 第一個值表示對齊方式,4, 8,16或     32. 第二個表達式值表示填充的值。
(2).end:表明源文件的結束。
(3).include:可以將指定的文件在使用.include 的地方展開,一般是頭文件,例如:
                .include “myarmasm.h”
(4).incbin僞操作可以將原封不動的一個二進制文件編譯到當前文件中,使用方法如下:
            .incbin "file"[,skip[,count]]
     skip表明是從文件開始跳過skip個字節開始讀取文件,count是讀取的字數.

4. .if僞操作
根據一個表達式的值來決定是否要編譯下面的代碼, 用.endif僞操作來表示條件判斷的結束, 中間可以使用.else來決定.if的條件不滿足的情況下應該編譯哪一部分代碼。
.if有多個變種:
 .ifdef symbol           @判斷symbol是否定義
 .ifc string1,string2   @字符串string1和string2是否相等,字符串可以用單引號括起來
 .ifeq expression       @判斷expression的值是否爲0
.ifeqs string1,string2  @判斷string1和string2是否相等,字符 串必須用雙引號括起來
.ifge expression         @判斷expression的值是否大於等於0
.ifgt absolute expression  @判斷expression的值是否大於0
.ifle expression         @判斷expression的值是否小於等於0
.iflt absolute expression  @判斷expression的值是否小於0
.ifnc string1,string2     @判斷string1和string2是否不相等, 其用法跟.ifc恰好相反。
.ifndef symbol, .ifnotdef symbol  @判斷是否沒有定義symbol,  跟.ifdef恰好相反
.ifne expression          @如果expression的值不是0, 那麼編譯器將編譯下面的代碼
.ifnes string1,string2    @如果字符串string1和string2不相 等, 那麼編譯器將編譯下面的代碼.

5. .global  .type   .title   .list
(1).global/ .globl :用來定義一個全局的符號,格式如下:
       .global symbol  或者  .globl symbol
(2).type:用來指定一個符號的類型是函數類型或者是對象類型, 對象類型一般是數據, 格式如下:
            .type 符號, 類型描述
【例6】
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 10
【例7】
.section .text
.type asmfunc, @function
.globl asmfunc
asmfunc:

mov pc, lr

(3)列表控制語句:
.title:用來指定彙編列表的標題,例如:
            .title “my program”
.list:用來輸出列表文件.

6. ARM特有的僞操作
(1) .reg: 用來給寄存器賦予別名,格式如下:
                   別名 .req 寄存器名
(2) .unreq: 用來取消一個寄存器的別名,格式如下:
       .unreq 寄存器別名
  注意被取消的別名必須事先定義過,否則編譯器就會報錯,這個僞操作也可以用來取消系統預製的別名, 例如r0, 但如果沒有必要的話不推薦那樣做。
(3) .code僞操作用來選擇ARM或者Thumb指令集,格式如下:
           .code 表達式
  如果表達式的值爲16則表明下面的指令爲Thumb指令,如果表達式的值爲32則表明下面的指令爲ARM指令.
(4) .thumb僞操作等同於.code 16, 表明使用Thumb指令, 類似的.arm等同於.code 32
(5) .force_thumb僞操作用來強制目標處理器選擇thumb的指令集而不管處理器是否支持
(6) .thumb_func僞操作用來指明一個函數是thumb指令集的函數
(7) .thumb_set僞操作的作用類似於.set, 可以用來給一個標誌起一個別名, 比.set功能增加的一點是可以把一個標誌標記爲thumb函數的入口, 這點功能等同於.thumb_func
(8) .ltorg用於聲明一個數據緩衝池(literal pool)的開始,它可以分配很大的空間。
(9) .pool的作用等同.ltorg
(9).space {,}
分配number_of_bytes字節的數據空間,並填充其值爲fill_byte,若未指定該值,缺省填充0。(與armasm中的SPACE功能相同)
(10).word {,} …
插入一個32-bit的數據隊列。(與armasm中的DCD功能相同)
可以使用.word把標識符作爲常量使用
 例如:
  Start:
  valueOfStart:
   .word Start
 這樣程序的開頭Start便被存入了內存變量valueOfStart中。
(11).hword {,} …
插入一個16-bit的數據隊列。(與armasm中的DCW相同)

八. GNU ARM彙編特殊字符和語法
代碼行中的註釋符號: ‘@’
整行註釋符號: ‘#’
語句分離符號: ‘;’
直接操作數前綴: ‘#’ 或 ‘$’

第二部分 GNU的編譯器和調試工具

一. 編譯工具
1.編輯工具介紹
GNU提供的編譯工具包括彙編器as、C編譯器gcc、C++編譯器g++、連接器ld和二進制轉換工具objcopy。基於ARM平臺的工具分別爲arm-linux-as、arm-linux-gcc、arm-linux-g++、arm- linux-ld和arm-linux- objcopy。GNU的編譯器功能非常強大,共有上百個操作選項,這也是這類工具讓初學者頭痛的原因。不過,實際開發中只需要用到有限的幾個,大部分可以採用缺省選項。GNU工具的開發流程如下:編寫C、C++語言或彙編源程序,用gcc或g++生成目標文件,編寫連接腳本文件,用連接器生成最終目標文件(elf格式),用二進制轉換工具生成可下載的二進制代碼。
(1)編寫C、C++語言或彙編源程序
通常彙編源程序用於系統最基本的初始化,如初始化堆棧指針、設置頁表、操作ARM的協處理器等。初始化完成後就可以跳轉到C代碼執行。需要注意的是,GNU的彙編器遵循AT&T的彙編語法,讀者可以從GNU的站點(www.gnu.org)上下載有關規範。彙編程序的缺省入口是 start標號,用戶也可以在連接腳本文件中用ENTRY標誌指明其它入口點(見下文關於連接腳本的說明)。

(2)用gcc或g++生成目標文件
如果應用程序包括多個文件,就需要進行分別編譯,最後用連接器連接起來。如筆者的引導程序包括3個文件:init.s(彙編代碼、初始化硬件)xmrecever.c(通信模塊,採用Xmode協議)和flash.c(Flash擦寫模塊)。
分別用如下命令生成目標文件: arm-linux-gcc-c-O2-oinit.oinit.s arm-linux-gcc-c-O2-oxmrecever.oxmrecever.c arm-linux-gcc-c-O2-oflash.oflash.c 其中-c命令表示只生成目標代碼,不進行連接;-o命令指明目標文件的名稱;-O2表示採用二級優化,採用優化後可使生成的代碼更短,運行速度更快。如果項目包含很多文件,則需要編寫makefile文件。關於makefile的內容,請感興趣的讀者參考相關資料。
(3)編寫連接腳本文件
gcc 等編譯器內置有缺省的連接腳本。如果採用缺省腳本,則生成的目標代碼需要操作系統才能加載運行。爲了能在嵌入式系統上直接運行,需要編寫自己的連接腳本文件。編寫連接腳本,首先要對目標文件的格式有一定了解。GNU編譯器生成的目標文件缺省爲elf格式。elf文件由若干段(section)組成,如不特殊指明,由C源程序生成的目標代碼中包含如下段:.text(正文段)包含程序的指令代碼;.data(數據段)包含固定的數據,如常量、字符串;.bss(未初始化數據段)包含未初始化的變量、數組等。C++源程序生成的目標代碼中還包括.fini(析構函數代碼)和. init(構造函數代碼)等。連接器的任務就是將多個目標文件的.text、.data和.bss等段連接在一起,而連接腳本文件是告訴連接器從什麼地址開始放置這些段。例如連接文件link.lds爲:
ENTRY(begin)
SECTION
{
.=0x30000000;
.text:{*(.text)}
.data:{*(.data)}
.bss:{*(.bss)}
}
其中,ENTRY(begin)指明程序的入口點爲begin標號;.=0x00300000指明目標代碼的起始地址爲0x30000000,這一段地址爲 MX1的片內RAM;.text:{*(.text)}表示從0x30000000開始放置所有目標文件的代碼段,隨後的.data:{* (.data)}表示數據段從代碼段的末尾開始,再後是.bss段。
(4)用連接器生成最終目標文件
有了連接腳本文件,如下命令可生成最終的目標文件:
arm-linux-ld –no stadlib –o bootstrap.elf -Tlink.lds init.o xmrecever.o flash.o
其中,ostadlib表示不連接系統的運行庫,而是直接從begin入口;-o指明目標文件的名稱;-T指明採用的連接腳本文件(也可以使用-Ttext address,address表示執行區地址);最後是需要連接的目標文件列表。
(5)生成二進制代碼
連接生成的elf文件還不能直接下載執行,通過objcopy工具可生成最終的二進制文件:
arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin
其中-O binary指定生成爲二進制格式文件。Objcopy還可以生成S格式的文件,只需將參數換成-O srec。還可以使用-S選項,移除所有的符號信息及重定位信息。如果想將生成的目標代碼反彙編,還可以用objdump工具:
 arm-linux-objdump -D bootstrap.elf
至此,所生成的目標文件就可以直接寫入Flash中運行了。

2.Makefile實例
example: head.s  main.c
 arm-linux-gcc -c -o head.o head.s
 arm-linux-gcc -c -o main.o main.c
 arm-linux-ld -Tlink.lds head.o ain.o -o example.elf
 arm-linux-objcopy -O binary -S example_tmp.o example
 arm-linux-objdump -D -b binary -m arm  example >ttt.s

二. 調試工具
Linux下的GNU調試工具主要是gdb、gdbserver和kgdb。其中gdb和gdbserver可完成對目標板上Linux下應用程序的遠程調試。gdbserver是一個很小的應用程序,運行於目標板上,可監控被調試進程的運行,並通過串口與上位機上的gdb通信。開發者可以通過上位機的gdb輸入命令,控制目標板上進程的運行,查看內存和寄存器的內容。gdb5.1.1以後的版本加入了對ARM處理器的支持,在初始化時加入- target==arm參數可直接生成基於ARM平臺的gdbserver。gdb工具可以從ftp: //ftp.gnu.org/pub/gnu/gdb/上下載。
對於Linux內核的調試,可以採用kgdb工具,同樣需要通過串口與上位機上的gdb通信,對目標板的Linux內核進行調試。可以從http://oss.sgi.com/projects/kgdb/上了解具體的使用方法。


參考資料:
1. Richard Blum,Professional Assembly Language
2. GNU ARM 彙編快速入門,http://blog.chinaunix.net/u/31996/showart.php?id=326146
3. ARM GNU 彙編僞指令簡介,http://www.cppblog.com/jb8164/archive/2008/01/22/41661.aspx
4. GNU彙編使用經驗,http://blog.chinaunix.net/u1/37614/showart_390095.html
5. GNU的編譯器和開發工具,http://blog.ccidnet.com/blog-htm-do-showone-uid-34335-itemid-81387-type-blog.html
6. 用GNU工具開發基於ARM的嵌入式系統,http://blog.163.com/liren0@126/blog/static/32897598200821211144696/
7. objcopy命令介紹,http://blog.csdn.net/junhua198310/archive/2007/06/27/1669545.aspx


=================================================================================

(補充)轉:http://hi.baidu.com/760159/blog/item/122980def7c9e11948540361.html/cmtid/959835c57b6a74a48226aca4

ARM的ADS彙編器與GCC彙編器

    
一:ads下的一段彙編程序:
__main
EXPORT BootReset
BootReset
B resetvec_reqset
IMPORT BootEntry
IMPORT |Image$$RO$$Limit|

AREA BOOTROM, CODE, READONLY
LDR r0, =|Image$$RO$$Limit|
BEQ %1
ldr pc, [pc,#-&F20]
轉換到gcc下的彙編程序爲:

__main
.global BootReset
BootReset:
B resetvec_reqset

.extern BootEntry

.extern Image_RO_Limit

# AREA BOOTROM, CODE, READONLY
LDR r0, =Image_RO_Limit

BEQ FUNC1
ldr pc, [pc,#-0xF20]

二:將ARM SDT下的彙編碼移植到GCC for ARM編譯器時,經常要做如下修改:
1、註釋行以“@”或""代替“;”
2、僞操作符替換:
INCLUDE 替換成 .INCLUDE
TCLK2 EQU PB25 替換成 .equ TCLK2, PB25
EXPORT 替換成 .global
IMPORT 替換成 .extern
DCD 替換成 .long
IF :DEF: 替換成 .IFDEF
ELSE 替換成 .ELSE
ENDIF 替換成 .ENDIF
:OR: 替換成 |
:SHL: 替換成 <<
END 替換成 .end


符號定義後加":"號
AREA Word, CODE, READONLY --> .text
AREA Block, DATA, READWRITE --> .data
CODE32 --> .arm
CODE16 --> .thumb
LTORG --> .ltorg


3、操作數及運算符號替換
ldr pc, [pc, #&18] 替換成 ldr pc, [pc, #+0x18]
“&”以“+0x”號替換

========================================================================


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章