home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / MachOViewer / Source / MachOFile.m < prev    next >
Encoding:
Text File  |  1994-05-03  |  14.0 KB  |  651 lines

  1. /*
  2.  * $Log:    MachOFile.m,v $
  3. Revision 1.11  94/05/04  11:19:29  ediger
  4. support for finding which segment a thread's pc is in
  5.  
  6. Revision 1.10  94/05/01  17:31:33  ediger
  7. added - (struct section *)sectionNamed:: method
  8.  
  9. Revision 1.9  94/02/07  21:36:27  ediger
  10. Use memory-mapped file object, add segment name display, change
  11. to using a matrix of buttons for choice of what to display.
  12.  
  13. Revision 1.8  94/01/30  16:26:59  ediger
  14. add threadList List instance var to hold all thread_command load commands
  15. seperately.
  16.  
  17. Revision 1.7  93/12/20  20:49:20  ediger
  18. better method of transferring load commands from a LoadFVMLibCommand
  19. LoadCommand object to the current MachOFile object's mapped segment list.
  20.  
  21. Revision 1.6  93/12/18  22:46:51  ediger
  22. changed -initFromFileNamed: method to -fillFromFileNamed: to reflect
  23. a bit more accuratly its function.
  24. Changed the way that subsidiary file's mapped segments get added in
  25. to the mapped segment list.  That process didn't work the old way.
  26.  
  27. Revision 1.5  93/12/07  23:30:31  ediger
  28. added some assert()s and cpuType and cpuSubtype methods
  29.  
  30. Revision 1.4  93/12/02  00:44:23  ediger
  31. added fileType method, and a load of comments
  32.  
  33. Revision 1.3  93/10/31  21:32:13  ediger
  34. ditched the -printSegments method, had been #ifdef'ed out anyway.
  35.  
  36. Revision 1.2  93/10/27  23:44:09  ediger
  37. Definition of MachOFile object that uses LoadCommand subclasses
  38.  
  39.  */
  40. #import <MachOFile.h>
  41.  
  42. @implementation MachOFile
  43.  
  44. static char rcsident[] = "$Id: MachOFile.m,v 1.11 94/05/04 11:19:29 ediger Exp Locker: ediger $";
  45.  
  46. //M+    -init
  47. //    PURPOSE:
  48. //    Set up a MachOFile object as much as possible without knowing
  49. //    anything about the file it is to represent.
  50. //
  51. //    EDITORIAL:
  52. //    Assumes that "self" is already allocated.
  53. //M-
  54. - init
  55. {
  56.     [super init];
  57.  
  58.     mappedSegmentList   = [SortedList alloc];
  59.     unmappedSegmentList = [List alloc];
  60.     threadList          = [List alloc];
  61.  
  62.     fileHeader    = NULL;
  63.     threadSegment = NULL;
  64.  
  65.     fileDescriptor = -1;
  66.  
  67.     mappedFileAddress = (vm_offset_t)0;
  68.  
  69.     addMappedFiles = FALSE;
  70.  
  71.     mapsFile = FALSE;
  72.  
  73.     return self;
  74. }
  75.  
  76. //M+    -free
  77. //    PURPOSE:
  78. //    Clean up after a MachOFile object as much as possible.
  79. //
  80. //    EDITORIAL:
  81. //M-
  82. - free
  83. {
  84.     if (mapsFile == TRUE && mappedFileAddress != 0)
  85.     {    kern_return_t tRet = vm_deallocate(task_self(),
  86.             (vm_address_t)mappedFileAddress, mappedFileSize);
  87.         if (tRet != KERN_SUCCESS)
  88.         {    lastErrno = 0;
  89.             lastKernReturn = tRet;
  90.         }
  91.     }
  92.  
  93.     if (fileDescriptor != -1 && close(fileDescriptor) < 0)
  94.     {    lastErrno = errno;
  95.         lastKernReturn = 0;
  96.     }
  97.  
  98.     fileDescriptor = -1;
  99.  
  100.     [[mappedSegmentList freeObjects] free];
  101.     [[unmappedSegmentList freeObjects] free];
  102.     [[threadList freeObjects] free];
  103.  
  104.     mappedSegmentList = NULL;
  105.     unmappedSegmentList = NULL;
  106.     threadList = NULL;
  107.  
  108.     if (threadSegment) free(threadSegment);
  109.     threadSegment = NULL;
  110.  
  111.     return [super free];
  112. }
  113.  
  114. // M+    -initLoadCommandLists:
  115. //    PURPOSE:
  116. //    Set up the sorted lists based on number of commands.
  117. //
  118. //    EDITORIAL:
  119. //    Overallocates.  mapped segment list _and_ unmapped segment list
  120. //    both are set up to have the total number of segments in them.
  121. // M-
  122. -initLoadCommandLists:(int)numberOfCommands
  123. {
  124.     assert(unmappedSegmentList != NULL && mappedSegmentList != NULL);
  125.  
  126.     [unmappedSegmentList initCount:numberOfCommands];
  127.     [mappedSegmentList   initCount:numberOfCommands];
  128.     [threadList initCount:1];
  129.  
  130.     [mappedSegmentList   setSortOrder:DESCENDING];
  131.     [mappedSegmentList   setKeyDataType:UNSIGNED_LONG];
  132.     [mappedSegmentList   setKeyMethod:@selector(getBaseAddress)];
  133.  
  134.     return self;
  135. }
  136.  
  137. // M+    -addLoadCommand:
  138. //    PURPOSE:
  139. //    Stick a new load command segment into the appropriate SortedList
  140. // M-
  141. -addLoadCommand:oNewSegment
  142. {
  143.     assert(oNewSegment != Nil);
  144.  
  145.     if ([oNewSegment isMapped])
  146.         [mappedSegmentList addObject:oNewSegment];
  147.     else if ([oNewSegment isThread])
  148.         [threadList addObject:oNewSegment];
  149.     else
  150.         [unmappedSegmentList addObject:oNewSegment];
  151.     return self;
  152. }
  153.  
  154. // M+    -addLoadCommandsFrom:
  155. //    PURPOSE:
  156. //    Based on a load command that represents a subsidiary file,
  157. //    account for the load commands of that subsidiary file.
  158. // M-
  159. -addLoadCommandsFrom:oNewSegment
  160. {
  161.     id otherFile;
  162.  
  163.     assert(oNewSegment != Nil);
  164.  
  165.     otherFile = [[oNewSegment loadOtherFile] otherFile];
  166.  
  167.     assert(otherFile != Nil);
  168.  
  169.     if ([otherFile mappedSegments] > 0)
  170.     {    id oOtherFileSegment;
  171.  
  172.         // Possibly too clever use of the fact that List objects contract
  173.         // dynamically when you take an element out of them.
  174.         while (Nil != (oOtherFileSegment = [otherFile mappedSegment:0]))
  175.         {    [mappedSegmentList addObject:oOtherFileSegment];
  176.             [otherFile removeSegment:oOtherFileSegment];
  177.         }
  178.     }
  179.  
  180.     return self;
  181. }
  182.  
  183. //M+    -fillFromFileNamed:
  184. //    PURPOSE:
  185. //    Fill in a MachOFile object now that it is known what file it represents.
  186. //M-
  187. - fillFromFileNamed: (char *) filename
  188. {
  189.     assert(filename != NULL);
  190.  
  191.     if ([self mapFile:filename])
  192.     {
  193.         mapsFile = TRUE;
  194.         [self fillFromAddress:(char *)mappedFileAddress];
  195.     }
  196.  
  197.     return self;
  198. }
  199.  
  200. //M+    -fillFromAddress:
  201. //    PURPOSE:
  202. //    Fill in a MachOFile object whose header is mapped in at the
  203. //    address passed as argument.
  204. //M-
  205. - fillFromAddress: (char *) address
  206. {
  207.  
  208.     int     iCC, iSegments;
  209.     caddr_t    tAddress;
  210.  
  211.     assert(address != NULL);
  212.  
  213.     fileHeader = (struct mach_header *)address;
  214.  
  215.     // cheesy check for a legit Mach-O file
  216.     if (fileHeader->magic != MH_MAGIC)
  217.     {    [self free];
  218.         return Nil;
  219.     }
  220.  
  221.     [self initLoadCommandLists:fileHeader->ncmds];
  222.  
  223.     tAddress = (caddr_t)(fileHeader + 1);
  224.  
  225.     for (iCC = 0; iCC < fileHeader->ncmds; ++iCC)
  226.     {    id oNewSegment = [LoadCommand new:tAddress];
  227.  
  228.         tAddress += [oNewSegment commandSize];
  229.  
  230.         [self addLoadCommand:oNewSegment];
  231.  
  232.         if (addMappedFiles && [oNewSegment representsMappedFile])
  233.             [self addLoadCommandsFrom:oNewSegment];
  234.     }
  235.  
  236.     // set up array of thread number to segment number mapping.
  237.     // thread number is index, segment number is value contained in
  238.     // the array at that index.
  239.     if ([self threads])
  240.     {
  241.         threadSegment = (int *)malloc([self threads] * sizeof(int));
  242.         iSegments = [self mappedSegments];
  243.         for (iCC = [self threads] - 1; iCC > -1; --iCC)
  244.         {
  245.             unsigned long ulThreadAddr = [[self thread:iCC] pc];
  246.             int i;
  247.     
  248.             threadSegment[iCC] = -1;
  249.  
  250.             for (i = 0; i < iSegments; ++i)
  251.             {
  252.                 id oSegment = [self mappedSegment:i];
  253.     
  254.                 if ([oSegment getBaseAddress] <= ulThreadAddr
  255.                     && [oSegment getUpperAddress] >= ulThreadAddr)
  256.                 {
  257.                     threadSegment[iCC] = i;
  258.                     break;
  259.                 }
  260.             }
  261.         }
  262.     }
  263.  
  264.     return self;
  265. }
  266.  
  267. //M+    -(int)mapFile:
  268. //    PURPOSE:
  269. //    Perform stuff to memory map the Mach-O file under consideration.
  270. //
  271. //    RETURN:
  272. //    1 on success, 0 on failure.
  273. //
  274. //    EDITORIAL:
  275. //    Apparently you must vm_deallocate() the stuff mapped in by map_fd().
  276. //    Call -free on this object or else.
  277. //M-
  278. - (int)mapFile: (char *)filename
  279. {
  280.     struct stat sStat;
  281.     int         irSuccess = 0;
  282.  
  283.     assert(filename != NULL);
  284.  
  285.     if (stat(filename, &sStat) < 0) {
  286.         lastErrno = errno;
  287.         lastKernReturn = 0;
  288.     } else if ((fileDescriptor = open(filename, O_RDONLY)) < 0) {
  289.         lastErrno = errno;
  290.         lastKernReturn = 0;
  291.     } else {
  292.         kern_return_t tRet;
  293.  
  294.         tRet = map_fd(fileDescriptor, (vm_offset_t)0, &mappedFileAddress, TRUE,
  295.             (size_t)sStat.st_size);
  296.  
  297.         if (tRet != KERN_SUCCESS) {
  298.             lastErrno = 0;
  299.             lastKernReturn = tRet;
  300.         } else if ((vm_address_t)mappedFileAddress != (vm_address_t)NULL) {
  301.  
  302.             struct mach_header *mhdr = (struct mach_header *)mappedFileAddress;
  303.             mappedFileSize = mhdr->sizeofcmds;
  304.  
  305.             tRet = vm_deallocate(task_self(),
  306.                 (vm_address_t)mappedFileAddress, (size_t)sStat.st_size);
  307.             if (tRet != KERN_SUCCESS) {
  308.                 lastErrno = 0;
  309.                 lastKernReturn = tRet;
  310.             } else {
  311.  
  312.                 tRet = map_fd(fileDescriptor, (vm_offset_t)0,
  313.                     &mappedFileAddress, TRUE,
  314.                     (size_t)mappedFileSize);
  315.  
  316.                 if (tRet != KERN_SUCCESS) {
  317.                     lastErrno = 0;
  318.                     lastKernReturn = tRet;
  319.                 } else
  320.                     irSuccess = 1;
  321.             }
  322.         }
  323.     }
  324.  
  325.     return irSuccess;
  326. }
  327.  
  328. //M+    -mappedSegments
  329. //    PURPOSE:
  330. //    Return number of load commands that represent stuff memory mapped
  331. //    from a file.
  332. //M-
  333. - (int)mappedSegments
  334. {
  335.     assert(mappedSegmentList != NULL);
  336.     return [mappedSegmentList count];
  337. }
  338.  
  339. //M+    -unmappedSegments
  340. //    PURPOSE:
  341. //    Return number of load commands that are aren't memory mapped from a file
  342. //M-
  343. - (int)unmappedSegments
  344. {
  345.     assert(unmappedSegmentList != NULL);
  346.     return [unmappedSegmentList count];
  347. }
  348.  
  349. //M+    -threads
  350. //    PURPOSE:
  351. //    Return number of load commands that represent threads
  352. //M-
  353. - (int)threads
  354. {
  355.     assert(threadList != NULL);
  356.     return [threadList count];
  357. }
  358.  
  359. //M+    -mappedSegment:
  360. //M-
  361. - mappedSegment:(int)segmentNumber
  362. {
  363.     assert(mappedSegmentList != NULL);
  364.     return [mappedSegmentList objectAt:segmentNumber];
  365. }
  366.  
  367. //M+    -unmappedSegment:
  368. //M-
  369. - unmappedSegment:(int)segmentNumber
  370. {
  371.     assert(unmappedSegmentList != NULL);
  372.     return [unmappedSegmentList objectAt:segmentNumber];
  373. }
  374.  
  375. //M+    -thread:
  376. //M-
  377. - thread:(int)threadNumber
  378. {
  379.     assert(threadList != NULL);
  380.     return [threadList objectAt:threadNumber];
  381. }
  382.  
  383. //M+    -considerMappedFiles
  384. //        Make an instance variable TRUE.  This instance var causes the object
  385. //        to also represent the mapped segments of the subsidiary files
  386. //        referenced by load commands.
  387. //M-
  388. - considerMappedFiles
  389. {
  390.     addMappedFiles = TRUE;
  391.     return self;
  392. }
  393.  
  394. //M+    -unconsiderMappedFiles
  395. //        Make an instance variable FALSE.  This instance var causes the object
  396. //        to also represent the mapped segments of the subsidiary files
  397. //        referenced by load commands.
  398. //M-
  399. - unconsiderMappedFiles
  400. {
  401.     addMappedFiles = FALSE;
  402.     return self;
  403. }
  404.  
  405. //M+    MachOFile -removeSegment:aLoadCommand
  406. //        Doesn't ever get called with an "unmapped" load command.
  407. //M-
  408. - removeSegment:aLoadCommand
  409. {
  410.     assert(mappedSegmentList != NULL);
  411.     assert(NX_NOT_IN_LIST != [mappedSegmentList indexOf:aLoadCommand]);
  412.     [mappedSegmentList removeObject:aLoadCommand];
  413.     return self;
  414. }
  415.  
  416. // M+    -(char *)fileType
  417. //    PURPOSE:
  418. //        Allow for the "type" of Mach-O file that this object represents
  419. //        to be discovered.
  420. //    EDITORIAL:
  421. //        This may be a bit less than generally useful.
  422. //        Should maybe just give back the _number_ of the filetype, lettting
  423. //        something else decide what phrase to use.
  424. // M-
  425. - (char *)fileType
  426. {
  427.     char *pc = NULL;
  428.     static char cBuf[512];
  429.  
  430.     assert(fileHeader != NULL);
  431.  
  432.     switch (fileHeader->filetype) {
  433.     case MH_OBJECT:
  434.         pc = "relocatable object file";
  435.         break;
  436.     case MH_EXECUTE:
  437.         pc = "demand paged executable file";
  438.         break;
  439.     case MH_FVMLIB:
  440.         pc = "fixed VM shared library file";
  441.         break;
  442.     case MH_CORE:
  443.         pc = "core file";
  444.         break;
  445.     case MH_PRELOAD:
  446.         pc = "preloaded executable file";
  447.         break;
  448.     default:
  449.         sprintf(cBuf, "Odd file type: 0x%lx\n", fileHeader->filetype);
  450.         pc = cBuf;
  451.         break;
  452.     }
  453.  
  454.     assert(pc != NULL);
  455.  
  456.     return pc;
  457. }
  458.  
  459. // M+    -(char *)cpuType
  460. //    PURPOSE:
  461. //        Allow for the type of cpu that the Mach-O file that this object
  462. //        represents to be discovered.
  463. //    EDITORIAL:
  464. //        This may be a bit less than generally useful.
  465. // M-
  466. - (char *)cpuType
  467. {
  468.     char *pc = NULL;
  469.     static char cBuf[512];
  470.  
  471.     assert(fileHeader != NULL);
  472.  
  473.     switch (fileHeader->cputype) {
  474.     case CPU_TYPE_VAX:
  475.         pc = "VAX";
  476.         break;
  477.     case CPU_TYPE_ROMP:
  478.         pc = "ROMP";
  479.         break;
  480.     case CPU_TYPE_NS32032:
  481.         pc = "NS32032";
  482.         break;
  483.     case CPU_TYPE_NS32332:
  484.         pc = "NS32332";
  485.         break;
  486.     case CPU_TYPE_NS32532:
  487.         pc = "NS32532";
  488.         break;
  489.     case CPU_TYPE_MC680x0:
  490.         pc = "MC680x0";
  491.         break;
  492.     case CPU_TYPE_I386:
  493.         pc = "I386";
  494.         break;
  495.     case CPU_TYPE_MIPS:
  496.         pc = "MIPS";
  497.         break;
  498.     case CPU_TYPE_HPPA:
  499.         pc = "HP-PA";
  500.         break;
  501.     case CPU_TYPE_ARM:
  502.         pc = "ARM";
  503.         break;
  504.     case CPU_TYPE_MC88000:
  505.         pc = "MC88000";
  506.         break;
  507.     case CPU_TYPE_SPARC:
  508.         pc = "SPARC";
  509.         break;
  510.     case CPU_TYPE_I860:
  511.         pc = "I860, big-endian";
  512.         break;
  513.     case CPU_TYPE_I860_LITTLE:
  514.         pc = "I860, little-endian";
  515.         break;
  516.     case CPU_TYPE_RS6000:
  517.         pc = "RS/6000";
  518.         break;
  519.     default:
  520.         sprintf(cBuf, "Odd CPU type: 0x%lx\n",
  521.             (unsigned long)fileHeader->cputype);
  522.         pc = cBuf;
  523.         break;
  524.     }
  525.  
  526.     assert(pc != NULL);
  527.  
  528.     return pc;
  529. }
  530.  
  531. // M+    -(char *)cpuSubtype
  532. //    PURPOSE:
  533. //        Allow for the subtype of cpu that the Mach-O file that this object
  534. //        represents to be discovered.
  535. //    EDITORIAL:
  536. //        This may be a bit less than generally useful.
  537. //        Doesn't have all of the cpu subtypes.  680x0 and Intel 80x86 only.
  538. // M-
  539. - (char *)cpuSubtype
  540. {
  541.     char *pc = NULL;
  542.     static char cBuf[512];
  543.  
  544.     assert(fileHeader != NULL);
  545.  
  546.     switch (fileHeader->cputype) {
  547.     case CPU_TYPE_MC680x0:
  548.         switch (fileHeader->cpusubtype) {
  549.         case    CPU_SUBTYPE_MC680x0_ALL:
  550.             pc = "all MC680x0";
  551.             break;
  552.         case CPU_SUBTYPE_MC68040:
  553.             pc = "MC68040";
  554.             break;
  555.         case    CPU_SUBTYPE_MC68030_ONLY:
  556.             pc = "MC68030 only";
  557.             break;
  558.         default:
  559.             sprintf(cBuf, "Odd MC680x0 CPU subtype: 0x%lx\n",
  560.                 (unsigned long)fileHeader->cpusubtype);
  561.             pc = cBuf;
  562.             break;
  563.         }
  564.         break;
  565.  
  566.     case CPU_TYPE_I386:
  567.         switch (fileHeader->cpusubtype) {
  568.         case CPU_SUBTYPE_I386_ALL:
  569.             pc = "all i386";
  570.             break;
  571.         case CPU_SUBTYPE_486:
  572.             pc = "80486";
  573.             break;
  574.         default:
  575.             sprintf(cBuf, "Odd I386 CPU subtype: 0x%lx\n",
  576.                 (unsigned long)fileHeader->cpusubtype);
  577.             pc = cBuf;
  578.             break;
  579.         }
  580.         break;
  581.  
  582.     default:
  583.         pc = " ";
  584.         break;
  585.     }
  586.  
  587.     assert(pc != NULL);
  588.  
  589.     return pc;
  590. }
  591.  
  592.  
  593. //M+
  594. //M-
  595. - (struct section *)sectionNamed:(char *)sectName inSegNamed:(char *)segName
  596. {
  597.     int        iSegments;
  598.     int        iCC;
  599.     struct section *sprAddr = NULL;
  600.  
  601.     assert(sectName != NULL && segName != NULL);
  602.  
  603.     iSegments = [self mappedSegments];
  604.     for (iCC = 0; iCC < iSegments && sprAddr == NULL; ++iCC)
  605.     {    id oLdCmd = [self mappedSegment:iCC];
  606.  
  607.         assert(oLdCmd != Nil);
  608.  
  609.         if (!strcmp(segName, [oLdCmd commandName]))
  610.         {    int iSect, iNumSects;
  611.  
  612.             iNumSects = [oLdCmd numberOfSections];
  613.             for( iSect = 0; iSect < iNumSects; ++iSect)
  614.             {    struct section *spSection;
  615.  
  616.                 spSection = [oLdCmd getSection:iSect];
  617.                 assert(spSection != NULL);
  618.  
  619.                 if (!strcmp(spSection->sectname, sectName))
  620.                 {    sprAddr = spSection;
  621.                     break;
  622.                 }
  623.             }
  624.         }
  625.     }
  626.  
  627.     return sprAddr;
  628. }
  629.  
  630. //M+ - (int)segmentOfThread:(int)threadNumber
  631. //    PURPOSE:
  632. //        return the segment number that contains address of thread
  633. //        threadNumber.
  634. //    RETURN:
  635. //        -1 if the segment number is invalid, or some error occurs.
  636. //        abort() call does something weird in kernel address space.
  637. //        no segment will contain program counter of that thread.
  638. //M-
  639. - (int)segmentOfThread:(int)threadNumber
  640. {
  641.     int irSegNo = -1;
  642.  
  643.     // not really an error condition if threadSegment is NULL
  644.     if (threadSegment && threadNumber < [self threads])
  645.         irSegNo = threadSegment[threadNumber];
  646.  
  647.     return irSegNo;
  648. }
  649.  
  650. @end
  651.