VxWorks函数栈帧剖析
VxWorks任务内存地址空间布局
VxWorks系统中,所有Tasks(包括系统任务和用户任务)都工作在特权模式,共享所有内存。A任务可以直接访问B任务的内存地址空间而不会触发任何“异常”。下图为单个任务的内存地址空间布局,任务ID(Task ID)分割任务控制块(TCB)和任务栈,任务栈中压栈函数栈帧,从高地址向低地址增长。
首先,我们查看任务列表
-> i NAME ENTRY TID PRI STATUS PC SP ERRNO DELAY ---------- ------------ -------- --- ---------- -------- -------- ------- ----- ^@tExcTask excTask 87df85b0 0 PEND 8178f244 87df8490 3006b 0 tLogTask logTask 87df5a00 0 PEND 8178f244 87df58e8 0 0 tCPUIdlCaptmeasureCPUId 57beefb0 1 DELAY 8173ad78 57beef28 0 17 tShell shell 878aea90 1 READY 8173c008 878ae6a8 0 0 active_shelshell_task 57b5ac40 2 PEND 80168bec 57b5abc8 0 0 tWdbTask wdbTask 878d1700 3 PEND 80168bec 878d1470 3d0002 0 Timer 8175f730 57bed110 5 DELAY 8173ad78 57bed088 0 55 keep_alive 810f90a4 5ee7e900 6 PEND 8178f244 5ee7e1b8 0 0 oind 810f829c 5eeaf260 7 PEND+T 80168bec 5eeaef08 3d0002 30 hind 810f8d64 5ee9eea0 8 PEND 8178f244 5ee9e1d0 3d0002 0 Co00 810f960c 5ee6e630 9 PEND 8178f244 5ee6e4f0 3d0002 0
我们以Timer为例,分析函数栈帧结构
-> ti Timer ^@ NAME ENTRY TID PRI STATUS PC SP ERRNO DELAY ---------- ------------ -------- --- ---------- -------- -------- ------- ----- Timer 8175f730 57bed110 5 DELAY 8173ad78 57bed088 0 22 stack: base 0x57bed110 end 0x57beb110 size 8176 high 292 margin 7884 options: 0xc VX_DEALLOC_STACK VX_FP_TASK VxWorks Events -------------- Events Pended on : Not Pended Received Events : 0x0 Options : N/A $0 = 0 t0 = 0 s0 = 0 t8 = 0 at = 0 t1 = 0 s1 = 0 t9 = 0 v0 = 0 t2 = 0 s2 = 0 k0 = 0 v1 = 0 t3 = 0 s3 = 0 k1 = 0 a0 = 0 t4 = 0 s4 = 0 gp = 82d6cba0 a1 = 0 t5 = 0 s5 = 57bed110 sp = 57bed088 a2 = 0 t6 = 0 s6 = 3c s8 = 57bed0d0 a3 = 0 t7 = 0 s7 = 832bb500 ra = 0 divlo = 0 divhi = 0 sr = 1000fc01 pc = 8173ad78 value = 0 = 0x0
从上面可以看出,tLogTask任务的栈空间为0x57bed110~0x57beb110地址段,栈顶指针SP值为0x57bed088。
继续查看函数调用栈
-> tt Timer ^@80169604 vxTaskEntry +c : TmrStopTimer (0, 0, 0, 0) 8175f768 TmrStopTimer +410: taskDelay (eeeeeeee, eeeeeeee, eeeeeeee, eeeeeeee) value = 0 = 0x0
此处,分析TmrStopTimer函数的栈帧
-> l TmrStopTimer,20 ^@ TmrStopTimer: 0x8175f358 27bdffd8 addiu sp,sp,65496(-40) // sp = sp - 40 0x8175f35c afbf0024 sw ra,36(sp) // [sp + 36] = ra 0x8175f360 afbe0020 sw s8,32(sp) // [sp + 32] = s8 0x8175f364 03a0f025 move s8,sp // s8 = sp 0x8175f368 afc40028 sw a0,40(s8) // [s8 + 40] = a0 第一个参数 0x8175f36c afc5002c sw a1,44(s8) // [s8 + 44] = a1 第二个参数 0x8175f370 8fc20028 lw v0,40(s8) // v0 = [s8 + 40] 0x8175f374 afc20014 sw v0,20(s8) // [s8 + 20] = v0 0x8175f378 afc00010 sw zero,16(s8) // [s8 + 16] = 0 0x8175f37c 3c0282ca lui v0,0x82ca 0x8175f380 8c422088 lw v0,8328(v0) 0x8175f384 10400008 beqz v0,0x8175f3a8 0x8175f388 00000000 nop 0x8175f38c 3c0482ca lui a0,0x82ca 0x8175f390 8c842088 lw a0,8328(a0) 0x8175f394 2405ffff li a1,65535 0x8175f398 0c05a189 jal semTake 0x8175f39c 00000000 nop 0x8175f3a0 085d7ced j 0x8175f3b4 0x8175f3a4 00000000 nop value = -2122976344 = 0x8175f3a8 = TmrStopTimer + 0x50
第一行指令表示把栈顶指针sp下移40个字节,表示TmrStopTimer函数的栈帧大小为40字节。
如上所示,TmrStopTimer函数栈帧的底部压栈返回地址ra和返回函数的栈顶指针。
对于叶子函数(不调用其他函数的函数),可能不会压栈ra,因为ra寄存器值不会被改写。此外,编译器的优化选项-O指定的优化级别,也会影响栈帧结构。
打印从sp~s8地址空间的栈数据:
-> d 0x57bed088,20,4 ^@57bed080: eeeeeeee eeeeeeee * ....* 57bed090: 57bed098 8175f834 82eaa780 0000001d *W....u.4........* 57bed0a0: eeeeeeee eeeeeeee 0000000f 00000001 *................* 57bed0b0: 0000001d 82eaaff8 eeeeeeee 8175f770 *.............u.p* 57bed0c0: 00000000 00000000 00000000 57bed0d0 *............W...*
可以看到,s8为0x57bed0d0,ra为0。(此处有点诡异,居然是反的,有可能函数汇编代码中移位了sp指针,待商榷)