先承認我自己很不滿意這篇,太亂了。只能當工具查keyword用。不過as 手冊的確就是指令和語法。原本是以英文字母順序說明,我只是把這些用自認的方式重新分類。很多地方也真的只有句意翻譯。就把他當作看手冊的導讀,有找的需要的再進去看手冊吧。
本篇只討論ELF部份,其他binary format跳過。
目錄
as
參數
只提幾個我有興趣的部份
-Z
:硬上,就算有錯誤照樣組譯沒有錯的部份。--gstabs+
:好東西,可以幫你加入debug資訊,然後直接用gdb除錯。- 如果檔案副檔名為
.s
,就是普通的組合語言原始檔。 - 如果檔案副檔名為
.S
,就可以使用cpp
(還記得c preporcessor吧?)來處理前置處理。
名詞解釋
symbol
:由字母、數字、和_
、.
、$
組成的字串。不得以數字開頭。label
:symbol
後面加:
.
開頭的symbol是gas 的directive
expression
:運算式,結果代表不是位址就是單純的數字- 原始碼不是以上的情況,由英文字母開頭組成的字串就是instruction
- 原始碼最後一行一定要是
\n
。目前網友Carl有提供為什麼這樣規定的link。
常數
- 字元常數:
'字元
- 顯示\:
'\\
- 字串:
"字串"
Section
一個連續的記憶體空間。這段連續空間都是為了處理某些單一特定的任務如執行程式碼、存放global變數等。
題外話,.bss
存在的目的是節省儲存空間,沒有初始的全域變數當然不需要在檔案中保留儲存空間。
undefined section
在組譯的時段只要位址無法決定的symbol,一律放到undefined section。然後祈禱linker幫你搞定。
relocation
前面的文有提到,linker功能之一就是把不同的object檔案黏成一個執行檔。要怎麼黏呢?
每個object 檔案的起始點都是address 0。由linker計算並設定每個object檔案最後在執行檔放置的address,避免這些object的內容互相覆蓋。
而linker要怎麼搬移和設定最後的位址呢?這是因為object檔案內已經有規範好的不同名稱的好幾個連續空間,也就是section。所以linker把這些object檔案中相同section名稱的連續空間搬到執行檔內相同名稱的空間,並且保證執行檔內這些section的空間也是連續的。而搬移的動作並設定section的runtime address就稱為relocation
。
Linker在relocation時需要考慮的問題,as也幫他處理了,這些問題是
- 目前這個位址要對應到object檔案的哪個地方?
- 這個位址會需要佔用多少byte的空間?不懂?int和char吃的空間總會不一樣吧。
- 目前位址對應到的是哪個section? 這個位址和對應section的offset為何?
- 目前的位址是絕對位址還是和program counter相對的位址?
另外要注意的是,大部分的位址可以表示成
1
|
|
Expression
expression的結果代表不是位址就是單純的數字。這些數字要嘛是絕對位址、要嘛就是某個section的offset。而expression之間可以有空白。
Empty expression
空白字元或是null,其值會被設為0
Integer expression
由一個以上的argument和operator組成的expression
Arguments
包含 symbols, numbers 或subexpressions,分別討論
- symbol:結果將會是 {section setction的offset數值},數值會是32位元的二的補數(就是有正負值啦)
- numbers:一般來說,是正整數。如果你要處理浮點數或是大數(超過32位元的數字)as會噴警告。你需要自己處理這種情況。
- subexpressions:指的是
- (expression)
- prefix operator 伴隨一個 argument
Operators
用來協助運算section中的offset位址。
- Infix Operators
- 就一般的binary operator如
+
,-
等
- 就一般的binary operator如
- Prefix Operators
-
:負號~
:補數,就是將argument的每個位元inverse
Infix也和C語言一樣,有優先順序、符號定義也大致相同,列出如下
- 最優先
*
,/
,>>
,<<
- 第二順位
|
,&
,^
,!
- 第三順位
+
,-
,==
,<>
,!=
,>
,<
,>=
,<=
<>
就是!=
- 最低順位
&&
,||
directives
重頭戲。directive又稱pseudo-ops,一律以.
開頭。照字面理解,這東西是用來協助使用開發,而不是真正的CPU instruction。這邊我只列出看得懂我感興趣的部份。有興趣請參考出處。另外和硬體相依的directive請參考這邊。
變數相關
.ascii "字串"
:可以用多個字串,中間以,
隔開。這些字串最終會被一起放在連續的記憶體中。.asciz "字串"
:和樓上的差別是字串後面會自動填\0
,和C語言的字串表示方式相同。.balign[wl] abs-expr, abs-expr, abs-expr
:和.align
差別在b是byte
,w是2-byte
,l是4-byte
。這代表什麼呢?代表要pad的數字(如果有指定的話)要注意fill byte數量。如.balignw 8, 0xbeef
。.byte expressions
:expression
數量可以從0個到多個,中間以,
隔開。這些expression
會依照順序排列。那麼要幹什麼用呢?你可以這樣玩。
1
|
|
.int expressions
.long expressions
- 上面兩個有同樣效果,
expression
為16-bit寬度。可以用,
隔開。和.byte
用法類似。長度以及order會和CPU架構相關。
- 上面兩個有同樣效果,
.hword expressions
.short expressions
- 上面兩個有同樣效果,
expression
為16-bit寬度。可以用,
隔開。和.byte
用法類似。
- 上面兩個有同樣效果,
.double flonums
:就浮點數,可以用,
隔開。和.byte
用法類似。表示方式要看target CPU架構。.float flonums
:就浮點數,可以用,
隔開。和.byte
用法類似。表示方式要看target CPU架構。.lcomm symbol, length
:為symbol
保留length
的空間,該symbol型態不會是global
,並且會被放在.bss
section。.octa 大數字
:為16-byte寬度。可以用,
隔開。和.byte
用法類似。.quad 大數字
:為8-byte寬度。可以用,
隔開。和.byte
用法類似。.string "字串"
:將字串放到object file中,看不出來和.ascii
差在那。.string16 "字串"
:將字串放到object file中,字串中的單個字元將會展開成2個bytes。看不出來和.ascii
差在那。.string32 "字串"
:將字串放到object file中,字串中的單個字元將會展開成4個bytes。看不出來和.ascii
差在那。.string64 "字串"
:將字串放到object file中,字串中的單個字元將會展開成8個bytes。看不出來和.ascii
差在那。.set symbol, expression
:將symbol
的值設成expression
的值。.size symbol, expression
:設定symbol
空間為expression
的值。
Symbol的描述
visibility:local, global or weak
.extern
:單純是相容性使用,特地列出來只是因為手冊說as將所有undefined symbols
視為extern
.global symbol
.globl symbol
- 以上兩個同樣效果,就是讓
linker
看得到這個symbol
,也就是說透過nm
觀察binary也可以看得到這些symbol
。
- 以上兩個同樣效果,就是讓
.local symbol
:讓linker
看不到這個symbol
。手冊上另外有提到.local
不支援alignment的問題和解法。我看不懂,有興趣自行去連結參考。.weak symbol
:組譯器找不到symbol
會產生一個。
Symbol type
.type symbol, type
:type 描述方式有五種。我只用我看順眼的那種說明。"function"
:這個symbol
是個function"object"
:這個symbol
用來存放資料"tls_object"
:這個symbol
用來存放thread local資料"notype"
:沒有指定"gnu_unique_object"
:保證該symbol
是唯一的symbol"gnu_indirect_function"
:看不懂
其他Symbol 相關
.desc symbol, abs-expression
:提供描述symbol的特性,細節請參考前面的說明。.equ symbol, expression
:將symbol
設成expression
的值.equiv symbol, expression
:和上面類似,但是如果該symbol
之前已經定義過,就會噴錯誤。
Section
.data
:不解釋.test
:不解釋.section name
:讓as把以下的東西組成name
的section。名字雖然可以亂取,但是也要看binary format有沒有支援。如a.out
就沒有這東西。
ELF 下的Section directive
ELF的話,這個directive有加料。說明如下: []
表示optional
.section name [, "flags"[, @type[,flag_specific_arguments]]]
flags
:可由下面的flag合體組成a
:allocatable,就是要在記憶體內吃空間,但是loader不一定會載入東西到該sectione
:非executable或是shared library的sectionw
:可寫入x
:可執行M
:可被mergeS
:該section有 zero terminated 字串G
:屬於某個section groupT
:給thread local存放東西用 (存放三小?)?
:看不懂,跳過
type
由於@
在某些平台如ARM上是註解的符號,這種情況需要用%
替代。
G
和M
有特別規範,必須隔離在雙引號外面。而同時要用這兩個flag要以MG
順序擺放,範例如下:
.section name , "flags"MG,...
Section group目前先假裝沒看到,有機會又看到再回來討論。
條件以及控制相關
- if 部份有點雜亂,懶得想範例測試,想像成C語言的
#ifdef
。剩下自己看手冊。 .irp symbol,values...
:和巨集概念很類似,把.irp ...
到.endr
之間的instruction用到symbol
的部份全部換成value。範例如下。
.irp item, 2, 3, 4 mov %r\item, $\item .endr
會展開成
1 2 3 |
|
.irpc symbol,values...
:手冊上面的說明幾乎和irp
相同,悲劇的是範例和.irp
完全一致。唯一差別是.iprc
中有提到character,只能猜測c是character。.offset loc
:將locale counter設定成loc。.org new-lc, fill
:同樣是更動locale counter,但是只能在同一個section中移動。另外一個要注意的是這個指令只能增加locale counter,硬要減少是不可能的。當locale couter移動後,中間的空白會填入fill
的值。不加上, fill
as會填0。.rept 次數
:重複.rept
到.endr
指定的次數。.skip size, fill
:產生size
長度,fill
值的資料。.fill repeat, size, value
:產生value
,佔用空間為size
。是否要產生多個,否的話repeat
填0
,是的話repeat
填要產生的個數。size
和value
為optional,size
預設為1
,value
預設為0
。.fill 2,,
.fill 2,,10
.fill 2,4,
.warning "string"
:印出警告訊息。.err
:噴錯誤,除非as有-Z指令,不然別想產生obj檔。.error "錯誤訊息"
:印出錯誤訊息然後GG。不帶錯誤訊息as會印出檔案名稱和用了.error
那行。.fail expression
:expression
值大於五百噴警告,小於五百噴錯誤。用在複雜的巢狀巨集或是條件式組合語言中。.print "字串"
:組譯的時候stdout會印出字串。.end
:表示組合語言程式結束
巨集
跳過,自行看手冊
ELF相關
.symver symbol, symbol2@nodename
:指定symbol的版本號碼,一般用在shared library中。詳細說明懶得看,那天GG再回來看。
ELF section stack
.subsection name
:把目前的section push到section stack中,並且把目前的subsection置換成name
。.popsection
:從section stack中pop最上面的section去覆蓋目前的section.pushsection name [, subsection] [, "flags"[, @type[,arguments]]]
:把目前的section push到section stack中,並且把目前的section置換成name
以及subsection
,type
和argument
和.section
的參數相同。
ELF visibility
.protected symbol
:不但外部看不到該symbol,連內部要使用讀取該symbol的另外一個symbol也要在內部定義。直接舉個虛擬C語言。
1 2 3 4 5 |
|
.hidden symbol
:想像C語言在function前面加上static
,觀念類似,讓該symbol無法被其他component看見。手冊這樣的symbol通常被視為.protect symbol
,目前懶得寫程式測試。單純猜測這兩個有不同,不然幹嘛要分成兩個指令。.internal symbol
:手冊上提到除了和.hidden
有同樣效果外,不同的CPU會針對這個symbol做特別處理,到底是哪些特別處理,手冊沒說。
除錯相關
大部分跳過,太多背景需要補完。
.def
.endef
.dim
:給compiler產生除錯用。.file 檔案行號 檔名
:DWARF2用的除錯,除錯時對應的原始碼行號。.func name[,label]
:只有開啟除錯有效,必須在結尾加入.endfunc
。label
就是組合語言內的label
,也就是該function的進入點。不填的話,就在name
加上prefix 字元當作進入點,通常prefix字元為_
。.loc fileno lineno [column] [options]
:DWARF2用的除錯。整理如下- 手冊假設我們很瞭debug內部資訊,但是我不會。看下來他們有提到
.debug_line
狀態機.debug_line
line number matrix- 不明暫存器:
is_stmt
register,isa
register等
- 手冊假設我們很瞭debug內部資訊,但是我不會。看下來他們有提到
- 資訊放在binary 的
.debug_line
section。 - 在debuger(?)載入
.debug_line
資訊時,讀到該行,會把參數fileno
,lineno
,等參數一併載入。 - options:
basic_block
:設定.debug_line
狀態為basic_block
prologue_end
:設定.debug_line
狀態為prologue_end
epilogue_begin
:設定.debug_line
狀態為epilogue_begin
is_stmt value
:設定is_stmt
register 在.debug_line
狀態為value
,合法數值只有0
或1
。isa value
:設定isa
register 在.debug_line
狀態為value
,合法數值只有0
或1
。discriminator value
:設定discriminator
register 在.debug_line
狀態為value
,合法數值只有0
或1
。
.loc_mark_labels enable
:是否enble,basic_block register
,細節完全看不懂。只知道和debug line number entry有關。.stabs symbol, type, other, desc, value
:用來提供資訊給symbolic debuger。詳細資訊請看手冊.tag structname
:compiler產生的輔助directive。用來從symbol table中找出structname
的instance。.val addr
:看不懂。自己看手冊。看起來是紀錄addr
的值,但是怎麼會和symbol table扯上關係??未分類
.include "file"
:從目前的位置,把file
全部原封不動地放到之後的位置。.align abs-expr1, abs-expr2, abs-expr3
:local counter
(請參考前面linker script文)結束要對齊的位址倍數。abs-expr1
:必填。要對齊的數字。根據CPU,數字代表可能是byte,有些代表的是bit。所以要對齊8有的CPU要填8,有些CPU要填3。心理的OS,欠揍。abs-expr2
:optional。如果需要填空,可以指定填入的數值。不填就使用預設值,0。abs-expr3
:optional。指定跳過數字最多可以幾個,超過就直接不對齊了。(手冊用skip而不用pad讓我在想這到底差別在那?)
如果後面兩個都不想填,可以直接下.align abs-expr1,,
收工。
.comm symbol, length
:我是這樣理解啦,就是很多C語言檔案要用同一個全域變數。先摸到的先贏。可以看我以前整理的說明。另外有兩點要注意- 如果有同樣的symbol,在不同檔案中,設定的長度又不同,gas會選最大的。
- ELF有隱藏的第3參數,用來指定alignment。
.gnu_attribute tag, value
:GNU屬性自己查.ident 字串
:不同binary format有不同處理,在ELF中會把字串放到.comment
section中。要注意,file
包括command line參數中-I
指定的路徑。.incbin "file"[,skip[,count]]
:從目前的位置,把file
原封不動地放到之後的位置。你可以透過skip
指定從檔案起始地幾個byte後跳過。另外你也可以透過count
指定檔案最多include幾個bytes。
另外一點要注意,file
包括command line參數中-I
指定的路徑。
.version "string"
:產生.note
section並且將字串放入該section。