沒想到《linux debugging:使用反彙編了解c++程式函數調用棧》發表了收到了大家的歡迎。但是有網友留言說不熟悉彙編,是以本書列了彙編的基礎文法。這些對于我們平時的調試應該是夠用了。
大學時候大家學的基本上都是intel的8086彙編語言,微軟采用的就是這種格式的彙編。gcc采用的是at&t的彙編格式, 也叫gas格式(gnu asembler gnu彙編器)。
1、寄存器命名不同
at&t
intel
說明
%eax
eax
intel的不帶百分号
2、操作數順序不同
movl %eax, %ebx
mov ebx, eax
intel的目的操作數在前,源操作數在後;at&t相反
3、常數/立即數的格式不同
movl $_value,%ebx
mov eax,_value
intel的立即數前面不帶$符号
movl $0xd00d,%ebx
mov ebx,0xd00d
規則同樣适用于16進制的立即數
4、操作數長度辨別
movw %ax,%bx
mov bx,ax
intel的彙編中, 操作數的長度并不通過指令符号來辨別。
at&t的格式中, 每個操作都有一個字元字尾, 表明操作數的大小. 例如:mov指令有三種形式:
movb 傳送位元組
movw 傳送字
movl 傳送雙字
如果沒有指定操作數長度的話,編譯器将按照目标操作數的長度來設定。比如指令“mov %ax, %bx”,由于目标操作數bx的長度為word,那麼編譯器将把此指令等同于“movw %ax, %bx”。
5、尋址方式
imm32(basepointer,
indexpointer,
indexscale)
[basepointer + indexpointer*indexscale + imm32)
兩種尋址的實際結果都應該是
imm32 + basepointer + indexpointer*indexscale
例如: 下面是一些尋址的例子:
mov 4(%ebp), %eax
mov eax, [ebp + 4]
基址尋址(base pointer addressing mode),用于通路結構體成員比較友善,例如一個結構體的基位址儲存在<code>eax</code>寄存器中,其中一個成員在結構體内的偏移量是4位元組,要把這個成員讀上來就可以用這條指令
<code>data_items(,%edi,4)</code>
[data_items+edi*4
變址尋址(indexed addressing mode),通路數組
movl $addr, %eax
mov eax, addr
直接尋址(direct addressing mode)
<code>movl (%eax), %ebx</code>
mov ebx, [eax]
間接尋址(indirect addressing mode),把<code>eax</code>寄存器的值看作位址,把記憶體中這個位址處的32位數傳送到<code>ebx</code>寄存器
mov $12, %eax
mov eax, 12
立即數尋址(immediate mode)
寄存器尋址(register addressing mode
6.跳轉方式不同
at&t 彙編格式中,絕對轉移和調用指令(jump/call)的操作數前要加上'*'作為字首,而在 intel 格式中則不需要。
jmp *%eax
jmp %eax
用寄存器%eax中的值作為跳轉目标
jmp *(%eax)
jmp (%eax)
以%eax中的值作為讀入的位址, 從存儲器中讀出跳轉目标
通過求一個數組的最大數,來進一步學習at&t的文法
[cpp]
view plaincopy
#purpose: this program finds the maximum number of a
# set of data items.
#
#variables: the registers have the following uses:
# %edi - holds the index of the data item being examined
# %ebx - largest data item found
# %eax - current data item
# the following memory locations are used:
# data_items - contains the item data. a 0 is used
# to terminate the data
.section .data #全局變量
data_items: #these are the data items
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi # move 0 into the index register
movl data_items(,%edi,4), %eax # load the first byte of data
movl %eax, %ebx # since this is the first item, %eax is
# the biggest
start_loop: # start loop
cmpl $0, %eax # check to see if we've hit the end
je loop_exit
incl %edi # load next value
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax # compare values
jle start_loop # jump to loop beginning if the new
# one isn't bigger
movl %eax, %ebx # move the value as the largest
jmp start_loop # jump to loop beginning
loop_exit:
# %ebx is the status code for the _exit system call
# and it already has the maximum number
movl $1, %eax #1 is the _exit() syscall
int $0x80
彙程式設計式中以<code>.</code>開頭的名稱并不是指令的助記符,不會被翻譯成機器指令,而是給彙編器一些特殊訓示,稱為彙編訓示(assembler directive)或僞操作(pseudo-operation),由于它不是真正的指令是以加個“僞”字。<code>.section</code>訓示把代碼劃分成若幹個段(section),程式被作業系統加載執行時,每個段被加載到不同的位址,作業系統對不同的頁面設定不同的讀、寫、執行權限。<code>.data</code>段儲存程式的資料,是可讀可寫的,相當于c++程式的全局變量。
<code>.text</code>段儲存代碼,是隻讀和可執行的,後面那些指令都屬于<code>.text</code>段。
<code>.long</code>訓示聲明一組數,每個數占32;.quad類似,占64位;.byte是8位;.word 是16位。<code>.ascii</code>,例如<code>.ascii "hello world"</code>,聲明11個數,取值為相應字元的ascii碼。
參考資料:
1.
最簡單的彙程式設計式
2.
第二個彙程式設計式
3. http://blog.chinaunix.net/uid-27717694-id-3942757.html
最後複習一下lea指令:
mov 4(%ebp) %eax #将%ebp+4位址處所存的值,mov到%eax
leal 4(%ebp) %eax #将%ebp+4的位址值, mov到%eax
leal 可以被mov取代:
addl $4, %ebp
mov. %ebp, %eax