TweetFollow Us on Twitter

June 96 - KON & BAL's Puzzle Page: New World Order

KON & BAL's Puzzle Page: New World Order

CAMERON ESFAHANI and ALEX ROSENBERG

See if you can solve this programming puzzle, presented in the form of a dialog between Cameron Esfahani (cam) and Alex Rosenberg. The dialog gives clues to help you. Keep guessing until you're done; your score is the number to the left of the clue that gave you the correct answer. Even if you never run into the particular problems being solved here, you'll learn some valuable debugging techniques that will help you solve your own programming conundrums. And you'll also learn interesting Macintosh trivia.

Alex Hey cam, Marathon crashed in a weird manner when I tried to play it under an early version of Mac OS 8.

cam Working hard, eh? I suppose you'll tell me this was compatibility testing.

Alex Yeah, well, it makes a good demo.

cam Hey, wait a minute! If it's an early version of Mac OS 8, Puzzle Page readers won't ever find this bug, and they'll write nasty letters to the editor about it.

Alex Well, they should know that they'll learn some valuable debugging techniques that they can apply to their own programming conundrums. Don't they read the intro to the Puzzle Page?

cam I guess not. Anyway, back to your problem. So, in what way does it fail?

Alex Just after launch, the machine freezes. This happens every time; it's 100% reproducible. I can't seem to get into MacsBug.

cam Philistine! We use the one true debugger, the Macintosh Debugger for PowerPC. Mac OS 8 debugging is generally done from a second machine over a serial cable. You're probably frozen because the program has crashed and the debugger has halted the machine, waiting to start a debugging session.

Alex What? I thought I gave that up with my Lisa. This is a Macintosh, after all.

cam Kernel-based operating systems are typically developed with two-machine debuggers. Besides, think of the wonderful third-party opportunity!

Alex Um, yeah. Anyway, you've hooked up the serial cable and are running the debugger on the second machine. After watching a progress bar for a while, you see a dialog that says "Access Fault."

cam An access fault is caused by an attempt to access an illegal address. The PC is at the instruction that caused the fault.

Alex There's a Show PC command in the debugger's Extras menu. It puts me at 0x626FDE50.

cam Right. We need to isolate whether this fault occurred in application code or in the system. Choose Show Fragment Info from the Views menu and type that address into it.

Alex I can't type anything; the machine is crashed. Oh, I get it: I have to keep switching my head back and forth between machines like a spectator at a tennis match. What fun. So, how long does this barber pole thingy spin for, anyway? Hey look, the Fragment Info window highlighted the Marathon code fragment. The PC is in Marathon's code.

cam What does the code around the PC look like?

Alex It looks like this:

   626FDE34   mflr       r0
626FDE38 stw r0,0x0008(SP)
626FDE3C stwu SP,-0x0038(SP)
626FDE40 lwz r4,0x0000(r3)
626FDE44 lha r3,0x0000(r4)
626FDE48 bl _eGetDCtlEntry
626FDE4C lwz RTOC,0x0014(SP)
626FDE50 lwz r12,0x0000(r3)
626FDE54 lbz r3,0x0028(r12)
626FDE58 extsb r3,r3
626FDE5C lwz r0,0x0040(SP)
626FDE60 addic SP,SP,56
626FDE64 mtlr r0
626FDE68 blr

R3 is the return value from the function call to _eGetDCtlEntry and apparently contained a bad address.

cam Choose Show Registers from the Views menu. This will show all the registers of the current process.

Alex It looks like the return value for _eGetDCtlEntry was 0. The lwz instruction is dereferencing R3 and putting the result in R12.

cam If you select the R12 register and choose Show Memory from the Views menu, you can see the memory at that address.

Alex That entire area of memory is full of 0xEEEEEEEE's.

cam That's unmapped memory. Is _eGetDCtlEntry the internal name of the routine GetDCtlEntry?

Alex Yes, the debugger is able to pick up that name by using a "trace-back table," which is the PowerPC equivalent of MacsBug symbols. I guess the next step would be to figure out why GetDCtlEntry is returning nil. What is it supposed to be doing?

cam According to Inside Macintosh: Devices, GetDCtlEntry returns the device control entry for the device specified by the value passed in refNum.

If we look at the rest of the code in this function, right before we call GetDCtlEntry, we seem to be getting the refNum from the first 16 bits of some "handle" (or some other kind of pointer to a pointer), which is getting passed into this function.

Alex All right, but we're going to have to restart. Any information passed into this function has been lost because we're after the call to GetDCtlEntry.

cam To restart we'll need to remember the offset into the Marathon fragment where the fault will occur, because the Marathon fragment could be loaded in a different address range.

Alex The offset can be calculated by subtracting the faulting address from the beginning address for the fragment, which was shown in the Fragment Info window. For this address, the offset is 0x4832C.

cam Right, but we'd like to get control a little before the actual crash. The offset to the beginning of that function is 0x48310. Restart the system, and hold down the Control key when you relaunch Marathon. On a debugging system, this will break into the debugger after it has completely loaded the application but just before it begins to execute it.

Alex All right. The machine seems to have stopped at that point. The new start of the Marathon code fragment is 0x6337D6A0. Adding in the offset of 0x48310, we get an address of 0x633C59B0.

cam Bring up the Show Instructions window and enter 0x633C59B0 as the address. It will be exactly the same code as what we saw before. Set a breakpoint at the first instruction in this function -- the mflr instruction -- and run.

Alex We've reached that breakpoint.

cam Do a stack crawl and see who called us. Head over to the ever useful Views menu; there's a Show Stack Crawl command.

Alex All right. Apparently the caller is address 0x633988E0.

cam OK, let's step through this function and see what they end up passing to GetDCtlEntry for the refNum.

Alex It looks like they're passing in 0 for the refNum.

cam Well, there's your problem: 0 is not a valid refNum. It seems that they're getting an invalid refNum from some part of the system and passing that to GetDCtlEntry. GetDCtlEntry is returning nil and we're crashing by dereferencing nil.

Alex Uh, that's great, but I still can't play Marathon.

cam Where does the caller of this function, 0x633988E0, get the "handle" from?

Alex I'll bring up an instruction window at that address:

63398778 mflr r0

...

633987B0 lwz r24,-0x0218(RTOC)

...

633988D8 lwz r3,0x0000(r24)

633988DC addic r3,r3,0x001C

633988E0 bl $+0x2D340 ; 0x633C5C20

633988E4 nop

633988E8 stw r3,0x0000(r30)

R3 seems to be loaded from a global. Let's figure out where this global gets initialized. The "handle" lives inside a structure that's pointed to by the global at -0x218(RTOC). This pointer has the "handle" stored at offset 0x1C within it.

cam We could try to track down where the field at offset 0x1C gets initialized. A pointer wouldn't move around, so we wouldn't have to worry about relocation. We can use the Data Breakpoint window feature of the Macintosh Debugger. The PowerPC 601 has a special register that allows you to stop execution whenever a specified address is read or written to. It's like hardware support for our old friend step spy.

Alex Sounds like a plan. So, I bring up the Data Breakpoint window and will break whenever someone writes to that address.

cam Of course, you realize that just as the code fragment could be loaded in a different place each time it's launched, the RTOC could have a different value as well.

Alex Good point. I'll be sure to use the new RTOC value when I restart Marathon.

cam What happens after we set up the data breakpoint?

Alex It's kind of strange. We seem to be stopping a lot, but people aren't writing to offset 0x1C in this structure; they seem to be writing 32 bits to offset 0x1A and overwriting 0x1C.

cam I don't understand. The routine that called the crashing routine was passing in a value at offset 0x1C.

Alex Apparently we calculated something wrong.

cam I don't know where we could have gone wrong. Wait a second. Look at address 0x633988E0; it says we're branching to address 0x633C5C20, but the routine we're crashing in is at 0x633C59B0.

Alex Well, maybe it's just a call to a different code fragment, a "cross-TOC" call, and it has to use some indirection to get to the crashing function.

cam No, it can't be a cross-TOC call, for two reasons: first, there's no TOC reload after the function call, and second, the routine we crashed in is in the Marathon fragment. You saw the Fragment Info results.

Alex I'll buy that. Now let's set our breakpoint just before this function call to the unknown address. We can step through that code.

cam All right. After we hit the breakpoint, step into that function.

45 Alex Holy cow! It's some totally different piece of code.

cam Look through the instruction disassembly of this new routine. Is there anywhere in there where they call the crashing function?

633C5C98 bl 0x6337E150

633C5C9C lwz RTOC,0x0014(SP)

633C5CA0 ori r31,r3,0x0000

...

633C5CDC ori r3,r31,0x0000

* 633C5CE0 bl 0x633C59B0

Alex Yeah. At address 0x633C5CE0, they call our crashing function with a parameter obtained from R31. Working further back in the instruction disassembly, we see that a function call is made and the result of that is put in R31. This occurs at 0x633C5C98. It calls a routine at address 0x6337E150.

cam And looking at that, it appears to be a cross-TOC call. I restart Marathon and step into the routine at 0x6337E150.

Alex It appears to be cross-TOC glue:

6337E150 lwz r12,-0x0A90(RTOC)

6337E154 stw RTOC,0x0014(SP)

6337E158 lwz r0,0x0000(r12)

6337E15C lwz RTOC,0x0004(r12)

6337E160 mtctr r0

6337E164 bctr

cam So apparently Marathon is calling another library to get this mystical "handle."

Alex Yep. Whenever you're going to go from one library context to another, you need to save and restore the TOC. That's one of the things this glue code does. As you can see, R12 is loaded from -0x0A90(RTOC). R12 will contain a pointer to a transition vector, which contains an address of a routine and a new TOC value. The transition vector is imported from the library we're linking against.

cam So we should be able to plop the transition vector address into the Fragment Info window and figure out which library that comes from, right?

Alex Good idea. Dumping the address at -0x0A90(RTOC) we get the following:

0200CC0C: 01FC9D98 01FC9DA4 01FC9DB0 01FC9DBC

0200CC1C: 01FC9DE0 01FC9DEC 01FC9D80 01FC9D74

...

cam I use the Fragment Info window to find out which fragment contains the address 0x01FC9D98.

Alex It seems to live in the QuickDraw data section, which makes sense, since a transition vector is data.

cam Aha! QuickDraw! That figures. And you wondered why they call it KON & BAL's Puzzle Page. I use the Show Exports button in the Fragment Info window to list all of the exports of the QuickDraw library.

Alex I was wondering when we were going to use that button. You end up getting a long list of all the routines exported by QuickDraw, sorted alphabetically.

cam But I have an address I want to match. If you click on the Address column title, that list will get resorted by address. I search through the list for address 0x01FC9D98.

Alex That address is the address of the GetDeviceList transition vector.

cam That makes sense. This "handle" we've been worrying about has a refNum in the first 16 bits of the structure, and a GDHandle has the refNum of the associated driver stored in the first field in the structure. I bring up a memory window and examine the device list stored in low memory to see if the gdRefNum is 0.

Alex It is. Who's responsible for initializing the GDevice record?

cam NewGDevice and InitGDevice. NewGDevice will pass the refNum to InitGDevice. Let's disassemble the code for NewGDevice.

Alex Apparently it does a NewHandleClear to allocate the GDHandle, and never initializes the refNum.

cam Whoops. Ah, the joys of pre-alpha software. Well, it should be reasonably easy to get one of the QuickDraw engineers to fix this bug. I install a fixed version of the QuickDraw shared library. We should be rockin' now!

Alex Not so fast! When I restart Marathon, I crash. If I do a stack crawl and examine the code, I seem to be crashing in exactly the same place. GetDCtlEntry still seems to return nil.

cam Just another day at the salt mines. OK, it's time to step through GetDCtlEntry. I put a breakpoint just before we call it.

Alex The refNum from the GDHandle is -51.

cam That looks like a valid refNum. Step into GetDCtlEntry.

Alex We first go through the cross-TOC glue and eventually get into GetDCtlEntry.

cam What does GetDCtlEntry look like?

Alex It seems fine, but when you step through the routine and actually fetch the DCtlHandle from the unit table, it ends up being nil.

...

626FC79C cntlzw r3,r3

626FC7A0 srwi r3,r3,5

...

cam That obviously shouldn't happen. There's a driver entry in the table and the refNum seems valid. What is the code at 0x626FC79C doing?

Alex It's performing a logical NOT operation on R3. Groovy, huh?

cam But the bitwise NOT of the refNum should be used as the index into the unit table, not the logical NOT!

Alex So, you claim that GetDCtlEntry is looking at the wrong place in the unit table to get the DCtlHandle. Are you sure?

cam Let's go to the source. What does Inside Macintosh: Devices say?

Alex It says, "The device reference number is the one's complement (logical NOT) of the unit number." But the logical NOT isn't the one's complement; the bitwise NOT is.

cam Um, OK, what does Inside Macintosh Volume II say?

Alex It claims that the unit number is "equal to -1 * (refNum + 1)."

cam And that's a bitwise NOT. So it seems that Inside Macintosh: Devices is wrong. Weird, wacky stuff. But I still don't understand why the stack crawl we did earlier pointed us to the wrong place.

Alex We did the stack crawl when we had just entered the crashing function, even before it executed the mflr instruction. The debugger, when it does a stack crawl, is going to look for stack frames to see where the callers are. Since we hadn't allocated a stack frame in the crashing function yet, we were still using the caller's stack frame. So that was the stack frame the debugger started from. If we had stepped a few more instructions in and allocated our stack frame, the debugger would have figured it out. It would be interesting to see if the debugger could actually detect the case of a nonexistent stack frame and use the link register to work back to the caller.

cam That also explains why the data breakpoint stuff didn't work. We thought that the data structure we were watching held the GDHandle. It didn't; it contained something totally unrelated, which was passed into the function that called the crashing routine.

Alex So, because NewGDevice didn't initialize the gdRefNum and GetDCtlEntry was returning the wrong entry from the unit table, I don't get to teach the Pfhor about large caliber, high-velocity rounds.

cam Nasty.

Alex Yeah.

SCORING

Your performance compares to these memorable screen roles:

80-100	 Chuck Heston in El Cid

55-75 Anne Parillaud in La Femme Nikita

30-50 Chow Yun-Fat in Hard Boiled

5-25 Richard Roundtree in Shaft*

CAMERON ESFAHANI (cameron_esfahani@powertalk.apple.com, AppleLink DIRTY) SWM, 24, 5' 7", 180 pounds, brown hair, brown eyes. Apple engineer. Loves movies and music. Plays golf and tennis, rollerblades, ice-skates, and is learning to ski again. Enjoys life but has a serious side. Likes cooking, reading, shopping. Once a dog guy, now a definite cat man. Believes the American musical of the '50s and '60s to be the second greatest invention of the twentieth century. Favorites include West Side Story, The Sound of Music, Music Man, and Singin' in the Rain.*

ALEX ROSENBERG (alexr@bungie.com) Alex's left brain works on everything from communications software to the latest 3D graphics tricks. His right brain is constantly thinking up interesting T-shirts that Apple's Marketing folks don't approve of. While working as a member of the Mac OS 8 "Ministry of Information," he experimented with optimization for PowerPC, worked closely with Apple's compiler team, and contributed to IBM's The PowerPC Compiler Writer's Guide. Now one of the minions at Bungie Software, Alex recently decided that eating is overrated.*

Thanks to Tom Dowdy, Wayne Meretsky, Mike Neil, Tom Saulpaugh, KON (Konstantin Othmer), and BAL (Bruce Leak) for reviewing this column.*

 
AAPL
$93.94
Apple Inc.
-0.49
MSFT
$44.84
Microsoft Corpora
+0.15
GOOG
$589.47
Google Inc.
-5.61

MacTech Search:
Community Search:

Software Updates via MacUpdate

OS X Yosemite 10.10 DP4 - Developer Prev...
Note: This is a Developer Preview. You must be a registered Apple Mac Developer to download this update. OS X Yosemite is Apple's newest operating system for Mac. An elegant design that feels... Read more
FinderPop 2.5.6 - Classic Mac utility, n...
FinderPop is a Universal preference pane that extends OS X's contextual menus using a FinderPop Items folder much as the Apple Menu Items folder used to do for the Apple menu. It has other features... Read more
SpiderOak 5.1.7 - Secure cloud backup, s...
SpiderOak is a multi-platform secure online backup, storage, access, and sharing solution engineered for the consumer and small businesses. You must first sign up to use SpiderOak. Running natively... Read more
Espionage 3.6 - Simple, state of the art...
Espionage offers state-of-the-art encryption and plausible deniability for your confidential data. Sometimes, encrypting your data isn't enough to protect it. That's why Espionage 3 goes beyond data... Read more
calibre 1.45.0 - Complete e-library mana...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
iFFmpeg 4.3.1 - Convert multimedia files...
iFFmpeg is a graphical front-end for FFmpeg, a command-line tool used to convert multimedia files between formats. The command line instructions can be very hard to master/understand, so iFFmpeg does... Read more
Chromium 36.0.1985.125 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
pwSafe 3.0 - Secure password management...
pwSafe provides simple and secure password management across devices and computers. pwSafe uses iCloud to keep your password databases backed-up and synced between Macs and iOS devices. It is... Read more
Day One 1.9.6 - Maintain a daily journal...
Day One is the easiest and best-looking way to use a journal / diary / text-logging application for the Mac. Day One is well designed and extremely focused to encourage you to write more through... Read more
Google Chrome 36.0.1985.125 - Modern and...
Google Chrome is a Web browser by Google, created to be a modern platform for Web pages and applications. It utilizes very fast loading of Web pages and has a V8 engine, which is a custom built... Read more

Latest Forum Discussions

See All

HELMUT Review
HELMUT Review By Andrew Fisher on July 21st, 2014 Our Rating: :: TRUNDLE SIMULATOR 2014Universal App - Designed for iPhone and iPad HELMUT is a fun, fleeting time-sink that offers a momentary distraction and nothing else.   | Read more »
Walkr Review
Walkr Review By Jennifer Allen on July 21st, 2014 Our Rating: :: ORIGINAL WALKINGiPhone App - Designed for the iPhone, compatible with the iPad Walking is a bit more exciting thanks to this planet building/discovering sim reliant... | Read more »
Zombie Commando Review
Zombie Commando Review By Jennifer Allen on July 21st, 2014 Our Rating: :: MINDLESS SLAUGHTERUniversal App - Designed for iPhone and iPad Briefly fun but ultimately forgettable, Zombie Commando will scratch an itch then be... | Read more »
Swords & Poker Adventures Review
Swords & Poker Adventures Review By Jennifer Allen on July 21st, 2014 Our Rating: :: SOULLESS POKER PLAYUniversal App - Designed for iPhone and iPad Swords & Poker Adventures is a mishmash of Poker and RPGing, but it lacks... | Read more »
Warhammer 40,000: The Horus Heresy: Drop...
Warhammer 40,000: The Horus Heresy: Drop Assault Coming Soon to iOS Posted by Jennifer Allen on July 21st, 2014 [ permalink ] Coming soon to iOS will be an all-new Warhammer 40,000 tactical strategy game by the name of The Horus Heresy: Drop As | Read more »
A Life Worth Dying For Review
A Life Worth Dying For Review By Jordan Minor on July 21st, 2014 Our Rating: :: A BEAUTIFUL MINDUniversal App - Designed for iPhone and iPad A Life Worth Dying For is a fascinating portrait of a serious subject.   | Read more »
Zombie Puzzle Panic Review
Zombie Puzzle Panic Review By Jordan Minor on July 21st, 2014 Our Rating: :: THE MATCHING DEADUniversal App - Designed for iPhone and iPad Zombie Puzzle Panic puts some pretty neat undead twists on Match-3 puzzling.   | Read more »
This Week at 148Apps: July 14-18, 2014
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Fallen Lords Review
Fallen Lords Review By Andrew Fisher on July 18th, 2014 Our Rating: :: FALLS SHORTiPad Only App - Designed for the iPad Fallen Lords is a decent game, but its similarity and inferiority compared to Ghost Stories makes it ultimately... | Read more »
Real Boxing’s New Combo Update is a Knoc...
Real Boxing’s New Combo Update is a Knockout Posted by Blake Grundman on July 18th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

Twelve South HiRise For MacBook – Height-Adju...
If you use your MacBook as a workhorse desktop substitute, as many of us do, a laptop stand combined with an external keyboard and pointing device are pretty much obligatory if you want to avoid... Read more
Why The Mac Was Not Included In The Apple/IBM...
TUAW’s Yoni Heisler cites Fredrick Paul of Network World whoi blogged last week that the Mac’s conspicuous absence from Apple and IBM’s landmark partnership agreement represents a huge squandered... Read more
Save $100 on 13-inch Retina MacBook Pros, plu...
Adorama has 13″ Retina MacBook Pros on sale for $100 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: - 13″ 2.4GHz/128GB MacBook Pro with Retina Display: $1199 - 13″ 2.... Read more
Blurr it 2.3 for iOS – Quickly Blurs Selected...
Hyderabad, India based TouchLabs has announced a new update of Blurr it 2.3, their photography app for iOS users. Blurr it allows you to blur part of the image to hide potentially sensitive or... Read more
MacBook Airs on sale for $100 off MSRP, start...
Best Buy has the new 2014 MacBook Airs on sale for up to $100 off MSRP on their online store. Choose free home shipping or free local store pickup (if available). Prices valid for online orders only... Read more
Amazon Announces Kindle Unlimited: Unlimited...
Amazon.com has introduced Kindle Unlimited — a new subscription service which allows customers to freely read as much as they want from over 600,000 Kindle books, and listen as much as they want to... Read more
New Linksys Wireless Range Extenders Boost Wi...
Linksys has announced its new lineup of Linksys Wi-Fi Range Extenders. Consumers often experience a weak wireless signal in some parts of their house or apartment caused by blocking elements such as... Read more
MacBook Airs available starting at $719
The Apple Store has Apple Certified Refurbished 2013 & 2012 MacBook Airs in stock today starting at $719. An Apple one-year warranty is included with each MacBook, and shipping is free: 2013... Read more
Get the best deals on iPad minis with Apple r...
The Apple Store has Certified Refurbished 2nd generation iPad minis with Retina Displays available for up to $130 off the cost of new models, starting at $339. Apple’s one-year warranty is included... Read more
Best Buy’s College Student Deals: $100 off Ma...
Take an additional $100 off all MacBooks and iMacs, $50 off iPad Airs and iPad minis, at Best Buy Online with their College Students Deals Savings, valid through July 25th. Anyone with a valid .EDU... Read more

Jobs Board

*Apple* Computer Technician - Fairfield Coun...
Company DescriptionWe are an Apple Authorized Sales and Service Provider. We have been selling and servicing Apple computers in the Fairfield County area for over 20 Read more
*Apple* Computer Technician - Fairfield Coun...
Company DescriptionWe are an Apple Authorized Sales and Service Provider. We have been selling and servicing Apple computers in the Fairfield County area for over 20 Read more
Mac Expert - *Apple* Online Store Mexico -...
…MUST be fluent in English and Spanish to be considered for this position At Apple , we believe that hard work, a fun environment, creativity and innovation fuel the Read more
*Apple* Computer Technician - Fairfield Coun...
Company DescriptionWe are an Apple Authorized Sales and Service Provider. We have been selling and servicing Apple computers in the Fairfield County area for over 20 Read more
Mac Expert - *Apple* Online Store - Apple (...
**Job Summary** At Apple , we believe that hard work, a fun environment, creativity and innovation fuel the ultimate customer experience. We believe each customer Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.