home *** CD-ROM | disk | FTP | other *** search
- /*
- * $Log: MachOFile.m,v $
- Revision 1.11 94/05/04 11:19:29 ediger
- support for finding which segment a thread's pc is in
-
- Revision 1.10 94/05/01 17:31:33 ediger
- added - (struct section *)sectionNamed:: method
-
- Revision 1.9 94/02/07 21:36:27 ediger
- Use memory-mapped file object, add segment name display, change
- to using a matrix of buttons for choice of what to display.
-
- Revision 1.8 94/01/30 16:26:59 ediger
- add threadList List instance var to hold all thread_command load commands
- seperately.
-
- Revision 1.7 93/12/20 20:49:20 ediger
- better method of transferring load commands from a LoadFVMLibCommand
- LoadCommand object to the current MachOFile object's mapped segment list.
-
- Revision 1.6 93/12/18 22:46:51 ediger
- changed -initFromFileNamed: method to -fillFromFileNamed: to reflect
- a bit more accuratly its function.
- Changed the way that subsidiary file's mapped segments get added in
- to the mapped segment list. That process didn't work the old way.
-
- Revision 1.5 93/12/07 23:30:31 ediger
- added some assert()s and cpuType and cpuSubtype methods
-
- Revision 1.4 93/12/02 00:44:23 ediger
- added fileType method, and a load of comments
-
- Revision 1.3 93/10/31 21:32:13 ediger
- ditched the -printSegments method, had been #ifdef'ed out anyway.
-
- Revision 1.2 93/10/27 23:44:09 ediger
- Definition of MachOFile object that uses LoadCommand subclasses
-
- */
- #import <MachOFile.h>
-
- @implementation MachOFile
-
- static char rcsident[] = "$Id: MachOFile.m,v 1.11 94/05/04 11:19:29 ediger Exp Locker: ediger $";
-
- //M+ -init
- // PURPOSE:
- // Set up a MachOFile object as much as possible without knowing
- // anything about the file it is to represent.
- //
- // EDITORIAL:
- // Assumes that "self" is already allocated.
- //M-
- - init
- {
- [super init];
-
- mappedSegmentList = [SortedList alloc];
- unmappedSegmentList = [List alloc];
- threadList = [List alloc];
-
- fileHeader = NULL;
- threadSegment = NULL;
-
- fileDescriptor = -1;
-
- mappedFileAddress = (vm_offset_t)0;
-
- addMappedFiles = FALSE;
-
- mapsFile = FALSE;
-
- return self;
- }
-
- //M+ -free
- // PURPOSE:
- // Clean up after a MachOFile object as much as possible.
- //
- // EDITORIAL:
- //M-
- - free
- {
- if (mapsFile == TRUE && mappedFileAddress != 0)
- { kern_return_t tRet = vm_deallocate(task_self(),
- (vm_address_t)mappedFileAddress, mappedFileSize);
- if (tRet != KERN_SUCCESS)
- { lastErrno = 0;
- lastKernReturn = tRet;
- }
- }
-
- if (fileDescriptor != -1 && close(fileDescriptor) < 0)
- { lastErrno = errno;
- lastKernReturn = 0;
- }
-
- fileDescriptor = -1;
-
- [[mappedSegmentList freeObjects] free];
- [[unmappedSegmentList freeObjects] free];
- [[threadList freeObjects] free];
-
- mappedSegmentList = NULL;
- unmappedSegmentList = NULL;
- threadList = NULL;
-
- if (threadSegment) free(threadSegment);
- threadSegment = NULL;
-
- return [super free];
- }
-
- // M+ -initLoadCommandLists:
- // PURPOSE:
- // Set up the sorted lists based on number of commands.
- //
- // EDITORIAL:
- // Overallocates. mapped segment list _and_ unmapped segment list
- // both are set up to have the total number of segments in them.
- // M-
- -initLoadCommandLists:(int)numberOfCommands
- {
- assert(unmappedSegmentList != NULL && mappedSegmentList != NULL);
-
- [unmappedSegmentList initCount:numberOfCommands];
- [mappedSegmentList initCount:numberOfCommands];
- [threadList initCount:1];
-
- [mappedSegmentList setSortOrder:DESCENDING];
- [mappedSegmentList setKeyDataType:UNSIGNED_LONG];
- [mappedSegmentList setKeyMethod:@selector(getBaseAddress)];
-
- return self;
- }
-
- // M+ -addLoadCommand:
- // PURPOSE:
- // Stick a new load command segment into the appropriate SortedList
- // M-
- -addLoadCommand:oNewSegment
- {
- assert(oNewSegment != Nil);
-
- if ([oNewSegment isMapped])
- [mappedSegmentList addObject:oNewSegment];
- else if ([oNewSegment isThread])
- [threadList addObject:oNewSegment];
- else
- [unmappedSegmentList addObject:oNewSegment];
- return self;
- }
-
- // M+ -addLoadCommandsFrom:
- // PURPOSE:
- // Based on a load command that represents a subsidiary file,
- // account for the load commands of that subsidiary file.
- // M-
- -addLoadCommandsFrom:oNewSegment
- {
- id otherFile;
-
- assert(oNewSegment != Nil);
-
- otherFile = [[oNewSegment loadOtherFile] otherFile];
-
- assert(otherFile != Nil);
-
- if ([otherFile mappedSegments] > 0)
- { id oOtherFileSegment;
-
- // Possibly too clever use of the fact that List objects contract
- // dynamically when you take an element out of them.
- while (Nil != (oOtherFileSegment = [otherFile mappedSegment:0]))
- { [mappedSegmentList addObject:oOtherFileSegment];
- [otherFile removeSegment:oOtherFileSegment];
- }
- }
-
- return self;
- }
-
- //M+ -fillFromFileNamed:
- // PURPOSE:
- // Fill in a MachOFile object now that it is known what file it represents.
- //M-
- - fillFromFileNamed: (char *) filename
- {
- assert(filename != NULL);
-
- if ([self mapFile:filename])
- {
- mapsFile = TRUE;
- [self fillFromAddress:(char *)mappedFileAddress];
- }
-
- return self;
- }
-
- //M+ -fillFromAddress:
- // PURPOSE:
- // Fill in a MachOFile object whose header is mapped in at the
- // address passed as argument.
- //M-
- - fillFromAddress: (char *) address
- {
-
- int iCC, iSegments;
- caddr_t tAddress;
-
- assert(address != NULL);
-
- fileHeader = (struct mach_header *)address;
-
- // cheesy check for a legit Mach-O file
- if (fileHeader->magic != MH_MAGIC)
- { [self free];
- return Nil;
- }
-
- [self initLoadCommandLists:fileHeader->ncmds];
-
- tAddress = (caddr_t)(fileHeader + 1);
-
- for (iCC = 0; iCC < fileHeader->ncmds; ++iCC)
- { id oNewSegment = [LoadCommand new:tAddress];
-
- tAddress += [oNewSegment commandSize];
-
- [self addLoadCommand:oNewSegment];
-
- if (addMappedFiles && [oNewSegment representsMappedFile])
- [self addLoadCommandsFrom:oNewSegment];
- }
-
- // set up array of thread number to segment number mapping.
- // thread number is index, segment number is value contained in
- // the array at that index.
- if ([self threads])
- {
- threadSegment = (int *)malloc([self threads] * sizeof(int));
- iSegments = [self mappedSegments];
- for (iCC = [self threads] - 1; iCC > -1; --iCC)
- {
- unsigned long ulThreadAddr = [[self thread:iCC] pc];
- int i;
-
- threadSegment[iCC] = -1;
-
- for (i = 0; i < iSegments; ++i)
- {
- id oSegment = [self mappedSegment:i];
-
- if ([oSegment getBaseAddress] <= ulThreadAddr
- && [oSegment getUpperAddress] >= ulThreadAddr)
- {
- threadSegment[iCC] = i;
- break;
- }
- }
- }
- }
-
- return self;
- }
-
- //M+ -(int)mapFile:
- // PURPOSE:
- // Perform stuff to memory map the Mach-O file under consideration.
- //
- // RETURN:
- // 1 on success, 0 on failure.
- //
- // EDITORIAL:
- // Apparently you must vm_deallocate() the stuff mapped in by map_fd().
- // Call -free on this object or else.
- //M-
- - (int)mapFile: (char *)filename
- {
- struct stat sStat;
- int irSuccess = 0;
-
- assert(filename != NULL);
-
- if (stat(filename, &sStat) < 0) {
- lastErrno = errno;
- lastKernReturn = 0;
- } else if ((fileDescriptor = open(filename, O_RDONLY)) < 0) {
- lastErrno = errno;
- lastKernReturn = 0;
- } else {
- kern_return_t tRet;
-
- tRet = map_fd(fileDescriptor, (vm_offset_t)0, &mappedFileAddress, TRUE,
- (size_t)sStat.st_size);
-
- if (tRet != KERN_SUCCESS) {
- lastErrno = 0;
- lastKernReturn = tRet;
- } else if ((vm_address_t)mappedFileAddress != (vm_address_t)NULL) {
-
- struct mach_header *mhdr = (struct mach_header *)mappedFileAddress;
- mappedFileSize = mhdr->sizeofcmds;
-
- tRet = vm_deallocate(task_self(),
- (vm_address_t)mappedFileAddress, (size_t)sStat.st_size);
- if (tRet != KERN_SUCCESS) {
- lastErrno = 0;
- lastKernReturn = tRet;
- } else {
-
- tRet = map_fd(fileDescriptor, (vm_offset_t)0,
- &mappedFileAddress, TRUE,
- (size_t)mappedFileSize);
-
- if (tRet != KERN_SUCCESS) {
- lastErrno = 0;
- lastKernReturn = tRet;
- } else
- irSuccess = 1;
- }
- }
- }
-
- return irSuccess;
- }
-
- //M+ -mappedSegments
- // PURPOSE:
- // Return number of load commands that represent stuff memory mapped
- // from a file.
- //M-
- - (int)mappedSegments
- {
- assert(mappedSegmentList != NULL);
- return [mappedSegmentList count];
- }
-
- //M+ -unmappedSegments
- // PURPOSE:
- // Return number of load commands that are aren't memory mapped from a file
- //M-
- - (int)unmappedSegments
- {
- assert(unmappedSegmentList != NULL);
- return [unmappedSegmentList count];
- }
-
- //M+ -threads
- // PURPOSE:
- // Return number of load commands that represent threads
- //M-
- - (int)threads
- {
- assert(threadList != NULL);
- return [threadList count];
- }
-
- //M+ -mappedSegment:
- //M-
- - mappedSegment:(int)segmentNumber
- {
- assert(mappedSegmentList != NULL);
- return [mappedSegmentList objectAt:segmentNumber];
- }
-
- //M+ -unmappedSegment:
- //M-
- - unmappedSegment:(int)segmentNumber
- {
- assert(unmappedSegmentList != NULL);
- return [unmappedSegmentList objectAt:segmentNumber];
- }
-
- //M+ -thread:
- //M-
- - thread:(int)threadNumber
- {
- assert(threadList != NULL);
- return [threadList objectAt:threadNumber];
- }
-
- //M+ -considerMappedFiles
- // Make an instance variable TRUE. This instance var causes the object
- // to also represent the mapped segments of the subsidiary files
- // referenced by load commands.
- //M-
- - considerMappedFiles
- {
- addMappedFiles = TRUE;
- return self;
- }
-
- //M+ -unconsiderMappedFiles
- // Make an instance variable FALSE. This instance var causes the object
- // to also represent the mapped segments of the subsidiary files
- // referenced by load commands.
- //M-
- - unconsiderMappedFiles
- {
- addMappedFiles = FALSE;
- return self;
- }
-
- //M+ MachOFile -removeSegment:aLoadCommand
- // Doesn't ever get called with an "unmapped" load command.
- //M-
- - removeSegment:aLoadCommand
- {
- assert(mappedSegmentList != NULL);
- assert(NX_NOT_IN_LIST != [mappedSegmentList indexOf:aLoadCommand]);
- [mappedSegmentList removeObject:aLoadCommand];
- return self;
- }
-
- // M+ -(char *)fileType
- // PURPOSE:
- // Allow for the "type" of Mach-O file that this object represents
- // to be discovered.
- // EDITORIAL:
- // This may be a bit less than generally useful.
- // Should maybe just give back the _number_ of the filetype, lettting
- // something else decide what phrase to use.
- // M-
- - (char *)fileType
- {
- char *pc = NULL;
- static char cBuf[512];
-
- assert(fileHeader != NULL);
-
- switch (fileHeader->filetype) {
- case MH_OBJECT:
- pc = "relocatable object file";
- break;
- case MH_EXECUTE:
- pc = "demand paged executable file";
- break;
- case MH_FVMLIB:
- pc = "fixed VM shared library file";
- break;
- case MH_CORE:
- pc = "core file";
- break;
- case MH_PRELOAD:
- pc = "preloaded executable file";
- break;
- default:
- sprintf(cBuf, "Odd file type: 0x%lx\n", fileHeader->filetype);
- pc = cBuf;
- break;
- }
-
- assert(pc != NULL);
-
- return pc;
- }
-
- // M+ -(char *)cpuType
- // PURPOSE:
- // Allow for the type of cpu that the Mach-O file that this object
- // represents to be discovered.
- // EDITORIAL:
- // This may be a bit less than generally useful.
- // M-
- - (char *)cpuType
- {
- char *pc = NULL;
- static char cBuf[512];
-
- assert(fileHeader != NULL);
-
- switch (fileHeader->cputype) {
- case CPU_TYPE_VAX:
- pc = "VAX";
- break;
- case CPU_TYPE_ROMP:
- pc = "ROMP";
- break;
- case CPU_TYPE_NS32032:
- pc = "NS32032";
- break;
- case CPU_TYPE_NS32332:
- pc = "NS32332";
- break;
- case CPU_TYPE_NS32532:
- pc = "NS32532";
- break;
- case CPU_TYPE_MC680x0:
- pc = "MC680x0";
- break;
- case CPU_TYPE_I386:
- pc = "I386";
- break;
- case CPU_TYPE_MIPS:
- pc = "MIPS";
- break;
- case CPU_TYPE_HPPA:
- pc = "HP-PA";
- break;
- case CPU_TYPE_ARM:
- pc = "ARM";
- break;
- case CPU_TYPE_MC88000:
- pc = "MC88000";
- break;
- case CPU_TYPE_SPARC:
- pc = "SPARC";
- break;
- case CPU_TYPE_I860:
- pc = "I860, big-endian";
- break;
- case CPU_TYPE_I860_LITTLE:
- pc = "I860, little-endian";
- break;
- case CPU_TYPE_RS6000:
- pc = "RS/6000";
- break;
- default:
- sprintf(cBuf, "Odd CPU type: 0x%lx\n",
- (unsigned long)fileHeader->cputype);
- pc = cBuf;
- break;
- }
-
- assert(pc != NULL);
-
- return pc;
- }
-
- // M+ -(char *)cpuSubtype
- // PURPOSE:
- // Allow for the subtype of cpu that the Mach-O file that this object
- // represents to be discovered.
- // EDITORIAL:
- // This may be a bit less than generally useful.
- // Doesn't have all of the cpu subtypes. 680x0 and Intel 80x86 only.
- // M-
- - (char *)cpuSubtype
- {
- char *pc = NULL;
- static char cBuf[512];
-
- assert(fileHeader != NULL);
-
- switch (fileHeader->cputype) {
- case CPU_TYPE_MC680x0:
- switch (fileHeader->cpusubtype) {
- case CPU_SUBTYPE_MC680x0_ALL:
- pc = "all MC680x0";
- break;
- case CPU_SUBTYPE_MC68040:
- pc = "MC68040";
- break;
- case CPU_SUBTYPE_MC68030_ONLY:
- pc = "MC68030 only";
- break;
- default:
- sprintf(cBuf, "Odd MC680x0 CPU subtype: 0x%lx\n",
- (unsigned long)fileHeader->cpusubtype);
- pc = cBuf;
- break;
- }
- break;
-
- case CPU_TYPE_I386:
- switch (fileHeader->cpusubtype) {
- case CPU_SUBTYPE_I386_ALL:
- pc = "all i386";
- break;
- case CPU_SUBTYPE_486:
- pc = "80486";
- break;
- default:
- sprintf(cBuf, "Odd I386 CPU subtype: 0x%lx\n",
- (unsigned long)fileHeader->cpusubtype);
- pc = cBuf;
- break;
- }
- break;
-
- default:
- pc = " ";
- break;
- }
-
- assert(pc != NULL);
-
- return pc;
- }
-
-
- //M+
- //M-
- - (struct section *)sectionNamed:(char *)sectName inSegNamed:(char *)segName
- {
- int iSegments;
- int iCC;
- struct section *sprAddr = NULL;
-
- assert(sectName != NULL && segName != NULL);
-
- iSegments = [self mappedSegments];
- for (iCC = 0; iCC < iSegments && sprAddr == NULL; ++iCC)
- { id oLdCmd = [self mappedSegment:iCC];
-
- assert(oLdCmd != Nil);
-
- if (!strcmp(segName, [oLdCmd commandName]))
- { int iSect, iNumSects;
-
- iNumSects = [oLdCmd numberOfSections];
- for( iSect = 0; iSect < iNumSects; ++iSect)
- { struct section *spSection;
-
- spSection = [oLdCmd getSection:iSect];
- assert(spSection != NULL);
-
- if (!strcmp(spSection->sectname, sectName))
- { sprAddr = spSection;
- break;
- }
- }
- }
- }
-
- return sprAddr;
- }
-
- //M+ - (int)segmentOfThread:(int)threadNumber
- // PURPOSE:
- // return the segment number that contains address of thread
- // threadNumber.
- // RETURN:
- // -1 if the segment number is invalid, or some error occurs.
- // abort() call does something weird in kernel address space.
- // no segment will contain program counter of that thread.
- //M-
- - (int)segmentOfThread:(int)threadNumber
- {
- int irSegNo = -1;
-
- // not really an error condition if threadSegment is NULL
- if (threadSegment && threadNumber < [self threads])
- irSegNo = threadSegment[threadNumber];
-
- return irSegNo;
- }
-
- @end
-