1. 程式人生 > >GNU ARM 彙編指令(注意是GNU,區別與armasm)

GNU ARM 彙編指令(注意是GNU,區別與armasm)

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格式允許的段標誌(flags)
<標誌> 含義
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      @ give the symbol add external linkage

_start:
<instruction code goes here>

五. 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)當前地址以“.”表示,在彙編程式中可以使用這個符號代表當前指令的地址;

          例如:  ".long ."    //這裡"."是連結地址,如果開啟MMU,就是虛擬地址
(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,[email protected]字串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(.code 16)則表明下面的指令為Thumb指令,如果表示式的值為32(.code 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
(10).space <number_of_bytes>{,<fill_byte>}
分配number_of_bytes位元組的資料空間,並填充其值為fill_byte,若未指定該值,預設填充0。(與armasm中的SPACE功能相同)
(11).word <word1>{,<word2>} …
插入一個32-bit的資料佇列。(與armasm中的DCD功能相同)
可以使用.word把識別符號作為常量使用
 例如:
 Start:
 valueOfStart:
 .word Start
 這樣程式的開頭Start便被存入了記憶體變數valueOfStart中。
(11) .hword <short1>{,<short2>} …
插入一個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.sarm-linux-gcc-c-O2-oxmrecever.oxmrecever.carm-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.oxmrecever.o flash.o
其中,ostadlib表示不連線系統的執行庫,而是直接從begin入口;-o指明目標檔案的名稱;-T指明採用的連線指令碼檔案(也可以使用-Ttextaddress,address表示執行區地址);最後是需要連線的目標檔案列表。
(5)生成二進位制程式碼
連線生成的elf檔案還不能直接下載執行,通過objcopy工具可生成最終的二進位制檔案:
arm-linux-objcopy –O binary bootstrap.elf bootstrap.bin
其中-O binary指定生成為二進位制格式檔案。Objcopy還可以生成S格式的檔案,只需將引數換成-Osrec。還可以使用-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 -oexample.elf
 arm-linux-objcopy -O binary -S example_tmp.oexample
 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/[email protected]/blog/static/32897598200821211144696/
7.objcopy命令介紹,http://blog.csdn.net/junhua198310/archive/2007/06/27/1669545.aspx

相關推薦

GNU ARM 彙編指令注意GNU,區別armasm

subs r0,r0,#1 @每次迴圈使r0=r0-1   bne 1f @跳轉到1標號去執行 區域性標號代表它所在的地址,因此也可以當作變數或者函式來使用。 三. Linux彙編程式中的分段 (1) .section偽操作 使用者可以通過.section偽操作來自定義一個段,格式如下:.section se

linux指令一、目錄檔案

一、文件與目錄 1.Linux檔案屬性 第一個字元代表這個檔案是『目錄、檔案或連結檔等等』: 當為[ d ]則是目錄; 當為[ - ]則是檔案; 若是[ l ]則表示為連結檔(

GNU ARM彙編--(十二)arm彙編指令的B真的那麼簡單嗎?

             說句題外話,在輸入“指令”二字的時候,就想起了google搜尋時,提示“令”不能搜尋,要我換詞彙.如果不能說髒話,我真就無語了.         在前面對具體晶片的各個基本模組做完了學習後,在上一篇小結中自以為已經具備了自己寫個bootloader

GNU風格 ARM 彙編指令語法總結

彙編源程式一般用於系統最基本的初始化:初始化堆疊指標、設定頁表、操作 ARM的協處理器等。這些初始化工作完成後就可以跳轉到C程式碼main函式中執行。 1、 GNU組合語言語句格式

ARM彙編指令集_學習筆記1

一、什麼是ARM彙編? 執行在ARM處理器上的組合語言就叫ARM彙編。 C程式執行在X86平臺,底層就是X86彙編;執行在ARM平臺,底層就是ARM彙編。ARM彙編與X86彙編有顯著區別。 X86屬於CISC(複雜指令集);ARM屬於RISC(精簡指令集)。 二、CISC存在的問題:

ARM-彙編指令總結

https://www.cnblogs.com/wxb20/p/6249580.html ARM彙編指令集 指令、偽指令 (彙編)指令: 是機器碼的助記符,經過彙編器編譯後,由CPU執行。 (彙編)偽指令:用來指導指令執行,是彙編器的產物,最終不會生成機器碼。 有兩種不同風格

GNU ARM彙編--(十七)u-boot的makefile和mkconfig解讀

        自己寫的bootloader可以引導kernel了,我以為曾經神祕的u-boot程式碼將變得毫無挑戰,然事實表明u-boot作為優秀的開原始碼,閱讀起來還是很有挑戰的,值得一讀!         閱讀碰到的頭等問題:Makefile和shell指令碼看不懂.

1.19.ARM彙編指令集7之儲存器訪問指令LDR和STR

ARM彙編指令集的儲存器訪問指令: LDR & STR & LDM & STM & SWP 這裡先總的介紹一下儲存器訪問指令,然後再詳解說前兩個(LDR&STR

GNU-ARM程式開發----GNU開發環境和工具

什麼是GNUGNU計劃,又稱革奴計劃。是由Richard Stallman在1983年9月27日公開發起的。目標是建立一套完全免費、自由的作業系統,基本原則是原始碼共享及思想共享。 所有在GNU計劃下開發的軟體均為GNU軟體。 為保證GNU軟體可以自由地“使用、複製、修改和

GNU ARM彙編--(一)開篇

        在大學的時候,彙編就是學的很爛.一是對彙編這門語言沒概念,二是那些指令集很難記清楚,用的機會也少,自然學的不好.但是現在覺得相當有必要重頭學習一下彙編.部分原因我在上一篇寫完裝置模型的總結時提到了而.最近在看一本書《ARM:Assembly Language

arm彙編指令總結不斷更新

/** ****************************************************************************** * @author    Maoxiao Hu * @version   V1.0.1 * @date       Jan-2015 *****

GNU ARM 彙編基礎

# ARM GNU彙編基礎 ## 0 前言 > 全文補充提醒: > > 筆者在閱讀ARM官方文件及查閱實際的u-boot原始碼中的彙編程式碼後,發現了一些不同於ARM官方文件中的彙編語法,查閱相關資料後,才發現主要由於彙編器的不同,有兩種不同的彙編語法: > > * ARM

常用的ARM彙編指令

一. 帶點的(一般都是ARM GNU偽彙編指令)   1. ".text"、".data"、".bss" 依次表示的是 “以下是程式碼段”, “以下是初始化資料段”, “以下是未初始化資料段”。 2.".global" 定義一個全

ARM彙編指令程式設計之選擇排序

題目:將整數陣列{0XAABBCC11,0XAABBCC00,0XAABBCC33,0XAABBCC22,0XAABBCC44}進行選擇排序。 實驗環境:ARM Developer Suite V1.2 程式碼如下: AREA test,CODE

常用的ARM彙編指令彙編呼叫C語言

***指令與偽指令: 指令:CPU機器指令的助記符,經過編譯後得到一串10組成的機器碼,可以被CPU直接讀取執行。 偽指令:編譯器環境提供,用來指導編譯過程,最終不會生成機器碼。 ***LDR/STR架構: CPU不能直接對記憶體的內容進行操作,必須藉助CPU

1.15.ARM彙編指令3之邏輯指令

ARM彙編指令之邏輯指令:and & orr & eor & bic * and 邏輯與操作指令,將operand2的值與暫存器Rn的值按位邏輯與操作,結果儲存到Rd中。 指令

1.16.ARM彙編指令4之比較指令

ARM比較指令:CMP & CMN & TST & TEQ * CMP 比較指令,指令使用Rn的值減去operand2的值,根據操作的結果更新CPSR暫存器相應的條件標誌位,

1.21.ARM彙編指令集9之協處理器指令

ARM協處理器指令:CDP & LDC & STC & MCR & MRC ARM支援協處理器操作,協處理器的控制要通過協處理器命令來實現。 * CDP 協處理器資

1.17.ARM彙編指令集5之乘法指令

ARM彙編指令集之乘法指令:MUL & MLA & UMULL & UMLAL & SMULL & SMLAL * MUL 32位乘法指令,指令將Rm和Rs中

專題3-ARM彙編指令詳解

1、彙編概述 1)為什麼要學習使用匯程式設計序? bootloader在硬體初始化(啟動程式碼)時用的組合語言,因為效率更高。linux核心中某些對執行效率要特殊要求的地方也會使用匯編語言。 2)ARM彙編分類 目前常用的ARM彙編指令有兩種: (1)