x86彙編程式基礎(AT&T語法)

来源:http://www.cnblogs.com/orlion/archive/2016/08/13/5765339.html
-Advertisement-
Play Games

一、簡單的彙編程式 以下麵這段簡單的彙編代碼為例 (註意是globl不是global;movl(MOVL)不是mov1(MOV一)) 將這段程式保存為demo.s,然後用彙編器as把彙編程式中的助記符翻譯成機器指令(彙編指令與機器指令是對應的)生成目標文件demo.o。然後用鏈接器ld把目標文件de ...


一、簡單的彙編程式

 以下麵這段簡單的彙編代碼為例

.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $4, %ebx
int $0x80

(註意是globl不是global;movl(MOVL)不是mov1(MOV一))

 

將這段程式保存為demo.s,然後用彙編器as把彙編程式中的助記符翻譯成機器指令(彙編指令與機器指令是對應的)生成目標文件demo.o。然後用鏈接器ld把目標文件demo.o鏈接成可執行文件demo(雖然只有一個目標文件但是也需要經過鏈接才能成為可執行文件因為鏈接器要修改目標文件中的一些信息)。這個程式只做了一件事就是退出,退出狀態為4。shell中可以echo $?得到上一條命令的退出狀態。

 

【解釋】:彙編程式中以"."開頭的名稱不是指令的助記符,不會被翻譯成機器指令,而是給彙編器一些特殊的指示,稱為彙編指示或偽操作。

.section .data
.section .text

.section指示把代碼劃分成若幹個段(section),程式被操作系統載入時,每個段被載入到不同的地址,具有不同的讀寫執行許可權。

.data段保存程式的數據是可讀寫的,C程式的全局變數也屬於.data段。上邊的程式沒定義數據所以.data是空的。

.text段保存代碼,是只讀和可執行的,後面那些指令都屬於這個.text段。

.globl  _start

_start是一個符號(Symbol),符號在彙編程式中代表一個地址,可以用在指令中,彙編程式經過彙編器的處理後所有的符號都被替換成它所代表的地址值。在C中我們可以通過變數名訪問一個變數,其實就是讀寫某個地址的記憶體單元,我們通過函數名調用一個函數其實就是調轉到該函數的第一條指令所在的地址,所以變數名和函數名都是符號,本質上是代表記憶體地址的。

.globl指示告訴彙編器_start這個符號要被鏈接器用到,所以要在目標文件的符號表中給它特殊標記。_start就像C程式的main函數一樣特殊是整個程式的入口,鏈接器在鏈接時會查找目標文件中的_start符號代表的地址,把它設置為整個程式的入口地址,所以每個彙編程式都要提供一個_start符號並且用.globl聲明。如果一個符號沒有用.globl指示聲明這個符號就不會被鏈接器用到。

_start:

_start在這裡就像C語言的語句標號一樣。彙編器在處理彙編程式時會計算每個數據對象和每條指令的地址,當彙編器看到這樣一個標號時,就把它下麵一條指令的地址作為_start這個符號所代表的地址。而_start這個符號又比較特殊事整個程式的入口地址,所以下一條指令movl $1, %eax就成了程式中第一條被執行的指令。

movl $1, %eax

這是一條數據傳送指令,CPU內部產生一個數字1, 然後傳送到eax寄存器中。mov後邊的l表示long,說明是32位的傳送指令。CPU內部產生的數稱為立即數,在彙編程式中立即數前面加"$"寄存器前面加"%",以便跟符號名區分開。

movl $4, %ebx

與上條指令類似,生成一個立即數4,傳送到ebx寄存器中。

int $0x80

前兩條指令都是為這條指令做準備的,執行這條指令時:

  1. int指令稱為軟中斷指令,可以用這條指令故意產生一個異常。異常的處理與中斷類似,CPU從用戶模式切換到特權模式,然後跳轉到內核代碼中執行異常處理程式。

  2. int指令中的立即數0x80是一個參數,在異常處理程式中根據這個參數決定如何處理,在linux內核中,int $0x80這種異常稱系統調用(System Call)。內核提供了許多系統服務供用戶程式使用,但這些系統服務不能像庫函數(比如printf)那樣調用,因為在執行用戶程式時CPU處於用戶模式不能直接調用內核函數,所以需要通過系統調用切換CPU模式,通過異常處理程式進入內核,用戶程式只能通過寄存器傳幾個參數,之後就要按內核設計好的代碼路線走,而不能由用戶程式隨心所欲想調那個內核函數,這樣保證了系統服務被安全的調用,在調用結束後CPU再切換回用戶模式,繼續執行int指令後面的指令,在用戶程式看來就像函數的調用和返回一樣。

  3. eax和ebx寄存器的值是傳遞給系統調用的兩個參數,eax的值是系統調用號,1表示_exit系統調用,ebx的值則是傳給_exit系統調用的參數,也就是退出狀態。_exit這個系統調用會終止掉當前進程,而不會返回它繼續執行。不同的系統調用需要的參數個數也不同,有的會需要ebx、ecx、edx三個寄存器的值做參數,大多數系統調用完成之後是會返回用戶程式繼續執行的,_exit系統調用特殊。

 

x86彙編的兩種語法:intel語法和AT&T語法
x86彙編一直存在兩種不同的語法,在intel的官方文檔中使
用intel語法,Windows也使用intel語法,而UNIX平臺的彙編器一
直使用AT&T語法,所以本書使用AT&T語法。 mov %edx,%eax 這條
指令如果用intel語法來寫,就是 mov eax,edx ,寄存器名不加 % 號,
並且源操作數和目標操作數的位置互換。本書不詳細討論這兩種
語法之間的區別,讀者可以參考[AssemblyHOWTO]。
介紹x86彙編的書很多,UNIX平臺的書都採用AT&T語法,例
如[GroudUp],其它書一般採用intel語法,例如[x86Assembly]。

 

二、x86的寄存器

  x86的通用寄存器eaxebxecxedxediesi。這些寄存器在大多數指令中是可以任意使用的。但有些指令限制只能用其中某些寄存器做某種用途,例如除法指令idivl規定被除數在eax寄存器中,edx寄存器必須是0,而除數可以是任何寄存器中。計算結果的商數保存在eax寄存器中(覆蓋被除數),餘數保存在edx寄存器。

  x86的特殊寄存器ebpespeipeflags。eip是程式計數器。eflags保存計算過程中產生的標誌位,包括進位、溢出、零、負數四個標誌位,在x86的文檔中這幾個標誌位分別稱為CF、OF、ZF、SF。ebp和esp用於維護函數調用的棧幀。

 

三、第二個彙編程式

求一組數最大值的彙編程式:

.section .data
data_items:
.long 3,10,9,29,5,19,9,36,0
.section .text
.globl _start
_start:
movl $0, %edi
movl data_items(,%edi,4), %eax
movl %eax, %ebx
start_loop:
cmpl $0, %eax
je loop_exit
incl &edi
movl data_item(, %edi,4), %eax
cmpl %ebx, %eax
jle start_loop
movl %eax, %ebx
jmp start_loop
loop_exit:
mov $1, %eax
int $0x80

彙編鏈接執行:

 

這個程式在一組數中找到一個最大的數,並把它作為程式的退出狀態。這段數在.data段給出:

data_items:
.long 3,10,9,29,5,19,9,36,0

 .long指示聲明一組數,每個數32位,相當於C數組。數組開頭有個標號data_items,彙編器會把數組的首地址作為data_items符號所代表的地址,data_items類似於C中的數組名。data_items這個標號沒有.globl聲明是因為它只在這個彙編程式內部使用,鏈接器不需要知道這個名字的存在。除了.long之外常用的聲明:

  • .byte,也是聲明一組數,每個數8位
  • .ascii,例: .ascii "Hello World",聲明瞭11個數,取值為相應字元的ASCII碼。和C語言不同的是這樣聲明的字元串末尾是沒有'\0'字元的。

data_items數組的最後一個數是0,我們在一個迴圈中依次比較每個數,碰到0的時候就終止迴圈。在這個迴圈中:

  • edi寄存器保存數組中的當前位置,每次比較完一個數就把edi的值加1,指向數組中的下一個數。
  • ebx寄存器保存到目前為止找打的最大值,如果發現有更大的數就更新ebx的值。
  • eax寄存器保存當前要比較的數,每次更新edi之後,就把下一個數讀到eax中。
_start:
movl $0, %edi

初始化edi,指向數組的第0個元素。

 

movl data_items(,%edi,4), %eax

這條指令把數組的第0個元素傳送到eax寄存器中。data_items是數組的首地址,edi的值是數組的下標,4表示數組的每個元素占4位元組,那麼數組中第edi個元素的地址應該是data_items+edi*4。從這個地址讀數據,寫成指令就是上面那樣。

 

movl %eax, %ebx

ebx的初始值也是數組的第0個元素。

 

下麵進入一個迴圈,在迴圈的開頭用標號start_loop表示,迴圈的末尾之後用標號loop_exit表示。

start_loop:
cmpl $0, %eax
je loop_exit

比較eax的值是不是0,如果是0就說明到了數組末尾了,就要跳出迴圈。cmpl指令將兩個操作數相減,但計算結果並不保存,只是根據計算結果改變eflags寄存器中的標誌位。如果兩個操作數相等,則計算結果為0,eflags中的ZF位置1。je是一個條件跳轉指令,它檢查eflags中的ZF位,ZF位為1則發生跳轉,ZF位為0則不跳轉繼續執行下一條指令。(條件跳轉指令和比較指令是配合使用的)je的e就表示equal

 

incl %edi
movl data_items(,%edi,4), %eax

將edi的值加1,把數組中的下一個數組傳送到eax寄存器中。

 

cmpl %ebx, %eax
jle start_loop

把當前數組元素eax和目前為止找到的最大值ebx做比較,如果前者小於等於後者,則最大值沒有變,跳轉到迴圈開頭比較下一個數,否則繼續執行下一條指令。jle也是一個條件跳轉指令,le表示less than or equal

 

movl %eax, %ebx
jmp start_loop

更新了最大值ebx然後跳轉到迴圈開頭繼續比較下一個數。jmp是一個無條件跳轉指令,什麼條件也不判斷直接跳轉。loop_exit標號後面的指令用_exit系統調用來退出程式。

 

四、定址方式

訪問記憶體時在指令中可以用多種方式表示記憶體地址。記憶體定址在指令中可以表示成如下的通用格式:

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必須是寄存器。在有些定址方式中會省略這4項中的某些項,相當於這些項是0。

  • 直接定址:只使用ADDRESS_OR_OFFSET定址,例如movl ADDRESS, %eax把ADDRESS地址處的32位數傳送到eax寄存器。
  • 變址定址:movl data_items(,%edi,4), %eax就屬於這種方式,用於訪問數組很方便
  • 間接定址:只使用BASE_OR_OFFSET定址,例如movl (%eax), %ebx,把eax寄存器的值看作地址,把這個地址處的32位數傳送到ebx寄存器。
  • 基址定址:只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET定址,例如movl 4(%eax), %ebx,用於訪問結構體成員比較方便,例如一個結構體的基地址保存在eax寄存器中,其中一個成員在結構體內偏移量是4位元組,要把這個成員讀上來就可以用這條指令。
  • 立即數定址:就是指令中有一個操作數是立即數,例:movl $3, %eax。
  • 寄存器定址:就是指令中有一個操作數是寄存器。在彙編程式中寄存器用助記符來表示,在機器指令中則要用幾個Bit表示寄存器的編號,這幾個Bit與可以看做寄存器的地址,但是和記憶體地址不在一個地址空間。

 

關於彙編程式的Hello World可以參看我的另一篇文章:http://www.cnblogs.com/orlion/p/5316519.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ®版權聲明:本文為博主原創文章,未經博主允許不得轉載。 一.ODE介紹與平臺搭建. 接觸到動力學模擬引擎, 是因為筆者的一款PLC模擬軟體需要3D模擬.我需要達到的效果是,以3D方式構建出工控行業中常見的元件,比如感應器,氣缸,機械手,拉帶.這些元件在場景中通過用戶的組合擺放,設置好相關的屬性後,可 ...
  • AOP概述 AOP技術的誕生並不算晚,早在1990年開始,來自Xerox Palo Alto Research Lab(即PARC)的研究人員就對面向對象思想的局限性進行了分析。他們研究出了一種新的編程思想,藉助這一思想或許可以通過減少代碼重覆模塊從而幫助開發人員提高工作效率。隨著研究的逐漸深入,A ...
  • 在離線環境中發佈.NET Core至Windows Server 2008 0x00 寫在開始 之前一篇博客中寫了在離線環境中使用.NET Core,之後一邊學習一邊寫了一些頁面作為測試,現在打算發佈一下試試。看了下官方給出的發佈教程感覺挺詳細的了(https://docs.asp.net/en/l ...
  • QrCodeNet下載地址:http://qrcodenet.codeplex.com/ Controller: View: 效果如下: ...
  • 現階段的項目是採用前後端分離的思想,前端使用的是Angular.JS,後端使用ABP框架,在後端我們通過WebAPI技術來向前端提供json數據。以前是通過MVC來寫前端的代碼,感覺後端有點在控制前端的節奏,一些少量的後端代碼還是需要在HTML頁面中寫的,這次採用的這種模式,前端不需要寫一點後端的C ...
  • 標簽: 依賴註入 Autofac ASPNETCore ASP.NET Core依賴註入解讀&使用Autofac替代實現 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac實現和自定義實現擴展方法 3.1 安裝Autofac 3.2 創建容器並註冊依賴 4. 參考 ...
  • 定義函數 Python中定義函數的格式為: 函數在執行到return語句時結束,並將結果返回。如果沒有return語句,函數執行完畢後返回None。 例: 結果為: my age is 20 ps :str(x)函數我一開始並不知道具體功能是什麼,只是覺得應該要將int型轉換成string類型再輸出 ...
  • ELF文件格式是一個開發標準,各種UNIX系統的可執行文件都採用ELF格式,它有三種不同的類型: 可重定位的目標文件 可執行文件 共用庫 現在分析一下上一篇文章中經過彙編之後生成的目標文件max.o和鏈接之後生成的可執行文件max的格式,從而理解彙編、鏈接和載入執行的過程。 一、目標文件 ELF文件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...