我的彙編學習之路(2):主要術語和概念

海風林影發表於2015-03-10

對於不折不扣的彙編新手來說,第一部分中出現的很多概念可能不是很明白,於是我決定寫更多有價值的文章。所以,讓我們開始《我的彙編學習之路》的第二部分的學習。

術語和概念

當我寫了第一篇之後,我從不同的讀者那獲得很多反饋,第一篇中有些部分不明白,這就是本文以及接下來幾篇從一些術語的描述開始的原因。

暫存器(Register):暫存器是處理器內小容量的儲存結構,處理器的主要功能是資料處理,處理器可以從記憶體中獲得資料,但這是一種低速的操作,這就是為什麼處理器為什麼要有自己資料儲存結構,稱為“暫存器”。

L小端(Little-endian):我們可以假設記憶體是一個大的陣列,它包含一個位元組一個位元組的數。每個地址儲存了記憶體“陣列”中的一個元素,每個元素一個位元組。舉例來說,我們有 4 位元組數:AA 56 AB FF,在小端模式下最低位存放在低地址上:

這裡,0、1、2、3 是記憶體地址。

大端(Big-endian):大端儲存資料與小端相反。所有上面的位元組序在大端模式下是:

系統呼叫(Syscall):系統呼叫是使用者程式要求作業系統為其完成某些工作的一種方式。你可以在這裡找到系統呼叫表。

(Stack):處理器中暫存器的個數非常有限。所以棧是一塊連續的記憶體空間,可以通過特殊暫存器如 RSP、SS、RIP 等來定址。在接下來的文章我會專門深入介紹棧。

(Section): 每個彙編程式都是由段來組成的,有以下的段:

  • data —— 用來宣告初始化的資料或常量
  • bss —— 用來宣告未初始化的變數
  • text —— 用來存放程式碼

通用暫存器(General-purpose register): 有 16 個通用暫存器:rax、rbx、rcx、rdx、rbp、rsp、rsi、rdi、r8、r9、r10、r11、r12、r13、r14、r15。

當然這不是與組合語言有關的全部的術語和概念,如果在接下來的文章中遇到奇怪的不熟悉的詞彙,我們再來解釋這些詞的意思。

資料型別

基本的資料型別有:位元組(bytes)、字(words)、雙字(doublewords)、四字(duadwords)以及雙四字(double dualwords),它們的長度分別為:8位、2個位元組、4個位元組、8個位元組、16個位元組(128位)。

現在我們只使用整數,所以只看看它的表示。整型有兩種型別:無符號和有符號。無符號整型是一個位元組、字、雙字、四字表示的無符號二進位制數,它們能表示的範圍分別為:0~255、0~65,535、0~2^32-1、0~2^64-1。有符號整型是一個位元組、字、雙字、四字表示的有符號的二進位制數。符號位在負數的時候是置位的,在正數和0的時候是清零的。整數能表示的範圍是:1個位元組 -128~127,1個字 -32,768~32,767,1個雙字 -2^31~2^31-1,1個四字 -2^63~2^63-1。

正如我上面提到的,每個彙編程式都是由段來組成的,它包含資料段、程式碼段、bss 段。我們先來看看資料段,這是主要用來定義初始化的常量。例如:

好了,這兒差不多清楚了,三個常量名字分別為 num1、num2 和 msg,值分別是 100、50 和 “Sum is correct”,10 。但是 db 、equ 又是什麼呢?實際上,NASM 支援大量偽指令:

  • DB、DW、DD、DQ、DT、DO、DY 和 DZ —— 用來定義初始化資料的。例如:

  • RESB、RESW、RESD、RESQ、REST、RESO、RESY、RESZ —— 用來定義非初始化變數
  • INCBIN —— 包含外部二進位制檔案
  • EQU —— 定義常量,例如:

  • TIMES —— 重複指令或資料(下一篇文章中描述)

算術操作

下面是算術操作指令的簡單列表:

  • ADD —— 整數加
  • SUB —— 減
  • MUL —— 無符號乘
  • IMUL —— 有符號乘
  • DIV —— 無符號除
  • IDIV —— 有符號除
  • INC —— 自增
  • DEC —— 自減
  • NEG —— 取反

本文會用到一些,其它的在接下來的文章有所覆蓋。

控制流

通常程式語言使用 ifcasegoto 等等來改變程式的執行順序,當然彙編也可以。這裡我們提及到一些。有一個專門用來比較兩個數大小的 cmp 指令,它被用來接著條件判斷指令來決定是否跳轉。例如:

cmp 指令僅僅比較兩個數,但是對它們的值沒有影響,也不會根據比較的結果執行任何東西。為了在比較之後執行操作,有條件跳轉指令,可以是下面的一個:

  • JE —— 如果相等
  • JZ —— 如果為零
  • JNE —— 如果不相等
  • JNZ —— 如果不為零
  • JG —— 如果第一個運算元比第二個大
  • JGE —— 如果第一個運算元比第二個大或者相等
  • JA —— 與 JG 指令相同,只不過比較的是無符號數
  • JAE —— 與 JGE 指令相同,只不過比較的是無符號數

例如如果我們想寫 C 語言中類似於 if/else 的語句:

在彙編中是這樣的:

也有一種無條件跳轉的指令語法:

例如:

這裡 _start 標籤後有一些程式碼,這些程式碼會被執行到,彙編最後控制轉向到 .exit 標籤處,該標籤後的程式碼開始執行。

通常無條件跳轉用在迴圈中,例如我們有 label 標籤,它後面有一些程式碼,程式碼執行完之後進行條件判斷,如果條件不成立將跳到該段程式碼的起始處。迴圈將在後面文章中介紹。

示例

我們看個簡單的例子:兩個數相加,得到它們的和,然後與預定義的一個數進行比較,如果相等輸出一些東西到螢幕上;如果不等退出。下面是例子的原始碼:

我們過一下這段程式碼。首先在資料段定義了三個數:num1、num2 和值為 “Sum is correctn” 的 msg。現在看到第 14 行,這是程式的入口的地方。我們將 num1 和 num2 的值放到通用暫存器 rax 和 rbx 中,使用 add 指令相加,在 add 指令執行完之後,rax 和 rbx 相加之和儲存到 rax 中,即現在 num1 和 num2 的和存放在 rax 暫存器中。

好了,我們讓 num1 是 100,num2 是 50,之和是 150,用 cmp 指令比較。在比較完 rax 和 150 之後,檢查比較的結果,如果 rax 和 150 不等,我們跳轉到 .exit 處,如果相等,跳到 .rightSum 標籤處。

接著有兩個標籤:.exit.rightSum。首先將 rax 設定為 60,這是 exit 系統呼叫號,以及將 rdi 設為 0,這是退出碼。然後,.rightSUm 相當簡單,只是列印出 Sum is corretn,如果你不能理解怎麼工作的,看看第一篇文章

總結

這是 《我的彙編學習之路》 系列文章的第二篇,如果你有任何問題或建議,給我留言。

相關文章