學習一門語言,最好的方式就是在運用中學習,那么在這一章節中,我們開始編寫我們的第一個匯編程序。當然作為第一個程序,其實十分的簡單,但可以給大家一個基本的輪廓,了解匯編大概是這樣的。
我們這個程序實際上沒什么作用,只是簡單的推出而已。下面就是程序的范例
# 目的: 退出程序並向Linux內核返回一個狀態碼
# 輸入: 無
# 輸出: 返回一個狀態碼。在程序運行結束后可以通過 echo $? 來讀取狀態碼
# 變量: %eax保存系統調用號 %ebx保存返回狀態
.section .data
.section .text
.globl _start
_start:
movl $1, %eax # 這是退出程序的Linux內核命令號
movl $0, %ebx # 返回的狀態碼
int $0x80 # 喚醒內核以執行退出命令
完成上面的源代碼,進行匯編
32位機器:as exit.s -o exit.o
64位機器:as –32 exit.s -o exit.o
在命令中,as是匯編命令,exit.s 是源文件 -o 是重命名,讓我們生成的文件叫exit.o,exit.o就是目標文件。在Linux為 .o 后綴,在Windows下為 .obj 后綴。
目標文件是用機器語言寫的代碼,接下來我們需要使用鏈接器進行鏈接。
32位機器:ld exit.o -o exit
64位機器:ld -melf_i386 exit.o -o exitls
ld就是鏈接器命令
要運行exit的話,可以使用 ./exit 命令。當你輸入命令,點完回車之后,光標只是進入了下一行,畢竟我們這個程序只是退出而已。
不過但你運行完程序馬上輸入 echo $? 會發現屏幕上會出現一個0。這個0就是返回的狀態碼
接下來我們開始分析每個部分。
以#號開頭的為注釋
.section .data
在匯編中,任何以小數點開始的指令都不會被翻譯為機器指令,這些針對匯編本身的指令有匯編程序處理,實際上並不會在計算機上運行,這些指令是匯編指令。.section 指令是把程序分成幾個區塊。
.section .data 命令是數據段的開始,數據段中要列出程序數據所需的內存空間。我們這個程序太過簡單,所以沒有數據,這個命令其實也可以不寫。保留這個指令只是為了保留程序的結構完整,知道程序的基本框架。
.section .text 是文本段,也就是代碼區啦,這里容納程序指令
.globl _start
這一條指令中 _start 是一個符號,幫助人理解而已,在匯編和鏈接過程中會被替換。一般來說,符號用來標記程序或數據的位置,所以通過符號而不是內存中的位置編號來指代他們。
.globl 表示匯編程序不應該在匯編之后把這個符號舍棄,因為連接器還需要它。_start 是一個特殊符號,總是使用 .globl 來標記,應為這個標記是程序的入口。
_start:
定義_start標簽的值。當匯編程序對程序進行匯編的時候,必須為每一個數值和指令分配地址。標簽告訴匯編程序以符號的值作為下一條指令或下一個數據元素的位置。這樣,如果數據或指令的實際物理地址更改了,也不需要重新引用,應為符號會自動獲取新值。
下面開始就是真正的計算機指令了。
movl $1, %eax
當程序運行時該指令將數值1移入%eax寄存器中。 movl 指令有兩個操作數,一個是源操作數,另一個是目的操作數。根據上一講,$1 是立即尋址方式,在指令中就包含需要的數字。這里1是源操作數,%eax是目的操作數。操作數的類型一般是數字,內存位置引用或寄存器。而 movl 的作用是從內存位置復制一個字大小的數據到另一個位置。(PS:movb的話操作數是字節)。1移入%eax是讓系統調用exit。進行系統調用時,必須把系統調用號加載到%eax中,有些系統調用還需要其他寄存器也包含有值。
x86處理器上的通用寄存器
- %eax
- %ebx
- %ecx
- %edx
- %edi
- %esi
除了通用寄存器,還有幾個專用寄存器
- %ebp
- %esp
- %eip
- %eflags
引申一點,在x86處理器的通用寄存器都以e開頭,e 是 extern 的意思,即擴展。主要是因為以前的x86處理器是16位的,后來才變為32位,為了向前兼容,保留了舊名,在前面加上了e作為32位的擴展寄存器。而在64位采用的是r前綴,比如說%rax就是64位的%eax寄存器。大家可以試着把前面的代碼改一下,匯編,鏈接,運行。
在進行系統調用的情況下,操作系統還需要將狀態嗎加載到%ebx,這個值后背返回給系統
movl $0, %ebx
這句命令和上面類似,就不解釋了。
int $0x80
int 表示中斷,0x80是中斷號。還有,0x80是16進制數,不是10進制的80。