golang 源碼分析:go 彙編

還是上一篇中的例子來分析 go 的彙編

可以看到

>6 a:=1
   b:=2

這兩行代碼對應的彙編代碼爲:

 >0x10a6d01 <main.main+33>        movq   $0x1,0x38(%rsp)                      │
│ 0x10a6d0a <main.main+42>        movq   $0x2,0x30(%rsp)

代表當前代碼所在的行。

AT&T 格式的彙編代碼中所有寄存器名字前面都有一個 % 符號,rsp 代碼 sp 寄存器,裏面存的是棧頂指針。

目前使用最爲廣泛的 AMD64 這種體系結構的 CPU,這種 CPU 共有 20 多個可以直接在彙編代碼中使用的寄存器,其中有幾個寄存器在操作系統代碼中才會見到,而應用層代碼一般只會用到如下分爲三類的 19 個寄存器。

  1. 通用寄存器:rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15 寄存器。CPU 對這 16 個通用寄存器的用途沒有做特殊規定,程序員和編譯器可以自定義其用途(下面會介紹,rsp/rbp 寄存器其實是有特殊用途的);

  2. 程序計數寄存器(PC 寄存器,有時也叫 IP 寄存器):rip 寄存器。它用來存放下一條即將執行的指令的地址,這個寄存器決定了程序的執行流程;

  3. 段寄存器:fs 和 gs 寄存器。一般用它來實現線程本地存儲(TLS),比如 AMD64 linux 平臺下 go 語言和 pthread 都使用 fs 寄存器來實現系統線程的 TLS,在本章線程本地存儲一節和第二章詳細分析 goroutine 調度器的時候我們可以分別看到 Linux 平臺下 Pthread 線程庫和 go 是如何使用 fs 寄存器的。

上述這些寄存器除了 fs 和 gs 段寄存器是 16 位的,其它都是 64 位的,也就是 8 個字節,其中的 16 個通用寄存器還可以作爲 32/16/8 位寄存器使用,只是使用時需要換一個名字,比如可以用 eax 這個名字來表示一個 32 位的寄存器,它使用的是 rax 寄存器的低 32 位。

rip 寄存器

rip 寄存器裏面存放的是 CPU 即將執行的下一條指令在內存中的地址。

這裏需要牢記的就是 rip 寄存器的值不是正在被 CPU 執行的指令在內存中的地址,而是緊挨這條正在被執行的指令後面那一條指令的地址。

rsp 棧頂寄存器和 rbp 棧基址寄存器

這兩個寄存器都跟函數調用棧有關,其中 rsp 寄存器一般用來存放函數調用棧的棧頂地址,而 rbp 寄存器通常用來存放函數的棧幀起始地址,編譯器一般使用這兩個寄存器加一定偏移的方式來訪問函數局部變量或函數參數

操作數寬度(即操作數的位數)

AT&T 格式的彙編指令中如果有寄存器操作數,則根據寄存器的名字(比如 rax, eax, ax, al 分別代表 64,32,16 和 8 位寄存器)就可以確定操作數到底是多少位(8,16,32 還是 64 位),所以不需要操作碼後綴,如果沒有寄存器操作數又是訪存指令的話,則操作碼需要加上後綴 b、w、l 或 q 來指定到底存取內存中的多少個字節。

而 go 彙編中,寄存器的名字沒有位數之分,比如 AX 寄存器沒有什麼 RAX, EAX 之類的名字,指令中一律只能使用 AX。所以如果指令中有操作數寄存器或是指令需要訪問內存,則操作碼都需要帶上後綴 B(8 位)、W(16 位)、D(32 位) 或 Q(64 位)。

立即操作數

        立即操作數需要加上 $ 符號做前綴,如  "mov $0x1 %rdi" 這條指令中第一個操作數不是寄存器,也不是內存地址,而是直接寫在指令中的一個常數,這種操作數叫做立即操作數。這條指令表示把數值 0x1 放入 rdi 寄存器中。

寄存器間接尋址

         寄存器間接尋址的格式爲  offset(%register),如果 offset 爲 0,則可以略去偏移不寫直接寫成 (%register)。何爲間接尋址呢?其實就是指指令中的寄存器並不是真正的源操作數或目的操作數,寄存器的值是一個內存地址,這個地址對應的內存纔是真正的源或目的操作數,比如 mov    %rax, (%rsp) 這條指令,第二個操作數 (%rsp) 中的寄存器的名字用括號括起來了,表示間接尋址,rsp 的值是一個內存地址,這條指令的真實意圖是把 rax 寄存器中的值賦值給 rsp 寄存器的值(內存地址)對應的內存,rsp 寄存器本身的值不會被修改,作爲比較,我們看一下 mov    %rax, %rsp 這條指令 ,這裏第二個操作數僅僅少了個括號,變成了直接尋址,意思完全不一樣了,這條指令的意思是把 rax 的值賦給 rsp,這樣 rsp 寄存器的值被修改爲跟 rax 寄存器一樣的值了

TEXT runtime·gogo(SB):指明在代碼區定義了一個名字叫 gogo 的全局函數(符號),該函數屬於 runtime 包。

上面就是 golang 中常用的彙編代碼。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/iZk2fFmnDGmGxgC85OMrGw