引用自:
ps:這段時間在研究linux源**,遇到了at&t彙編,故轉貼個不錯的at&t彙編貼。
就像軟體的真諦——「給我乙個標準,我用我的邏輯舞動世界」一樣,at&t彙編是組合語言裡的另一種標準,這是相對於鼎鼎大名的intel的x86彙編來說。即使對於電子專業的學生來說,一旦掌握了c51微控制器的彙編,x86的彙編也已經入門了,但x86畢竟有著強大的暫存器,在串操作指令和作業系統類指令方面,微控制器還是望塵莫及的。
下面的兩段話就輕易的涉及到了關於字首、運算元的方向、操作碼的字尾這些概念的不同。關於字首,at&t彙編中,暫存器前被冠以「%」,立即數前被冠以「$」,十六進製制數前被冠以「0x」。所以,如果gemfield在at&t語境中說到386的通用暫存器時,會這樣描述:8個32-bit暫存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp。比如:在stage1.s中,有這麼乙個定義,#define abs(x) (x-_start+0x7c00),那麼你就會知到0x7c00是個十六進製制數(_start函式的入口位址就位於記憶體的0x7c00處)。
而在設定int 0x13的0x42功能號時,它是這麼說的:movb $0x42,%ah 。這句告訴了我們一些不同之處:首先,操作碼的字尾l表示的是操作碼的大小,l是長整數32位,那麼相應的,movw是16位,movb是8位;其次,立即數是用$字首來表示的,就像$0x42;再次,暫存器的名字是有%字首的,像例子中的%ah;最後,運算元的方向有點不一樣,比如把立即數$0x42放到暫存器%ah中,用的是movb $0x42,%ah,也即源運算元在前,目的運算元在後。
對於記憶體單元運算元來說,在at&t中是把暫存器用()括起來,而非【】。比如,movl %ebx,8(%si),將ebx暫存器裡的值放到記憶體位址是8(%si)的記憶體單元上。正好,這裡同時遇到了另乙個問題,就是在at&t彙編中,間接定址方式是有別於x86的。上例中的8(%si)就相當於x86中的【si+8】。
還有一種叫做label(標號)的程式控制語句,比如,在stage1.s中,有這麼一段指令:
cmpb $grub_invalid_drive,%al
je1f
movb %al,%dl
1:pushw %dx
上面就用到了標號,je1f,前面的兩個數進行比較,如果相等就跳轉到1的位置。注意,1後面的f表示的是forward,即從je指令後繼續往前走來尋找1這個標號。所以,如果程式中有好幾個叫做1的標號,就要看是1f還是1b了,b代表backward,方向和f相反。青島之光論壇裡有這麼乙個例子可以更好的幫助我們理解:
je 1f或者je 1b 是跳轉到對應的標號的地方。這裡的1表示標號(label),f和b表示向前還是向後,f(forward)向前,b(backward)向後,如下例:
1行 1: cmp $0, (%si)
2行je 1f ///跳轉到後面的1標示的地方,也就是第6行
3行 movsb
4行 stosb
5行 jmp 1b 跳轉到前面1表示的地方 ,也就是第1行
6行 1: jmp 1b 跳轉到前面1表示的地方,第6行,其實就是個死迴圈
然後,在at&t彙編中出現最多的大概就是稱作assembler directive的東西了,我們稱作「at&t匯程式設計序**控制」。所有的彙編器命令名都由句號('.')開頭。命令名的其餘是字母,通常使用小寫。在青島之光論壇
處有一篇關於程式**控制的詳細介紹,下面gemfield只挑出幾個常用的來說明一下。
一、.byte 表示式(expression_rs)
.byte.byte可不帶引數或者帶多個表示式引數,表示式之間由逗點分隔。每個表示式引數都被彙編成下乙個位元組。在stage1.s中,有這麼一段**:
after_bpb:
cli.byte 0x80,0xca
那麼編譯器在編譯時,就會在cli指令的下面接著放上0x80和0xca,因為每個表示式要占用1個位元組,所以此處一共占用2個位元組。
二、.word 表示式
這個表示式表示任意一節中的乙個或多個表示式(同樣用逗號分開),表示式佔乙個字(兩個位元組)。類似的還有.long。例:.word 0x800
三、.file 字元(string)
.file 通告編譯器我們準備開啟乙個新的邏輯檔案。 string 是新檔名。總的來說,檔名是否使用引號『"』都可以;但如果您希望規定乙個空檔名時,必須使用引號""。本語
句將來可能不再使用—允許使用它只是為了與舊版本的as編譯器程式相容。在as的一些配置中,已經刪除了.file以避免與其它的彙編器衝突。例如stage1.s中:.file 」stage1.s」。
四、.text 小節(subsection)
通知as編譯器把後續語句彙編到編號為subsection的正文子段的末尾,subsection是乙個純粹的表示式。如果省略了引數subsection,則使用編號為0的子段。例:.text
五 、.code16
告訴編譯器生成16位的指令
六、.globl symbol
.globl使得連線程式(ld)能夠看到symbol,如果gemfield在區域性程式中定義了symbol,那麼與這個區域性程式鏈結的區域性程式也能訪問symbol,例:
.globl symbol_name(idt) 定義idt為全域性符號。
七、.fill repeat , size , value
repeat, size 和value都必須是純粹的表示式。本命令生成size個位元組的repeat個副本。
repeat可以是0或更大的值。size 可以是0或更大的值, 但即使size大於8,也被視作8,以
相容其它的彙編器。各個副本中的內容取自乙個8位元組長的數。最高4個位元組為零,最低的
4個位元組是value,它以as正在彙編的目標計算機的整數位元組順序排列。每個副本中的size
個位元組都取值於這個數最低的size個位元組。再次說明,這個古怪的動作只是為了相容其他
的彙編器。size引數和value引數是可選的。如果不存在第2個逗號和value引數,則假定value為零。如果不存在第1個逗號和其後的引數,則假定size為1。
例如,在linux初始化的過程中,對全域性描述符表gdt進行設定的最後一句為:
.fill nr_cpus*4,8,0,意思是.fill給每個cpu留有存放4個描述符的位置,並且每個描述符是8個位元組。
等等,不一而足。不過要注意的是,這種包含程式已初始化資料的節(.data)和包含程式程式還未初始化的資料的節(.bss),編譯器會把它們在4位元組上對齊,例如,.data是25位元組,那麼編譯器會將它放在28個位元組上。.
當這種以字尾名.s編寫的a t&t格式的彙編**完成後,就是編譯和鏈結了。linux下有兩種方式,一種是使用匯程式設計序gas和連線程式ld:
as filename.s –o filename.o
ld filename.o –o filename 最終將源**轉換為目標檔案.o再連線為可執行檔案filename
另一種就是大名鼎鼎的被gemfield提到過的gcc:
gcc –o gemfield gemfield.s
源程式gemfield.s的字尾名可以使用大寫,是因為這樣可以使gcc自動識別匯程式設計序中的c預處理命令,包括標頭檔案中的情況,像#include、#define 、#ifdef等。
本文少了嵌入式彙編的形式,才使得at&t 彙編看起來井井有條,而非想象中的艱難。如果想要真正鍛鍊一下這種彙編,源**stage1.s就是乙個絕佳的習題。
AT T彙編簡介
1.暫存器引用 引用暫存器要在暫存器號前加百分號 如 movl eax,ebx 80386有如下暫存器 8個32 bit暫存器 eax,ebx,ecx,edx,edi,esi,ebp,esp 8個16 bit暫存器,它們事實上是上面8個32 bit暫存器的低16位 ax,bx,cx,dx,di,si...
AT T彙編簡介
at t語法與intel匯程式設計序使用的語法很不一樣,他們之間的主要區別有以下幾點 這種定址方式常常用於在資料結構陣列中訪問特定元素內的乙個字段,base為陣列的起始位址,scale為每個陣列元素的大小,index為下標。如果陣列元素是資料結構,則disp為具體欄位在結構中的偏移。at t彙編器不...
ATT 彙編語法
在研華的pc104上使用看門狗要使用彙編。使用彙編來修改cmos裡面的引數。也就是內聯彙編。linux下gcc只支援att彙編。所以這兒有必要將att語法學習學習。以後需要的時候翻出來溫習溫習。1,運算元的長度 運算元的長度用加在指令後的符號表示 b byte,8 bit w word,16 bit...