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


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

PS: 这个只是基于《我自己》的理解,
如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明

  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)

前言


  由于某些工作的需要,我需要掌握X86以及ARM的一些调用规则,让自己可以大致看懂ASM代码。于是,我总结了一下我需要的东西。

  调用约定有啥用?

  对于现在习惯使用高级程序的人来说,这一切都是闲的蛋疼才会去看这些,不止浪费时间,还浪费表情。但对于有需求使用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

  说明:按从右至左的顺序压参数入栈,由调用者把参数弹出栈。

  对子程序分析:其他详见注释。具体栈帧分布图,见下图:

rep_img


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。

  其他分析见下图:

rep_img

特别说明:此图保留区域写错了,对于此编译器来说,应该是4个4bytes*N(N大于0的整数)的分配本地变量的方式





后记


  无

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

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

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

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


文章作者: Sky
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Sky !
  目录