September 96 - Balance of Power: Stalking the Wild Defect
Balance of Power: Stalking the Wild Defect
Dave Evans
Once again I found myself bleary eyed and fighting sleep, yet I continued to search for
understanding. Having already struck down two possible causes for my enigma, I was
now searching for new clues. I stubbornly refused to rest until I had flushed out the
software defect.
My journey had begun modestly enough as I chanced upon a capricious crash in my
software. I wondered which assumption or logic was at fault. Armed with only my
low-level debugger, I began a hunt that would consume me into the dead of night. On
this adventure through the dark Mac OS interior, I crossed rivers of mode switches,
hopped islands of cross-TOC glue, and set snares in a jungle of native PowerPC code.
In this column I'll walk you through one facet of that relentless pursuit, pointing out
the key landmarks I used to navigate and demonstrating the tools I used to survive. This
should help guide you through your own future explorations of the innards of PowerPC
code.
ON THE HUNT
Programming for a Power Macintosh may appear similar to your efforts on a
680x0-based Macintosh, but on close inspection you'll find PowerPC code far more
interesting to debug. The relatively simple landscape of a 680x0 world gives way to
confusing and insidious terrain on a Power Macintosh. Routine descriptors, dual
assembly languages, and native glue are obstacles that impede your progress.
My subject was a crash that occurred when PowerPC applications called
MaxApplZone. I was certain the problem was in my recent system software changes,
but I needed to see what happened right before the crash to understand it. I started by
setting a breakpoint when an application called MaxApplZone. (Later I'll describe a
good technique for setting these breakpoints.) Then I traced through the system routine
and looked for anything startling.
One application executed the following code just before calling MaxApplZone:
0093B260 mflr r0
0093B264 stw r31,-0x0004(SP)
0093B268 stw r30,-0x0008(SP)
0093B26C stw r29,-0x000C(SP)
0093B270 stw r0,0x0008(SP)
0093B274 stwu SP,-0x0050(SP)
0093B278 lwz r30,-0x3940(RTOC)
0093B27C bl MaxApplZone
The preamble to MaxApplZone saves registers R29 to R31 on the stack, creates a stack
frame, and loads a local variable into R30 from the application's TOC globals before
calling the routine. If we trace through this and then step into the bl (branch and link)
instruction to MaxApplZone, we find the following:
0094CBFC lwz r12,-0x7E60(RTOC)
0094CC00 stw RTOC,0x0014(SP)
0094CC04 lwz r0,0x0000(r12)
0094CC08 lwz RTOC,0x0004(r12)
0094CC0C mtctr r0
0094CC10 bctr
This code is standard cross-TOC glue. The caller of a routine has the responsibility to
set the TOC register (RTOC) correctly for it. Routines imported from other code
fragments will have a different TOC value than the application. The PowerPC Code
Fragment Manager supplies the correct TOC value and the address of the imported
routine in a pair of long words called a transition vector, or TVector. In this case, the
TVector is stored as global data at the application's TOC value minus $7E60 bytes. This
glue code loads the TVector's address in R12 and then uses that to load the address of the
routine in R0 and the new TOC value. It uses the counter register and the bctr (branch
to counter register) instruction to jump to the correct address, so the return address
in the link register will not be changed.
After tracing through this glue code, we find ourselves in a different kind of glue. The
MaxApplZone TVector points to a routine in the InterfaceLib code fragment, as listed
below. On this computer, you can guess that the code fragment is in ROM because the