关于函数调用时,用户栈的变化

hmsuccess 2008-10-06 05:31:34
源码

#include <stdio.h>

#define __va_rounded_size(TYPE) \
(((sizeof(TYPE)+sizeof(int)-1)/sizeof(int))*sizeof(int))

#define va_start(AP,LASTART) \
(AP = ((char* )&LASTART+ __va_rounded_size(LASTART)))


#define va_arg(AP,TYPE) \
(AP += __va_rounded_size(TYPE), \
*((TYPE* )(AP - __va_rounded_size(TYPE))))


void get_display(int first,int second,...)
{
char* args;
va_start(args,second);
second = va_arg(args,int);
while(second>=0)
{
printf("%d\n",second);
second = va_arg(args,int);
}
}

int main()
{
get_display(1,2,3,4,5,6,7);
getchar();
return 0;
}



汇编代码

.file "test_args.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl get_display
.type get_display, @function
get_display:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
leal 12(%ebp), %eax
addl $4, %eax
movl %eax, -4(%ebp)
addl $4, -4(%ebp)
movl -4(%ebp), %eax
subl $4, %eax
movl (%eax), %eax
movl %eax, 12(%ebp)
jmp .L2
.L3:
movl 12(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
addl $4, -4(%ebp)
movl -4(%ebp), %eax
subl $4, %eax
movl (%eax), %eax
movl %eax, 12(%ebp)
.L2:
movl 12(%ebp), %eax
testl %eax, %eax
jns .L3
leave
ret
.size get_display, .-get_display
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $7, 24(%esp)
movl $6, 20(%esp)
movl $5, 16(%esp)
movl $4, 12(%esp)
movl $3, 8(%esp)
movl $2, 4(%esp)
movl $1, (%esp)
call get_display
call getchar
movl $0, %eax
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"
.section .note.GNU-stack,"",@progbits

我的疑问就是main中的汇编代码,谁能比较详细的讲讲,谢谢!!!
...全文
190 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
hmsuccess 2008-10-10
  • 打赏
  • 举报
回复
还有人讲解吗?
hmsuccess 2008-10-06
  • 打赏
  • 举报
回复
谢谢baihacker ,和Demon__Hunter

andl $-16, %esp

我觉得这句有些迷茫,
Demon__Hunter说是对齐用,能不能具体讲讲
还有就是,将返回地址入栈的话,pushl %ecx就可以了
为什么还需要pushl -4(%ecx)

一般情况下,函数调用只是把帧栈入栈,即就是ebp入栈
one_associator 2008-10-06
  • 打赏
  • 举报
回复
不同平台也是不同的

另外,还需要看调用约定,退栈的处理的时候有不同

__stdcall,__fastcall,__cdecl,和c++的thiscall(非关键字)

详见msdn中的calling convention

默认c++调用的时候会把this指针push到ecx里,而普通的c函数没有this
机智的呆呆 2008-10-06
  • 打赏
  • 举报
回复
恩 个人见解~~~高手见笑
机智的呆呆 2008-10-06
  • 打赏
  • 举报
回复

.file "test_args.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl get_display
.type get_display, @function
get_display:
pushl %ebp ;
movl %esp, %ebp;
subl $24, %esp
leal 12(%ebp), %eax
addl $4, %eax
movl %eax, -4(%ebp)
addl $4, -4(%ebp)
movl -4(%ebp), %eax
subl $4, %eax
movl (%eax), %eax
movl %eax, 12(%ebp)
jmp .L2
.L3:
movl 12(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
addl $4, -4(%ebp)
movl -4(%ebp), %eax
subl $4, %eax
movl (%eax), %eax
movl %eax, 12(%ebp)
.L2:
movl 12(%ebp), %eax
testl %eax, %eax
jns .L3
leave
ret
.size get_display, .-get_display
.globl main
.type main, @function
main:
leal 4(%esp), %ecx ;ecx=esp+4
andl $-16, %esp ;esp指针下移16,对齐用
pushl -4(%ecx) ;将函数返回地址入栈,ecx=esp+4;所以esp=ecx-4;
pushl %ebp
movl %esp, %ebp ;跟上句维护了一个栈环境
pushl %ecx ;ecx记录着main函数返回地址相关信息
subl $36, %esp ;栈顶指针下移36 为用户的变量分配足够的空间
movl $7, 24(%esp)
movl $6, 20(%esp)
movl $5, 16(%esp)
movl $4, 12(%esp)
movl $3, 8(%esp)
movl $2, 4(%esp)
movl $1, (%esp) ;相关参数从右到左依次入栈
call get_display ;调用get_display函数,程序跳转到get_display:,从那开始执行指令
call getchar
movl $0, %eax ;main函数的返回值设为0
addl $36, %esp ;栈顶指针恢复先前位置,相当于清栈
popl %ecx ;取栈顶值存入ecx,实际就是取的ecx
popl %ebp
leal -4(%ecx), %esp ;main函数返回地址设为ecx-4
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"
.section .note.GNU-stack,"",@progbits


adriano119 2008-10-06
  • 打赏
  • 举报
回复
bd
baihacker 2008-10-06
  • 打赏
  • 举报
回复

在call的时候call语句的下一条语句的代码的地址会入栈

00411C70 push ebp ;保存主调函数的局部变量栈的最高地址
00411C71 mov ebp,esp ;将当前栈顶作为这个函数的栈上的变量的分配地址
00411C73 sub esp,40h ;40是作为一个空白空间,一般的时候会大于40h这些就是栈上的变量所占的空间,这个时候如果有函数调用就会从已经有变化的esp上开始分配空间
00411C76 push ebx;
00411C77 push esi;
00411C78 push edi;保存必要的值



9: return 0;
00411C79 xor eax,eax;设置返回值
10: }
00411C7B pop edi;
00411C7C pop esi;
00411C7D pop ebx;还原必要的值
00411C7E mov esp,ebp;还原esp
00411C80 pop ebp;还原被调函数的栈变量的最高地址
00411C81 ret;保存在栈上的那个call语句的下一条语句的地址出栈,并放到EIP里,并开始执行那里的代码

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧