LOOKING AT AN 860 STACK FRAME UNDER VXWORKS Vic Sperry 10/18/00 GPR1 is the stack frame pointer. Upon entry to a function, this register points to a two-word (8-byte) memory location set up by the calling function. The first word contains the caller's stack linkage pointer. The second word is uninitialized and used by the current function to store the link register (LR). The stack grows towards smaller addresses. The first thing a called function does is allocate area on the stack by decrementing GPR1 and simultaneously saving the new value on the stack using a "store and update" instruction. It then saves LR at 4 past where R1 _used_ to point. Example: Upon entry to a function address value @ addr description -------- ------------ ----------- 03ffffe0 trash 03ffffe4 trash 03ffffe8 trash 03ffffec trash GPR1 = 03fffff0 --> 03fffff0 03fffff8 caller's link pointer 03fffff4 trash 03fffff8 00000000 caller's caller's link ptr (TOS) 03fffffc 00000000 caller's caller's return address After allocating local storage for a one-word (4 bytes) variable address value @ addr description -------- ------------ ----------- 03ffffe0 trash GPR1 = 03ffffe4 --> 03ffffe4 03fffff0 this function's link pointer 03ffffe8 trash (for use by a sub-funcion) 03ffffec this function's variable 03fffff0 03fffff8 caller's link pointer 03fffff4 LR caller's return address 03fffff8 00000000 caller's caller's link ptr (TOS) 03fffffc 00000000 caller's caller's return address ------------------------------------------------------------------------------ UNWINDING A STACK FRAME This is trivial once you understand the above. 1. The current value in GPR1 gives you the current function's stack frame. 2. The current value in PC gives you the current function's executing address. 3. The value at *(GPR1+4) is meaningless. 4. To return to caller and recover his stack frame do this: a. newFramePointer = *(GPR1) b. callersReturnAddress = *(newFramePointer+4) 5. Repeat step 4 until newFramePointer = 0 (normal stack frame) OR newFramePointer < 0x3000 (exception stack frame) ------------------------------------------------------------------------------ THE BIG PICTURE Here's how a task's memory is laid out. Note that the task ID is the same as the stack base. The numbers used are examples. lower addresses --------------- stack end (e.g. 0x2cf99d0) -> task's name as a string (e.g. "tILMI") unused stack space filled with 0xeeeeeeee ... formerly-used old, incoherant stack frames stack space ... current stack frame (e.g. R1 = 0x2cfb3f0) -> 0x02cfb410 (next stack frame) one word of trash (see UNWINDING A STACK FRAME) current function's stack variables ... ^ | | stack starts at stack base and grows "up" toward lower addresses stack base = task ID (e.g. 0x2cfb528) -> task control block (struct WIND_TCB) stack base+0x13c (e.g. 2cfb664) -> task's registers as saved in TCB stack base+0x200 -> end of TCB ---------------- higher addresses ------------------------------------------------------------------------------ SIGNALS On 12/11/00, I chased down what I thought was a stack corruption. tDbsTask was initializing ISAM, and eventually ended up in a call to vxWorks' signal(SIGHUP,SIG_IGN). Upon returning from this call, the upper 688 bytes of tDbsTask's stack had been written on. I believe this is some data struct used to implement signals (perhaps "struct sigcontext"), and is not a corruption. After the call to signal, tDbsTask's stack looked likt this: -> d 0x0051d518,200,4 0051d510: 74446273 5461736b * tDbsTask* 0051d520: 00eeeeee eeeeeeee 00000000 00000000 *................* 0051d530: 00000000 00000001 00000000 00000000 *................* 0051d540: 00000000 00000000 00000000 00000000 *................* 0051d550: 00000000 00000000 00000000 00000000 *................* 0051d560: 00000000 00000000 00000000 00000000 *................* 0051d570: 00000000 00000000 00000000 00000000 *................* 0051d580: 00000000 00000000 00000000 00000000 *................* 0051d590: 00000000 00000000 00000000 00000000 *................* 0051d5a0: 00000000 00000000 00000000 00000000 *................* 0051d5b0: 00000000 00000000 00000000 00000000 *................* 0051d5c0: 00000000 00000000 00000000 00000000 *................* 0051d5d0: 00000000 00000000 00000000 00000000 *................* 0051d5e0: 00000000 00000000 00000000 00000000 *................* 0051d5f0: 00000000 00000000 00000000 00000000 *................* 0051d600: 00000000 00000000 00000000 00000000 *................* 0051d610: 00000000 00000000 00000000 00000000 *................* 0051d620: 00000000 00000000 00000000 00000000 *................* 0051d630: 00000000 00000000 00000000 00000000 *................* 0051d640: 00000000 00000000 00000000 00000000 *................* 0051d650: 00000000 00000000 00000000 00000000 *................* 0051d660: 00000000 00000000 00000000 00000000 *................* 0051d670: 00000000 00000000 00000000 00000000 *................* 0051d680: 00000000 00000000 00000000 00000000 *................* 0051d690: 00000000 00000000 00000000 00000000 *................* 0051d6a0: 00000000 00000000 0051d6a8 0051d6a8 *.........Q...Q..* 0051d6b0: 0051d6b0 0051d6b0 0051d6b8 0051d6b8 *.Q...Q...Q...Q..* 0051d6c0: 0051d6c0 0051d6c0 0051d6c8 0051d6c8 *.Q...Q...Q...Q..* 0051d6d0: 0051d6d0 0051d6d0 0051d6d8 0051d6d8 *.Q...Q...Q...Q..* 0051d6e0: 0051d6e0 0051d6e0 0051d6e8 0051d6e8 *.Q...Q...Q...Q..* 0051d6f0: 0051d6f0 0051d6f0 0051d6f8 0051d6f8 *.Q...Q...Q...Q..* 0051d700: 0051d700 0051d700 0051d708 0051d708 *.Q...Q...Q...Q..* 0051d710: 0051d710 0051d710 0051d718 0051d718 *.Q...Q...Q...Q..* 0051d720: 0051d720 0051d720 0051d728 0051d728 *.Q. .Q. .Q.(.Q.(* 0051d730: 0051d730 0051d730 0051d738 0051d738 *.Q.0.Q.0.Q.8.Q.8* 0051d740: 0051d740 0051d740 0051d748 0051d748 *.Q.@.Q.@.Q.H.Q.H* 0051d750: 0051d750 0051d750 0051d758 0051d758 *.Q.P.Q.P.Q.X.Q.X* 0051d760: 0051d760 0051d760 0051d768 0051d768 *.Q.`.Q.`.Q.h.Q.h* 0051d770: 0051d770 0051d770 0051d778 0051d778 *.Q.p.Q.p.Q.x.Q.x* 0051d780: 0051d780 0051d780 0051d788 0051d788 *.Q...Q...Q...Q..* 0051d790: 0051d790 0051d790 0051d798 0051d798 *.Q...Q...Q...Q..* 0051d7a0: 0051d7a0 0051d7a0 00000000 00000000 *.Q...Q..........* 0051d7b0: 00000000 00000000 00000000 00000000 *................* 0051d7c0: 00000000 eeeeeeee eeeeeeee eeeeeeee *................* 0051d7d0: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d7e0: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d7f0: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d800: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d810: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d820: eeeeeeee eeeeeeee eeeeeeee eeeeeeee *................* 0051d830: eeeeeeee eeeeeeee *........ * value = 0 = 0x0 ------------------------------------------------------------------------------ THE EXCEPTION STACK FRAME Note: this section is correct for the PPC 604, but I haven't verified it's the same for the PPC860. When an exception occurs, the normal stack frame is built as above, and the vxWorks exception handler saves partial context as shown below. "GPR1" is the value of GPR1 _after_ local storage has been allocated by the exception handler. The routine that builds this stack frame is excEnt(). *(GPR1+00) link pointer (GPR1+0xc0) *(GPR1+04) exception number (e.g. 0x00000400) *(GPR1+08) trash *(GPR1+0c) DAR *(GPR1+10) DSISR *(GPR1+14) trash *(GPR1+18) GPR0 *(GPR1+1c) GPR1 *(GPR1+20) GPR2 *(GPR1+24) GPR3 *(GPR1+28) GPR4 *(GPR1+2c) GPR5 *(GPR1+30) GPR6 *(GPR1+34) GPR7 *(GPR1+38) GPR8 *(GPR1+3c) GPR9 *(GPR1+40) GPR10 *(GPR1+44) GPR11 *(GPR1+48) GPR12 *(GPR1+4c) GPR13 *(GPR1+50) GPR14 *(GPR1+54) GPR15 *(GPR1+58) GPR16 *(GPR1+5c) GPR17 *(GPR1+60) GPR18 *(GPR1+64) GPR19 *(GPR1+68) GPR20 *(GPR1+6c) GPR21 *(GPR1+70) GPR22 *(GPR1+74) GPR23 *(GPR1+78) GPR24 *(GPR1+7c) GPR25 *(GPR1+80) GPR26 *(GPR1+84) GPR27 *(GPR1+88) GPR28 *(GPR1+8c) GPR29 *(GPR1+90) GPR30 *(GPR1+94) GPR31 *(GPR1+98) SRR1 *(GPR1+9c) LR *(GPR1+a0) CTR *(GPR1+a4) SRR0 *(GPR1+a8) CR *(GPR1+ac) XER *(GPR1+b0) trash *(GPR1+b4) trash *(GPR1+b8) trash *(GPR1+bc) trash *(GPR1+c0) caller's link pointer *(GPR1+c4) return-to address