home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 October: Mac OS SDK / Dev.CD Oct 96 SDK / Dev.CD Oct 96 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc Source Code / Utilities / Crawl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-22  |  31.5 KB  |  1,128 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Crawl.cpp
  3.  
  4.     Contains:    Debugging utility to create and interpret stack crawls
  5.  
  6.     Owned by:    Jens Alfke (based on code from the Macintosh Debugger.)
  7.  
  8.     Copyright:    © 1992 - 1995 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12.  
  13. #ifndef _CRAWL_
  14. #include "Crawl.h"
  15. #endif
  16.  
  17. #ifndef __LOWMEM__
  18. #include <LowMem.h>
  19. #endif
  20.  
  21. #ifndef __MEMORY__
  22. #include <Memory.h>
  23. #endif
  24.  
  25. #ifndef __STRING__
  26. #include <string.h>
  27. #endif
  28.  
  29. #ifndef __STDARG__
  30. #include <stdarg.h>
  31. #endif
  32.  
  33. #ifndef __ERRORS__
  34. #include <Errors.h>
  35. #endif
  36.  
  37. #include <setjmp.h>
  38. #include <stdio.h>
  39. #include <Unmangler.h>
  40.  
  41.  
  42. //==============================================================================
  43. // Declarations for embedded-symbol stuff
  44. //==============================================================================
  45.  
  46. typedef unsigned long    ULongWord;
  47. typedef unsigned short    UWord;
  48. typedef unsigned char    UByte;
  49.  
  50. typedef size_t            TargetAddress;
  51.  
  52. typedef OSErr (*ReadMemFn)( void *loc, ULongWord size, void *buffer, va_list args );
  53.  
  54. #define kNoErr            noErr
  55. #define kSymbolNotFound fragSymbolNotFound
  56.  
  57.  
  58. #ifdef GENERATINGPOWERPC
  59. static OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  60.         ReadMemFn readMemCallback, ...);
  61. #endif
  62. static OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  63.         ReadMemFn readMemCallback, ...);
  64.  
  65. #define PACKET_MAX_DATA_LENGTH    512
  66.  
  67.  
  68. //==============================================================================
  69. // Stack-crawl types & constants
  70. //==============================================================================
  71.  
  72. struct LinkAreaPPC {
  73.     void*    backChain;
  74.     void*    savedCR;
  75.     void*    savedLR;
  76.     void*    reserved;
  77.     void*    savedTOC;
  78. };
  79.  
  80. struct LinkArea68k {
  81.     void*    backChain;
  82.     void*    returnAddress;
  83. };
  84.  
  85. union LinkArea {
  86.     LinkAreaPPC fPPC;
  87.     LinkArea68k f68k;
  88. };
  89.  
  90. const size_t kMagicA6 = 0xFFFFFFFF;                // Signals 68k->PPC switch
  91. const size_t kPPCInstrLen = 4;                    // Gotta love them RISCs
  92.  
  93. #if GENERATINGPOWERPC
  94. const long kjmp_bufStackFrameIndex = 3;            // $$$$$ Compiler dependent
  95. #else
  96. const long kjmp_bufStackFrameIndex = 10;        // $$$$$ Compiler dependent
  97. #endif
  98.  
  99.  
  100. //==============================================================================
  101. // StackCrawl
  102. //==============================================================================
  103.  
  104. StackCrawl*
  105. StackCrawl::New( long startAt, long endAt )
  106. {
  107.     StackCrawl *stackCrawl = NULL;
  108.     const void *stackTop;
  109.     
  110.     // Skanky way to read the CPU registers:
  111.     {
  112.         jmp_buf regs;
  113.         (void) setjmp(regs);
  114.         stackTop = regs[kjmp_bufStackFrameIndex];
  115.     }
  116.     
  117.     // We're going to do this loop twice. The first time just count the number of frames.
  118.     // Then allocate enough memory to store them. Second time, write the frame data:
  119.     for( ;; ) {
  120.     
  121.         const LinkArea *stackFrame = (LinkArea*)stackTop;
  122.         const LinkArea *lastStackFrame = NULL;
  123.         Boolean isNative = GENERATINGPOWERPC ?true :false;
  124.         const void *pc;
  125.         
  126.         // Crawl up the stack:
  127.         long nFrames = 0;
  128.         while( stackFrame!=NULL && stackFrame>lastStackFrame ) {
  129.             const LinkArea *nextStackFrame;
  130.             
  131.             if( ! isNative ) {
  132.                 nextStackFrame = (LinkArea*) stackFrame->f68k.backChain;
  133.                 if( ((size_t*)nextStackFrame)[-1] == kMagicA6 ) {        // 68k->PPC switch
  134. #ifdef GENERATINGPOWERPC
  135.                     isNative = true;
  136.                     stackFrame = nextStackFrame;                        // Skip switch frame
  137.                 } else
  138. #else
  139.                     DebugStr("\pOD: PPC frame on 68k machine? Weird!");
  140.                 }
  141. #endif
  142.                     pc = stackFrame->f68k.returnAddress;
  143.             }
  144.  
  145. #ifdef GENERATINGPOWERPC
  146.             if( isNative ) {
  147.                 nextStackFrame = (const LinkArea*) stackFrame->fPPC.backChain;
  148.                 if( (long)nextStackFrame & 1 ) {                    // PPC->68k switch
  149.                     nextStackFrame = (LinkArea*)( (size_t)nextStackFrame -1);
  150.                     isNative = false;
  151.                     pc = nextStackFrame->f68k.returnAddress;
  152.                     nextStackFrame = (const LinkArea*) nextStackFrame->f68k.backChain;
  153.                 } else {
  154.                     pc = (void*)( (size_t)nextStackFrame->fPPC.savedLR - kPPCInstrLen );
  155.                 }
  156.             }
  157. #endif
  158.             
  159.             // Write PC value into list if in write phase:
  160.             if( stackCrawl )
  161.                 if( startAt<=nFrames && nFrames<=endAt )
  162.                     stackCrawl->fFrame[nFrames-startAt] = (const void*)( (size_t)pc | isNative );
  163.             
  164.             // Advance to next frame:
  165.             lastStackFrame = stackFrame;
  166.             stackFrame = (LinkArea*)nextStackFrame;
  167.             nFrames++;
  168.             
  169.             // Stop if we've hit the base of the stack:
  170.             size_t postFrame = (size_t)stackFrame + (isNative ?sizeof(LinkAreaPPC)
  171.                                                               :sizeof(LinkArea68k));
  172.             if( postFrame >= (size_t)LMGetCurStackBase() )
  173.                 break;
  174.         }
  175.         
  176.         if( stackCrawl )
  177.             break;    // Done
  178.         else {
  179.             // We've counted the frames, now allocate storage and go round again:
  180.             // Adjust startAt/endAt:
  181.             if( startAt < 0 )
  182.                 startAt = 0;
  183.             if( endAt <= 0 )
  184.                 endAt += nFrames-1;
  185.             else if( endAt >= nFrames )
  186.                 endAt = nFrames-1;
  187.             if( endAt<startAt )
  188.                 return NULL;
  189.             // Create the StackCrawl object:
  190.             stackCrawl = new (nFrames) StackCrawl(endAt-startAt+1);
  191.         }
  192.     }
  193.     
  194.     return stackCrawl;
  195. }
  196.  
  197.  
  198. void* StackCrawl::operator new( size_t baseSize, long nFrames )
  199. {
  200.     return ::operator new( baseSize + (offsetof(StackCrawl,fFrame)-sizeof(StackCrawl))
  201.                                     + nFrames * sizeof(void*) );
  202. }
  203.  
  204.  
  205. Boolean
  206. StackCrawl::IsFrameNative( long i ) const
  207. {
  208.     return (long)fFrame[i] & 1;
  209. }
  210.  
  211.  
  212. const void*
  213. StackCrawl::GetFramePC( long i ) const
  214. {
  215.     return (const void*)( (long)fFrame[i] & ~1);
  216. }
  217.  
  218.  
  219. static OSErr GetMem( void *loc, ULongWord size, void *buffer, va_list args )
  220. {
  221.     // Try to get away without a CPU exception handler by sanity checking:
  222.     if( loc<&SystemZone()->heapData || (void*)((size_t)loc+size)>LMGetBufPtr() )
  223.         return -1;
  224.     else {
  225.         BlockMoveData(loc,buffer,size);
  226.         return noErr;
  227.     }
  228. }
  229.  
  230. Boolean
  231. StackCrawl::LookupSymbol( long i, char fnName[], size_t *offset ) const
  232. {
  233.     size_t pc = (size_t) this->GetFramePC(i);
  234.     size_t begin,end;
  235.     
  236.     OSErr err;
  237.     char symbol[256];
  238.     if( this->IsFrameNative(i) )
  239. #ifdef GENERATINGPOWERPC
  240.         err= LookupPowerPCSym(pc,symbol,&begin,&end, &GetMem);
  241. #else
  242.         err= 12345;
  243. #endif
  244.     else
  245.         err= Lookup68KSym(pc,symbol,&begin,&end, &GetMem);
  246.     *offset = pc-begin;
  247.     if( err==noErr && symbol[0] ) {
  248.         // For some reason the traceback names seem to start with "."
  249.         unmangle(fnName,&symbol[1+(symbol[1]=='.')],255);
  250.         return true;
  251.     } else {
  252.         sprintf(fnName,"%08p (%s)", pc, this->IsFrameNative(i) ?"PPC" :"68k");
  253.         return false;
  254.     }
  255. }
  256.  
  257.  
  258. Boolean
  259. StackCrawl::GetFrameName( long i, char name[] ) const
  260. {
  261.     size_t offset;
  262.     
  263.     if( this->LookupSymbol(i,name,&offset) ) {
  264.         sprintf(name+strlen(name)," +%lx", offset);
  265.         return true;
  266.     } else
  267.         return false;
  268. }
  269.  
  270.  
  271. void
  272. StackCrawl::AsString( char str[], long maxLen, const char delimiter[] ) const
  273. {
  274.     char fnName[256];
  275.     size_t offset;
  276.     long len;
  277.     
  278.     str[0] = '\0';
  279.     for( long i=0; i<fNFrames; i++ ) {
  280.         if( i>0 ) {
  281.             len = strlen(delimiter);
  282.             maxLen -= len;
  283.             if( maxLen < 0 )
  284.                 return;
  285.             strcat(str,delimiter);
  286.             str += len;
  287.         }
  288.         this->LookupSymbol(i,fnName,&offset);
  289.         
  290.         len = strlen(fnName);
  291.         maxLen -= len;
  292.         if( maxLen < 0 )
  293.             return;
  294.         strcat(str,fnName);
  295.         str += len;
  296.     }
  297. }
  298.  
  299.  
  300. Boolean
  301. GetNameOfCaller( char str[] )
  302. {
  303.     StackCrawl *c = StackCrawl::New(2,2);
  304.     Boolean gotName = c->GetFrameName(0,str);
  305.     delete c;
  306.     return gotName;
  307. }
  308.  
  309.  
  310. Boolean
  311. StackCrawl::operator== ( const StackCrawl &sc ) const
  312. {
  313.     if( fNFrames != sc.fNFrames )
  314.         return false;
  315.     else
  316.         return memcmp(fFrame,sc.fFrame, fNFrames*sizeof(void*)) ==0;
  317.             
  318. }
  319.  
  320. unsigned long
  321. StackCrawl::Hash ( ) const
  322. {
  323.     unsigned long hash = 0;
  324.     for (long i = 0; i < fNFrames; i++)
  325.     {
  326.         hash ^= (unsigned long) fFrame[i];
  327.     }
  328.     return hash;
  329. }
  330.  
  331. //==============================================================================
  332. // Embedded Symbol Extraction (stolen from debugger nub)
  333. //==============================================================================
  334.  
  335.  
  336. /*
  337.     ************************************** WARNING ******************************************
  338.     *                                                                                        *
  339.     * The master copy of this file is in the project 'Panache:NuKernel:NuKernel'.            *
  340.     * DO NOT edit a copy from any other project- changes will be lost!  Instead, send your    *
  341.     * changes to Bill Kincaid.  Thanks.                                                        *
  342.     *                                                                                        *
  343.     *****************************************************************************************
  344.  
  345.  
  346.     File:        NubEmbeddedSymbols.c
  347.  
  348.     Contains:    Common debugger nub code:
  349.                 68K and PowerPC embedded symbol lookup functions (they are together to facilitate
  350.                 host features for mixed mode debugging, such as mixed mode stack crawls)
  351.  
  352.     Owned by:    Bill Kincaid
  353.                 (The 68K symbol lookup code was entirely scavenged from Macsbug
  354.                 code by Danny Kubota).
  355.  
  356.     Copyright:    © 1992-94 by Apple Computer, Inc. - All rights reserved.
  357.  
  358.     Change History (most recent first):
  359.  
  360.         <11>    02/28/95    KTL        Changed FindTracebackTbl to return a Traceback table for symbols > 512,
  361.                                     also, changed the max symbol namelength to 256 (was 128).
  362.         <10>    10/18/94    DKK        Changed Lookup68KSym to search backwards from the original symbol address
  363.                                     rather than the end of function. Also return bounds of search when a symbol
  364.                                     is not found. Increased performance of GetProcStart by reading a large buffer
  365.                                     of data rather than one word at a time. Fixed bugs in ValidName where stepping
  366.                                     through symbol name would only catch invalid characters if they were the last
  367.                                     characters in the name. Fixed bug in GetModuleName, where the length bytes
  368.                                     were not accounted for, which caused the last characters of the module name
  369.                                     to be interpreted as the literal pool size. Led to behaviour where address
  370.                                     range returned did not contain the address we were searching for symbols on.
  371.                                     Added #Pragma Segment.
  372.          <9>      6/8/94    WSK        Add limits to fn boundary scan in LookupPowerPCSym.
  373.          <8>      6/3/94    WSK        Make embedded symbol lookups return fn boundaries even if name
  374.                                     not found.
  375.          <7>     3/16/94    JLR        Adding Segment information.
  376.         <7>        12/16/93    WSK        Added some casts to make xlc happy.
  377.         <6>        11/10/93    WSK        Misc. cleanup for xlC.
  378.         <5>        08/03/93    WSK        Fixed cut and paste bug in FindTracebackTbl.
  379.         <4>        06/02/93    WSK        Added ReadMem callback fn instead making direct memory
  380.                                     accesses.
  381.         <3>        12/11/92    WSK        Added warning comment about other projects.
  382.         <2>        12/02/92    WSK        Nuked unmangling code- can be done on host.
  383.                                     Fixed bug in FindTracebackTbl- not incrementing i.
  384.                                     Reduced kMaxPwrPCFnLength to 131072.  Changed nameLength
  385.                                     to unsigned.  Added pad bytes where necessary to make
  386.                                     returned strings TProtocolStrings.
  387.         <1>        11/04/92    WSK        First version.
  388.  
  389. */
  390.  
  391. #pragma segment NubCommon
  392.  
  393. /*
  394. #include "NubUtilities.h"
  395. #include "TargetErrors.h"
  396. #ifndef __NUBDRIVER__
  397. #include "NubDriver.h"
  398. #endif
  399. */
  400.  
  401.  
  402. // max length of an embedded symbol (longer will be truncated)
  403. #define kMaxNameLength                        256
  404. #define kTTblMaxNameLength                    256
  405. #define kMaxTracebackNameSearchLength        512
  406.  
  407.  
  408.  
  409. // PowerPC embedded symbol stuff:
  410.  
  411. #define kMaxPwrPCFnLength    262144
  412. #define kMaxPwrPCFnInstrs    (kMaxPwrPCFnLength >> 2)
  413.  
  414. typedef struct TracebackTblEndAlt
  415.     {
  416.         ULongWord        unknown;
  417.         ULongWord        fnLength;
  418.         UWord            nameLength;
  419.         char            name[kTTblMaxNameLength];
  420.     } TracebackTblEndAlt;
  421.  
  422. typedef struct TracebackTblEnd
  423.     {
  424.         ULongWord        fnLength;
  425.         UWord            nameLength;
  426.         char            name[kTTblMaxNameLength];
  427.     } TracebackTblEnd;
  428.  
  429. typedef struct TracebackTbl
  430.     {
  431.         ULongWord            zero;
  432.         char                version;
  433.         char                language;
  434.         char                flags[6];
  435.         union {
  436.         TracebackTblEnd        end;    // if flags[4] == 0
  437.         TracebackTblEndAlt    endAlt;    // if flags[4] > 0
  438.         } u;
  439.     } TracebackTbl;
  440.  
  441.  
  442. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args);
  443.  
  444.  
  445.  
  446. // 68K embedded symbol stuff:
  447.  
  448. #define kMax68KFnLength        32768
  449.  
  450. #define kLINKA6                0x4E56
  451. #define kJMPA0                0x4ED0
  452. #define kRTS                0x4E75
  453. #define kRTD                0x4E74
  454.  
  455.  
  456.  
  457. static UByte    *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args);
  458. static UByte    *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args);
  459. static Boolean    ValidName (UByte *name, ReadMemFn readMemCallback, va_list args);
  460. static void        GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args);
  461. static Boolean    FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args);
  462. static Boolean    LegalSymbolChar (short ch);
  463. static Boolean    LegalTargetChar (short ch);
  464.  
  465. #pragma segment CodeTracking
  466.  
  467. #ifdef GENERATINGPOWERPC
  468. //
  469. // Tries to find a traceback table for the routine in which addr is presumed to be.
  470. // If successful, returns the name of the routine in name and the addresses of the
  471. // beginning and end of the routine in fnBegin and fnEnd.  Else scans forward for
  472. // a blr instruction and backward for an mflr instruction to guesstimate fnBegin
  473. // and fnEnd, and leaves name empty.
  474. //
  475. OSErr LookupPowerPCSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  476.         ReadMemFn readMemCallback, ...)
  477. {
  478.     va_list            args;
  479.     TracebackTbl    ttbl;
  480.     TracebackTbl    *where;
  481.     char            *namePtr;
  482.     UWord            nameLength;
  483.     ULongWord        codeStart, fnLength;
  484.     ULongWord        instr;
  485.     long            i, offset;
  486.  
  487.     va_start( args, readMemCallback );
  488.     
  489.     #define    kmflrInstr    0x7C0802A6
  490.     #define    kblrInstr    0x4E800020
  491.     
  492.     *fnBegin = addr;
  493.     *fnEnd = addr;
  494.  
  495.     where = FindTracebackTbl ((void*) addr, &ttbl, readMemCallback, args);
  496.  
  497.     if (where)
  498.     {
  499.         fnLength = ttbl.flags[4] ? ttbl.u.endAlt.fnLength : ttbl.u.end.fnLength;
  500.         codeStart = (ULongWord) where - fnLength;
  501.         offset = addr - codeStart;
  502.         if (offset >= 0)
  503.         {
  504.             namePtr = ttbl.flags[4] ? ttbl.u.endAlt.name : ttbl.u.end.name;
  505.             nameLength = ttbl.flags[4] ? ttbl.u.endAlt.nameLength : ttbl.u.end.nameLength;
  506.             if (nameLength > kMaxNameLength - 2)
  507.                 // leave room for leading length byte and terminating null
  508.                 nameLength = kMaxNameLength - 2;
  509.             strncpy (&name[1], namePtr, nameLength);
  510.             name[0] = nameLength;            // now it's a Pascal string
  511.             name[nameLength + 1] = 0;        // terminating null to make it a TProtocolString
  512.             if (nameLength & 1)                // do we need a pad byte?
  513.                 name[nameLength + 2] = 0;    // add one
  514.             *fnBegin = (TargetAddress) codeStart;
  515.             *fnEnd = (TargetAddress) where;
  516.             return kNoErr;
  517.         }
  518.     }
  519.  
  520.     // couldn't find a traceback, so no embedded name, but we'll scan for the fn boundaries anyway
  521.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  522.     {
  523.         if ((*readMemCallback) ((void*) *fnBegin, 4, &instr, args) != kNoErr)
  524.             return kSymbolNotFound;
  525.         if (instr == kmflrInstr)
  526.             break;
  527.         else if (instr == kblrInstr)
  528.             {
  529.             // probably a leaf routine, and we've backed up into the preceding fn
  530.             *fnBegin += 4;
  531.             break;
  532.             }
  533.         *fnBegin -= 4;
  534.     }
  535.     
  536.     if (i == kMaxPwrPCFnInstrs)
  537.     {
  538.         // didn't find the fn begin
  539.         *fnBegin = addr;
  540.         *fnEnd = addr;
  541.         return kSymbolNotFound;
  542.     }
  543.  
  544.     // found the fn begin, now try to find the fn end
  545.     for (i = 0; i < kMaxPwrPCFnInstrs; ++i)
  546.     {
  547.         if ((*readMemCallback) ((void*) *fnEnd, 4, &instr, args) != kNoErr)
  548.             return kSymbolNotFound;
  549.         *fnEnd += 4;
  550.         if (instr == kblrInstr)
  551.             break;
  552.     }
  553.     
  554.     if (i == kMaxPwrPCFnInstrs)
  555.     {
  556.         // didn't find the fn end
  557.         *fnBegin = addr;
  558.         *fnEnd = addr;
  559.         return kSymbolNotFound;
  560.     }
  561.     
  562.     return kNoErr;
  563. }
  564.  
  565.  
  566. //
  567. // Tries to find a traceback table for the routine in which addr is presumed to be.  If
  568. // successful, returns a pointer to the traceback table and copies its contents into ttbl.
  569. // If not successful, returns NULL.
  570. //
  571. static TracebackTbl *FindTracebackTbl (void *addr, TracebackTbl *ttbl, ReadMemFn readMemCallback, va_list args)
  572. {
  573.     ULongWord        where = (ULongWord) addr;
  574.     ULongWord        i, memLongWord, fnLength;
  575.     char            *name;
  576.     UWord            nameLength;
  577.     
  578.     for (i = 0; i < kMaxPwrPCFnLength; i += 4)
  579.     {
  580.         if ((*readMemCallback) ((void *) where, 4, &memLongWord, args) != kNoErr)
  581.             return NULL;
  582.         if (memLongWord == 0)
  583.             break;
  584.         where += 4;
  585.     }
  586.     
  587.     if (i == kMaxPwrPCFnLength)
  588.         return NULL;
  589.  
  590.     if ((*readMemCallback) ((void *) where, sizeof(TracebackTbl), ttbl, args) != kNoErr)
  591.         return NULL;
  592.     
  593.     // now for some sanity checks
  594.     fnLength = ttbl->flags[4] ? ttbl->u.endAlt.fnLength : ttbl->u.end.fnLength;
  595.     name = ttbl->flags[4] ? ttbl->u.endAlt.name : ttbl->u.end.name;
  596.     nameLength = ttbl->flags[4] ? ttbl->u.endAlt.nameLength : ttbl->u.end.nameLength;
  597.     if ((fnLength > kMaxPwrPCFnLength) || (nameLength > kMaxTracebackNameSearchLength) || (name[0] < ' ') || (name[0] > '}'))
  598.         return NULL;
  599.     else
  600.         return (TracebackTbl *) where;
  601. }
  602. #endif /*GENERATINGPOWERPC*/
  603.  
  604. //
  605. // Tries to find a Macsbug symbol for the routine in which addr is presumed to be.
  606. // If successful, returns the name of the routine in name and the addresses of the
  607. // beginning and end of the routine in fnBegin and fnEnd.
  608. // Name is a "PC" string (a Pascal string with a terminating null).
  609. // fnBegin should normally always be less than or equal to addr, but if addr happens
  610. // to be somewhere strange (like in an embedded symbol itself!), we may actually
  611. // find the next function.
  612. //
  613. OSErr Lookup68KSym (TargetAddress addr, char *name, TargetAddress *fnBegin, TargetAddress *fnEnd,
  614.         ReadMemFn readMemCallback, ...)
  615. {
  616.     va_list            args;
  617.     UByte*            address = (UByte*) addr;
  618.     UByte*            limit = (UByte*) (addr + kMax68KFnLength);
  619.     UByte*            addressOfFnEnd;
  620.     UByte*            codeStart;
  621.     UByte*            ignore1;
  622.     UByte*            ignore2;
  623.     Boolean            hasName;
  624.  
  625.     va_start( args, readMemCallback );
  626.     
  627.     addressOfFnEnd = FindNextModule (address, limit, &hasName, readMemCallback, args);
  628.     
  629.     if (addressOfFnEnd)
  630.         {
  631.         //    10/18/94 dkk
  632.         //    Code here used to search backwards from the end of the function to find the start of this function.
  633.         //    If the start of the function wasn't before the address we were interested in finding the symbol for
  634.         //    then we searched again from the original address. We then would search forward again for the end of
  635.         //    the function. This seemed like a lot of extra work. I changed the code look for the start of the
  636.         //    function from the original start address, and don't look for the end of function twice.
  637.         //    We've found a module for the address, find the start of the procedure.
  638.         //
  639.         codeStart = GetProcStart (address, readMemCallback, args);
  640.  
  641.         if (hasName)
  642.             GetModuleName (addressOfFnEnd, name, &ignore1, &ignore2, readMemCallback, args);
  643.         else
  644.             *name = 0;
  645.             
  646.         *fnBegin = (TargetAddress) codeStart;
  647.         *fnEnd = (TargetAddress) addressOfFnEnd;
  648.         return kNoErr;
  649.         }
  650.     
  651.     *fnBegin = addr;
  652.     *fnEnd = addr + kMax68KFnLength;
  653.     return kSymbolNotFound;
  654. }
  655.  
  656.  
  657. //    
  658. //    GetProcStart
  659. //
  660. //        Search backwards for the start of the procedure and return its address.
  661. //
  662. //        10/18/94 - dkk
  663. //        This code was pulled from MacsBug, which didn't suffer any overhead hit from reading one word of memory
  664. //        at a time. Since when MacsBug is executing, it always has its own bus error handler installed, there
  665. //        is no need to install a special handler for each memory access. For the nubs, this is not the case.
  666. //        Each memory access require installation and removal of the bus error handler. For this reason, this code
  667. //        was changes to read a buffer at a time, and step through the buffer. In addition the check for RTS,
  668. //        JMP (A0), and RTD were moved to here, even though this duplicateds code in FindNextModule. FindNextModule
  669. //        will also do the check, but another memory access is required to do the check. By checking here first,
  670. //        we save the overhead of that memory access most of the time.
  671.  
  672. static UByte *GetProcStart (UByte *addressOfFnEnd, ReadMemFn readMemCallback, va_list args)
  673. {
  674.     UWord        instr;
  675.     UByte        *codeStart;
  676.     UByte        *limit = NULL;
  677.     UByte        *prevDataStart;
  678.     char        prevProcName[kMaxNameLength];
  679.     char        memBuffer[PACKET_MAX_DATA_LENGTH];
  680.     short        offset;
  681.     Boolean        hasName;
  682.  
  683.     if ((ULongWord) addressOfFnEnd >= (ULongWord) kMax68KFnLength)
  684.         limit = addressOfFnEnd - kMax68KFnLength;
  685.  
  686.     codeStart = addressOfFnEnd - 2 - (PACKET_MAX_DATA_LENGTH - 2);
  687.     
  688.     if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  689.         return NULL;
  690.     offset = PACKET_MAX_DATA_LENGTH - 2;
  691.     instr = *(short *) &memBuffer[offset];
  692.  
  693.     //    RTD is a 4 byte instruction. RTS and JMP (A0) are 2 byte instructions.
  694.     //
  695.     if ((instr != kRTS) && (instr != kJMPA0))
  696.         offset -= 2;
  697.  
  698.     //    Step backwards looking for the start of the procedure.
  699.     //
  700.     while (codeStart > limit /* gCurBlock.data */)
  701.         {
  702.         offset -= 2;
  703.  
  704.         if (offset < 0)
  705.         {
  706.             codeStart -= PACKET_MAX_DATA_LENGTH;
  707.             offset = PACKET_MAX_DATA_LENGTH - 2;
  708.             if ((*readMemCallback) ((void *) codeStart, PACKET_MAX_DATA_LENGTH, memBuffer, args) != kNoErr)
  709.                 return NULL;
  710.         }
  711.         instr = *(short *) &memBuffer[offset];
  712.  
  713.         //    LinkA6 starts a procedure.
  714.         //
  715.         if (instr == kLINKA6)
  716.             return (codeStart + offset);
  717.  
  718.         if ((instr == kJMPA0) || (instr == kRTS) || (instr == kRTD))
  719.             {
  720.             addressOfFnEnd = FindNextModule (codeStart + offset, codeStart + offset + 2, &hasName, readMemCallback, args);
  721.             if (addressOfFnEnd)
  722.                 {
  723.                 // Found the previous procedure. Its dataEnd is the same as the codeStart we are looking for.
  724.                 //    10/18/94 dkk
  725.                 //    This used to call GetModuleName all the time. Can save the overhead of determining the module
  726.                 //    name again, by checking hasName returned from FindNextModule.
  727.                 //
  728.                 if (hasName)
  729.                     {
  730.                     GetModuleName (addressOfFnEnd, prevProcName, &prevDataStart, &codeStart, readMemCallback, args);
  731.                     return (codeStart);
  732.                     }
  733.                 else
  734.                     return (addressOfFnEnd);
  735.                 }
  736.             }
  737.         }
  738.     return NULL /* (gCurBlock.data) */;
  739. }
  740.  
  741.  
  742. //
  743. //    FindNextModule
  744. //
  745. //        Search for the next module in the address range start to limit-2.  Module names immediately follow either
  746. //        an RTS, RTD (plus offset), or JMP (A0) instruction.  The three formats for legal module names are described
  747. //        in the header comments for ValidName.  If no module name found, return address of end of module where name
  748. //        would have been.  hasName parameter set to indicate whether a name was found.
  749. //
  750. //        return value    -    address of next module
  751. //
  752.  
  753. static UByte *FindNextModule (UByte *start, UByte *limit, Boolean *hasName, ReadMemFn readMemCallback, va_list args)
  754. {
  755.     UByte *fnEnd;
  756.     UByte *firstFollowingModule = NULL;
  757.  
  758.     while ((start < limit) && FindReturn (start, limit, &fnEnd, readMemCallback, args))
  759.         {
  760.         // After the call to FindReturn, fnEnd contains the address following the return instruction (either an RTS,
  761.         //    RTD, or JMP (A0)).  This will be the address of procedure name, if there is one.
  762.         //
  763.         if (firstFollowingModule == NULL)
  764.             firstFollowingModule = fnEnd;
  765.         
  766.         if (ValidName (fnEnd, readMemCallback, args))
  767.             {
  768.             // Found a module name, return its address.
  769.             //
  770.             *hasName = true;
  771.             return (fnEnd);
  772.             }
  773.         else
  774.             // Wasn't a valid name, keep looking starting after return instruction.
  775.             //
  776.             start = fnEnd;
  777.         }
  778.     
  779.     *hasName = false;
  780.     return firstFollowingModule;
  781. }
  782.  
  783.  
  784. //
  785. //    ValidName
  786. //
  787. //        Checks to see if the name at the pointer one of three valid formats for a module name.
  788. //
  789. //        The three formats are:
  790. //
  791. //            Variable length:    The first byte is in the range $80 to $9F and is a length in the
  792. //                                    range 0 to $1F. The high order bit must be set. A length of 0
  793. //                                    implies the second byte is the actual length in the range $01 
  794. //                                    thru $FF. The length byte(s) and name may be an odd number of
  795. //                                    bytes. However, the data after the name is word aligned.
  796. //            Fixed length 8:        The first byte is in the range $20 to $7F and is an ASCII character.
  797. //                                    The high order bit may be set but is not required to be.
  798. //            Fixed length 16:    The first byte is in the range $20 to $7F and is an ASCII character.
  799. //                                    The high order bit may be set but is not required to be.
  800. //                                    The high order bit in the second byte is required to be set.
  801. //                                    This distinguishes the two types of fixed names.
  802. //
  803.  
  804. static Boolean ValidName (UByte *name, ReadMemFn readMemCallback, va_list args)
  805. {
  806.     char        nameCopy[256];
  807.     UByte        *namePtr = (UByte*) nameCopy;
  808.     UWord        length;
  809.     Boolean        valid;
  810.  
  811.     if ((*readMemCallback) ((void *) name, 256, nameCopy, args) != kNoErr)
  812.         return false;
  813.     
  814.     valid = true;
  815.  
  816.     // The high order bit of the first byte of the name is set for variable length names.
  817.     //
  818.     if (*namePtr & 0x80)
  819.         {
  820.         length = *namePtr++ & 0x7F;
  821.  
  822.         // After clearing the high order bit, the first byte should be in the range of 0-1F, for variable
  823.         //    length names.  If the length is 0, the length is in the next byte.  The next byte should not be 0.
  824.         //
  825.         if (length == 0)
  826.             if (*namePtr)
  827.                 length = *namePtr++;
  828.             else
  829.                 valid = false;
  830.         else
  831.             // Maximum length if in the first byte is 1F.
  832.             //
  833.             if (length > 0x1F)
  834.                 valid = false;
  835.         
  836.         if (valid)
  837.             // So far the name is valid, loop through the name checking for the valid symbol characters 
  838.             //    'a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.' (Variable length names cannot have spaces)
  839.             //    10/18/94 dkk
  840.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  841.             //    in name used to be.
  842.             //
  843.             for (;(length > 0) && valid; --length)
  844.                 if (valid)
  845.                     valid = LegalSymbolChar ((short) *namePtr++);
  846.         
  847.         }
  848.     else
  849.         {
  850.         //    High order bit in first byte of fixed length name may or may not be set.
  851.         //
  852.         valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  853.         if (valid)
  854.  
  855.             {
  856.             // First character was legal, check the high order bit of the second byte to see whether it is a 8 byte or
  857.             // 16 byte fixed length name.
  858.             //
  859.             if (*namePtr & 0x80)
  860.             
  861.                 // 16 byte name.  Check the second character and set the length for the remaining 14 characters.
  862.                 //
  863.                 {
  864.                 valid = LegalTargetChar ((short) (*namePtr++ & 0x7F));
  865.                 length = 14;
  866.                 }
  867.             else
  868.                 // 8 byte name.  Check the second character and set the length for the remaining 6 characters.
  869.                 //
  870.                 {
  871.                 valid = LegalTargetChar ((short) *namePtr++);
  872.                 length = 6;
  873.                 }
  874.             
  875.             //    10/18/94 dkk
  876.             //    Added check to see that valid was still true. Used to set valid to whatever the last character
  877.             //    in name used to be.
  878.             //
  879.             for (;(length > 0) && valid; --length)
  880.                 if (valid)
  881.                     valid = LegalTargetChar ((short) *namePtr++);
  882.             }
  883.         }
  884.     return (valid);
  885. }
  886.     
  887.  
  888. //
  889. //    GetModuleName
  890. //
  891. //   Copy the valid MacsBug module name at address into the name string.
  892. //   Set dataStart to point to the first byte after the name after making sure it is word aligned.
  893. //   Set dataEnd to point to the first byte of the next procedure.
  894. //
  895.  
  896. static void GetModuleName (UByte *address, char *name, UByte **dataStart, UByte **dataEnd, ReadMemFn readMemCallback, va_list args)
  897. {
  898.     UWord        memWord;
  899.     UByte        ch, index;
  900.     char        nameCopy[kMaxNameLength];
  901.     UByte        *namePtr = (UByte*) nameCopy;
  902.  
  903.     *name = 0;
  904.     *dataStart = address;
  905.     *dataEnd = address;
  906.     
  907.     if ((*readMemCallback) ((void *) address, kMaxNameLength, nameCopy, args) != kNoErr)
  908.         return;
  909.  
  910.     // Read first character of name, stripping off most significant bit.
  911.     //
  912.     ch = *namePtr++ & 0x7F;
  913.  
  914.     // Variable length name, if value is 0 thru 1F.
  915.     //
  916.     if (ch < 0x20)
  917.         {
  918.         // The name is the new variable format. This may be a module or a method name.
  919.         //
  920.         if (ch == 0)
  921.             // Second byte is the actual length.
  922.             //
  923.             ch = *namePtr++;
  924.  
  925.         name[0] = 0;
  926.         for (index = 1; index <= ch; ++index)
  927.             // When the name gets over kMaxNameLength, truncate the name and just increment to the end of the name.
  928.             //
  929.             if (index <= kMaxNameLength)
  930.                 name[++name[0]] = *namePtr++;
  931.             else
  932.                 break;
  933.         
  934.         // The name may not be word aligned. Data after the name always starts on a word boundary.
  935.         //    10/18/94 dkk
  936.         //    Added additional 2 character offset to address. In translation from MacsBug my guess is the length
  937.         //    bytes were accidentally omitted. This caused a bug where the last character of the symbol name was
  938.         //    added to the symbol name address as the literal pool size. This caused the returned address range
  939.         //    to not contain the address we were interested in.
  940.         //
  941.         address += ch + 2;
  942.         if ((ULongWord) address % 2)
  943.             ++address;
  944.             
  945.         *dataStart = address;
  946.  
  947.         //    Variable format names are followed by a word which defines the length of the literal pool
  948.         //    after the code of this procedure and before the code of the next procedure. If the word is
  949.         //    odd then assume it is not a length.
  950.         //
  951.         if ((*readMemCallback) ((void *) address, sizeof(UWord), &memWord, args) != kNoErr)
  952.             return;
  953.         if (memWord & 1)
  954.             // Length word is odd, not the length.
  955.             //
  956.             *dataEnd = address;
  957.         else
  958.             // Add length word + length.
  959.             //
  960.             *dataEnd = address + 2 + memWord;
  961.  
  962.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  963.         if (name[0] & 1)                // do we need a pad byte?
  964.             name[name[0] + 2] = 0;        // add one
  965.         }
  966.     else
  967.         {
  968.         // If the most significant bit of the second character is set, the name is the 16 byte class.method format,
  969.         // other wise its the 8 byte format.
  970.         //
  971.         if (*namePtr & 0x80)
  972.             {
  973.             //    The name is the 16 byte class.method format. Class and method are stored in reverse order in memory.
  974.             // Skip to byte 9 of the name to copy.  Address currently points to byte 2.
  975.             //
  976.             namePtr += 7;
  977.  
  978.             // Initialize length byte.
  979.             //
  980.             name[0] = 0;
  981.             
  982.             // Copy characters in the class.
  983.             //
  984.             for (index = 1; index <= 8; ++index)
  985.  
  986.                 // Strip the spaces used to pad to 8 characters.
  987.                 //
  988.                 if (*namePtr != ' ')
  989.                     name[++name[0]] = *namePtr++;
  990.                 else
  991.                     ++namePtr;
  992.  
  993.             //    Insert the '.' to indicate a method
  994.             //
  995.             name[++name[0]] = '.';
  996.             
  997.             // Reset pointer to beginning of the name.
  998.             //
  999.             namePtr -= 16;
  1000.             
  1001.             // Copy characters in method
  1002.             //
  1003.             for (index = 1; index <= 8; ++index)
  1004.  
  1005.                 // Strip the spaces used to pad to 8 characters.  First two bytes also have most significant bit set.
  1006.                 //
  1007.                 if (*namePtr & 0x7F != ' ')
  1008.                     name[++name[0]] = *namePtr++ & 0x7F;
  1009.                 else
  1010.                     ++namePtr;
  1011.  
  1012.             // Skip to byte after end of name.
  1013.             //
  1014.             address += 16;
  1015.             }
  1016.         else
  1017.             {
  1018.             // 8 byte format.  Copy first character and initialize length byte.
  1019.             //
  1020.             name[1] = ch;
  1021.             name[0] = 1;
  1022.             
  1023.             // Copy remaining characters in name.
  1024.             //
  1025.             for (index = 2; index <= 8; ++index)
  1026.  
  1027.                 // Strip the spaces used to pad to 8 characters.
  1028.                 //
  1029.                 if (*namePtr != ' ')
  1030.                     name[++name[0]] = *namePtr++;
  1031.                 else
  1032.                     ++namePtr;
  1033.  
  1034.             // Skip to byte after end of name.
  1035.             //
  1036.             address += 8;
  1037.             }
  1038.  
  1039.         //    Fixed length names do not indicate the data length. Assumed immediately after the name.
  1040.         //
  1041.         *dataStart = address;
  1042.         *dataEnd = address;
  1043.  
  1044.         name[name[0] + 1] = 0;            // terminating null to make it a TProtocolString
  1045.         if (name[0] & 1)                // do we need a pad byte?
  1046.             name[name[0] + 2] = 0;        // add one
  1047.         }
  1048.     
  1049. }
  1050.  
  1051.  
  1052. //
  1053. //    FindReturn
  1054. //
  1055. //        Search for an RTS, RTD or JMP (A0) instruction.  The address following the instruction is returned in afterReturn.
  1056. //        Function value is true if return is found, false otherwise.
  1057. //
  1058. //        start                -    Address to start search for return.
  1059. //        limit                -    Address to stop search for return.
  1060. //        afterReturn        -    Address after return instruction (if found).
  1061. //        return value    -    True if return is found, false otherwise.
  1062. //
  1063.  
  1064. static Boolean FindReturn (UByte *start, UByte *limit, UByte **afterReturn, ReadMemFn readMemCallback, va_list args)
  1065. {
  1066.     UWord        memWord;
  1067.     
  1068.     while (start < limit)
  1069.         {
  1070.         if ((*readMemCallback) ((void *) start, sizeof(UWord), &memWord, args) != kNoErr)
  1071.             return false;
  1072.         
  1073.         switch (memWord)
  1074.             {
  1075.             case kJMPA0:
  1076.             case kRTS:
  1077.                 *afterReturn = start + 2;            // JMP (A0) and RTS instruction are both two byte instructions
  1078.                 return (true);
  1079.                 break;
  1080.     
  1081.             case kRTD:
  1082.                 *afterReturn = start + 4;            //    RTD is a four byte instruction.
  1083.                 return (true);
  1084.                 break;
  1085.     
  1086.             default:
  1087.                 start += 2;
  1088.                 break;
  1089.             }
  1090.         }
  1091.         
  1092.     return (false);
  1093. }
  1094.  
  1095.  
  1096. //
  1097. //    LegalSymbolChar
  1098. //
  1099. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.']
  1100. //
  1101.  
  1102. static Boolean LegalSymbolChar (short ch)
  1103. {
  1104.     return (((ch >= 'a') && (ch <= 'z')) ||
  1105.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1106.                  ((ch >= '0') && (ch <= '9')) ||
  1107.                   (ch == '_') || (ch == '%') || (ch == '.'));
  1108. }
  1109.  
  1110.  
  1111. //
  1112. //    LegalTargetChar
  1113. //
  1114. //        Return True if ch is in the set ['a'..'z', 'A'..'Z', '0'..'9', '_', '%', '.', ' '].  Same as LegalSymbolChar
  1115. //        except that a space is also a legal character.
  1116. //
  1117.  
  1118. static Boolean LegalTargetChar (short ch)
  1119. {
  1120.     return (((ch >= 'a') && (ch <= 'z')) ||
  1121.                 ((ch >= 'A') && (ch <= 'Z')) ||
  1122.                  ((ch >= '0') && (ch <= '9')) ||
  1123.                   (ch == '_') || (ch == '%') || (ch == '.') || (ch == ' '));
  1124. }
  1125.  
  1126.  
  1127.  
  1128.