文章标题

来源:互联网 发布:mac照片上传icloud 编辑:程序博客网 时间:2024/06/05 15:17

抛开JOS不谈,一个函数在调用时,肯定要压入参数给函数体传值,然后要压入函数结束后的下一条指令的地址,以便函数可以正确的返回,其次因为公用一个堆栈所以要压入BP也就是基址寄存器的值,和在函数体中使用到的寄存器的值,以便返回时可以恢复现场。但是这些值压入的顺序和规则目前还是不知道的,需要一些额外的资料。
这里写图片描述
esp的含义是“这个地址以下的空间是未被使用的堆栈控件”,
ebp的含义是“这个地址以下至esp的空间是属于目前所执行函数的堆栈空间”,所以图中saved%ebp和 ret%eip就是属于调用此函数的函数的ebp和eip。
通过阅读汇编代码我们可以发现,一个函数在调用之前,其调用者会将参数压栈(顺序没深究,和编译器有关),也就是压入arg2 和arg1,
然后调用call,call的动作会把ret%eip压栈,同时转到函数体执行,在函数体执行的开头有一段预处理代码,也就是图中的prologue,会将ebp寄存器(call指令不改变ebp的值,此时的ebp还是上一个函数的)内容压栈,
然后将当前esp赋值给ebp,随后进行现场保存的工作,存储在local variables空间里,值得注意的是,在预处理时会一下申请足够的空间,包括保存现场所需空间,局部变量所需空间(这大概也就是标准C的变量声明需要放在函数开头的原因吧,为了方便编译器),调用其它函数所压入变量的空间,换句话说图中arg1,arg2是属于上一个函数的local variables空间,这也就是backtrace不能准确的判断出函数所传参数个数而统一要求打印出5个参数的原因。

int mon_backtrace(int argc,char** argv,struct Trapframe*tf){                   uint32_t *ebp,*eip;   uint32_t   arg0,arg1,arg2,arg3,arg4; ebp=(uint32_t*)read_ebp(); eip=(uint32_t*)ebp[1]; arg0=ebp[2]; arg1=ebp[3]; arg2=ebp[4]; arg3=ebp[5]; arg4=ebp[6];  cprintf("Stackbacktrace:\n"); while(ebp!=0){     cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",ebp,eip ,arg0,arg1,arg2,arg3,arg4); ebp=(uint32_t*)ebp[0];eip=(uint32_t*)ebp[1];arg0=ebp[2];arg1=ebp[3];arg2=ebp[4];arg3=ebp[5];arg4=ebp[6];}return 0; }
int mon_backtrace(int argc,char** argv,struct Trapframe*tf){                  uint32_t *ebp; ebp=(uint32_t*)read_ebp(); cprintf("Stackbacktrace:\n"); while(ebp!=0x0){     cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",ebp,ebp[1],ebp[2],ebp[3],ebp[4],ebp[5],ebp[6]); ebp=(uint32_t*)ebp[0];struct Eipdebuginfo info;debuginfo_eip(ebp[1],&info);cprintf("%s,%d,%.*s+%d\n",info.eip_file,                           info.eip_line,                           info.eip_fn_namelen,                           info.eip_fn_name,                           ebp[1]-info.eip_fn_addr      );           ebp=(uint32_t*)ebp[0];                           }return 0; }
0 0