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