久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

x86、arm,、mips架構(gòu)函數(shù)調(diào)用實(shí)例分析

 TUBOSS 2014-09-11

原文網(wǎng)址:http://nieyong.github.com/wiki_cpu/


在看過(guò)了上面的幾節(jié)之后,在潛意識(shí)中你想記住的東西肯定很多了,。這個(gè)時(shí)候,,你需要靜下心來(lái)休息一下在沉淀一下。

"Now is a good point to take a break to let this information sink in."

下面,,我們就看看C語(yǔ)言撰寫的程序,,在不同的CPU架構(gòu)下,生成的匯編語(yǔ)言是怎么樣的,,各有什么特點(diǎn),,這和前面介紹的各種CPU架構(gòu)的知識(shí)是如何聯(lián)系的。如果你覺(jué)得還不夠,,也很高興一起來(lái)探討一下在不同的CPU架構(gòu)下,,函數(shù)的調(diào)用時(shí)如何實(shí)現(xiàn)的,以及各有什么特點(diǎn),。

反匯編文件

測(cè)試用的C源碼如下,,main.c文件:

  1. #include <stdio.h>  
  2.   
  3. int add(int a,int b)  
  4. {  
  5.     return a+b;  
  6. }  
  7.   
  8.   
  9. int main(int argc,char** argv)  
  10. {  
  11.     int a,b,c;  
  12.   
  13.     a = 1;  
  14.     b = 2;  
  15.   
  16.     c = fn(a,b);  
  17.   
  18.     return c;  
  19. }  

下面為編譯以及反匯編的過(guò)程。當(dāng)然,,ARM和MIPS的編譯和反匯編是使用的交叉編譯工具鏈,。例如在我的平臺(tái)下,對(duì)應(yīng)的命令就是ccarm,,objdumparm和ccmips,,objdumpmips。

#gcc -o main.o -c main.c
#objdump -d main.o>main.a

編譯成x86下面的main.o文件,,注意,,沒(méi)有鏈接,。然后反匯編為main_x86.a如下所示。這里需要指出的是,,下面的匯編語(yǔ)言并不是我們?cè)凇段C(jī)原理》課本上學(xué)習(xí)到的x86的匯編(我們稱為intel匯編),,而叫做AT&T匯編。那么,,什么是AT&T匯編呢,?

在將Unix移植到80386處理器上時(shí),Unix圈內(nèi)人士根據(jù)Unix領(lǐng)域的習(xí)慣和需要而定義了AT&T匯編,。而GNU主要是在Unix領(lǐng)域活動(dòng),,因此GNU開(kāi)發(fā)的各種系統(tǒng)工具繼承了AT&T的386匯編格式,這也是我們使用GNU工具進(jìn)行反匯編的時(shí)候,,看到的匯編語(yǔ)言,。
  1. main.o:     file format elf32-i386  
  2.   
  3.   
  4. Disassembly of section .text:  
  5.   
  6. 00000000 <add>:  
  7.    0:   55                      push   %ebp  
  8.    1:   89 e5                   mov    %esp,%ebp  
  9.    3:   8b 55 0c                mov    0xc(%ebp),%edx  
  10.    6:   8b 45 08                mov    0x8(%ebp),%eax  
  11.    9:   01 d0                   add    %edx,%eax  
  12.    b:   5d                      pop    %ebp  
  13.    c:   c3                      ret      
  14.   
  15. 0000000d <main>:  
  16.    d:   8d 4c 24 04             lea    0x4(%esp),%ecx  
  17.   11:   83 e4 f0                and    $0xfffffff0,%esp  
  18.   14:   ff 71 fc                pushl  -0x4(%ecx)  
  19.   17:   55                      push   %ebp  
  20.   18:   89 e5                   mov    %esp,%ebp  
  21.   1a:   51                      push   %ecx  
  22.   1b:   83 ec 24                sub    $0x24,%esp  
  23.   1e:   c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%ebp)  
  24.   25:   c7 45 f4 02 00 00 00    movl   $0x2,-0xc(%ebp)  
  25.   2c:   8b 45 f4                mov    -0xc(%ebp),%eax  
  26.   2f:   89 44 24 04             mov    %eax,0x4(%esp)  
  27.   33:   8b 45 f0                mov    -0x10(%ebp),%eax  
  28.   36:   89 04 24                mov    %eax,(%esp)  
  29.   39:   e8 fc ff ff ff          call   3a <main+0x2d>  
  30.   3e:   89 45 f8                mov    %eax,-0x8(%ebp)  
  31.   41:   8b 45 f8                mov    -0x8(%ebp),%eax  
  32.   44:   83 c4 24                add    $0x24,%esp  
  33.   47:   59                      pop    %ecx  
  34.   48:   5d                      pop    %ebp  
  35.   49:   8d 61 fc                lea    -0x4(%ecx),%esp  
  36.   4c:   c3                      ret      

編譯成mips下面的main.o文件,沒(méi)有鏈接,。然后反匯編為main_mips.a:

  1. main.o:     file format elf32-bigmips  
  2.   
  3. Disassembly of section .text:  
  4.   
  5. 0000000000000000 <add>:  
  6.    0:   27bdfff8    addiu   $sp,$sp,-8  
  7.    4:   afbe0000    sw  $s8,0($sp)  
  8.    8:   03a0f021    move    $s8,$sp  
  9.    c:   afc40008    sw  $a0,8($s8)  
  10.   10:   afc5000c    sw  $a1,12($s8)  
  11.   14:   8fc20008    lw  $v0,8($s8)  
  12.   18:   8fc3000c    lw  $v1,12($s8)  
  13.   1c:   00000000    nop  
  14.   20:   00431021    addu    $v0,$v0,$v1  
  15.   24:   03c0e821    move    $sp,$s8  
  16.   28:   8fbe0000    lw  $s8,0($sp)  
  17.   2c:   03e00008    jr  $ra  
  18.   30:   27bd0008    addiu   $sp,$sp,8  
  19.   
  20. 0000000000000034 <main>:  
  21.   34:   27bdffd8    addiu   $sp,$sp,-40  
  22.   38:   afbf0024    sw  $ra,36($sp)  
  23.   3c:   afbe0020    sw  $s8,32($sp)  
  24.   40:   03a0f021    move    $s8,$sp  
  25.   44:   afc40028    sw  $a0,40($s8)  
  26.   48:   afc5002c    sw  $a1,44($s8)  
  27.   4c:   24020001    li  $v0,1  
  28.   50:   afc20010    sw  $v0,16($s8)  
  29.   54:   24020002    li  $v0,2  
  30.   58:   afc20014    sw  $v0,20($s8)  
  31.   5c:   8fc40010    lw  $a0,16($s8)  
  32.   60:   8fc50014    lw  $a1,20($s8)  
  33.   64:   0c000000    jal 0 <add>  
  34.   68:   00000000    nop  
  35.   6c:   afc20018    sw  $v0,24($s8)  
  36.   70:   8fc20018    lw  $v0,24($s8)  
  37.   74:   03c0e821    move    $sp,$s8  
  38.   78:   8fbf0024    lw  $ra,36($sp)  
  39.   7c:   8fbe0020    lw  $s8,32($sp)  
  40.   80:   03e00008    jr  $ra  
  41.   84:   27bd0028    addiu   $sp,$sp,40  
  42.     ...  

編譯成arm下的main.o,,沒(méi)有鏈接。然后反匯編為main_arm.a:

  1. main.o:     file format elf32-littlearm  
  2.   
  3. Disassembly of section .text:  
  4.   
  5. 00000000 <add>:  
  6.    0:   e1a0c00d    mov r12, sp  
  7.    4:   e92dd800    stmdb   sp!, {r11, r12, lr, pc}  
  8.    8:   e24cb004    sub r11, r12, #4    ; 0x4  
  9.    c:   e24dd008    sub sp, sp, #8  ; 0x8  
  10.   10:   e50b0010    str r0, [r11, -#16]  
  11.   14:   e50b1014    str r1, [r11, -#20]  
  12.   18:   e51b3010    ldr r3, [r11, -#16]  
  13.   1c:   e51b2014    ldr r2, [r11, -#20]  
  14.   20:   e0833002    add r3, r3, r2  
  15.   24:   e1a00003    mov r0, r3  
  16.   28:   ea000009    b   2c <add+0x2c>  
  17.   2c:   e91ba800    ldmdb   r11, {r11, sp, pc}  
  18.   
  19. 00000030 <main>:  
  20.   30:   e1a0c00d    mov r12, sp  
  21.   34:   e92dd800    stmdb   sp!, {r11, r12, lr, pc}  
  22.   38:   e24cb004    sub r11, r12, #4    ; 0x4  
  23.   3c:   e24dd014    sub sp, sp, #20 ; 0x14  
  24.   40:   e50b0010    str r0, [r11, -#16]  
  25.   44:   e50b1014    str r1, [r11, -#20]  
  26.   48:   e3a03001    mov r3, #1  ; 0x1  
  27.   4c:   e50b3018    str r3, [r11, -#24]  
  28.   50:   e3a03002    mov r3, #2  ; 0x2  
  29.   54:   e50b301c    str r3, [r11, -#28]  
  30.   58:   e51b0018    ldr r0, [r11, -#24]  
  31.   5c:   e51b101c    ldr r1, [r11, -#28]  
  32.   60:   ebfffffe    bl  0 <add>  
  33.   64:   e1a03000    mov r3, r0  
  34.   68:   e50b3020    str r3, [r11, -#32]  
  35.   6c:   e51b3020    ldr r3, [r11, -#32]  
  36.   70:   e1a00003    mov r0, r3  
  37.   74:   ea00001c    b   78 <main+0x48>  
  38.   78:   e91ba800    ldmdb   r11, {r11, sp, pc}  

不同CPU架構(gòu)下匯編語(yǔ)言的特點(diǎn)

代碼長(zhǎng)度

首先,,我們看同一個(gè)C語(yǔ)言程序,,在不同的處理器下,代碼長(zhǎng)度,。代碼長(zhǎng)度為反匯編文件的第一列,,可以看到,在x86下是0x4c+1個(gè)字節(jié),,在MIPS下是0x84+4個(gè)字節(jié),,在ARM下是0x78+4個(gè)字節(jié)。為什么這樣呢,?

首先看的是CISC和RISC的區(qū)別 CISC架構(gòu)下,,指令的長(zhǎng)度不是固定的,而RISC為了實(shí)現(xiàn)流水線,,每條指令的長(zhǎng)度都是固定的,。看到反匯編文件的第二列,,x86的指令中,,最短為一個(gè)字節(jié),例如push,,pop,,ret等指令,最長(zhǎng)為7個(gè)字節(jié),,例如movl指令,。而對(duì)于ARM和MIPS,,所有的指令長(zhǎng)度都固定為4個(gè)字節(jié)。這也是ARM和MIPS的機(jī)器代碼長(zhǎng)度要明顯比x86長(zhǎng)的原因,。

另外一個(gè)原因,,就是CISC可能為某個(gè)特殊操作實(shí)現(xiàn)了一條指令,而RISC處理器則需要用多條指令組合來(lái)完成該操作,。最明顯的就是出入棧操作。

然后我們看MIPS和ARM的區(qū)別 ARM的代碼長(zhǎng)度比MIPS代碼長(zhǎng)度稍稍小,,這也驗(yàn)證了人們常說(shuō)的MIPS是純粹的RISC架構(gòu),,而ARM則在RISC的基礎(chǔ)上吸收了CISC中的某些優(yōu)點(diǎn)。我們還是要看看,,ARM是因?yàn)槲樟四男〤ISC的特點(diǎn),,才做到代碼長(zhǎng)度的減少的呢?最明顯的是出入棧,,在ARM中實(shí)現(xiàn)了多寄存器load/store指令,,請(qǐng)注意main_arm.a文件的0x4,0x2c,,0x34,,0x78行的stmdb和ldmdb指令。那么,,這和CISC有什么關(guān)系呢,?仔細(xì)想想,這就是CISC中的為了某個(gè)特殊的操作而實(shí)現(xiàn)一條指令的思想,。另外,,多寄存器的load/store指令破壞了RISC中指令的執(zhí)行周期必須是單周期的規(guī)定,這一點(diǎn),,也是人們指責(zé)ARM不是純粹的RISC架構(gòu)的證據(jù)之一,。

另外還有就是ARM實(shí)現(xiàn)了條件標(biāo)志,實(shí)現(xiàn)條件執(zhí)行,,這樣可以減少分支指令的數(shù)目,,提高代碼的密度。不過(guò)在我們這個(gè)實(shí)例中沒(méi)有這方面的應(yīng)用,。

出棧入棧

入棧指將CPU通用寄存器的值放入棧(存儲(chǔ)器)中保存起來(lái),。出棧則是指將棧中的值恢復(fù)到CPU中相應(yīng)通用寄存器。

在x86下面,,有專門的出棧和入棧指令pop和push,。出棧和入棧在棧中的位置是當(dāng)前堆棧指針sp所指向的位置。在MIPS和ARM下面,,沒(méi)有專門的指令,,所以入棧和出棧首先使用的是load/store指令將數(shù)據(jù)放入堆?;蛘邚棾龆褩#缓笫褂胊dd/stub指令修改堆棧指針的值,。比較特殊的是ARM有特殊的多寄存器load/store指令來(lái)快速的完成出入棧,。由此可見(jiàn),在MIPS和ARM下面,,出入棧都是使用幀指針或者堆棧指針的相對(duì)位置,,在執(zhí)行之后,并不會(huì)影響堆棧指針sp的值,。

棧的生長(zhǎng) 棧的生長(zhǎng)指堆棧指針sp的變化,。例如,在函數(shù)調(diào)用的時(shí)候,,需要一次性的分配被調(diào)用函數(shù)的??臻g大小。在x86,,ARM,,MIPS下都是使用的對(duì)堆棧指針寄存器直接加減的辦法。而且,,在MIPS和ARM下,,也只有這種辦法可以改變堆棧指針SP的值。而在x86下,,除了直接加減的辦法之外,,出入棧操作指令pop和push還可以改變sp的值。

不同CPU架構(gòu)下函數(shù)調(diào)用的特點(diǎn)

C語(yǔ)言函數(shù)的調(diào)用,,請(qǐng)參考C語(yǔ)言-Stack的相關(guān)內(nèi)容,。涉及堆棧幀(stack frame),活動(dòng)記錄(active record),,調(diào)用慣例(call convention)等相關(guān)概念,。建議參考《程序員的自我修養(yǎng)-鏈接、裝載與庫(kù)》的第10.2節(jié)-棧和調(diào)用慣例,。

毫無(wú)疑問(wèn),,這里都是使用的C語(yǔ)言的默認(rèn)調(diào)用慣例cdecl。cdecl調(diào)用管理的特點(diǎn)如下:

  • 調(diào)用的出棧方為函數(shù)調(diào)用方,;
  • 參數(shù)的傳遞為從右向左入棧,;
  • 名字修飾為下劃線+函數(shù)名;

首先我們給出一個(gè)cdecl調(diào)用慣例的模型,,不針對(duì)任何處理器架構(gòu),,然后我們?cè)倏纯床煌奶幚砥骷軜?gòu)下cdecl調(diào)用慣例有什么樣的特點(diǎn)。下圖為cdecl調(diào)用慣例的一般情況示意圖,。

CPU體系架構(gòu)-image/cdel_call_convention.png

下面分析cdecl調(diào)用慣例中,,調(diào)用函數(shù)和被調(diào)用函數(shù)分別負(fù)責(zé)活動(dòng)記錄(堆幀棧)中的什么操作呢,?首先是被調(diào)用函數(shù),在函數(shù)的入口,,需要做以下操作:

  • 需要將返回地址入棧,,例如MIPS下的 sw \(ra,28(\)sp) 一句,就是將函數(shù)的返回地址入棧,。對(duì)于x86架構(gòu),,由于call命令會(huì)自動(dòng)將返回地址入棧,所以在函數(shù)入口處沒(méi)有返回地址入棧的指令,;
  • 需要將調(diào)用函數(shù)的幀指針入棧,,然后設(shè)置本身自己函數(shù)的幀指針。在x86下,,是 push %ebp;mov %esp,%ebp兩句,而在MIPS下,,是sw \(s8,24(\)sp);move \(s8,\)sp;兩句,。這里也可以看出x86的擁有專門入棧指令的特點(diǎn)push的特點(diǎn);
  • 調(diào)整堆棧指針的大小,,升棧到函數(shù)需要的堆棧大小,。

上面是被調(diào)用函數(shù)需要負(fù)責(zé)的事情,那么調(diào)用函數(shù)需要負(fù)責(zé)什么呢,?主要有一下幾點(diǎn):

  • 準(zhǔn)備好函數(shù)調(diào)用參數(shù),;
  • 調(diào)用指令call跳轉(zhuǎn)到被調(diào)用函數(shù)。

X86的函數(shù)調(diào)用

這個(gè)調(diào)用中沒(méi)有調(diào)整堆棧指針的操作,,因?yàn)椴恍枰玫筋~外的堆??臻g。所以,,只有上面提到的第一點(diǎn)和第二點(diǎn),。

  1. push   %ebp  
  2. mov    %esp,%ebp  
  3. ......  
  4. pop    %ebp  
  5. ret      

MIPS的函數(shù)調(diào)用

MIPS的堆棧,在被調(diào)用函數(shù)中有8bytes的升棧操作,,下面的ARM中也是一樣的,。

  1. addiu   $sp,$sp,-8  
  2. sw  $s8,0($sp)  
  3. move    $s8,$sp  
  4. ......  
  5. move    $sp,$s8  
  6. lw  $s8,0($sp)  
  7. jr  $ra  
  8. addiu   $sp,$sp,8  

ARM的函數(shù)調(diào)用

相比于MIPS架構(gòu),一條stmdb和ldmdb就可以完成多個(gè)寄存器的存儲(chǔ)(store)和加載(load),。

  1. mov r12, sp  
  2. stmdb   sp!, {r11, r12, lr, pc}  
  3. sub r11, r12, #4    ; 0x4  
  4. sub sp, sp, #8  ; 0x8  
  5. ......  
  6. b   2c <add+0x2c>  
  7. ldmdb   r11, {r11, sp, pc}  




    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多