The Alpha AXP, part 17: Reconstructing a call stack


I'm going to wrap up the formal part of the series by applying the information we've been learning over the past several days weeks: We're going to reconstruct a broken stack.

Suppose you have a debug session where all the k command says is

Callee-SP Return-RA  Call Site
 0235d380 777b63e4 : contoso!OnSysColorChange+0x60

That's it. We'll have to unwind the stack manually. We need to keep track of the stack pointer depth and watch where the compiler stashed the return address. We can do this by going forward or backward. I'll demonstrate both.

Let's go backward:

kd> u .-60
contoso!OnSysColorChange:
777b6380 : 23deffe0 lda    sp,-20(sp)
777b6384 : b53e0000 stq    s0,0(sp)
777b6388 : b55e0008 stq    s1,8(sp)
777b638c : b75e0010 stq    ra,10(sp)

From the function prologue, we see that it allocates 0x20 bytes of stack and puts the return address at offset 0x10. So we can pull the first return address right away:

kd> dd @sp+10 L1
0235d390  7773b4e8

Since our local stack frame is 0x20 bytes, that means that the caller's stack begins at @sp + 20. Let's look at the caller.

kd> u 7773b4e8
contoso!WndProc+1f58:
7773b4e8 : a20b0040 ldl    a0,40(s2)
7773b4ec : 47ef0411 bis    zero,fp,a1
7773b4f0 : 47ed0412 bis    zero,s4,a2
7773b4f4 : 47ec0413 bis    zero,s3,a3
7773b4f8 : e61ff989 beq    a0,0000000077739b20 contoso!WndProc+590
7773b4fc : d3407858 bsr    ra,0000000077759660 contoso!ForwardMessage
7773b500 : 47ff0400 bis    zero,zero,v0
7773b504 : c3e00122 br     zero,000000007773b990 contoso!WndProc+2400

Referring to the source code reveals that the last two lines are a return 0, so that last jump goes to the function epilogue. This time, we're debugging forward.

kd> u 7773b990
contoso!WndProc+2400:
7773b990 : a75e0048 ldq    ra,48(sp)
7773b994 : a57e0020 ldq    s2,20(sp)
7773b998 : a59e0028 ldq    s3,28(sp)
7773b99c : a5be0030 ldq    s4,30(sp)
7773b9a0 : a5de0038 ldq    s5,38(sp)
7773b9a4 : a53e0010 ldq    s0,10(sp)
7773b9a8 : a55e0018 ldq    s1,18(sp)
7773b9ac : a5fe0040 ldq    fp,40(sp)
7773b9b0 : 63ff0000 trapb
7773b9b4 : 23de01b0 lda    sp,1b0(sp)
7773b9b8 : 6bfa8001 ret    zero,(ra),1  contoso!OnSysColorChange+60

(The disassembler is "helpfully" resolving the (ra) to contoso!OnSysColorChange+60, based on the current value in the ra register. It's not correct because the ra register will certainly change between the current execution point and the ret, but we'll give the debugger a nice pat on the head for trying.)

By studying the epilogue, we see that the function keeps its return address at offset 0x48. Since we already had an adjustment of 0x20 from WndProc, the combined offset from @sp is

kd> dd @sp+20+48 L1
0235d3e8  77c9a028

And now we just repeat this procedure until we get the full stack trace or we get bored.

kd> u 77c9a028
user32!CallWindowProcAorW+1d8:
77c9a028 : b01e0040 stl    v0,40(sp)
77c9a02c : 43e00000 addl   zero,v0,v0
77c9a030 : a75e0030 ldq    ra,30(sp)
77c9a034 : a53e0008 ldq    s0,8(sp)
77c9a038 : a55e0010 ldq    s1,10(sp)
77c9a03c : a57e0018 ldq    s2,18(sp)
77c9a040 : a59e0020 ldq    s3,20(sp)
77c9a044 : a5be0028 ldq    s4,28(sp)
77c9a048 : 23de0060 lda    sp,60(sp)
77c9a04c : 6bfa8001 ret    zero,(ra),1  contoso!OnSysColorChange+60
kd> dd @sp+20+1b0+30 l1
0235d580  77cb64c0
kd> u 77cb64c0
user32!CallWindowProcW+10:
77cb64c0 : a75e0000 ldq    ra,0(sp)
77cb64c4 : 23de0010 lda    sp,10(sp)
77cb64c8 : 6bfa8001 ret    zero,(ra),1  contoso!OnSysColorChange+60
kd> dd @sp+20+1b0+60+0 l1
0235d5b0  777a7c04

So we have successfully reconstructed this call stack:

contoso!OnSysColorChange+0x60
contoso!WndProc+1f58
user32!CallWindowProcAorW+1d8
user32!CallWindowProcW+10

Lather, rinse, repeat. (In this particular case, I needed to go back around 20 stack frames in order to find out why the WM_SYS­COLOR­CHANGE message was coming in at such a strange time.)

That concludes our rather lengthy whirlwind tour of the Alpha AXP processor. Maybe you found it interesting, maybe not, but there you have it.

Next up is the MIPS R4000. But I don't do it right now, since you're probably all tired of this CPU architecture stuff. I'll wait a while and then spring it on you when you least expect it.

Comments (13)
  1. skSdnW says:

    Thank you for another wonderful series! And after MIPS, hopefully we will get another trip back in time with the PPC or a ride into the future with ARM64?

  2. pc says:

    I can’t wait for the next installment. I have to wonder just how much esoteric processor knowledge Raymond has in his head.

  3. Ted Spence says:

    Esoteric processor information is the best. Fascinating how tiny decisions the designers use to optimize something can turn out to have huge effects on software development.

  4. Darran Rowe says:

    This was a really interesting series of posts, a view into a really early 64 bit processor is interesting.
    I’m also looking forward to the MIPS series.

  5. Simon Clarkstone says:

    These processor architecture posts are fascinating stuff. I still have an Alpha lying around somewhere, but I put BSD on it.

    I thought that Windows binaries usually had metadata and restrictions on function prologue/epilogue form to allow automatic unwinding. I have only looked at the x64 though, so was this not done on the Alpha, or does the debugger not have the ability to read it, or did this binary not include the required information?

    1. Debuggers weren’t that smart back in the day.

  6. CarlD says:

    Bring on the MIPS, and then the i860!

  7. MC says:

    I enjoyed the series. Hopefully it’s completely useless information to me, but always good to have your mind broadened a bit.

    1. Steve says:

      Amen to that. The vast majority of it has whooshed straight over my head, but I’ve enjoyed picking up the few scraps that have sunk in …

  8. bmm6o says:

    Since you said you won’t do it right away, tomorrow is when we least expect it!
    (Also, interesting series)

    1. D-Coder says:

      No one expects the MIPS R4000!

  9. Yukkuri says:

    I never get tired of CPU architecture stuff

  10. viila says:

    Ooh, MIPS! Finally that’ll be something I can follow. Only assembly programming I’ve ever done was 68000 on Amiga and MIPS2000 at university for the CPU architecture course.

Comments are closed.

Skip to main content