匯編語言基礎:寄存器和系統調用


本文轉載自匯編語言基礎:寄存器和系統調用

寄存器

寄存器是處理器臨時保存數據指令的的一部分。在x86_64架構中,寄存器能處理高達64位的數據。這意味着每個寄存器都可以保存該值
沒符號整數:0〜18,446,744,073,709,551,616
有符號整數:-9,223,372,036,854,775,808 - 9,223,372,036,854,775,807

這是x86_64架構下的所有寄存器的邏輯圖表。

img

這是我們的寄存器列表,表中有有四列:

  • 64位寄存器的列;
  • 32位寄存器
  • 16位寄存器
  • 8位寄存器:

各個寄存器的關系

這里是rax寄存器的圖,你可以看到我們的一個64位寄存器叫做rax,還有eax 32位寄存器,那實際上不是它自己的寄存器,它是rax寄存器的一半,那么ax是16位寄存器是eax寄存器的一半。同理al寄存器是ax寄存器的一半。使用al寄存器就好像是一個8位寄存器,但它是 實際上修改了ax寄存器的低8位。以上說的並不需要刻意記憶,因為本文的立足點是讓你能夠讀懂別人或C/C++編譯器轉譯的匯編代碼,你不記得的話,可以收藏本文已被你翻查。

img

不同數據長度的寄存器之間的關系

系統調用(Syscall)

系統調用是指程序向系統內核請求服務,系統調用因操作系統的不同而不同,因為不同的操作系統使用不同的內核。

  • 所有系統調用都有一個與之關聯的ID(一個數字)

  • 系統調用的輸入接受一個參數列表。

    img

    寄存器參數順序表

這是一個非常重要的表,當你使用系統調用時,它有許多輸入是基於存儲在寄存器中的值,並且是講求順序的,如果你想使用系統調用,你可以將對應系統調用的ID加載到rax寄存器中 並按上表指定的順序加載參數在你的RAX寄存器中,如果你在這里還沒有概念的話,可以跳過,往下看完一個示例,回頭看就明白了

  • 第一個參數是rdi寄存器,
  • 第二個參數是rsi寄存器,
  • 第三個參數是rdx寄存器
  • 第四個參數是r10寄存器
  • 第五個參數是r8寄存器
  • 第六個參數是r9寄存器.

系統調用列表

我們這里只列出常用系統調用的ID,完整的系統調用ID列表,可以在網上都可以找得到。

img

系統調用ID示例表

  1. 正如你所見,文件操作的系統調用是和Unix/Linux交互的常用接口.ID列對應的數字和對應syscall列對應系統調用名稱相關聯。
  2. 與系統調用相關的還有參數;我這里用顏色標記了,用'#'符號和紅色標記的表明是直接來自寄存器的一個數字,用'$'符號和綠色標記的也是來自寄存器,但它不是真實的值,而是寄存器里面緩存的內存地址所指向的位置保存的值(如果你有C指針的基礎,這就很容易理解了)。

sys_write系統調用示例

前文寫到Hello World程序將"Hello World"字符串打印到屏幕,已經用到了sys_write系統調用,我們作為示例來講解一下寄存器和系統調用之間是如何交互的。

img

sys_write參數類型

  • 第一個參數是文件描述符0代表文件輸入,1代表文件輸出,2代表文件錯誤。如果你只是將文本寫到屏幕,那么就使用標准輸出即可
  • 第二個文件描述符是緩存,即字符串輸出到屏幕之前,首先寫入的是高速緩存的位置
  • 第三個參數是字符串的長度

看完上面參數表,再回顧一下上面的寄存器ID參數表,看如何使用寄存器ID參數和下表的參數相關聯,其實很簡單,只需要將那些寄存器的值替換下表的參數列名即可。

img

所以,正如你所見

  • rax寄存器應該處理值1(也即是sys_write系統調用的ID),
  • rdi寄存器應該處理我們的文件描述符,
  • rsi寄存器應該處理字符串的緩存對象,
  • rdx寄存器應該處理字符串的長度.

最后,按照《參數類型表》的描述替換為各列寄存器對應的值,具體分析邏輯如下,這樣就生成我們最后關於Hello World程序關於系統調用的相關指令。

img

匯編語言常用關鍵字

  • mov:表示move的意思就是移動數據,所以諸如如下語句
    • move rax,1的意思就是將ID為1的sys_write系統調用移動寄存器rax,那么你應該記住,以后遇到類似move rax ID這樣格式的命令就應該記住這是通過寄存器執行某項系統調用
    • "move rsi text":的意思就是從text變量指定的內存位置加載字符串到rsi寄存器
  • syscall:每調用move指令后以syscall結尾,表示執行一次系統調用,也就是說你執行一個不同的系統調用都需要以syscall關鍵字結束。
  • 從上面的兩點理解,應該不難看出mov rax 60這條指令是關於系統調用ID等於60(從網上查到是sys_exit),關於告知操作系統,你的程序已經執行完畢,同理,move rdi 0是執行與描述符有關的操作,即告知程序退出的狀態 ,事實上這個狀態碼你可以任意定義的,按照程序設計的一般約定,0代表程序執行正常退出,其他為異常狀態
  • section:所有x86_64的匯編文件有三種section
    • .data:該section下定義的所有變量,在匯編之前,編譯器會完成所有相關變量指向對應內存位置的值的替換。你可以理解為C/C++的變量相關的操作。
    • .bss 該section,為某些功能的使用而分配內存,和.data section有些類似
    • .text 該section,主要定義程序運行的邏輯和各種的系統調用。
  • label:標簽用於標記代碼的一部分。在編譯時,編譯將計算標簽在內存中的位置。 每次使用標簽的名稱后,該名稱將被編譯器替換為內存中的位置。如果你看看helloword定義text的位置,它基本上是相同的,除了我們可以在位置插入這些標簽。在我們的代碼中將會看到為什么它在將來會有用武之地。
    • _start:標簽對所有程序都至關重要。當您編寫程序並稍后執行時,它首先在“_start”的位置執行。如果鏈接器找不到“_start”標簽,則會拋出錯誤。
    • global:當您希望鏈接器能夠知道某個標簽的地址時,使用global關鍵字。 生成的目標文件將包含指向“global”的每個標簽的鏈接。在helloworld這個示例中,我們必須將“_start”聲明為全局,因為代碼必須正確鏈接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM