簡單的匯程式設計序分析

2022-05-28 10:45:08 字數 4533 閱讀 2945

.section .data

.section .text

.globl _start

_start:

movl $1, %eax

movl $4, %ebx

int $0x80

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

as hello.s -o hello.o

ld hello.o -o hello

./hello

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系統呼叫特殊。

求一組數最大值的匯程式設計序:

.section .data

data_items:

.long 3,67,34,222,45,75,54,34,44,33,22,11,66,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_items(, %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,67,34,222,45,75,54,34,44,33,22,11,66,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系統呼叫來退出程式。

最簡單的AT T匯程式設計序

把這個程式儲存成檔案start.s 匯程式設計序通常以.s作為檔名字尾 用彙編器 assembler as把匯程式設計序中的助記符翻譯成機器指令,生成目標檔案start.o as start.s o start.o 然後用鏈結器 linker,或link editor ld把目標檔案start.o鏈...

對乙個簡單匯程式設計序分析

程式 assume cs codesg codesg segment mov ax,0123h mov bx,0456h add ax,bx add ax,ax mov ax,4c00h int 21h codesg ends end偽指令 偽指令是寫給編譯器看的,cpu不會執行,在源程式中,包括兩...

匯程式設計序 退出

作為第乙個匯程式設計序,本程式除了退出以外,並沒有執行其他的功能。目的 退出並向linux核心返回乙個狀態碼的簡單程式 輸入 無 輸出 返回乙個狀態碼.在執行程式後可通過輸入echo 來讀取狀態碼 變數 eax儲存系統呼叫號 ebx儲存返回狀態 section data section text g...