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的整数)的分配本地变量的方式

扫描二维码关注公众号,回复: 1122591 查看本文章

#PS:请尊重原创,不喜勿喷

#PS:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复

猜你喜欢

转载自blog.csdn.net/u011728480/article/details/79092194
今日推荐