国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

linux下匯編語言開發總結

2024-06-28 13:25:21
字體:
來源:轉載
供稿:網友
linux下匯編語言開發總結

匯編語言是直接對應系統指令集的低級語言,在語言越來越抽象的今天,匯編語言并不像高級語言那樣使用廣泛,僅僅在驅動程序,嵌入式系統等對性能要求苛刻的領域才能見到它們的身影。但是這并不表示匯編語言就已經沒有用武之地了,通過閱讀匯編代碼,有助于我們理解編譯器的優化能力,并分析代碼中隱含的低效率,所以能夠閱讀和理解匯編代碼也是一項很重要的技能。因為我平時都是在linux環境下工作的,這篇文章就講講linux下的匯編語言。

一、匯編語法風格

匯編語言分為intel風格和AT&T風格,前者被Microsoft Windows/Visual C++采用,Linux下,基本采用的是AT&T風格匯編,兩者語法有很多不同的地方。

1. 寄存器訪問格式不同。在 AT&T 匯編格式中,寄存器名要加上 '%' 作為前綴;而在 Intel 匯編格式中,寄存器名不需要加前綴。例如:

AT&T

Intel

pushl %eax

push eax

2. 立即數表示不同。在 AT&T 匯編格式中,用 '$' 前綴表示一個立即操作數;而在 Intel 匯編格式中,立即數的表示不用帶任何前綴。例如:

AT&T

Intel

pushl $1

push 1

3. 操作數順序不同。在 Intel 匯編格式中,目標操作數在源操作數的左邊;而在 AT&T 匯編格式中,目標操作數在源操作數的右邊。例如:

AT&T

Intel

addl $1, %eax

add eax, 1

4. 字長表示不同。在 AT&T 匯編格式中,操作數的字長由操作符的最后一個字母決定,后綴'b'、'w'、'l'分別表示操作數為byte、Word和long;而在 Intel 匯編格式中,操作數的字長是用 "byte ptr" 和 "word ptr" 等前綴來表示的。例如:

AT&T

Intel

movb val, %eax

mov al, byte ptr val

5. 尋址方式表示不同。在 AT&T 匯編格式中,內存操作數的尋址方式是

section:disp(base, index, scale)

而在 Intel 匯編格式中,內存操作數的尋址方式為:

section:[base + index*scale + disp]

由于 Linux 工作在保護模式下,用的是 32 位線性地址,所以在計算地址時不用考慮段基址和偏移量,而是采用如下的地址計算方法:

disp + base + index * scale

由此分為以下幾種尋址方式:

Intel

AT&T

內存直接尋址

seg_reg: [base + index * scale + immed32]

seg_reg: immed32 (base, index, scale)

寄存器間接尋址

[reg]

(%reg)

寄存器變址尋址

[reg + _x]

_x(%reg)

立即數變址尋址

[reg + 1]

1(%reg)

整數數組尋址

[eax*4 + array]

_array (,%eax, 4)

二、IA32寄存器

1.通用寄存器

顧名思義,通用寄存器是那些你可以根據自己的意愿使用的寄存器,但有些也有特殊作用,IA32處理器包括8個通用寄存器,分為3組

1) 數據寄存器

EAX 累加寄存器,常用于運算;在乘除等指令中指定用來存放操作數,另外,所有的I/O指令都使用這一寄存器與外界設備傳送數據。

EBX 基址寄存器,常用于地址索引

ECX 計數寄存器,常用于計數;常用于保存計算值,如在移位指令,循環(loop)和串處理指令中用作隱含的計數器.EDX 數據寄存器,常用于數據傳遞。

2) 變址寄存器

ESI 源地址指針

EDI 目的地址指針

3) 指針寄存器

EBP為基址指針(Base Pointer)寄存器,存儲當前棧幀的底部地址。

ESP為堆棧指針(Stack Pointer)寄存器,一直記錄棧頂位置,不可直接訪問,push時ESP減小,pop時增大。

2. 指令指針寄存器

Eip 保存了下一條要執行的指令的地址, 每執行完一條指令EIP都會增加當前指令長度的位移,指向下一條指令。用戶不可直接修改EIP的值,但jmp、call和ret等指令也會改變EIP的值,jmp將EIP修改為目的指令地址,call修改EIP為被調函數第一條指令地址,ret從棧中取出(pop)返回地址存入EIP。

三、函數調用過程

函數調用時的具體步驟如下:

1. 調用函數將被調用函數參數入棧,入棧順序由調用約定規定,包括cdecl,stdcall,fastcall,naked call等,c編譯器默認使用cdecl約定,參數從右往座入棧。

2. 執行call命令。

call命令做了兩件事情,一是將EIP寄存器內的值壓入棧中,稱為返回地址,函數完成后還要到這個地址繼續執行程序。然后將被調用函數第一條指令地址存入EIP中,由此進入被調函數。

3. 被調函數開始執行,先準備當前棧幀的環境,分為3步

pushl %ebp 保存調用函數的基址到棧中,

movl %esp, %ebp 設置EBP為當前被調用函數的基址指針,即當前棧頂

subl $xx, %esp 為當前函數分配xx字節??臻g用于存儲局部變量

4. 執行被調函數主體

5. 被調函數結束返回,恢復現場,第3步的逆操作,由leave和ret兩條指令完成,

leave 主要恢復??臻g,相當于

movl %ebp, %esp 釋放被調函數棧空間

popl %ebp 恢復ebp為調用函數基址

ret 與call指令對應,等于pop %EIP,

6. 返回到調用函數,從下一條語句繼續執行

我們來看兩個具體例子,第一個求數組和,

int ArraySum(int *array, int n){  int t = 0;  for(int i=0; i<n; ++i) t += array[i];  return t;}int main() {  int a[5] = {1, 2, 3, 4, 5 };  int sum = ArraySum(a, 5);  return sum;}

編譯成匯編代碼

gcc -std=c99 -S -o sum.s sum.c

gcc加入了很多匯編器和連接器用到的指令,與我們討論的內容無關,簡化匯編代碼如下:

ArraySum:    pushl    %ebp    movl    %esp, %ebp       subl    $16, %esp  //分配16字節??臻g    movl    $0, -8(%ebp)  //初始化t    movl    $0, -4(%ebp)  //初始化i    jmp    .L2.L3:    movl    -4(%ebp), %eax    sall    $2, %eax  //i<<2, 即i*4, 一個int占4字節    addl    8(%ebp), %eax  //得到array[i]地址,array+i*4    movl    (%eax), %eax   //array[i]    addl    %eax, -8(%ebp) //t+=array[i]    addl    $1, -4(%ebp).L2:    movl    -4(%ebp), %eax       cmpl    12(%ebp), %eax  //比較i<n    jl    .L3    movl    -8(%ebp), %eax //return t; 默認eax存函數返回值    leave    retmain:.LFB1:    pushl    %ebp    movl    %esp, %ebp    subl    $40, %esp           movl    $1, -24(%ebp) //初始化a[0]    movl    $2, -20(%ebp) //初始化a[1]    movl    $3, -16(%ebp) //初始化a[2]    movl    $4, -12(%ebp) //初始化a[3]    movl    $5, -8(%ebp)   //初始化a[4]    movl    $5, 4(%esp)    //5作為第二個參數傳給 ArraySum    leal    -24(%ebp), %eax  //leal產生數組a的地址    movl    %eax, (%esp)   //作為第一個參數傳給ArraySum    call    ArraySum    movl    %eax, -4(%ebp)  //返回值傳給sum    movl    -4(%ebp), %eax  //return sum    leave    ret

棧變化過程如下:

執行call指令前 執行call指令后

從圖中可以看出

1. 數組連續排列,用move指令逐個賦值,讀取數組元素方法是,用leal得到數組首地址,再計算偏移量

2. 參數從右往左入棧

3. gcc為了保證數據是嚴格對齊的,分配的空間大于使用的空間,有部分空間是浪費的

下面這個例子說明了struct結構的實現方法,

struct Point{  int x;  int y;};void PointInit(struct Point *p, int x, int y){  p->x = x;  p->y = y;}int main() {  struct Point p;  int x = 10;  int y = 20;  PointInit(&p, x, y);  return 0;}

編譯成匯編代碼,簡化如下:

PointInit:    pushl    %ebp    movl    %esp, %ebp    movl    8(%ebp), %eax    //p的地址    movl    12(%ebp), %edx  //x    movl    %edx, (%eax)      //p->x=x    movl    8(%ebp), %eax    movl    16(%ebp), %edx  //y    movl    %edx, 4(%eax)    //p->y=y    popl    %ebp    retmain:    pushl    %ebp    movl    %esp, %ebp    subl    $28, %esp    movl    $10, -8(%ebp)  //x=10    movl    $20, -4(%ebp)  y=20    movl    -4(%ebp), %eax    movl    %eax, 8(%esp)    movl    -8(%ebp), %eax    movl    %eax, 4(%esp)    leal    -16(%ebp), %eax  //取p地址&p    movl    %eax, (%esp)    call    PointInit    movl    $0, %eax    leave    ret

棧圖就不畫了,可以清楚地看出struct跟數組類似,連續排列,通過相對位移訪問struct的成員,p->y與*(p+sizeof(p->x))有一樣的效果。

、disassembleobjdump

在linux下有兩個跟匯編有重要關系的命令,一個是objdump,另一個是gdb中的disassemble。

objdump幫助我們從可執行文件中反匯編出匯編代碼,從而逆向分析工程。

objdump -d sum

部分匯編代碼如下

080483b4 <ArraySum>: 80483b4:    55                       push   %ebp 80483b5:    89 e5                    mov    %esp,%ebp 80483b7:    83 ec 10                 sub    $0x10,%esp 80483ba:    c7 45 f8 00 00 00 00     movl   $0x0,-0x8(%ebp) 80483c1:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp) 80483c8:    eb 12                    jmp    80483dc <ArraySum+0x28> 80483ca:    8b 45 fc                 mov    -0x4(%ebp),%eax 80483cd:    c1 e0 02                 shl    $0x2,%eax 80483d0:    03 45 08                 add    0x8(%ebp),%eax 80483d3:    8b 00                    mov    (%eax),%eax 80483d5:    01 45 f8                 add    %eax,-0x8(%ebp) 80483d8:    83 45 fc 01              addl   $0x1,-0x4(%ebp) 80483dc:    8b 45 fc                 mov    -0x4(%ebp),%eax 80483df:    3b 45 0c                 cmp    0xc(%ebp),%eax 80483e2:    7c e6                    jl     80483ca <ArraySum+0x16> 80483e4:    8b 45 f8                 mov    -0x8(%ebp),%eax 80483e7:    c9                       leave   80483e8:    c3                       ret

disassemble可以顯示調試程序的匯編代碼,用法如下

disas 反匯編當前函數

disas sum 反匯編sum函數

disas 0x801234 反匯編位于地址 0x801234附近的函數

disas 0x801234 0x802234 返匯編指定范圍內函數

reference:

http://zh.wikipedia.org/wiki/%E6%B1%87%E7%BC%96

http://www.ibm.com/developerworks/cn/linux/l-assembly/


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 彰化市| 西林县| 正阳县| 卓尼县| 泗水县| 凤庆县| 论坛| 沾益县| 龙川县| 鄂尔多斯市| 兴国县| 贵定县| 且末县| 朔州市| 康保县| 东丽区| 黎城县| 嵩明县| 昌都县| 武冈市| 铁力市| 小金县| 宁波市| 精河县| 农安县| 巴中市| 徐水县| 资兴市| 金阳县| 武山县| 辽阳县| 武宁县| 常德市| 常州市| 辰溪县| 航空| 三明市| 油尖旺区| 大连市| 奉贤区| 师宗县|