1. 程式人生 > >x86 常見呼叫約定(cdecl,fastcall,stdcall) & x86和ARM呼叫約定的棧幀分析 & ARM ATPCS(ARM-THUMB procedure call standard)

x86 常見呼叫約定(cdecl,fastcall,stdcall) & x86和ARM呼叫約定的棧幀分析 & ARM ATPCS(ARM-THUMB procedure call standard)

#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是由於有44byte的區域性變數,但是如果不是4個區域性變數,此版本的編譯器是按照16byte*N(N取大於0的整數)來分配的區域性棧。列如:34byte變數,區域性棧大小是16bytes,54byte變數,區域性棧大小為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:要轉載請註明出處,本人版權所有.

有問題請留言,看到後我會第一時間回覆