x86 常見呼叫約定(cdecl,fastcall,stdcall) & x86和ARM呼叫約定的棧幀分析 & ARM ATPCS(ARM-THUMB procedure call standard)
阿新 • • 發佈:2019-01-27
#PS:要轉載請註明出處,本人版權所有
#PS:這個只是 《 我自己 》理解,如果和你的
#原則相沖突,請諒解,勿噴
由於某些工作的需要,我需要掌握X86以及ARM的一些呼叫規則,讓自己可以大致看懂ASM程式碼。於是,我總結了一下我需要的東西。
環境:
X86:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)
ARM:gcc version 4.8.3 20131202 (prerelease) (Hisilicon_v400)
呼叫約定有啥用?
對於現在習慣使用高階程式的人來說,這一切都是閒的蛋疼才會去看這些,不止浪費時間,還浪費表情。但對於有需求使用ASM+C或者C艹的混合程式設計或者純ASM程式設計的時候,這就得注意這些了。因為這代表著你的目標能否成功的問題。
x86 常見呼叫以及對應的棧幀分析
cdecl用在C/C++,MFC的預設方式, 可變引數
//cdecl
extern "C"
int __attribute__((cdecl)) Func2(int a, int b, int c, int d, int e, int f){
int aa;
int bb;
int cc;
int dd;
aa = bb = cc= dd = a;
return 0;
}
@調用子程式過程
pushl $6
pushl $5
pushl $4
pushl $3
pushl $2
pushl $1
call Func2
@call,eip入棧,跳轉到子程式
addl $24, %esp
@esp-24,清空臨時棧
@子程式過程
.LFE1021:
.size Func1, .-Func1
.globl Func2
.type Func2, @function
Func2:
.LFB1022:
.cfi_startproc
pushl %ebp
@ebp入棧
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
@esp賦值給ebp
.cfi_def_cfa_register 5
subl $16, %esp
@注意,雖然上面esp減16是由於有4個4byte的區域性變數,但是如果不是4個區域性變數,此版本的編譯器是按照16byte*N(N取大於0的整數)來分配的區域性棧。列如:3個4byte變數,區域性棧大小是16bytes,5個4byte變數,區域性棧大小為32bytes,其他類似方式分配,不要看不懂為啥多分配了,或者少分配了。
movl 8(%ebp), %eax
@a 賦值給eax
movl %eax, -16(%ebp)
@ eax 賦值給dd
movl -16(%ebp), %eax
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl $0, %eax
@返回值放在eax
leave
@leave = mov ebp,esp 以及 pop ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
@pop eip
.cfi_endproc
說明:按從右至左的順序壓引數入棧,由呼叫者把引數彈出棧。
對子程式分析:其他詳見註釋。具體棧幀分佈圖,見下圖:
stdcall,Win API
extern "C"
int __attribute__((stdcall)) Func3(int a, int b, int c, int d, int e, int f){
int aa;
int bb;
int cc;
aa = bb = cc;
return 0;
}
pushl $6
pushl $5
pushl $4
pushl $3
pushl $2
pushl $1
call Func3
.LFE1022:
.size Func2, .-Func2
.globl Func3
.type Func3, @function
Func3:
.LFB1023:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl -12(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret $24
@pop eip , subl 24,esp
.cfi_endproc
說明:按從右至左的順序壓引數入棧,由被呼叫者從棧中彈出引數。
其他參考見上文。
fastcall,要求速度快
extern "C"
int __attribute__((fastcall)) Func4(int a, int b, int c, int d, int e, int f){
int aa;
int bb;
int cc;
aa = bb = cc;
return 0;
}
pushl $6
pushl $5
pushl $4
pushl $3
movl $2, %edx
movl $1, %ecx
call Func4
.LFE1023:
.size Func3, .-Func3
.globl Func4
.type Func4, @function
Func4:
.LFB1024:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl %ecx, -20(%ebp)
movl %edx, -24(%ebp)
movl -12(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret $16
.cfi_endproc
說明:按從右至左的順序壓引數入棧,引數1,引數2通過ecx,edx傳遞,由被呼叫者從棧中彈出引數。
其他參考見上文。
ARM ATPCS
extern "C"
int Func1(int a, int b, int c, int d, int e, int f){
int aa;
int bb;
int cc;
int dd;
int ee;
aa = bb = cc= dd = ee = a;
return 0;
}
mov r3, #5
str r3, [sp]
mov r3, #6
str r3, [sp, #4]
mov r0, #1
mov r1, #2
mov r2, #3
mov r3, #4
bl Func1
.text
.align 2
.global Func1
.type Func1, %function
Func1:
.fnstart
.LFB971:
@ args = 8, pretend = 0, frame = 40
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
sub sp, sp, #44
str r0, [fp, #-32]
str r1, [fp, #-36]
str r2, [fp, #-40]
str r3, [fp, #-44]
ldr r3, [fp, #-32]
str r3, [fp, #-8]
ldr r3, [fp, #-8]
str r3, [fp, #-12]
ldr r3, [fp, #-12]
str r3, [fp, #-16]
ldr r3, [fp, #-16]
str r3, [fp, #-20]
ldr r3, [fp, #-20]
str r3, [fp, #-24]
mov r3, #0
mov r0, r3
sub sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.cantunwind
.fnend
分析:
r15 PC The Program Counter.
r14 LR The Link Register.
r13 SP The Stack Pointer.
r12 IP The Intra-Procedure-call scratch register. (可簡單的認為暫存SP)
R11 可選,被稱為FP,即frame pointer。
其他分析見下圖:
特別說明:此圖保留區域寫錯了,對於此編譯器來說,應該是4個4bytes*N(N大於0的整數)的分配本地變數的方式
#PS:請尊重原創,不喜勿噴
#PS:要轉載請註明出處,本人版權所有.
有問題請留言,看到後我會第一時間回覆