700
windows
arm平台函數傳遞參數,反匯編實例分析
測試前,需要了解下sysv的傳參方式:
1、輸入參數通過r0-r3傳遞,多餘的放入堆棧中;返回值放入r0,不夠的話放入{r0,r1}或者{r0,r1,r2,r3},比如:
int foo(int a, int b, int c, int d), 輸入:r0 = a, r1 = b, r2 = c, r3 = d,返回:r0 = 類型為int的retvalue
int *foo(char a, double b, int c, char d), 輸入:r0 = a, r1用於對齊(double 要求8字節對齊), b = {r2, r3},c放在堆棧的sp[0]位置,d放在堆棧的sp[4]位置,這裏的sp是指進入函數時的sp;返回:r0 = 類型為int *的retvalue
2、注意如果返回值是結構體,情況有些特殊:
struct client foo(int a, char b, float c), 輸入:r0 = 一個strcut client *變量,由調用者給出, r1 = a, r2 = b, r3 = c;返回:strcut client *變量,和調用者給的一樣
為了測試arm平台函數參數如何對齊,多餘參數如何傳遞,以及如何返回一個結構體類型的變量,編寫如下代碼:
#include <stdio.h> #include <stdlib.h> typedef struct _Foo{ int a; char b; double c; float d; }Foo; Foo test(int a, char b, double c, float d) { Foo *f = (Foo *)malloc(sizeof(Foo)); f->a = a; f->b = b; f->c = c; f->d = d; return *f; } int main(void) { Foo retvalue; retvalue = test(1,2,3,4); return retvalue.a; }malloc會有內存溢出,這樣寫是為了反匯編更簡單些,編譯時不開優化,使用-marm參數指定使用arm指令集,然後反匯編得到:
00000000 <test>: 0: e92d4810 push {r4, fp, lr} 4: e28db008 add fp, sp, #8 //fp = sp + 8 8: e24dd01c sub sp, sp, #28 //sp = sp -28 c: e50b0018 str r0, [fp, #-24] ; 0xffffffe8 //fp[-24] = r0 = Foo * temp 10: e50b101c str r1, [fp, #-28] ; 0xffffffe4 //fp[-28] = r1 = int a 14: e1a03002 mov r3, r2 //fp[-29] = r2 = char b, r3 not used 18: e54b301d strb r3, [fp, #-29] ; 0xffffffe3 1c: e3a00018 mov r0, #24 20: ebfffffe bl 0 <malloc> //malloc(24) 24: e1a03000 mov r3, r0 //r3 = f 28: e50b3010 str r3, [fp, #-16] //fp[-16] = f 2c: e51b3010 ldr r3, [fp, #-16] //r3 = f 30: e51b201c ldr r2, [fp, #-28] ; 0xffffffe4 //r2 = fp[-28] = int a 34: e5832000 str r2, [r3] //f->a = r2 = a 38: e51b3010 ldr r3, [fp, #-16] 3c: e55b201d ldrb r2, [fp, #-29] ; 0xffffffe3 //r2 = fp[-29] = char b 40: e5c32004 strb r2, [r3, #4] //f->b = r2 = b 44: e51b2010 ldr r2, [fp, #-16] //r2 = f 48: e99b0018 ldmib fp, {r3, r4} //double c = {r3, r4} 4c: e5823008 str r3, [r2, #8] //f[8] = r3 50: e582400c str r4, [r2, #12] //f[12] = r4, f->c = c 54: e51b3010 ldr r3, [fp, #-16] //r3 = f 58: e59b200c ldr r2, [fp, #12] //r2 = float d 5c: e5832010 str r2, [r3, #16] //f->d = float d 60: e51b2018 ldr r2, [fp, #-24] ; 0xffffffe8 //r2 = r0 = Foo *temp 64: e51b3010 ldr r3, [fp, #-16] //r3 = f 68: e1a0c002 mov ip, r2 //ip = r0 = Foo *temp 6c: e1a0e003 mov lr, r3 //lr = f 70: e8be000f ldm lr!, {r0, r1, r2, r3} //拷貝f指向的前16個字節到Foo *temp指向的 74: e8ac000f stmia ip!, {r0, r1, r2, r3} 78: e89e0003 ldm lr, {r0, r1} //拷貝後麵8個字節,加起來=24=sizeof(Foo) 7c: e88c0003 stm ip, {r0, r1} 80: e51b0018 ldr r0, [fp, #-24] ; 0xffffffe8 //返回r0 = Foo *temp 84: e24bd008 sub sp, fp, #8 88: e8bd8810 pop {r4, fp, pc} 0000008c <main>: 8c: e92d4810 push {r4, fp, lr} 90: e28db008 add fp, sp, #8 //fp = sp + 8 94: e24dd02c sub sp, sp, #44 ; 0x2c //sp = sp - 44 98: e24b0024 sub r0, fp, #36 ; 0x24 //r0 = fp - 36 = &retvalue = Foo *temp 9c: e59f3028 ldr r3, [pc, #40] ; cc <main+0x40> a0: e58d3008 str r3, [sp, #8] //float d, 放入堆棧 a4: e3a03000 mov r3, #0 a8: e59f4020 ldr r4, [pc, #32] ; d0 <main+0x44> ac: e88d0018 stm sp, {r3, r4} //double c, 放入堆棧 b0: e3a02002 mov r2, #2 //b = 2 b4: e3a01001 mov r1, #1 //a = 1 b8: ebfffffe bl 0 <test> bc: e51b3024 ldr r3, [fp, #-36] ; 0xffffffdc //r3 = retvalue.a c0: e1a00003 mov r0, r3 //r0 = r3 = retvalue.a,main返回值 c4: e24bd008 sub sp, fp, #8 c8: e8bd8810 pop {r4, fp, pc} cc: 40800000 .word 0x40800000 d0: 40080000 .word 0x40080000
重點分析test函數,它的參數為:
r0: struct Foo *temp,通過main函數傳遞過來的,用於存放struct Foo結構體 r1: int a r2: char b, 即使是char,也獨立占一個寄存器,不與其他參數共用寄存器 r3: for alignment,下一個參數是double,要求對齊為8 c: sp[0-7],多餘的參數放在堆棧上,這裏是double c d: sp[8-11],float dtest函數的堆棧結構為:
下麵,逐行分析test的匯編代碼,來驗證上述內容。
1、進入test函數時,sp指向double c的低四字節,然後push {r4, fp, lr}之後,sp指向保存r4的位置:
0: e92d4810 push {r4, fp, lr}
2、fp=sp+8, sp=sp-28:
4: e28db008 add fp, sp, #8 //fp = sp + 8 8: e24dd01c sub sp, sp, #28 //sp = sp -28
3、把struct Foo *temp,也就是r0,存到fp-24的位置上:
c: e50b0018 str r0, [fp, #-24] ; 0xffffffe8 //fp[-24] = r0 = Foo *temp
4、把int a,也就是r1,存到fp-28的位置上:
10: e50b101c str r1, [fp, #-28] ; 0xffffffe4 //fp[-28] = r1 = int a
5、把char b,也就是r2,存到fp-29的位置上,注意隻放了一個字節,還剩下三個字節沒有使用;注意r3沒有有效值,隻是為了對齊的,所以可以直接覆蓋:
14: e1a03002 mov r3, r2 //fp[-29] = r2 = char b, r3 not used 18: e54b301d strb r3, [fp, #-29] ; 0xffffffe3
6、至此,輸入參數已全部保存在堆棧上(double c, float d, 一開始就在堆棧高地址上)
7、調用malloc(24)函數,因為sizeof(Foo)=24,返回值r0賦值給r3,保存在fp-16位置,也就是變量Foo *f:
1c: e3a00018 mov r0, #24 20: ebfffffe bl 0 <malloc> //malloc(24) 24: e1a03000 mov r3, r0 //r3 = f 28: e50b3010 str r3, [fp, #-16] //fp[-16] = f 2c: e51b3010 ldr r3, [fp, #-16] //r3 = f
8、從fp-28取出int a,保存到f+0位置上,也就是f->a=a:
30: e51b201c ldr r2, [fp, #-28] ; 0xffffffe4 //r2 = fp[-28] = int a 34: e5832000 str r2, [r3] //f->a = r2 = a
9、從fp-29取出char b,保存到f+4位置上,也就是f->b=b:
38: e51b3010 ldr r3, [fp, #-16] 3c: e55b201d ldrb r2, [fp, #-29] ; 0xffffffe3 //r2 = fp[-29] = char b 40: e5c32004 strb r2, [r3, #4] //f->b = r2 = b
10、ldmib fp, {r3, r4},地址先增加4,然後取值保存到r3,地址再增加4,取值保存到r4,地址值不回寫,也就是取出double c,放入r3,r4,然後保存到f+8地址和f+12地址上,也就是f->c=c:
44: e51b2010 ldr r2, [fp, #-16] //r2 = f 48: e99b0018 ldmib fp, {r3, r4} //double c = {r3, r4} 4c: e5823008 str r3, [r2, #8] //f[8] = r3 50: e582400c str r4, [r2, #12] //f[12] = r4, f->c = c
11、取fp+12位置的float d,保存到f+16地址,也就是f->d=d:
54: e51b3010 ldr r3, [fp, #-16] //r3 = f 58: e59b200c ldr r2, [fp, #12] //r2 = float d 5c: e5832010 str r2, [r3, #16] //f->d = float d
12、ip = Foo * temp = [fp - 24] = r0@entry,lr = f = [fp - 16],從f指向的地址取16字節,保存到ip指向的地址,然後再取8字節,保存到ip指向的地址,也就是按值拷貝f指向的結構體到Foo *temp = r0指向的結構體:
60: e51b2018 ldr r2, [fp, #-24] ; 0xffffffe8 //r2 = r0 = Foo *temp 64: e51b3010 ldr r3, [fp, #-16] //r3 = f 68: e1a0c002 mov ip, r2 //ip = r0 = Foo *temp 6c: e1a0e003 mov lr, r3 //lr = f 70: e8be000f ldm lr!, {r0, r1, r2, r3} //拷貝f指向的前16個字節到r0 = Foo *temp指向的 74: e8ac000f stmia ip!, {r0, r1, r2, r3} 78: e89e0003 ldm lr, {r0, r1} //拷貝後麵8個字節,加起來=24=sizeof(Foo) 7c: e88c0003 stm ip, {r0, r1}
13、設置返回值r0,為Foo *temp,返回:
80: e51b0018 ldr r0, [fp, #-24] ; 0xffffffe8 //返回r0 = Foo *temp 84: e24bd008 sub sp, fp, #8 88: e8bd8810 pop {r4, fp, pc}
然後分析main函數
main的堆棧為:
這裏的SP=SP-44也就是test函數堆棧的SP@entry
main的匯編代碼為:
1、進入函數後,push{r4, fp, lr},sp指向存放r4的位置:
8c: e92d4810 push {r4, fp, lr}2、fp=sp+8, sp=sp-44:
90: e28db008 add fp, sp, #8 //fp = sp + 8 94: e24dd02c sub sp, sp, #44 ; 0x2c //sp = sp - 443、為臨時變量Foo retvalue申請內存,&retvalue為r0 = fp-36,也就是傳遞給test函數的那個Foo *temp:
98: e24b0024 sub r0, fp, #36 ; 0x24 //r0 = fp - 36 = &retvalue = Foo *temp4、取fload d的輸入值,放入sp+8位置:
9c: e59f3028 ldr r3, [pc, #40] ; cc <main+0x40> a0: e58d3008 str r3, [sp, #8] //float d, 放入堆棧
5、取double c的輸入值,放入sp+0位置:
a4: e3a03000 mov r3, #0 a8: e59f4020 ldr r4, [pc, #32] ; d0 <main+0x44> ac: e88d0018 stm sp, {r3, r4} //double c, 放入堆棧6、設置r1 = int a = 1, r2 = char b = 2,注意到r0 = Foo * temp = &retvalue,r3用於對齊,double c 和float d已放入堆棧sp0-sp12的位置上,調用test函數:
b0: e3a02002 mov r2, #2 //b = 2 b4: e3a01001 mov r1, #1 //a = 1 b8: ebfffffe bl 0 <test>7、取retvalue.a的值到r3,然後賦值給r0,為函數main的返回值:
bc: e51b3024 ldr r3, [fp, #-36] ; 0xffffffdc //r3 = retvalue.a c0: e1a00003 mov r0, r3 //r0 = r3 = retvalue.a,main返回值
8、main函數返回:
c4: e24bd008 sub sp, fp, #8 c8: e8bd8810 pop {r4, fp, pc} cc: 40800000 .word 0x40800000 d0: 40080000 .word 0x40080000
最後更新:2017-10-26 17:34:29