您的位置:首页 > 编程语言 > PHP开发

x86 常见调用约定(cdecl,fastcall,stdcall) & x86和ARM调用约定的栈帧分析 & ARM ATPCS(ARM-THUMB procedure call standard)

2018-01-18 11:29 555 查看
#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:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: