一 數據表示
1
十進制結尾用D或d
二進制結尾用B或b
十六進制結尾用H或h
八進制結尾用Q或q
2 字符的ASCII表示
標準的ASCII字符集分爲4組,每組32個字符。
第一組 0~1FH 是一組不可打印字符,稱爲控制字符
第二組 各種標點符號、專用字符和數字
第三組 26個大寫字母(41H~5AH)及6個專用字符
第四組 26個小寫字母及(61H~7AH)5個專用字符和一個控制符
3 BCD
BCD是一種十進制數的二進制編碼表示,有以下兩種格式:
a 壓縮BCD格式
用4個二進制位表示一個十進制位
b 非壓縮BCD格式
4 CPU
cpu主要由算術邏輯部件、控制部件和寄存器組成,其任務是執行內存中的指令序列。
5 彙編程序通常以.s作爲文件名後綴
as hello.s -o hello.o
ld hello.o -o hello
6 彙編程序中以.開頭的 名稱並不是指令的助記符,不會被翻譯成機器指令。
.section指示把代碼劃分成若干個段,程序被操作系統加載執行時,每個段被加載到不同的地址,具有不同的讀、寫、執行權限。
.data斷保存程序的數據,是可讀可寫的,C程序的全局變量屬於.data段
符號在彙編中代表一個地址,可以用在指令中。
movl $1,%eax//數據傳送指令,CPU內部產生一個數字1,然後傳送到eax寄存器中。mov的後綴l表示long,說明是32位的傳送指令。
CPU內部產生的數叫做立即數,在彙編程序中,立即數前面要加$,寄存器前面要加%,以便跟符號名區分開。
int $0x80
a int指令稱爲軟中斷指令,可以用這條指令故意產生一個異常,異常的處理和中斷類似,CPU從用戶模式切換到特權模式,然後跳轉到內核代碼中執行異常處理程序。
b int指令中哦姑娘的立即數0x80是一個參數,在異常處理中要根據這個參數決定如何處理。在linux內核中int 0x80這種異常稱爲系統調用。
c eax和ebx寄存器的值是傳遞給系統調用的兩個參數。eax的值是系統調用號,1表示_exit調用,ebx的值則是傳遞給_exit的參數,也就是退出狀態。
7 X86的寄存器
X86的通用寄存器有eax、ebx、ecx、edx、edi、esi。
除法指令要求被除數在eax寄存器中,餘數在edx寄存器中。
X86的特殊寄存器有ebp、esp、eip、efiags。
eip:程序計數器
eflags:保存計算過程中產生的標誌位
進位 CF 溢出 OF 零ZF 負數 SF
ebp和esp用於維護函數調用的堆棧。
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.long指示聲明一組數,每個數佔32位,相當於C中的數組
數組開頭有個標號data_items,彙編器會把數組的首地址作爲data_items符號所代表的地址。
出了.long之外,常用的數據聲明還有:
.byte 聲明一組數,每個數佔8位
.ascii 例如:.ascii "hello word"
edi(目的變址寄存器)寄存器保存數組的當前位置。
ebx 保存到目前爲止找到的最大值。
movl data_items(,%edi,4), %eax
把數組的第0個元素傳送到eax寄存器上。data_items是數組的首地址,edi的值是數組的下標,4表示數組的每個元素佔有4個字節。那麼數組第edi個元素的地址應該是data_items+edi*4。
start_loop表示開始循環,loop_exit表示結束循環。
cmpl $0, %eax
cmpl指令將兩個操作數相減,但計算結果並不保存,只是根據計算結果改變eflags寄存器中的標誌位。如果兩個操作數相等,則計算結果爲0,eflags中的ZF位置1。
je是一個條件跳轉指令,它檢查eflags中的ZF位,ZF位爲1則發生跳轉,ZF位爲0則不跳轉。
比較指令和條件跳轉指令是配合使用的,前者改變標誌位,後者根據標誌位作出判斷。
incl %edi //將edi 的值加1
cmpl %ebx, %eax
jle start_loop
把當前數組元素eax和目前爲止找到的最大值ebx 做比較,如果前者小於等於後者,則最大值沒有變,跳轉到循環開頭比較下一個數,否則繼續執行下一條指令。jle 也是一個條件跳轉指令,le表示less than or equal。
jmp 是一個無條件跳轉指令,什麼條件也不判斷,直接跳轉。
8 寄存器
1 eax 累加器 2 ebx 基址寄存器 3 ecx 計數寄存器 4 edx 數據寄存器
1~~4 數據寄存器
5 esi 源變址寄存器 6 edi 目的變址寄存器
5~6變地寄存器
1~~6 通用寄存器
7 ebp 基址指針 8 esp 堆棧指針
7~8 指針寄存器
9 eflags 標誌寄存器 10 eip 指令指針
9~~10 專用寄存器
11 CS 代碼寄存器 12 ds 數據斷寄存器 13 ss 堆棧寄存器 14 es 附加段寄存器
11~~14 段寄存器
15 fs
16 gs
9 尋址方式
內存尋址在指令中可以表示成如下的通用格式:
ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)
它所表示的地址可以這樣計算出來:
FINAL ADDRESS = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER * INDEX
其中ADDRESS_OR_OFFSET和MULTIPLIER必須是常數,BASE_OR_OFFSET和INDEX必須是寄存器
直接尋址:
只使用ADDRESS_OR_OFFSET尋址,例如movl ADDRESS, %eax 把ADDRESS地址處的32位數傳送到eax 寄存器。
變址尋址(Indexed Addressing Mode) 。movl data_items(,%edi,4), %eax 就屬於這種尋址方式,用於訪問數組元素比較方便。
間接尋址(Indirect Addressing Mode)。只使用BASE_OR_OFFSET尋址,例如movl (%eax), %ebx ,把eax 寄存器的值看作地址,把這個地址處的32位數傳送到ebx 寄存器。注意和movl %eax, %ebx 區分開。
基址尋址(Base Pointer Addressing Mode)。只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET尋址,例如movl 4(%eax), %ebx ,用於訪問結構體成員比較方便,例如一個結構體的基地址保存在eax 寄存器中,其中一個成員在結構體內的偏移量是4字節,要把這個成員讀上來就可以用這條指令。
立即數尋址(Immediate Mode)。就是指令中有一個操作數是立即數,例如movl $12,%eax 中的$12 ,這其實跟尋址沒什麼關係,但也算作一種尋址方式。
寄存器尋址(Register Addressing Mode)。就是指令中有一個操作數是寄存器,例如movl $12, %eax 中的%eax ,這跟內存尋址沒什麼關係,但也算作一種尋址方式。
10 ELF文件
可重定位的目標文件(Relocatable)
可執行文件(Executable)
共享庫(Shared Object)
目標文件:
ELF文件格式提供了兩種不同的視角,在彙編器和鏈接器看來,ELF文件是由Section Header Table描述的一系列Section的集合,而執行一個ELF文件時,在加載器(Loader)看來它是由Program Header Table描述的一系列Segment的集合。
我們在彙編程序中用.section 聲明的Section會成爲目標文件中的Section,此外匯編器還會自動添加一些Section(比如符號表)。
Segment是指在程序運行時加載到內存的具有相同屬性的區域,由一個或多個Section組成,比如有兩個Section都要求加載到內存後可讀可寫,就屬於同一個Segment。
目標文件需要鏈接器做進一步處理,所以一定有Section Header Table;可執行文件需要加載運行,所以一定有Program Header Table;而共享庫既要加載運行,又要在加載時做動態鏈接,所以既有Section Header Table又有Program Header Table。
們知道,C語言的全局變量如果在代碼中沒有初始化,就會在程序加載時用0初始化。這種數據屬於.bss 段,在加載時它和.data 段一樣都是可讀可寫的數據,但是在ELF文件中.data 段需要佔用一部分空間保存初始值,而.bss 段則不需要。也就是說,.bss 段在文件中只佔一個Section Header而沒有對應的Section,程序加載時.bss 段佔多大內存空間在Section Header中描述。
.rel.text 告訴鏈接器指令中的哪些地方需要重定位
objdump工具可以把程序中的機器指令反彙編