CSAPP筆記 5 組合語言 資料

2022-03-09 02:30:05 字數 4084 閱讀 3645

本部落格對於彙編的介紹基於32位機器的intel x86系列處理器和ia32指令集,也涉及少部分x86-64。由於彙編知識相對複雜,這裡只做簡單介紹和記錄,詳細請參照書本!

下面這張**中體現了c語言基本資料型別和ia32的對應表示。

c語言中的宣告

intel 資料型別

彙編**字尾

大小(位元組)

char

位元組b(byte)

1short

字w(word)

2int

雙字l(long)

4long int雙字l

4long long int

「四字」-4

char *雙字l

4float

單精度s

4double

雙精度l

8long double

擴充套件精度

t10 or 12

大多數的常用資料型別是用雙字形式儲存的。短整數(short)、普通整數(int)和長整數(long int)的區別是「短」和「普通」整數是固定的2和4位元組,而「長」整數使用的是機器的全字長,因為是32位機器,所以這裡long int 是4位元組。

long long是由8個位元組來表示的,在硬體上 ia32 是不支援的這種資料型別的(x86-64可以)。

指標(char *)是使用機器的全字長的,因為指標是儲存位址的變數。位址和字長有關,字長(word size)表明整數和指標資料的標稱大小(nominal size)。在沒有涉及到儲存器的細節時,我們會把儲存器看成乙個非常大的陣列,所以字長決定乙個很重要的屬性是虛擬空間的最大大小(每乙個位元組都要用乙個唯一的數字來標識,而這個數字是要編碼的)。對於乙個字長為 w 位的機器而言,虛擬位址的範圍是0 ~ \((2^w)-1\),程式最多訪問\(2^w\) 個位元組。

單精度和雙精度浮點數是4和8位元組,不多說。擴充套件精度(long double)的大小為10或12位元組,擴充套件精度的作用以後會提到。

彙編**字尾的意思是大多數彙編**指令的後面帶有乙個字元字尾,表明運算元的大小。例如:movb(傳送位元組)、movw(傳送字)、movl(傳送雙字)。注意:用 l 表示double和4位元組整數不會產生歧義,因為浮點數使用的是一套完全不同的指令和暫存器。

乙個ia32**處理器單元(cpu,central processing unit)包含一組8個儲存32位值的暫存器(register),可以看到暫存器的數量是很少的。下圖顯示了這八個暫存器的簡單表示。它們的名字都以%e開頭,實際上它們另有特殊的名字。

在大多數情況下,前六個暫存器可以看成通用暫存器,對它們的使用沒有限制(「大多數情況」下分具體情況,具體情況也是用特殊的暫存器)。最後兩個暫存器(%ebp和%esp)儲存指向程式的棧中重要位置的指標,所以只有根據棧管理的標準才能修改這兩個暫存器的值。

可以看到,位元組操作指令可以獨立地寫更短的地位位元組。這是為了向後相容(backwards compatiblity),也就是能讓更早的**正常地工作。下面是x86-64的暫存器,以%r開頭的是64位暫存器,可以看到它也是向後相容的。

將資料從乙個位置複製到另乙個位置是最頻繁使用的指令。我們把指令分成指令類,一類指令執行的操作是一樣的,只不過運算元的大小不同。

mov指令

效果mov s, d

d ← s

movs s, d

d ← (符號擴充套件)s

movz s, d

d ← (零擴充套件)s

例如 mov 是一類指令,根據傳送位元組、字還是雙字分為三種指令:movb、movw、movl。

mov 移動的資料的大小和目的位置的大小是一樣的。與 mov 不同,movs 和 movz 指令類是將乙個較小的資料放到乙個較大的資料位置。擴充套件方式分為符號擴充套件或者零擴充套件兩種方式。如果要將乙個較小的資料放到乙個較大的資料位置,要麼使用擴充套件版本的指令,要麼就選擇正確的 mov 字尾。不能將較大的資料放在乙個較小的資料位置。

下面要介紹乙個非常重要的概念——定址。

大多數指令有乙個或多個運算元(operand),運算元可能指代要引用的資料值、資料**、或者要存放的目標位置。形成運算元的有效位址的過程,被稱為定址(addressing)。

運算元可以被分為三個型別:

立即數(immediate)

也就是乙個常數值,書寫方式是乙個美元符號後面跟著乙個用標準c表示法表示的整數,例如$0x1f。

暫存器(register)

表示某個暫存器的內容。用符號 $ e_a $ 來表示任意暫存器a,用引用 $ r[e_a] $ 來表示它的值。這是將暫存器集合看成乙個陣列r,用暫存器的名稱作為索引。

儲存器引用(memory)

它根據乙個計算出來的位址(稱為有效位址)訪問某個儲存器的位置。因為我們是將儲存器看成乙個很大的位元組陣列。我們用m[addr]表示對儲存器中的位元組值的引用。儲存器定址的內容就多了,包括:絕對定址、間接定址(對啦就是熟悉的指標!)、基址+偏移量定址、變址定址、比例變址定址。在這裡除了下面的程式中設計到的定址方法之外的就不一一講了。

movq 的字尾 q 是四字的意思,是 x86-64 中支援64位資料的寫法。接下來,對下圖中的一些定址方式做簡單介紹:

mov 的第乙個運算元是源s,s要麼是乙個暫存器,要麼是乙個立即數,要麼是乙個儲存器位置。第二個運算元是目的地d,只能是暫存器或者儲存器位置。

①:第一條指令的第乙個運算元是 $0x4 ,說明這是乙個立即數,也就是常數。第二個數是暫存器的名字,這種定址方式叫做:暫存器定址。對應的c語言就是為乙個區域性變數賦常數值。

②:第二條指令與第一條不同的是第二個運算元的暫存器兩邊加上了括號。這其是是間接定址的意思。形如 \((e_a)\) 的格式代表的運算元值是 \(m[r[e_a]]\),也就是說,此時暫存器裡存的是乙個指標,也就是乙個位址,括號可以看成乙個c語言的間接引用運算子*。

③:兩個暫存器直接的資料傳遞。

④:把乙個區域性變數的值複製到乙個指標指向的內容。

⑤:把指標指向的內容賦給區域性變數temp

這裡有一點需要注意的是:不允許mov指令的兩個運算元同時為儲存器位址。也許你會感到奇怪:把儲存器中乙個地方的值傳送到另乙個地方不是很正常嗎為什麼沒有這條指令?一開始對這個有疑惑是很正常的,這種操作需要兩條指令——第一條指令將原值載入到暫存器中,第二條指令將該暫存器裡的值寫到目的儲存器位置。學到後面自然會理解為什麼沒有兩個運算元都是儲存器位置的mov指令。

剩下的定址方法這裡就不介紹了:

分析下面這個交換函式的彙編**,有助於理解暫存器、儲存器、定址、位址等各種概念。

變數 xp 和 yp 是指標,內容是位址,用暫存器 %rdi 和 %rsi 存放。臨時變數 t0 和 t1 用暫存器 %rax 和 %rdx 存放。呼叫 swap 函式後,可以看到在左邊的 memory 一列,0x120 和 0x130 裡存放了資料 123 和 456,引數 xp 和 yp 已經存到了對應的暫存器。通過間接定址,將儲存器裡的值存放到臨時變數 t0 和 t1 對應的暫存器裡。

交換後,* xp 的值和 * yp 的值成功對調。

學習筆記 組合語言5

對於 bx bx 和記憶體單元的描述 要完整的描述乙個記憶體單元,需要兩種資訊 記憶體單元的位址 記憶體單元的長度 bx 也表示乙個記憶體單元,它的偏移位址在bx中,段位址在ds中 中可以是值也可以是暫存器 定義乙個描述性符號 來表示乙個暫存器或乙個記憶體單元 直接用數值要寫實體地址,20位 二進位...

組合語言筆記

1 暫存器操作 cs暫存器不能用mov指令賦值,需要用jmp等調轉指令。但還可以將cs暫存器的值mov到暫存器或者記憶體 ds暫存器可以用mov賦值,通常是這樣操作 mov bx 位址 mov ds bx。可以將ds中的值mov到暫存器或者記憶體。2 實模式和保護模式 實模式 實模式下訪問記憶體是通...

組合語言筆記

原作者是哪位大佬已經不可考,寫的挺好的 一波,侵刪 8086有14個暫存器 控制暫存器 ip flag 段暫存器 8086中 中的 只能是bx bp si di。此時bp預設ss段,di預設es段,bx si預設ds。16位 flags 32位 eflags 標誌位控制標誌位 系統標誌位 影響標誌暫...