mirror of
https://github.com/QuasarApp/macdependency.git
synced 2025-04-28 13:24:32 +00:00
initial checkin of Cocoa version of macdependency
This commit is contained in:
commit
c733d07131
36
MacDependency/ArchitectureModel.h
Normal file
36
MacDependency/ArchitectureModel.h
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// ArchitectureModel.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 22.08.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MachOModel.h"
|
||||
#include "macho/machoarchitecture.h"
|
||||
#include "MachO/machoheader.h"
|
||||
|
||||
@class MyDocument;
|
||||
|
||||
@interface ArchitectureModel : NSObject {
|
||||
MachOArchitecture* architecture;
|
||||
MachOModel* machOModel;
|
||||
MachO* file;
|
||||
MyDocument* document;
|
||||
NSMutableArray* symbolEntries;
|
||||
|
||||
}
|
||||
- (id) initWithArchitecture:(MachOArchitecture*)architecture file:(MachO*)file document:(MyDocument*)document isRoot:(BOOL)isRoot;
|
||||
- (NSString*) label;
|
||||
- (NSString*) fileType;
|
||||
- (NSString*) idName;
|
||||
- (NSString*) size;
|
||||
- (NSString*) version;
|
||||
|
||||
+ (NSString*) cpuType:(MachOHeader::CpuType)cpuType;
|
||||
|
||||
- (void) refreshSymbols;
|
||||
- (NSArray*) symbols;
|
||||
- (void) setSymbols:(NSMutableArray*) symbolEntries;
|
||||
@end
|
225
MacDependency/ArchitectureModel.mm
Normal file
225
MacDependency/ArchitectureModel.mm
Normal file
@ -0,0 +1,225 @@
|
||||
//
|
||||
// ArchitectureModel.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 22.08.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ArchitectureModel.h"
|
||||
#import "VersionFormatter.h"
|
||||
#import "ConversionStdString.h"
|
||||
|
||||
#include "MachO/machoexception.h"
|
||||
#include "MachO/symboltablecommand.h"
|
||||
|
||||
#import "SymbolTableEntryModel.h"
|
||||
#import "MyDocument.h"
|
||||
|
||||
// declare private methods
|
||||
@interface ArchitectureModel ()
|
||||
- (void) initSymbols;
|
||||
@end
|
||||
|
||||
|
||||
@implementation ArchitectureModel
|
||||
|
||||
- (id) initWithArchitecture:(MachOArchitecture*)architecture file:(MachO*)file document:(MyDocument*)document isRoot:(BOOL)isRoot{
|
||||
self->architecture = architecture;
|
||||
self->file = file;
|
||||
self->document = document;
|
||||
self->symbolEntries = nil;
|
||||
|
||||
// create model with the current architecture selected (create from existing model)
|
||||
if (isRoot) {
|
||||
machOModel = nil;
|
||||
} else {
|
||||
machOModel = [[MachOModel alloc]initWithFile:file document:document architecture:architecture loadChildren:NO];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[symbolEntries release];
|
||||
[machOModel release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSArray*) rootModel {
|
||||
// always reload model (for error messages)
|
||||
[machOModel release];
|
||||
machOModel = [[MachOModel alloc]initWithFile:file document:document architecture:architecture loadChildren:YES];
|
||||
NSArray* rootModel = [NSArray arrayWithObject:machOModel];
|
||||
return rootModel;
|
||||
}
|
||||
|
||||
+ (NSString*) cpuType:(MachOHeader::CpuType)cpuType {
|
||||
NSString* label;
|
||||
switch (cpuType) {
|
||||
case MachOHeader::CpuTypePowerPc:
|
||||
label = NSLocalizedString(@"CPU_TYPE_PPC", nil);
|
||||
break;
|
||||
case MachOHeader::CpuTypeI386:
|
||||
label = NSLocalizedString(@"CPU_TYPE_I386", nil);
|
||||
break;
|
||||
case MachOHeader::CpuTypePowerPc64:
|
||||
label = NSLocalizedString(@"CPU_TYPE_PPC64", nil);
|
||||
break;
|
||||
case MachOHeader::CpuTypeX8664:
|
||||
label = NSLocalizedString(@"CPU_TYPE_X8664", nil);
|
||||
break;
|
||||
case MachOHeader::CpuTypeOther:
|
||||
label = NSLocalizedString(@"UNDEFINED", nil);
|
||||
break;
|
||||
|
||||
}
|
||||
return label;
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (NSString*) label {
|
||||
return [ArchitectureModel cpuType:architecture->getHeader()->getCpuType()];
|
||||
}
|
||||
|
||||
- (NSString*) fileType {
|
||||
NSString* type;
|
||||
switch(architecture->getHeader()->getFileType()) {
|
||||
case MachOHeader::FileTypeObject: /* relocatable object file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_OBJECT", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeExecutable: /* demand paged executable file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_EXECUTABLE", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeVmLib: /* fixed VM shared library file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_VM_SHARED_LIBRARY", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeCore: /* core file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_CORE", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypePreload: /* preloaded executable file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_PRELOADED_EXECUTABLE", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeDylib: /* dynamically bound shared library */
|
||||
type = NSLocalizedString(@"FILE_TYPE_SHARED_LIBRARY", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeDylinker: /* dynamic link editor */
|
||||
type = NSLocalizedString(@"FILE_TYPE_DYNAMIC_LINKER", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeBundle: /* dynamically bound bundle file */
|
||||
type = NSLocalizedString(@"FILE_TYPE_BUNDLE", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeDylibStub: /* shared library stub for static linking only, no section contents */
|
||||
type = NSLocalizedString(@"FILE_TYPE_STATIC_LIBRARY", nil);
|
||||
break;
|
||||
case MachOHeader::FileTypeDsym: /* companion file with only debug sections */
|
||||
type = NSLocalizedString(@"FILE_TYPE_DSYM", nil);
|
||||
break;
|
||||
default:
|
||||
type = NSLocalizedString(@"UNDEFINED", @"Unknown");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
- (NSString*) idName {
|
||||
NSString* idName = [NSString string];
|
||||
DylibCommand* dynamicLibraryIdCommand = architecture->getDynamicLibIdCommand();
|
||||
if (dynamicLibraryIdCommand != NULL) {
|
||||
idName = [NSString stringWithStdString:dynamicLibraryIdCommand->getName()];
|
||||
}
|
||||
return idName;
|
||||
}
|
||||
|
||||
- (NSString*) size {
|
||||
NSString* size;
|
||||
// somehow we can't use this directly in stringWithFormat, because then the second parameter is always zero.
|
||||
unsigned int architectureSize = architecture->getSize();
|
||||
unsigned int fileSize = [machOModel size];
|
||||
if (fileSize != architectureSize) {
|
||||
size = [NSString stringWithFormat:NSLocalizedString(@"SIZE_FORMAT_ARCHITECTURE", nil),
|
||||
fileSize/1024, architectureSize/1024];
|
||||
} else {
|
||||
size = [NSString stringWithFormat:NSLocalizedString(@"SIZE_FORMAT", nil),
|
||||
fileSize/1024];
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
- (NSString*) version {
|
||||
NSString* version = @"?";
|
||||
DylibCommand* dynamicLibraryIdCommand = architecture->getDynamicLibIdCommand();
|
||||
if (dynamicLibraryIdCommand != NULL) {
|
||||
VersionFormatter* versionFormatter = [[VersionFormatter alloc] init];
|
||||
|
||||
time_t timestamp = dynamicLibraryIdCommand->getTimeStamp();
|
||||
NSString* currentVersion = [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:dynamicLibraryIdCommand->getCurrentVersion()]];
|
||||
NSString* compatibleVersion = [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:dynamicLibraryIdCommand->getCompatibleVersion()]];
|
||||
if ([currentVersion length]+[compatibleVersion length] > 0) {
|
||||
if (timestamp > 1) {
|
||||
NSDate* date = [NSDate dateWithTimeIntervalSince1970:timestamp];
|
||||
|
||||
// this date formatter should be identical to NSDateFormatter in IB
|
||||
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
|
||||
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
|
||||
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
|
||||
|
||||
version = [NSString stringWithFormat:NSLocalizedString(@"VERSION_FORMAT_TIMESTAMP", nil),
|
||||
currentVersion,
|
||||
compatibleVersion,
|
||||
[dateFormatter stringFromDate:date]];
|
||||
}
|
||||
else {
|
||||
version = [NSString stringWithFormat:NSLocalizedString(@"VERSION_FORMAT", nil),
|
||||
currentVersion,
|
||||
compatibleVersion];
|
||||
}
|
||||
}
|
||||
[versionFormatter release];
|
||||
} else {
|
||||
version = [machOModel version];
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
- (void) initSymbols {
|
||||
symbolEntries = [NSMutableArray arrayWithCapacity:20];
|
||||
[symbolEntries retain];
|
||||
for (MachOArchitecture::LoadCommandsConstIterator it = architecture->getLoadCommandsBegin();
|
||||
it != architecture->getLoadCommandsEnd();
|
||||
++it)
|
||||
{
|
||||
// check if it is dylibcommand
|
||||
SymbolTableCommand* command = dynamic_cast<SymbolTableCommand*> (*it);
|
||||
if (command != 0) {
|
||||
for (SymbolTableCommand::SymbolTableEntriesConstIterator it = command->getSymbolTableEntryBegin(); it != command->getSymbolTableEntryEnd(); it++) {
|
||||
SymbolTableEntryModel* symbolModel = [[SymbolTableEntryModel alloc] initWithEntry:*(it) demangleNamesPtr:[[document symbolTableController]demangleNamesPtr]];
|
||||
[symbolEntries addObject:symbolModel];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*) symbols {
|
||||
if (symbolEntries == nil) {
|
||||
[self initSymbols];
|
||||
}
|
||||
return symbolEntries;
|
||||
}
|
||||
|
||||
- (void) setSymbols:(NSMutableArray*) symbolEntries {
|
||||
[symbolEntries retain];
|
||||
[self->symbolEntries release];
|
||||
self->symbolEntries = symbolEntries;
|
||||
}
|
||||
|
||||
- (void) refreshSymbols {
|
||||
[symbolEntries release];
|
||||
[self initSymbols];
|
||||
[self setSymbols:symbolEntries];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
16
MacDependency/ArchitecturesController.h
Normal file
16
MacDependency/ArchitecturesController.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// ArchitecturesController.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 22.08.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class MyDocument;
|
||||
|
||||
@interface ArchitecturesController : NSArrayController {
|
||||
IBOutlet MyDocument* document;
|
||||
}
|
||||
@end
|
47
MacDependency/ArchitecturesController.mm
Normal file
47
MacDependency/ArchitecturesController.mm
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// ArchitecturesController.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 22.08.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Only used for root architectures.
|
||||
//
|
||||
#import "ArchitecturesController.h"
|
||||
#import "MyDocument.h"
|
||||
|
||||
// declare private methods
|
||||
@interface ArchitecturesController ()
|
||||
@end
|
||||
|
||||
@implementation ArchitecturesController
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (self) {
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
}
|
||||
|
||||
- (void)setSelectionIndex:(NSUInteger)index {
|
||||
// clear log
|
||||
[document clearLog];
|
||||
[document resetNumDependencies];
|
||||
[super setSelectionIndex:index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
16
MacDependency/AutoExpandOutlineView.h
Normal file
16
MacDependency/AutoExpandOutlineView.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// AutoExpandOutlineView.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 03.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface AutoExpandOutlineView : NSOutlineView {
|
||||
|
||||
}
|
||||
|
||||
@end
|
26
MacDependency/AutoExpandOutlineView.m
Normal file
26
MacDependency/AutoExpandOutlineView.m
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// AutoExpandOutlineView.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 03.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AutoExpandOutlineView.h"
|
||||
|
||||
|
||||
@implementation AutoExpandOutlineView
|
||||
|
||||
|
||||
// gets called after binding has changed
|
||||
- (void)reloadData;
|
||||
{
|
||||
[super reloadData];
|
||||
|
||||
// auto expand root item
|
||||
NSTreeNode* item = [self itemAtRow:0];
|
||||
if (item)
|
||||
[self expandItem:item];
|
||||
}
|
||||
|
||||
@end
|
19
MacDependency/ConversionStdString.h
Normal file
19
MacDependency/ConversionStdString.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// ConversionStdString.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 17.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@interface NSString (ConversionStdString)
|
||||
|
||||
+ (NSString*) stringWithStdString:(const std::string&)string;
|
||||
- (std::string) stdString;
|
||||
+ (NSString*) stringWithStdWString:(const std::wstring&)string;
|
||||
- (std::wstring) stdWString;
|
||||
@end
|
46
MacDependency/ConversionStdString.mm
Normal file
46
MacDependency/ConversionStdString.mm
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// ConversionStdString.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 17.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Conversion functions for NSString from/to std::string and std::wstring
|
||||
#import "ConversionStdString.h"
|
||||
|
||||
|
||||
@implementation NSString (ConversionStdString)
|
||||
+ (NSString*) stringWithStdString:(const std::string&)string {
|
||||
return [NSString stringWithCString:string.c_str() encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
- (std::string) stdString {
|
||||
return std::string([self cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
}
|
||||
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
const NSStringEncoding kEncoding_wchar_t =
|
||||
CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32BE);
|
||||
#else
|
||||
const NSStringEncoding kEncoding_wchar_t =
|
||||
CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32LE);
|
||||
#endif
|
||||
|
||||
+(NSString*) stringWithStdWString:(const std::wstring&)ws
|
||||
{
|
||||
char* data = (char*)ws.data();
|
||||
unsigned size = ws.size() * sizeof(wchar_t);
|
||||
|
||||
NSString* result = [[[NSString alloc] initWithBytes:data length:size
|
||||
encoding:kEncoding_wchar_t] autorelease];
|
||||
return result;
|
||||
}
|
||||
|
||||
-(std::wstring) stdWString
|
||||
{
|
||||
NSData* asData = [self dataUsingEncoding:kEncoding_wchar_t];
|
||||
return std::wstring((wchar_t*)[asData bytes], [asData length] /
|
||||
sizeof(wchar_t));
|
||||
}
|
||||
|
||||
@end
|
14
MacDependency/ExtTreeModel.h
Normal file
14
MacDependency/ExtTreeModel.h
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// ExtTreeModel.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 02.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Protocol which provides a method to get the parent (used by TreeControllerExtension)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@protocol ExtTreeModel
|
||||
- (id) parent;
|
||||
@end
|
47
MacDependency/Info.plist
Normal file
47
MacDependency/Info.plist
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>app</string>
|
||||
<string>txt</string>
|
||||
<string>*</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>DocumentType</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>MyDocument</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.googlecode.${PRODUCT_NAME:identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
7
MacDependency/MacDependency_Prefix.pch
Normal file
7
MacDependency/MacDependency_Prefix.pch
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'MacDependency' target in the 'MacDependency' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
52
MacDependency/MachOModel.h
Normal file
52
MacDependency/MachOModel.h
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// MachOModel.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 13.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "ExtTreeModel.h"
|
||||
#import "MachO/machoarchitecture.h"
|
||||
#import "MachO/dylibcommand.h"
|
||||
#import "MachO/macho.h"
|
||||
#import "ArchitecturesController.h"
|
||||
|
||||
@class MyDocument;
|
||||
|
||||
typedef enum State{
|
||||
StateNormal,
|
||||
StateWarning,
|
||||
StateError
|
||||
};
|
||||
|
||||
@interface MachOModel : NSObject <ExtTreeModel> {
|
||||
NSMutableArray* children;
|
||||
MachO* file;
|
||||
MyDocument* document;
|
||||
MachOModel* parent;
|
||||
MachOArchitecture* architecture; // current architecture
|
||||
DylibCommand* command;
|
||||
State state;
|
||||
}
|
||||
|
||||
- (id) initWithFile:(MachO*)machO document:(MyDocument*)document architecture:(MachOArchitecture*)architecture loadChildren:(BOOL)loadChildren;
|
||||
- (id) initWithFilename:(std::string&)filename command:(DylibCommand*)command document:(MyDocument*)document parent:(MachOModel*)parent;
|
||||
|
||||
- (NSArray*) children;
|
||||
- (BOOL) isLeaf;
|
||||
- (NSColor*) textColor;
|
||||
- (NSString*) name;
|
||||
- (NSNumber*) currentVersion;
|
||||
- (NSNumber*) compatibleVersion;
|
||||
- (NSString*) filename;
|
||||
- (NSString*) dependencyType;
|
||||
- (NSString*) version;
|
||||
- (NSString*) idName;
|
||||
- (NSDate*) lastModificationTime;
|
||||
- (NSArray*) architectures;
|
||||
- (unsigned int) size;
|
||||
|
||||
|
||||
@end
|
327
MacDependency/MachOModel.mm
Normal file
327
MacDependency/MachOModel.mm
Normal file
@ -0,0 +1,327 @@
|
||||
//
|
||||
// MachOModel.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 13.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MachOModel.h"
|
||||
#import "MyDocument.h"
|
||||
|
||||
#import "ConversionStdString.h"
|
||||
#import "VersionFormatter.h"
|
||||
#import "ArchitectureModel.h"
|
||||
|
||||
#include "MachO/machoexception.h"
|
||||
#include "MachO/machoheader.h"
|
||||
#include "MachO/symboltablecommand.h"
|
||||
|
||||
|
||||
// declare private methods
|
||||
@interface MachOModel ()
|
||||
- (void) initChildren;
|
||||
- (void) checkConstraints:(DylibCommand*) dylibId;
|
||||
- (void) setStateWithWarning:(BOOL)isWarning;
|
||||
- (id) initWithFile:(MachO*)file command:(DylibCommand*)command document:(MyDocument*)document parent:(MachOModel*)parent architecture:(MachOArchitecture*)architecture;
|
||||
@end
|
||||
|
||||
@implementation MachOModel
|
||||
|
||||
- (id) initWithFile:(MachO*)machO document:(MyDocument*)document architecture:(MachOArchitecture*)architecture loadChildren:(BOOL)loadChildren {
|
||||
self->state = StateNormal;
|
||||
[self initWithFile:machO command:nil document:document parent:nil architecture:architecture];
|
||||
if (loadChildren) {
|
||||
[self initChildren];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// called by initChildren
|
||||
- (id) initWithFilename:(std::string&)filename command:(DylibCommand*)command document:(MyDocument*)document parent:(MachOModel*)parent {
|
||||
BOOL isWeakReference = (command && !command->isNecessary());
|
||||
self->state = StateNormal;
|
||||
try {
|
||||
file = [document cache]->getFile(filename, parent->file); // throws exception in case file is not found
|
||||
architecture = file->getCompatibleArchitecture(parent->architecture);
|
||||
if (!architecture) {
|
||||
[self setStateWithWarning:isWeakReference];
|
||||
NSString* log = [NSString stringWithFormat:NSLocalizedString(@"ERR_ARCHITECTURE_MISMATCH", nil), parent->command->getName().c_str(), [parent name]];
|
||||
[document appendLogLine:log withModel:self state:state];
|
||||
|
||||
}
|
||||
|
||||
} catch (MachOException& exc) {
|
||||
[self setStateWithWarning:isWeakReference];
|
||||
NSString* log = [NSString stringWithStdString:exc.getCause()];
|
||||
[document appendLogLine:log withModel:self state:state];
|
||||
// distinguish between weak and strong. In both cases append to tree with a status color
|
||||
}
|
||||
[document incrementNumDependencies];
|
||||
[self initWithFile:file command:command document:document parent:parent architecture:architecture];
|
||||
return self;
|
||||
}
|
||||
|
||||
// private
|
||||
- (id) initWithFile:(MachO*)file command:(DylibCommand*)command document:(MyDocument*)document parent:(MachOModel*)parent architecture:(MachOArchitecture*)architecture {
|
||||
if (self = [super init]) {
|
||||
self->document = document;
|
||||
self->parent = parent;
|
||||
self->command = command;
|
||||
self->file = file;
|
||||
self->children = nil;
|
||||
self->architecture = architecture;
|
||||
BOOL isWeakReference = (command && !command->isNecessary());
|
||||
if (architecture) {
|
||||
DylibCommand* dylibId = architecture->getDynamicLibIdCommand();
|
||||
if (dylibId && command) {
|
||||
[self checkConstraints:dylibId];
|
||||
}
|
||||
}
|
||||
|
||||
} return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[children release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) setStateWithWarning:(BOOL)isWarning {
|
||||
State state = StateError;
|
||||
if (isWarning) {
|
||||
state = StateWarning;
|
||||
}
|
||||
|
||||
if (self->state < state) {
|
||||
self->state = state;
|
||||
}
|
||||
}
|
||||
|
||||
- (id) parent {
|
||||
return parent;
|
||||
}
|
||||
|
||||
- (void) checkConstraints:(DylibCommand*) dylibId {
|
||||
// check version information (only compatible version information is relevant)
|
||||
unsigned int minVersion = dylibId->getCompatibleVersion();
|
||||
unsigned int requestedMinVersion = command->getCompatibleVersion();
|
||||
unsigned int requestedMaxVersion = command->getCurrentVersion();
|
||||
|
||||
VersionFormatter* versionFormatter = [[VersionFormatter alloc] init];
|
||||
|
||||
BOOL isWeakReference = (command && !command->isNecessary());
|
||||
|
||||
NSString* log;
|
||||
// check minimum version
|
||||
if (minVersion != 0 && requestedMinVersion != 0 && minVersion < requestedMinVersion) {
|
||||
[self setStateWithWarning:isWeakReference];
|
||||
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_MINIMUM_VERSION", nil), parent->command->getName().c_str(), [parent name], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:requestedMinVersion]], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:minVersion]]];
|
||||
[document appendLogLine:log withModel:self state:state];
|
||||
|
||||
}
|
||||
|
||||
// extended checks which are currently not done by dyld
|
||||
|
||||
// check maximum version
|
||||
if (minVersion != 0 && requestedMaxVersion != 0 && minVersion > requestedMaxVersion) {
|
||||
[self setStateWithWarning:YES];
|
||||
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_MAXIMUM_VERSION", nil), command->getName().c_str(), [parent name], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:requestedMaxVersion]], [versionFormatter stringForObjectValue:[NSNumber numberWithUnsignedInt:minVersion]]];
|
||||
[document appendLogLine:log withModel:self state:state];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// check names
|
||||
if (dylibId->getName() != command->getName()) {
|
||||
[self setStateWithWarning:YES];
|
||||
log = [NSString stringWithFormat:NSLocalizedString(@"ERR_NAME_MISMATCH", nil), command->getName().c_str(), [parent name], dylibId->getName().c_str()];
|
||||
[document appendLogLine:log withModel:self state:state];
|
||||
}
|
||||
[versionFormatter release];
|
||||
}
|
||||
|
||||
- (NSIndexPath*) calculateIndexPath {
|
||||
unsigned int depth = 0;
|
||||
|
||||
// determine current depth
|
||||
MachOModel* model = self;
|
||||
while (model = model->parent) {
|
||||
depth++;
|
||||
}
|
||||
unsigned int length = depth+1;
|
||||
NSUInteger* indices = new NSUInteger[length];
|
||||
MachOModel* child;
|
||||
// search me in parent (recursively)
|
||||
model = child = self;
|
||||
|
||||
while (model = model->parent) {
|
||||
// go through children
|
||||
NSEnumerator *enumerator = [[model children] objectEnumerator];
|
||||
NSUInteger index = 0;
|
||||
while (child != [enumerator nextObject]) {
|
||||
index++;
|
||||
}
|
||||
indices[depth--] = index;
|
||||
child = model;
|
||||
}
|
||||
indices[0] = 0;
|
||||
return [NSIndexPath indexPathWithIndexes:indices length:length];
|
||||
}
|
||||
|
||||
- (NSArray*)children {
|
||||
if (children == nil) {
|
||||
[self initChildren];
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
- (void) initChildren {
|
||||
// TODO: tweak capacity
|
||||
children = [NSMutableArray arrayWithCapacity:20];
|
||||
[children retain];
|
||||
if (architecture) {
|
||||
for (MachOArchitecture::LoadCommandsConstIterator it = architecture->getLoadCommandsBegin();
|
||||
it != architecture->getLoadCommandsEnd();
|
||||
++it) {
|
||||
|
||||
|
||||
LoadCommand* childLoadCommand = (*it);
|
||||
// check if it is dylibcommand
|
||||
DylibCommand* dylibCommand = dynamic_cast<DylibCommand*> (childLoadCommand);
|
||||
if (dylibCommand != NULL && !dylibCommand->isId()) {
|
||||
// TODO: check RPath handling, must use root for working path
|
||||
std::string filename = architecture->getResolvedName(dylibCommand->getName(), "");
|
||||
|
||||
MachOModel* child = [MachOModel alloc];
|
||||
[children addObject:child]; // must add children before initializing them, because in init we rely on parent children being correct
|
||||
[child initWithFilename:filename command:dylibCommand document:document parent:self];
|
||||
[child release];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isLeaf {
|
||||
[self children];
|
||||
if ([children count] == 0)
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSColor*) textColor {
|
||||
NSColor* color;
|
||||
switch(state) {
|
||||
case StateWarning:
|
||||
color = [NSColor blueColor];
|
||||
break;
|
||||
case StateError:
|
||||
color = [NSColor redColor];
|
||||
break;
|
||||
default:
|
||||
color= [NSColor blackColor];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
- (NSString*) filename {
|
||||
std::string filename;
|
||||
if (file)
|
||||
filename = file->getFileName();
|
||||
return [NSString stringWithStdString:filename];
|
||||
}
|
||||
|
||||
- (NSString*) version {
|
||||
if (file) {
|
||||
return [NSString stringWithStdString:file->getVersion()];
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (unsigned int) size {
|
||||
if (file)
|
||||
return file->getSize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSString*) name {
|
||||
std::string name;
|
||||
if (command) {
|
||||
name = command->getName();
|
||||
} else if (file) {
|
||||
name = file->getName();
|
||||
}
|
||||
return [NSString stringWithStdString:name];
|
||||
}
|
||||
|
||||
- (NSNumber*) currentVersion {
|
||||
if (command) {
|
||||
return [NSNumber numberWithUnsignedInt:command->getCurrentVersion()];
|
||||
}
|
||||
return [NSNumber numberWithUnsignedInt:0];
|
||||
}
|
||||
|
||||
- (NSNumber*) compatibleVersion {
|
||||
if (command) {
|
||||
return [NSNumber numberWithUnsignedInt:command->getCompatibleVersion()];
|
||||
}
|
||||
return [NSNumber numberWithUnsignedInt:0];
|
||||
}
|
||||
|
||||
- (NSDate*) lastModificationTime {
|
||||
if (file)
|
||||
return [NSDate dateWithTimeIntervalSince1970:file->getLastModificationTime()];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (NSString*) dependencyType {
|
||||
NSString* type;
|
||||
if (!command) {
|
||||
return @"";
|
||||
}
|
||||
switch(command->getType()) {
|
||||
|
||||
case DylibCommand::DependencyWeak:
|
||||
type = NSLocalizedString(@"DEPENDENCY_TYPE_WEAK", nil);
|
||||
break;
|
||||
case DylibCommand::DependencyDelayed:
|
||||
type = NSLocalizedString(@"DEPENDENCY_TYPE_DELAYED", nil);
|
||||
break;
|
||||
case DylibCommand::DependencyNormal:
|
||||
type = NSLocalizedString(@"DEPENDENCY_TYPE_NORMAL", nil);
|
||||
break;
|
||||
default:
|
||||
type = NSLocalizedString(@"UNDEFINED", @"Unknown");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
- (NSArray*) architectures {
|
||||
NSMutableArray* architectures = [NSMutableArray arrayWithCapacity:4];
|
||||
|
||||
if (file) {
|
||||
for (MachO::MachOArchitecturesIterator iter = file->getArchitecturesBegin(); iter != file->getArchitecturesEnd(); iter++) {
|
||||
// create model for architecture
|
||||
ArchitectureModel* currentArchitecture = [[ArchitectureModel alloc]initWithArchitecture:(*iter) file:file document:document isRoot:NO];
|
||||
// correct order (current architecture should have first index)
|
||||
if ((*iter) == architecture) {
|
||||
[architectures insertObject:currentArchitecture atIndex:0]; // insert at beginning
|
||||
} else {
|
||||
[architectures addObject:currentArchitecture]; // insert at end
|
||||
}
|
||||
|
||||
[currentArchitecture release];
|
||||
}
|
||||
}
|
||||
return architectures;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (NSString*) idName {
|
||||
// TODO: what should we return here?
|
||||
return @"";
|
||||
}
|
||||
|
||||
|
||||
@end
|
44
MacDependency/MyDocument.h
Normal file
44
MacDependency/MyDocument.h
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// MyDocument.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 19.06.09.
|
||||
// Copyright Konrad Windszus 2009 . All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MachO/macho.h"
|
||||
#import "MachO/machocache.h"
|
||||
#import "MachOModel.h"
|
||||
#import "PrioritySplitViewDelegate.h"
|
||||
#import "SymbolTableController.h"
|
||||
@interface MyDocument : NSDocument
|
||||
{
|
||||
MachOCache* cache;
|
||||
MachO* machO;
|
||||
NSArray* contents;
|
||||
NSMutableAttributedString* log;
|
||||
PrioritySplitViewDelegate* splitViewDelegate;
|
||||
IBOutlet NSTreeController* dependenciesController;
|
||||
IBOutlet NSTextField* textFieldFilename;
|
||||
IBOutlet NSTextField* textFieldBottomBar;
|
||||
IBOutlet NSSplitView* mainSplitView;
|
||||
IBOutlet SymbolTableController* symbolTableController;
|
||||
unsigned int numDependencies;
|
||||
}
|
||||
- (NSAttributedString*)log;
|
||||
- (void)setLog:(NSMutableAttributedString *)newLog;
|
||||
- (void)appendLogLine:(NSString *)line withModel:(MachOModel*)model state:(State)state;
|
||||
- (void)clearLog;
|
||||
|
||||
- (void)incrementNumDependencies;
|
||||
- (void)resetNumDependencies;
|
||||
|
||||
- (MachOCache*)cache;
|
||||
|
||||
- (NSArray*)architectures;
|
||||
- (IBAction)clickRevealInFinder:(id)sender;
|
||||
- (NSString*)dependencyStatus;
|
||||
- (SymbolTableController*)symbolTableController;
|
||||
@end
|
286
MacDependency/MyDocument.mm
Normal file
286
MacDependency/MyDocument.mm
Normal file
@ -0,0 +1,286 @@
|
||||
//
|
||||
// MyDocument.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 19.06.09.
|
||||
// Copyright Konrad Windszus 2009 . All rights reserved.
|
||||
//
|
||||
|
||||
#import "MyDocument.h"
|
||||
#import "ConversionStdString.h"
|
||||
#import "MachOModel.h"
|
||||
#import "TreeControllerExtension.h"
|
||||
#import "ArchitectureModel.h"
|
||||
#include "MachO/machoexception.h"
|
||||
|
||||
|
||||
|
||||
// declare private methods
|
||||
@interface MyDocument ()
|
||||
- (NSString*) serializeIndexPath:(NSIndexPath*)indexPath;
|
||||
- (NSIndexPath*) deserializeIndexPath:(NSString*)link;
|
||||
@end
|
||||
|
||||
@implementation MyDocument
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
// Add your subclass-specific initialization here.
|
||||
// If an error occurs here, send a [self release] message and return nil.
|
||||
//contents = [[NSMutableArray alloc] init];
|
||||
cache = new MachOCache();
|
||||
log = [[NSAttributedString alloc] initWithString:@""];
|
||||
numDependencies = 0;
|
||||
splitViewDelegate = [[PrioritySplitViewDelegate alloc] init];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
delete cache;
|
||||
[splitViewDelegate release];
|
||||
[contents release];
|
||||
[log release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)windowNibName
|
||||
{
|
||||
// Override returning the nib file name of the document
|
||||
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
|
||||
return @"MyDocument";
|
||||
}
|
||||
|
||||
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
|
||||
{
|
||||
[super windowControllerDidLoadNib:aController];
|
||||
|
||||
// Add any code here that needs to be executed once the windowController has loaded the document's window.
|
||||
}
|
||||
|
||||
|
||||
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
|
||||
{
|
||||
// Insert code here to write your document to data of the specified type. If the given outError != NULL, ensure that you set *outError when returning nil.
|
||||
|
||||
// You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
|
||||
|
||||
// For applications targeted for Panther or earlier systems, you should use the deprecated API -dataRepresentationOfType:. In this case you can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.
|
||||
|
||||
if ( outError != NULL ) {
|
||||
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
|
||||
{
|
||||
/*if ( outError != NULL ) {
|
||||
|
||||
//An error occurred
|
||||
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
|
||||
[errorDetail setValue:@"Failed to do something wicked" forKey:NSLocalizedDescriptionKey];
|
||||
*outError = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];
|
||||
//*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:dskFulErr userInfo:nil];
|
||||
return NO;
|
||||
|
||||
|
||||
}*/
|
||||
//return YES;
|
||||
|
||||
// load file
|
||||
NSString* file = [super fileName];
|
||||
|
||||
// convert to std:string
|
||||
std::string fileString = [file stdString];
|
||||
try {
|
||||
machO = cache->getFile(fileString, NULL);
|
||||
} catch (MachOException& exc) {
|
||||
NSString* msg = [NSString stringWithUTF8String:exc.getCause().c_str()];
|
||||
|
||||
|
||||
// Make and return custom domain error
|
||||
NSArray *objArray = [NSArray arrayWithObjects:@"hello", msg, @"Try again with another file", nil];
|
||||
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedFailureReasonErrorKey, NSLocalizedRecoverySuggestionErrorKey, nil];
|
||||
|
||||
NSDictionary *eDict = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
|
||||
|
||||
// fill outError
|
||||
*outError = [NSError errorWithDomain:@"MachO" code:0 userInfo:eDict];
|
||||
|
||||
//NSAlert* alert = [NSAlert alertWithMessageText:msg defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:nil];
|
||||
//[alert runModal];
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)awakeFromNib {
|
||||
[[textFieldBottomBar cell] setBackgroundStyle:NSBackgroundStyleRaised];
|
||||
|
||||
[splitViewDelegate setPriority:1 forViewAtIndex:0];
|
||||
[splitViewDelegate setPriority:0 forViewAtIndex:1];
|
||||
[splitViewDelegate setMinimumLength:150 forViewAtIndex:0];
|
||||
[splitViewDelegate setMinimumLength:400 forViewAtIndex:1];
|
||||
[mainSplitView setDelegate:splitViewDelegate];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)log {
|
||||
return log;
|
||||
}
|
||||
|
||||
- (void)setLog:(NSMutableAttributedString *)newLog {
|
||||
[newLog retain];
|
||||
[log release];
|
||||
log = newLog;
|
||||
}
|
||||
|
||||
- (void)clearLog {
|
||||
NSAttributedString* newLog = [[NSAttributedString alloc] initWithString:@""];
|
||||
[self setLog:newLog];
|
||||
[newLog release];
|
||||
}
|
||||
|
||||
- (void)appendLogLine:(NSString *)line withModel:(MachOModel*)model state:(State)state {
|
||||
|
||||
NSMutableAttributedString* newLog = [[NSMutableAttributedString alloc] init];
|
||||
[newLog appendAttributedString:log];
|
||||
|
||||
NSString* prefix;
|
||||
switch (state) {
|
||||
case StateError:
|
||||
prefix = NSLocalizedString(@"LOG_PREFIX_ERROR", nil);
|
||||
break;
|
||||
default:
|
||||
prefix = NSLocalizedString(@"LOG_PREFIX_WARNING", nil);
|
||||
break;
|
||||
}
|
||||
|
||||
NSString* newLine = [NSString stringWithFormat:@"%@%@\n\n", prefix, line];
|
||||
// new line consist of prefix and link
|
||||
//NSString* link = [self serializeIndexPath:indexPath];
|
||||
|
||||
|
||||
NSDictionary* attributes;
|
||||
|
||||
if (model) {
|
||||
attributes = [NSDictionary dictionaryWithObjectsAndKeys:model, NSLinkAttributeName, [NSCursor pointingHandCursor], NSCursorAttributeName, NSLocalizedString(@"LOG_LINK_TOOLTIP", nil), NSToolTipAttributeName, nil];
|
||||
} else {
|
||||
attributes = [NSDictionary dictionary];
|
||||
}
|
||||
NSAttributedString* newLogLine = [[NSAttributedString alloc]initWithString:newLine attributes:attributes];
|
||||
|
||||
[newLog appendAttributedString:newLogLine];
|
||||
[newLogLine release];
|
||||
[self setLog:newLog];
|
||||
[newLog release];
|
||||
}
|
||||
|
||||
- (NSString*) serializeIndexPath:(NSIndexPath*)indexPath {
|
||||
NSMutableString* link = [NSMutableString stringWithCapacity:20];
|
||||
for (int depth = 0; depth < [indexPath length]; depth++) {
|
||||
[link appendFormat:@"%d;", [indexPath indexAtPosition:depth]];
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
- (NSIndexPath*) deserializeIndexPath:(NSString*)link {
|
||||
NSIndexPath* indexPath;
|
||||
|
||||
// tokenize string
|
||||
NSArray* indices = [link componentsSeparatedByString:@";"];
|
||||
|
||||
// go through tokens
|
||||
NSEnumerator *enumerator = [indices objectEnumerator];
|
||||
NSString* token = [enumerator nextObject];
|
||||
if (token) {
|
||||
indexPath = [NSIndexPath indexPathWithIndex:[token intValue]];
|
||||
while (token = [enumerator nextObject]) {
|
||||
if ([token length] > 0)
|
||||
indexPath = [indexPath indexPathByAddingIndex:[token intValue]];
|
||||
}
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
|
||||
// delegate method
|
||||
- (BOOL)textView:(NSTextView *)aTextView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex {
|
||||
|
||||
// link must be NSString which should be deserialized here
|
||||
//NSIndexPath* indexPath = [self deserializeIndexPath:link];
|
||||
[dependenciesController setSelectedObject:link];
|
||||
|
||||
|
||||
// we need no further processing of the link
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (MachOCache*)cache {
|
||||
return cache;
|
||||
}
|
||||
|
||||
- (NSArray*) architectures {
|
||||
NSMutableArray* architectures = [NSMutableArray arrayWithCapacity:4];
|
||||
|
||||
if (machO) {
|
||||
MachOArchitecture* architecture = machO->getHostCompatibleArchitecture();
|
||||
if (!architecture) {
|
||||
// no host-compatible architecture found (just take first architecture)
|
||||
architecture = *(machO->getArchitecturesBegin());
|
||||
}
|
||||
for (MachO::MachOArchitecturesIterator iter = machO->getArchitecturesBegin(); iter != machO->getArchitecturesEnd(); iter++) {
|
||||
// create model for architecture
|
||||
ArchitectureModel* currentArchitecture = [[ArchitectureModel alloc]initWithArchitecture:(*iter) file:machO document:self isRoot:YES];
|
||||
// correct order (current architecture should have first index)
|
||||
if ((*iter) == architecture) {
|
||||
[architectures insertObject:currentArchitecture atIndex:0]; // insert at beginning
|
||||
} else {
|
||||
[architectures addObject:currentArchitecture]; // insert at end
|
||||
}
|
||||
|
||||
[currentArchitecture release];
|
||||
}
|
||||
}
|
||||
return architectures;
|
||||
}
|
||||
|
||||
- (IBAction)clickRevealInFinder:(id)sender {
|
||||
NSString* filename = [textFieldFilename stringValue];
|
||||
|
||||
[[NSWorkspace sharedWorkspace] selectFile:filename
|
||||
inFileViewerRootedAtPath:nil];
|
||||
|
||||
}
|
||||
|
||||
- (void)incrementNumDependencies {
|
||||
[self willChangeValueForKey:@"dependencyStatus"];
|
||||
numDependencies++;
|
||||
[self didChangeValueForKey:@"dependencyStatus"];
|
||||
}
|
||||
|
||||
- (void)resetNumDependencies {
|
||||
[self willChangeValueForKey:@"dependencyStatus"];
|
||||
numDependencies = 0;
|
||||
[self didChangeValueForKey:@"dependencyStatus"];
|
||||
}
|
||||
|
||||
|
||||
- (NSString*)dependencyStatus {
|
||||
NSString* status = [NSString stringWithFormat:NSLocalizedString(@"DEPENDENCY_STATUS", nil), numDependencies, cache->getNumEntries()];
|
||||
return status;
|
||||
}
|
||||
|
||||
- (SymbolTableController*)symbolTableController {
|
||||
return symbolTableController;
|
||||
}
|
||||
|
||||
@end
|
16
MacDependency/MyDocumentWindow.h
Normal file
16
MacDependency/MyDocumentWindow.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// MyDocumentWindow.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 04.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface MyDocumentWindow : NSWindow {
|
||||
|
||||
}
|
||||
|
||||
@end
|
21
MacDependency/MyDocumentWindow.m
Normal file
21
MacDependency/MyDocumentWindow.m
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// MyDocumentWindow.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 04.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Window class for document windows
|
||||
//
|
||||
#import "MyDocumentWindow.h"
|
||||
|
||||
|
||||
@implementation MyDocumentWindow
|
||||
|
||||
-(void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
|
||||
// set bottom bar
|
||||
[self setContentBorderThickness:24.0 forEdge:NSMinYEdge];
|
||||
}
|
||||
@end
|
22
MacDependency/PrioritySplitViewDelegate.h
Normal file
22
MacDependency/PrioritySplitViewDelegate.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// PrioritySplitViewDelegate.h
|
||||
// ColumnSplitView
|
||||
//
|
||||
// Created by Matt Gallagher on 2009/09/01.
|
||||
// Copyright 2009 Matt Gallagher. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface PrioritySplitViewDelegate : NSObject
|
||||
{
|
||||
NSMutableDictionary *lengthsByViewIndex;
|
||||
NSMutableDictionary *viewIndicesByPriority;
|
||||
}
|
||||
|
||||
- (void)setMinimumLength:(CGFloat)minLength
|
||||
forViewAtIndex:(NSInteger)viewIndex;
|
||||
- (void)setPriority:(NSInteger)priorityIndex
|
||||
forViewAtIndex:(NSInteger)viewIndex;
|
||||
|
||||
@end
|
180
MacDependency/PrioritySplitViewDelegate.m
Normal file
180
MacDependency/PrioritySplitViewDelegate.m
Normal file
@ -0,0 +1,180 @@
|
||||
//
|
||||
// PrioritySplitViewDelegate.m
|
||||
// ColumnSplitView
|
||||
//
|
||||
// Created by Matt Gallagher on 2009/09/01.
|
||||
// Copyright 2009 Matt Gallagher. All rights reserved.
|
||||
//
|
||||
// see http://cocoawithlove.com/2009/09/nssplitview-delegate-for-priority-based.html
|
||||
//
|
||||
#import "PrioritySplitViewDelegate.h"
|
||||
|
||||
|
||||
@implementation PrioritySplitViewDelegate
|
||||
|
||||
|
||||
- (void)setMinimumLength:(CGFloat)minLength forViewAtIndex:(NSInteger)viewIndex
|
||||
{
|
||||
if (!lengthsByViewIndex)
|
||||
{
|
||||
lengthsByViewIndex = [[NSMutableDictionary alloc] initWithCapacity:0];
|
||||
}
|
||||
[lengthsByViewIndex
|
||||
setObject:[NSNumber numberWithDouble:minLength]
|
||||
forKey:[NSNumber numberWithInteger:viewIndex]];
|
||||
}
|
||||
|
||||
- (void)setPriority:(NSInteger)priorityIndex forViewAtIndex:(NSInteger)viewIndex
|
||||
{
|
||||
if (!viewIndicesByPriority)
|
||||
{
|
||||
viewIndicesByPriority = [[NSMutableDictionary alloc] initWithCapacity:0];
|
||||
}
|
||||
[viewIndicesByPriority
|
||||
setObject:[NSNumber numberWithInteger:viewIndex]
|
||||
forKey:[NSNumber numberWithInteger:priorityIndex]];
|
||||
}
|
||||
|
||||
- (CGFloat)splitView:(NSSplitView *)sender
|
||||
constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset
|
||||
{
|
||||
NSView *subview = [[sender subviews] objectAtIndex:offset];
|
||||
NSRect subviewFrame = subview.frame;
|
||||
CGFloat frameOrigin;
|
||||
if ([sender isVertical])
|
||||
{
|
||||
frameOrigin = subviewFrame.origin.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
frameOrigin = subviewFrame.origin.y;
|
||||
}
|
||||
|
||||
CGFloat minimumSize =
|
||||
[[lengthsByViewIndex objectForKey:[NSNumber numberWithInteger:offset]]
|
||||
doubleValue];
|
||||
|
||||
return frameOrigin + minimumSize;
|
||||
}
|
||||
|
||||
- (CGFloat)splitView:(NSSplitView *)sender
|
||||
constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
|
||||
{
|
||||
NSView *growingSubview = [[sender subviews] objectAtIndex:offset];
|
||||
NSView *shrinkingSubview = [[sender subviews] objectAtIndex:offset + 1];
|
||||
NSRect growingSubviewFrame = growingSubview.frame;
|
||||
NSRect shrinkingSubviewFrame = shrinkingSubview.frame;
|
||||
CGFloat shrinkingSize;
|
||||
CGFloat currentCoordinate;
|
||||
if ([sender isVertical])
|
||||
{
|
||||
currentCoordinate =
|
||||
growingSubviewFrame.origin.x + growingSubviewFrame.size.width;
|
||||
shrinkingSize = shrinkingSubviewFrame.size.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCoordinate =
|
||||
growingSubviewFrame.origin.y + growingSubviewFrame.size.height;
|
||||
shrinkingSize = shrinkingSubviewFrame.size.height;
|
||||
}
|
||||
|
||||
CGFloat minimumSize =
|
||||
[[lengthsByViewIndex objectForKey:[NSNumber numberWithInteger:offset + 1]]
|
||||
doubleValue];
|
||||
|
||||
return currentCoordinate + (shrinkingSize - minimumSize);
|
||||
}
|
||||
|
||||
- (void)splitView:(NSSplitView *)sender
|
||||
resizeSubviewsWithOldSize:(NSSize)oldSize
|
||||
{
|
||||
NSArray *subviews = [sender subviews];
|
||||
NSInteger subviewsCount = [subviews count];
|
||||
|
||||
BOOL isVertical = [sender isVertical];
|
||||
|
||||
CGFloat delta = [sender isVertical] ?
|
||||
(sender.bounds.size.width - oldSize.width) :
|
||||
(sender.bounds.size.height - oldSize.height);
|
||||
|
||||
NSInteger viewCountCheck = 0;
|
||||
|
||||
for (NSNumber *priorityIndex in
|
||||
[[viewIndicesByPriority allKeys] sortedArrayUsingSelector:@selector(compare:)])
|
||||
{
|
||||
NSNumber *viewIndex = [viewIndicesByPriority objectForKey:priorityIndex];
|
||||
NSInteger viewIndexValue = [viewIndex integerValue];
|
||||
if (viewIndexValue >= subviewsCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NSView *view = [subviews objectAtIndex:viewIndexValue];
|
||||
|
||||
NSSize frameSize = [view frame].size;
|
||||
NSNumber *minLength = [lengthsByViewIndex objectForKey:viewIndex];
|
||||
CGFloat minLengthValue = [minLength doubleValue];
|
||||
|
||||
if (isVertical)
|
||||
{
|
||||
frameSize.height = sender.bounds.size.height;
|
||||
if (delta > 0 ||
|
||||
frameSize.width + delta >= minLengthValue)
|
||||
{
|
||||
frameSize.width += delta;
|
||||
delta = 0;
|
||||
}
|
||||
else if (delta < 0)
|
||||
{
|
||||
delta += frameSize.width - minLengthValue;
|
||||
frameSize.width = minLengthValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
frameSize.width = sender.bounds.size.width;
|
||||
if (delta > 0 ||
|
||||
frameSize.height + delta >= minLengthValue)
|
||||
{
|
||||
frameSize.height += delta;
|
||||
delta = 0;
|
||||
}
|
||||
else if (delta < 0)
|
||||
{
|
||||
delta += frameSize.height - minLengthValue;
|
||||
frameSize.height = minLengthValue;
|
||||
}
|
||||
}
|
||||
|
||||
[view setFrameSize:frameSize];
|
||||
viewCountCheck++;
|
||||
}
|
||||
|
||||
NSAssert1(viewCountCheck == [subviews count],
|
||||
@"Number of valid views in priority list is less than the subview count"
|
||||
@" of split view %p.",
|
||||
sender);
|
||||
NSAssert3(fabs(delta) < 0.5,
|
||||
@"Split view %p resized smaller than minimum %@ of %f",
|
||||
sender,
|
||||
isVertical ? @"width" : @"height",
|
||||
sender.frame.size.width - delta);
|
||||
|
||||
CGFloat offset = 0;
|
||||
CGFloat dividerThickness = [sender dividerThickness];
|
||||
for (NSView *subview in subviews)
|
||||
{
|
||||
NSRect viewFrame = subview.frame;
|
||||
NSPoint viewOrigin = viewFrame.origin;
|
||||
viewOrigin.x = offset;
|
||||
[subview setFrameOrigin:viewOrigin];
|
||||
offset += viewFrame.size.width + dividerThickness;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
29
MacDependency/SymbolTableController.h
Normal file
29
MacDependency/SymbolTableController.h
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// SymbolTableController.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 18.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@class MyDocument;
|
||||
|
||||
@interface SymbolTableController : NSArrayController {
|
||||
NSPredicate* nameFilter;
|
||||
IBOutlet NSSegmentedControl* typeFilterControl;
|
||||
IBOutlet NSButton* demangleNamesControl;
|
||||
BOOL demangleNames;
|
||||
IBOutlet MyDocument* document;
|
||||
}
|
||||
|
||||
- (void)setNameFilter:(NSPredicate*) nameFilter;
|
||||
- (NSPredicate*)nameFilter;
|
||||
|
||||
- (IBAction)typeFilterChanged:(id)sender;
|
||||
|
||||
- (BOOL)demangleNames;
|
||||
- (BOOL*)demangleNamesPtr;
|
||||
- (void)setDemangleNames:(BOOL)demangleNames;
|
||||
|
||||
@end
|
120
MacDependency/SymbolTableController.mm
Normal file
120
MacDependency/SymbolTableController.mm
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// SymbolTableController.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 18.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SymbolTableController.h"
|
||||
#import "MyDocument.h"
|
||||
#include "MachO/symboltableentry.h"
|
||||
#include "MachO/machodemangleexception.h"
|
||||
|
||||
@interface SymbolTableController()
|
||||
-(void)setFilter;
|
||||
- (NSPredicate*) typeFilter;
|
||||
@end
|
||||
|
||||
@implementation SymbolTableController
|
||||
|
||||
|
||||
const int TYPE[] = {SymbolTableEntry::TypeExported, SymbolTableEntry::TypeImported};
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (self) {
|
||||
demangleNames = true;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// called when all connections were made
|
||||
- (void)awakeFromNib {
|
||||
[self setFilter];
|
||||
}
|
||||
|
||||
- (NSPredicate*) typeFilter {
|
||||
NSMutableString* typeFilter = [NSMutableString string];
|
||||
|
||||
for (int segment=0; segment < sizeof(TYPE)/sizeof(*TYPE); segment++) {
|
||||
if ([typeFilterControl isSelectedForSegment:segment]) {
|
||||
NSString* condition = [NSString stringWithFormat:@"type=%d", TYPE[segment]];
|
||||
if ([typeFilter length] > 0) {
|
||||
[typeFilter appendString:@" or "];
|
||||
}
|
||||
[typeFilter appendString:condition];
|
||||
}
|
||||
}
|
||||
|
||||
// select nothing if no filter set
|
||||
NSPredicate* predicate;
|
||||
if ([typeFilter length] == 0) {
|
||||
predicate = [NSPredicate predicateWithValue:NO];
|
||||
} else {
|
||||
predicate = [NSPredicate predicateWithFormat:typeFilter];
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
|
||||
- (NSPredicate*)nameFilter {
|
||||
return nameFilter;
|
||||
}
|
||||
|
||||
|
||||
- (void)setNameFilter:(NSPredicate*) nameFilter {
|
||||
[nameFilter retain];
|
||||
[self->nameFilter release];
|
||||
self->nameFilter = nameFilter;
|
||||
[self setFilter];
|
||||
}
|
||||
|
||||
- (IBAction)typeFilterChanged:(id)sender {
|
||||
[self setFilter];
|
||||
}
|
||||
|
||||
-(void)setFilter {
|
||||
NSPredicate* typeFilter = [self typeFilter];
|
||||
NSPredicate* predicate;
|
||||
if (nameFilter) {
|
||||
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:nameFilter, typeFilter, nil]];
|
||||
} else {
|
||||
predicate = typeFilter;
|
||||
}
|
||||
//NSLog(@"%@", predicate);
|
||||
[self setFilterPredicate:predicate];
|
||||
}
|
||||
|
||||
- (BOOL)demangleNames {
|
||||
return demangleNames;
|
||||
}
|
||||
|
||||
- (BOOL*)demangleNamesPtr {
|
||||
return &demangleNames;
|
||||
}
|
||||
|
||||
- (void)setDemangleNames:(BOOL)demangleNames {
|
||||
self->demangleNames = demangleNames;
|
||||
|
||||
// refresh
|
||||
[self rearrangeObjects];
|
||||
|
||||
/*// TODO: call refreshSymbols for the current architecture (over ArchitecturesController)
|
||||
NSArray* objects = [dependenciesController selectedObjects];
|
||||
if (objects && [objects lastObject]) {
|
||||
MachOModel* model = [objects lastObject];
|
||||
//[model refreshSymbols];
|
||||
}*/
|
||||
}
|
||||
|
||||
- (id)valueForKeyPath:(NSString *)keyPath {
|
||||
try {
|
||||
[super valueForKeyPath:keyPath];
|
||||
}
|
||||
catch (MachODemangleException& exc) {
|
||||
NSString* error = NSLocalizedString(@"ERR_NO_DEMANGLER", nil);
|
||||
[document appendLogLine:error withModel:nil state:StateError];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
21
MacDependency/SymbolTableEntryModel.h
Normal file
21
MacDependency/SymbolTableEntryModel.h
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// SymbolEntryModel.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 13.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include "MachO/machofile.h"
|
||||
#include "MachO/symboltableentry.h"
|
||||
|
||||
@interface SymbolTableEntryModel : NSObject {
|
||||
const SymbolTableEntry* entry;
|
||||
const BOOL* demangleNames;
|
||||
}
|
||||
|
||||
- (id) initWithEntry:(const SymbolTableEntry*) entry demangleNamesPtr:(BOOL*)demangleNames;
|
||||
- (NSString*) name;
|
||||
- (NSNumber*) type;
|
||||
@end
|
39
MacDependency/SymbolTableEntryModel.mm
Normal file
39
MacDependency/SymbolTableEntryModel.mm
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// SymbolEntryModel.mm
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 13.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SymbolTableEntryModel.h"
|
||||
#import "MachOModel.h"
|
||||
#import "ConversionStdString.h"
|
||||
|
||||
@implementation SymbolTableEntryModel
|
||||
|
||||
- (id) initWithEntry:(const SymbolTableEntry*)entry demangleNamesPtr:(BOOL*)demangleNames {
|
||||
if (self = [super init]) {
|
||||
self->entry = entry;
|
||||
self->demangleNames = demangleNames;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*) name {
|
||||
//try {
|
||||
return [NSString stringWithStdString:entry->getName(*demangleNames)];
|
||||
/*} catch (MachODemangleException& e) {
|
||||
|
||||
// TODO: disable demangling and show error message
|
||||
NSLog([NSString stringWithStdString:e.getCause()]);
|
||||
return [NSString stringWithStdString:entry->getName(false)];
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
- (NSNumber*) type {
|
||||
return [NSNumber numberWithUnsignedInt:entry->getType()];
|
||||
}
|
||||
|
||||
@end
|
16
MacDependency/SymbolTableEntryTypeFormatter.h
Normal file
16
MacDependency/SymbolTableEntryTypeFormatter.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// SymbolTableEntryTypeFormatter.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 18.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface SymbolTableEntryTypeFormatter : NSFormatter {
|
||||
|
||||
}
|
||||
|
||||
@end
|
42
MacDependency/SymbolTableEntryTypeFormatter.mm
Normal file
42
MacDependency/SymbolTableEntryTypeFormatter.mm
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// SymbolTableEntryTypeFormatter.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 18.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Formatter for types of symbol table entries. We need a formatter to provide a valid sort order.
|
||||
#import "SymbolTableEntryTypeFormatter.h"
|
||||
#include "MachO/symboltableentry.h"
|
||||
|
||||
@implementation SymbolTableEntryTypeFormatter
|
||||
// conversion to string
|
||||
- (NSString*) stringForObjectValue:(id)obj {
|
||||
// must be a NSNumber
|
||||
if (![obj isKindOfClass:[NSNumber class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// NSNumber contains the version as unsigned int
|
||||
unsigned int typeNumber = [obj unsignedIntValue];
|
||||
NSString* type;
|
||||
switch(typeNumber) {
|
||||
case SymbolTableEntry::TypeExported:
|
||||
type = NSLocalizedString(@"SYMBOL_TYPE_EXPORT", @"Export");
|
||||
break;
|
||||
case SymbolTableEntry::TypeImported:
|
||||
type = NSLocalizedString(@"SYMBOL_TYPE_IMPORT", @"Import");
|
||||
break;
|
||||
default:
|
||||
type = NSLocalizedString(@"UNKNOWN", @"Unknown");
|
||||
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
// conversion from string (not necessary)
|
||||
- (BOOL) getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)errorString {
|
||||
return NO;
|
||||
}
|
||||
@end
|
22
MacDependency/TreeControllerExtension.h
Normal file
22
MacDependency/TreeControllerExtension.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// TreeControllerExtension.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 02.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// NSTreeController-DMExtensions.h
|
||||
// Library
|
||||
//
|
||||
// Created by William Shipley on 3/10/06.
|
||||
// Copyright 2006 Delicious Monster Software, LLC. Some rights reserved,
|
||||
// see Creative Commons license on wilshipley.com
|
||||
// @see http://wilshipley.com/blog/2006/04/pimp-my-code-part-10-whining-about.html
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "ExtTreeModel.h"
|
||||
|
||||
@interface NSTreeController (TreeControllerExtension)
|
||||
- (BOOL)setSelectedObjects:(NSArray *)newSelectedObjects;
|
||||
- (BOOL)setSelectedObject:(id <ExtTreeModel>)object;
|
||||
- (NSIndexPath *)indexPathToObject:(id <ExtTreeModel>)object;
|
||||
@end
|
95
MacDependency/TreeControllerExtension.m
Normal file
95
MacDependency/TreeControllerExtension.m
Normal file
@ -0,0 +1,95 @@
|
||||
//
|
||||
// TreeControllerExtension.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 02.09.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Extension to NSTreeController to provide methods to select an item if only its data pointer is known.
|
||||
|
||||
|
||||
#import "TreeControllerExtension.h"
|
||||
|
||||
|
||||
@interface NSTreeController (TreeControllerExtension_Private)
|
||||
- (NSIndexPath *)_indexPathFromIndexPath:(NSIndexPath *)baseIndexPath inChildren:(NSArray **)children
|
||||
toObject:(id <ExtTreeModel>)object;
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSTreeController (TreeControllerExtension)
|
||||
|
||||
- (BOOL)setSelectedObjects:(NSArray *)newSelectedObjects
|
||||
{
|
||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||
unsigned int selectedObjectIndex;
|
||||
for (selectedObjectIndex = 0; selectedObjectIndex < [newSelectedObjects count];
|
||||
selectedObjectIndex++) {
|
||||
id selectedObject = [newSelectedObjects objectAtIndex:selectedObjectIndex];
|
||||
NSIndexPath *indexPath = [self indexPathToObject:selectedObject];
|
||||
if (indexPath)
|
||||
[indexPaths addObject:indexPath];
|
||||
}
|
||||
return [self setSelectionIndexPaths:indexPaths];
|
||||
}
|
||||
|
||||
- (BOOL)setSelectedObject:(id <ExtTreeModel>)object
|
||||
{
|
||||
NSIndexPath *indexPath = [self indexPathToObject:object];
|
||||
if (indexPath) {
|
||||
return [self setSelectionIndexPath:indexPath];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathToObject:(id <ExtTreeModel>)object
|
||||
{
|
||||
NSArray *children = [self content];
|
||||
return [self _indexPathFromIndexPath:nil inChildren:&children toObject:object];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSTreeController (TreeControllerExtension_Private)
|
||||
|
||||
// inspired by: http://wilshipley.com/blog/2006/04/pimp-my-code-part-10-whining-about.html but much faster and does not rely
|
||||
// on dirty and undocumented selectors of arrangedObjects.
|
||||
- (NSIndexPath *)_indexPathFromIndexPath:(NSIndexPath *)baseIndexPath inChildren:(NSArray **)children
|
||||
toObject:(id <ExtTreeModel>)object;
|
||||
{
|
||||
|
||||
NSIndexPath* indexPath = nil;
|
||||
// go back to root
|
||||
id <ExtTreeModel> parent = [object parent];
|
||||
if (parent) {
|
||||
baseIndexPath = [self _indexPathFromIndexPath:baseIndexPath inChildren:children toObject:parent];
|
||||
if (!baseIndexPath)
|
||||
return nil;
|
||||
}
|
||||
|
||||
// go through children (content)
|
||||
unsigned int childIndex;
|
||||
NSMutableArray* sortedChildren = [NSMutableArray arrayWithArray:*children];
|
||||
[sortedChildren sortUsingDescriptors:[self sortDescriptors]];
|
||||
|
||||
for (childIndex = 0; childIndex < [sortedChildren count]; childIndex++) {
|
||||
id childObject = [sortedChildren objectAtIndex:childIndex];
|
||||
if ([object isEqual:childObject]) {
|
||||
|
||||
NSString *childrenKeyPath = [self childrenKeyPath];
|
||||
NSArray* childsChildren = [childObject valueForKey:childrenKeyPath];
|
||||
*children = childsChildren;
|
||||
|
||||
// create current NSIndex
|
||||
if (!baseIndexPath) {
|
||||
indexPath = [NSIndexPath indexPathWithIndex:childIndex];
|
||||
} else {
|
||||
indexPath = [baseIndexPath indexPathByAddingIndex:childIndex];
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
}
|
||||
return nil; // something wrong here (can't find object)
|
||||
}
|
||||
|
||||
@end
|
16
MacDependency/VersionFormatter.h
Normal file
16
MacDependency/VersionFormatter.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// VersionFormatter.h
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 17.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface VersionFormatter : NSFormatter {
|
||||
|
||||
}
|
||||
|
||||
@end
|
40
MacDependency/VersionFormatter.m
Normal file
40
MacDependency/VersionFormatter.m
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// VersionFormatter.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 17.07.09.
|
||||
// Copyright 2009 Konrad Windszus. All rights reserved.
|
||||
//
|
||||
// Formatter for version integers (4 byte) in the form xxxx.xx.xx
|
||||
#import "VersionFormatter.h"
|
||||
|
||||
#define HIBYTE(x) ( (unsigned char) ((x) >> 8) )
|
||||
#define LOBYTE(x) ( (unsigned char) (x) )
|
||||
#define HIWORD(x) ( (unsigned short) ( (x) >> 16) )
|
||||
#define LOWORD(x) ( (unsigned short) (x) )
|
||||
|
||||
@implementation VersionFormatter
|
||||
|
||||
// conversion to string
|
||||
- (NSString*) stringForObjectValue:(id)obj {
|
||||
// must be a NSNumber
|
||||
if (![obj isKindOfClass:[NSNumber class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// NSNumber contains the version as unsigned int
|
||||
unsigned int version = [obj unsignedIntValue];
|
||||
|
||||
if (version == 0) {
|
||||
return [NSString string];
|
||||
}
|
||||
NSString* versionString = [NSString stringWithFormat:@"%d.%d.%d", HIWORD(version), (unsigned short)HIBYTE(LOWORD(version)), (unsigned short)LOBYTE(LOWORD(version)) ];
|
||||
return versionString;
|
||||
}
|
||||
|
||||
|
||||
// conversion from string (not necessary)
|
||||
- (BOOL) getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)errorString {
|
||||
return NO;
|
||||
}
|
||||
@end
|
14
MacDependency/main.m
Normal file
14
MacDependency/main.m
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// main.m
|
||||
// MacDependency
|
||||
//
|
||||
// Created by Konrad Windszus on 19.06.09.
|
||||
// Copyright Konrad Windszus 2009 . All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, (const char **) argv);
|
||||
}
|
26
MachO/Info.plist
Normal file
26
MachO/Info.plist
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.googlecode.${PRODUCT_NAME:identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
7
MachO/MachO_Prefix.pch
Normal file
7
MachO/MachO_Prefix.pch
Normal file
@ -0,0 +1,7 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'MachO' target in the 'MachO' project.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
50
MachO/demangler.cpp
Normal file
50
MachO/demangler.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "demangler.h"
|
||||
#include "machodemangleexception.h"
|
||||
|
||||
using namespace boost::process;
|
||||
/**
|
||||
class for using c++flt to demangle names. Uses Boost.Process from http://www.highscore.de/cpp/process/index.html
|
||||
*/
|
||||
Demangler::Demangler() : child(NULL), isRunning(false)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
Demangler::~Demangler()
|
||||
{
|
||||
if (child)
|
||||
child->terminate();
|
||||
delete child;
|
||||
}
|
||||
|
||||
string Demangler::demangleName(const string& name) {
|
||||
if (isRunning){
|
||||
(*stdin) << name << endl;
|
||||
string line;
|
||||
getline(*stdout, line);
|
||||
return line;
|
||||
} else {
|
||||
throw MachODemangleException("Could not find/start process c++flt.");
|
||||
}
|
||||
}
|
||||
|
||||
void Demangler::init() {
|
||||
try {
|
||||
std::string exec = find_executable_in_path("c++filt3");
|
||||
std::vector<std::string> args;
|
||||
args.push_back("--strip-underscore");
|
||||
context ctx;
|
||||
ctx.environment = self::get_environment();
|
||||
ctx.stdout_behavior = capture_stream();
|
||||
ctx.stdin_behavior = capture_stream();
|
||||
child = new boost::process::child(launch(exec, args, ctx));
|
||||
stdout = &child->get_stdout();
|
||||
stdin = &child->get_stdin();
|
||||
isRunning = true;
|
||||
// TODO: check exceptions
|
||||
} catch (boost::filesystem::filesystem_error& e) {
|
||||
// errors during finding executable
|
||||
} catch (boost::system::system_error& e2) {
|
||||
// errors during starting of process
|
||||
}
|
||||
}
|
24
MachO/demangler.h
Normal file
24
MachO/demangler.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef DEMANGLER_H
|
||||
#define DEMANGLER_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <boost/process.hpp>
|
||||
|
||||
class Demangler
|
||||
{
|
||||
public:
|
||||
Demangler();
|
||||
virtual ~Demangler();
|
||||
|
||||
string demangleName(const string& name);
|
||||
private:
|
||||
boost::process::child* child;
|
||||
boost::process::pistream* stdout;
|
||||
boost::process::postream* stdin;
|
||||
bool isRunning;
|
||||
|
||||
void init();
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // DEMANGLER_H
|
48
MachO/dylibcommand.cpp
Normal file
48
MachO/dylibcommand.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "dylibcommand.h"
|
||||
#include "machofile.h"
|
||||
#include "machoheader.h"
|
||||
#include <sstream>
|
||||
|
||||
#define HIBYTE(x) ( (unsigned char) ((x) >> 8) )
|
||||
#define LOBYTE(x) ( (unsigned char) (x) )
|
||||
#define HIWORD(x) ( (unsigned short) ( (x) >> 16) )
|
||||
#define LOWORD(x) ( (unsigned short) (x) )
|
||||
|
||||
#define MAKEVERSION(x,y,z) 0x00000000 | (x << 16) | (y << 8) | z
|
||||
|
||||
DylibCommand::DylibCommand(MachOHeader* header, DependencyType type) :
|
||||
LoadCommand(header), type(type)
|
||||
{
|
||||
file.readBytes((char*)&command, sizeof(command));
|
||||
}
|
||||
|
||||
DylibCommand::~DylibCommand() {
|
||||
}
|
||||
|
||||
unsigned int DylibCommand::getSize() const {
|
||||
return file.getUint32(command.cmdsize);
|
||||
}
|
||||
|
||||
string DylibCommand::getName() const {
|
||||
return string(getLcDataString(command.dylib.name.offset));
|
||||
}
|
||||
|
||||
unsigned int DylibCommand::getCurrentVersion() const {
|
||||
return file.getUint32(command.dylib.current_version);
|
||||
}
|
||||
|
||||
unsigned int DylibCommand::getCompatibleVersion() const {
|
||||
return file.getUint32(command.dylib.compatibility_version);
|
||||
}
|
||||
|
||||
time_t DylibCommand::getTimeStamp() const {
|
||||
return file.getUint32(command.dylib.timestamp);
|
||||
}
|
||||
|
||||
string DylibCommand::getVersionString(unsigned int version) {
|
||||
stringstream versionString;
|
||||
versionString << HIWORD(version) << "." << (unsigned short)HIBYTE(LOWORD(version)) << "." << (unsigned short)LOBYTE(LOWORD(version));
|
||||
return versionString.str();
|
||||
}
|
||||
|
||||
|
35
MachO/dylibcommand.h
Normal file
35
MachO/dylibcommand.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef DYLIBCOMMAND_H
|
||||
#define DYLIBCOMMAND_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include "loadcommand.h"
|
||||
class MACHOSHARED_EXPORT DylibCommand : public LoadCommand
|
||||
{
|
||||
public:
|
||||
enum DependencyType {
|
||||
DependencyWeak, // dependency is allowed to be missing
|
||||
DependencyDelayed, // dependency is loaded when it is needed (not at start)
|
||||
DependencyNormal,
|
||||
DependencyId
|
||||
};
|
||||
|
||||
DylibCommand(MachOHeader* header, DependencyType type);
|
||||
virtual ~DylibCommand();
|
||||
virtual unsigned int getSize() const;
|
||||
virtual unsigned int getStructureSize() const { return sizeof(command); }
|
||||
bool isId() const { return type==DependencyId; }
|
||||
bool isNecessary() const { return type!=DependencyWeak; }
|
||||
DependencyType getType() const { return type; }
|
||||
string getName() const;
|
||||
unsigned int getCurrentVersion() const;
|
||||
unsigned int getCompatibleVersion() const;
|
||||
time_t getTimeStamp() const;
|
||||
static string getVersionString(unsigned int version);
|
||||
|
||||
private:
|
||||
dylib_command command;
|
||||
DependencyType type;
|
||||
|
||||
};
|
||||
|
||||
#endif // DYLIBCOMMAND_H
|
314
MachO/dynamicloader.cpp
Normal file
314
MachO/dynamicloader.cpp
Normal file
@ -0,0 +1,314 @@
|
||||
#include "dynamicloader.h"
|
||||
#include "machofile.h"
|
||||
#include "machoarchitecture.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
/*
|
||||
This class emulates the search path mechanism of dyld
|
||||
http://developer.apple.com/documentation/Darwin/Reference/Manpages/man1/dyld.1.html
|
||||
Unfortunately the several documents from apple contradict each other. Therefore I analyzed the source of the original dyld
|
||||
http://www.opensource.apple.com/source/dyld/dyld-97.1/src/dyld.cpp
|
||||
*/
|
||||
|
||||
const char* DynamicLoader::EnvironmentPathVariable::PATHS_SEPARATOR = ";";
|
||||
const char* DynamicLoader::EnvironmentPathVariable::HOME_PATH = getenv("HOME");
|
||||
|
||||
DynamicLoader::EnvironmentPathVariable::EnvironmentPathVariable() {
|
||||
// default constructor (should not be used explicitly)
|
||||
}
|
||||
|
||||
DynamicLoader::EnvironmentPathVariable::EnvironmentPathVariable(const string& name, const StringList& defaultValues)
|
||||
{
|
||||
const char* envValue = getenv(name.c_str());
|
||||
string values;
|
||||
if (envValue) {
|
||||
values = envValue;
|
||||
}
|
||||
|
||||
if (!values.empty()) {
|
||||
size_t start = 0;
|
||||
size_t end;
|
||||
while ((end = values.find(PATHS_SEPARATOR)) != string::npos) {
|
||||
addPath(values.substr(start, end-start));
|
||||
start = end+1;
|
||||
}
|
||||
} else {
|
||||
setPaths(defaultValues);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLoader::EnvironmentPathVariable::setPaths(const StringList& paths) {
|
||||
this->paths = paths;
|
||||
|
||||
for (StringList::iterator it = this->paths.begin(); it!=this->paths.end(); ++it) {
|
||||
replaceHomeDirectory(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicLoader::EnvironmentPathVariable::addPath(const string& path) {
|
||||
paths.push_back(path);
|
||||
replaceHomeDirectory(paths.back());
|
||||
}
|
||||
|
||||
bool DynamicLoader::EnvironmentPathVariable::replaceHomeDirectory(string& path) {
|
||||
size_t homePos = path.find("~");
|
||||
if (homePos != string::npos) {
|
||||
path.replace(homePos, 1, HOME_PATH);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicLoader::EnvironmentPathVariable::isEmpty() const {
|
||||
if (!paths.empty()) {
|
||||
return paths.front().empty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// the order must be the order of the enum!
|
||||
const char* DynamicLoader::ENVIRONMENT_VARIABLE_NAMES[DynamicLoader::NumEnvironmentVariables] = {
|
||||
"LD_LIBRARY_PATH",
|
||||
"DYLD_FRAMEWORK_PATH",
|
||||
"DYLD_LIBRARY_PATH",
|
||||
"DYLD_FALLBACK_FRAMEWORK_PATH",
|
||||
"DYLD_FALLBACK_LIBRARY_PATH",
|
||||
"DYLD_IMAGE_SUFFIX"
|
||||
};
|
||||
|
||||
const char* DynamicLoader::PLACEHOLDERS[DynamicLoader::NumPlaceholders] = {
|
||||
"@executable_path",
|
||||
"@loader_path",
|
||||
"@rpath"
|
||||
};
|
||||
|
||||
const char* DynamicLoader::PATH_SEPARATOR = "/";
|
||||
|
||||
const char* DynamicLoader::DEFAULT_FRAMEWORK_PATH[] = {
|
||||
"~/Library/Frameworks",
|
||||
"/Library/Frameworks",
|
||||
"/Network/Library/Frameworks",
|
||||
"/System/Library/Frameworks"
|
||||
};
|
||||
|
||||
const char* DynamicLoader::DEFAULT_LIBRARY_PATH[] = {
|
||||
"~/lib",
|
||||
"/usr/local/lib",
|
||||
"/lib",
|
||||
"/usr/lib"
|
||||
};
|
||||
|
||||
// unfortunately cannot make stringList const here, but treat it as const
|
||||
const StringList DynamicLoader::ENVIRONMENT_VARIABLE_DEFAULT_VALUES[DynamicLoader::NumEnvironmentVariables] = {
|
||||
StringList(),
|
||||
StringList(),
|
||||
StringList(),
|
||||
StringList(DEFAULT_FRAMEWORK_PATH, DEFAULT_FRAMEWORK_PATH + sizeof(DEFAULT_FRAMEWORK_PATH) / sizeof(*DEFAULT_FRAMEWORK_PATH)),
|
||||
StringList(DEFAULT_LIBRARY_PATH, DEFAULT_LIBRARY_PATH + sizeof(DEFAULT_LIBRARY_PATH) / sizeof(*DEFAULT_LIBRARY_PATH)),
|
||||
StringList()
|
||||
};
|
||||
|
||||
DynamicLoader::DynamicLoader()
|
||||
{
|
||||
// init/read out some variables
|
||||
for (unsigned int i=0; i < NumEnvironmentVariables; i++) {
|
||||
environmentVariables[i] = EnvironmentPathVariable(ENVIRONMENT_VARIABLE_NAMES[i], ENVIRONMENT_VARIABLE_DEFAULT_VALUES[i]);
|
||||
}
|
||||
}
|
||||
|
||||
string DynamicLoader::replacePlaceholder(const string& name, const MachOArchitecture* architecture) const {
|
||||
string resolvedName = name;
|
||||
if (name.find(PLACEHOLDERS[ExecutablePath]) == 0) {
|
||||
resolvedName.replace(0, strlen(PLACEHOLDERS[ExecutablePath]), architecture->getFile()->getExecutablePath());
|
||||
} else if (name.find(PLACEHOLDERS[LoaderPath]) == 0) {
|
||||
resolvedName.replace(0, strlen(PLACEHOLDERS[LoaderPath]), architecture->getFile()->getPath());
|
||||
} else {
|
||||
return string();
|
||||
}
|
||||
return resolvedName;
|
||||
}
|
||||
|
||||
string DynamicLoader::getPathname(const string& name, const MachOArchitecture* architecture, const string& workingPath) const {
|
||||
// simple name (only the last part of the name, after the last PATH_SEPARATOR)
|
||||
size_t lastSlashPosition = name.rfind(PATH_SEPARATOR);
|
||||
string simpleName;
|
||||
if (lastSlashPosition != string::npos && lastSlashPosition < name.length() - 1) {
|
||||
simpleName = name.substr(lastSlashPosition+1);
|
||||
} else {
|
||||
simpleName = name;
|
||||
}
|
||||
|
||||
// try LD_LIBRARY_PATH
|
||||
string pathName;
|
||||
pathName = getExistingPathname(simpleName, environmentVariables[LdLibraryPath]);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
|
||||
string frameworkName = getFrameworkName(name);
|
||||
if (!frameworkName.empty()) {
|
||||
// strip the already contained suffix
|
||||
pathName = getExistingPathname(frameworkName, environmentVariables[DyldFrameworkPath]);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
}
|
||||
|
||||
pathName = getExistingPathname(simpleName, environmentVariables[DyldLibraryPath]);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
|
||||
// resolve placeholder
|
||||
string resolvedName = replacePlaceholder(name, architecture);
|
||||
if (!resolvedName.empty()) {
|
||||
pathName = getExistingPathname(resolvedName);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
}
|
||||
|
||||
if (name.find(PLACEHOLDERS[RPath]) == 0) {
|
||||
// substitute @rpath with all -rpath paths up the load chain
|
||||
std::vector<string*> rPaths = architecture->getRPaths();
|
||||
|
||||
for (std::vector<string*>::iterator it = rPaths.begin(); it != rPaths.end(); ++it) {
|
||||
resolvedName = name;
|
||||
resolvedName.replace(0, strlen(PLACEHOLDERS[RPath]), (**it));
|
||||
pathName = getExistingPathname(resolvedName);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
}
|
||||
|
||||
// after checking against all stored rpaths substitute @rpath with LD_LIBRARY_PATH (if it is set)
|
||||
EnvironmentPathVariable ldLibraryPaths = environmentVariables[LdLibraryPath];
|
||||
if (!ldLibraryPaths.isEmpty()) {
|
||||
for (StringList::const_iterator it = ldLibraryPaths.getPaths().begin(); it != ldLibraryPaths.getPaths().end(); ++it) {
|
||||
resolvedName = name;
|
||||
resolvedName.replace(0, strlen(PLACEHOLDERS[RPath]), (*it));
|
||||
pathName = getExistingPathname(resolvedName);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check pure path (either absolute or relative to working directory)
|
||||
if (name.find(PATH_SEPARATOR) == 0) {
|
||||
pathName = getExistingPathname(name);
|
||||
} else {
|
||||
pathName = getExistingPathname(name, workingPath);
|
||||
}
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
|
||||
// try fallbacks (or its defaults)
|
||||
if (!frameworkName.empty()) {
|
||||
pathName = getExistingPathname(frameworkName, environmentVariables[DyldFallbackFrameworkPath]);
|
||||
if (!pathName.empty())
|
||||
return pathName;
|
||||
}
|
||||
|
||||
return getExistingPathname(name, environmentVariables[DyldFallbackLibraryPath]);
|
||||
}
|
||||
|
||||
// returns the name is of a framework without any preceeding path information if name specifies a framework, otherwise an invalid string
|
||||
string DynamicLoader::getFrameworkName(const string& name, const bool strippedSuffix) const {
|
||||
// fail fast in case of dylibs
|
||||
if (name.find(".framework/") == string::npos) {
|
||||
return string();
|
||||
}
|
||||
|
||||
/* first look for the form Foo.framework/Foo
|
||||
next look for the form Foo.framework/Versions/A/Foo
|
||||
A and Foo are arbitrary strings without a slash */
|
||||
|
||||
// get Foo (part after last slash)
|
||||
size_t lastSlashPosition = name.rfind(PATH_SEPARATOR);
|
||||
if (lastSlashPosition == string::npos || lastSlashPosition == name.length() -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const string foo = name.substr(lastSlashPosition+1);
|
||||
const string frameworkPart = foo+".framework/";
|
||||
const string framework = frameworkPart + foo;
|
||||
|
||||
if (endsWith(name, framework)) {
|
||||
// strip first part
|
||||
return framework;
|
||||
}
|
||||
int startPosition = name.find(frameworkPart+"Versions/");
|
||||
bool hasCorrectEnd = endsWith(name, foo);
|
||||
|
||||
// TODO: check between Versions/ and foo there must be no additional slash
|
||||
if (startPosition != string::npos) {
|
||||
if (hasCorrectEnd) {
|
||||
return name.substr(startPosition);
|
||||
} else if (strippedSuffix == false) {
|
||||
// maybe we have a case, where name contains a suffix in foo (which then of course occurs only in the last foo)
|
||||
// does foo already contain a suffix?
|
||||
size_t suffixStart = foo.rfind("_");
|
||||
if (suffixStart != string::npos) {
|
||||
string newName = name;
|
||||
newName.erase(lastSlashPosition+1+suffixStart);
|
||||
return getFrameworkName(newName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we are at this part the given name was no framework
|
||||
return string();
|
||||
}
|
||||
|
||||
string DynamicLoader::getExistingPathname(const string& name, const EnvironmentPathVariable& environmentPathVariable) const {
|
||||
string result;
|
||||
const StringList directories = environmentPathVariable.getPaths();
|
||||
for (StringList::const_iterator it = directories.begin(); it != directories.end(); ++it) {
|
||||
result = getExistingPathname(name, *it);
|
||||
if (!result.empty())
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string DynamicLoader::getExistingPathname(const string& file, const string& directory) const {
|
||||
string name = file;
|
||||
if (!directory.empty()) {
|
||||
if (!endsWith(directory, "/")) {
|
||||
name = directory + "/" + file;
|
||||
} else {
|
||||
name = directory + file;
|
||||
}
|
||||
}
|
||||
return getExistingPathname(name);
|
||||
}
|
||||
|
||||
string DynamicLoader::getExistingPathname(const string& name) const {
|
||||
|
||||
boost::filesystem::path path;
|
||||
// first try with suffix
|
||||
if (!environmentVariables[DyldImageSuffix].isEmpty()) {
|
||||
const string suffix = environmentVariables[DyldImageSuffix].getPaths().front();
|
||||
string nameWithSuffix = name;
|
||||
// where should we append suffix?
|
||||
if (endsWith(name, ".dylib")) {
|
||||
nameWithSuffix.insert(name.rfind("."), suffix);
|
||||
} else {
|
||||
nameWithSuffix += suffix;
|
||||
}
|
||||
|
||||
path = nameWithSuffix;
|
||||
if (boost::filesystem::exists(path)) {
|
||||
return nameWithSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
// then without suffix
|
||||
path = name;
|
||||
if (boost::filesystem::exists(path)) {
|
||||
return name;
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
bool DynamicLoader::endsWith(const string& str, const string& substr) {
|
||||
size_t i = str.rfind(substr);
|
||||
return (i != string::npos) && (i == (str.length() - substr.length()));
|
||||
}
|
75
MachO/dynamicloader.h
Normal file
75
MachO/dynamicloader.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef DYNAMICLOADER_H
|
||||
#define DYNAMICLOADER_H
|
||||
|
||||
#include "macho_global.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
typedef list<string> StringList;
|
||||
|
||||
class MachOArchitecture;
|
||||
class DynamicLoader
|
||||
{
|
||||
public:
|
||||
DynamicLoader();
|
||||
virtual ~DynamicLoader() {}
|
||||
|
||||
string replacePlaceholder(const string& name, const MachOArchitecture* architecture) const;
|
||||
string getPathname(const string& name, const MachOArchitecture* architecture, const string& workingDirectory) const;
|
||||
|
||||
private:
|
||||
class EnvironmentPathVariable
|
||||
{
|
||||
|
||||
public:
|
||||
EnvironmentPathVariable();
|
||||
EnvironmentPathVariable(const string& name, const StringList& defaultValues = StringList());
|
||||
|
||||
bool isEmpty() const;
|
||||
const StringList& getPaths() const { return paths; }
|
||||
|
||||
private:
|
||||
void setPaths(const StringList& paths);
|
||||
void addPath(const string& path);
|
||||
bool replaceHomeDirectory(string& path);
|
||||
StringList paths;
|
||||
static const char* PATHS_SEPARATOR;
|
||||
static const char* HOME_PATH;
|
||||
};
|
||||
|
||||
enum {
|
||||
LdLibraryPath,
|
||||
DyldFrameworkPath,
|
||||
DyldLibraryPath,
|
||||
DyldFallbackFrameworkPath,
|
||||
DyldFallbackLibraryPath,
|
||||
DyldImageSuffix,
|
||||
NumEnvironmentVariables
|
||||
};
|
||||
|
||||
enum Placeholder {
|
||||
ExecutablePath,
|
||||
LoaderPath,
|
||||
RPath,
|
||||
NumPlaceholders
|
||||
};
|
||||
|
||||
static const char* PLACEHOLDERS[NumPlaceholders];
|
||||
static const char* ENVIRONMENT_VARIABLE_NAMES[NumEnvironmentVariables];
|
||||
static const char* PATH_SEPARATOR;
|
||||
static const StringList ENVIRONMENT_VARIABLE_DEFAULT_VALUES[NumEnvironmentVariables];
|
||||
|
||||
static const char* DEFAULT_FRAMEWORK_PATH[];
|
||||
static const char* DEFAULT_LIBRARY_PATH[];
|
||||
|
||||
EnvironmentPathVariable environmentVariables[NumEnvironmentVariables];
|
||||
|
||||
string getFrameworkName(const string& name, const bool strippedSuffix = false) const;
|
||||
string getExistingPathname(const string& name, const EnvironmentPathVariable& environmentPathVariable) const;
|
||||
string getExistingPathname(const string& name, const string& directory) const;
|
||||
string getExistingPathname(const string& name) const;
|
||||
|
||||
static bool endsWith(const string& str, const string& substr);
|
||||
};
|
||||
|
||||
#endif // DYNAMICLOADER_H
|
13
MachO/genericcommand.cpp
Normal file
13
MachO/genericcommand.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "genericcommand.h"
|
||||
#include "machofile.h"
|
||||
#include "machoheader.h"
|
||||
|
||||
GenericCommand::GenericCommand(MachOHeader* header) :
|
||||
LoadCommand(header)
|
||||
{
|
||||
file.readBytes((char*)&command, sizeof(command));
|
||||
}
|
||||
|
||||
unsigned int GenericCommand::getSize() const {
|
||||
return file.getUint32(command.cmdsize);
|
||||
}
|
15
MachO/genericcommand.h
Normal file
15
MachO/genericcommand.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef GENERICCOMMAND_H
|
||||
#define GENERICCOMMAND_H
|
||||
|
||||
#include "loadcommand.h"
|
||||
|
||||
class GenericCommand : public LoadCommand
|
||||
{
|
||||
public:
|
||||
GenericCommand(MachOHeader* header);
|
||||
virtual unsigned int getSize() const;
|
||||
private:
|
||||
load_command command;
|
||||
};
|
||||
|
||||
#endif // GENERICCOMMAND_H
|
97
MachO/internalfile.cpp
Normal file
97
MachO/internalfile.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "internalfile.h"
|
||||
#include "machoexception.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace boost::filesystem;
|
||||
|
||||
// use reference counting to reuse files for all used architectures
|
||||
InternalFile* InternalFile::create(InternalFile* file) {
|
||||
file->counter++;
|
||||
return file;
|
||||
}
|
||||
|
||||
InternalFile* InternalFile::create(const string& filename) {
|
||||
return new InternalFile(filename);
|
||||
}
|
||||
|
||||
void InternalFile::release() {
|
||||
counter--;
|
||||
if (counter < 1) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
InternalFile::InternalFile(const string& filename) :
|
||||
filename(filename), counter(1)
|
||||
{
|
||||
// open file handle
|
||||
file.open(this->filename);
|
||||
if (file.fail()) {
|
||||
ostringstream error;
|
||||
error << "Couldn't open file '" << filename << "'.";
|
||||
throw MachOException(error.str());
|
||||
}
|
||||
}
|
||||
|
||||
// destructor is private since we use reference counting mechanism
|
||||
InternalFile::~InternalFile() {
|
||||
file.close();
|
||||
}
|
||||
|
||||
string InternalFile::getPath() const {
|
||||
return filename.parent_path().string();
|
||||
|
||||
}
|
||||
|
||||
/* returns whole filename (including path)*/
|
||||
string InternalFile::getName() const {
|
||||
|
||||
/* unfortunately canonized is not available in newer versions of boost filesystem.
|
||||
For the reasons see the filsystem proposal at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1889.html.
|
||||
As an alternative I use realpath but I don't know if it handles unicode strings also.
|
||||
*/
|
||||
|
||||
// try to canonicalize path
|
||||
char* resolvedName = realpath(filename.file_string().c_str(), NULL);
|
||||
if (!resolvedName)
|
||||
return filename.file_string();
|
||||
string resolvedFileName(resolvedName);
|
||||
free(resolvedName);
|
||||
return resolvedName;
|
||||
}
|
||||
|
||||
/* returns filename without path */
|
||||
string InternalFile::getTitle() const {
|
||||
return filename.filename();
|
||||
}
|
||||
|
||||
long long int InternalFile::getSize() const {
|
||||
return file_size(filename);
|
||||
}
|
||||
|
||||
bool InternalFile::seek(long long int position) {
|
||||
file.seekg(position, ios_base::beg);
|
||||
if (file.fail()) {
|
||||
file.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
streamsize InternalFile::read(char* buffer, streamsize size) {
|
||||
file.read(buffer, size);
|
||||
if (file.fail()) {
|
||||
file.clear();
|
||||
return file.gcount();
|
||||
}
|
||||
// TODO: handle badbit
|
||||
return size;
|
||||
}
|
||||
|
||||
long long int InternalFile::getPosition() {
|
||||
return file.tellg();
|
||||
}
|
||||
|
||||
time_t InternalFile::getLastModificationTime() const {
|
||||
return last_write_time(filename);
|
||||
}
|
33
MachO/internalfile.h
Normal file
33
MachO/internalfile.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef INTERNALFILE_H
|
||||
#define INTERNALFILE_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
class InternalFile
|
||||
{
|
||||
|
||||
public:
|
||||
static InternalFile* create(InternalFile* file);
|
||||
static InternalFile* create(const string& filename);
|
||||
void release();
|
||||
|
||||
string getPath() const;
|
||||
string getName() const;
|
||||
string getTitle() const;
|
||||
long long int getSize() const;
|
||||
bool seek(long long int position);
|
||||
streamsize read(char* buffer, streamsize size);
|
||||
long long int getPosition();
|
||||
time_t getLastModificationTime() const;
|
||||
|
||||
private:
|
||||
unsigned int counter;
|
||||
virtual ~InternalFile();
|
||||
InternalFile(const string& filename);
|
||||
boost::filesystem::ifstream file;
|
||||
boost::filesystem::path filename;
|
||||
};
|
||||
|
||||
#endif // INTERNALFILE_H
|
64
MachO/loadcommand.cpp
Normal file
64
MachO/loadcommand.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "loadcommand.h"
|
||||
#include "dylibcommand.h"
|
||||
#include "genericcommand.h"
|
||||
#include "symboltablecommand.h"
|
||||
#include "rpathcommand.h"
|
||||
#include "machoexception.h"
|
||||
#include "machoheader.h"
|
||||
#include "/usr/include/mach-o/loader.h"
|
||||
|
||||
LoadCommand* LoadCommand::getLoadCommand(unsigned int cmd, MachOHeader* header) {
|
||||
|
||||
LoadCommand* loadCommand;
|
||||
switch(cmd) {
|
||||
case LC_LAZY_LOAD_DYLIB: // dependency is loaded when it is needed
|
||||
loadCommand = new DylibCommand(header, DylibCommand::DependencyDelayed);
|
||||
break;
|
||||
case LC_LOAD_WEAK_DYLIB: // dependency is allowed to be missing
|
||||
loadCommand = new DylibCommand(header, DylibCommand::DependencyWeak);
|
||||
break;
|
||||
case LC_REEXPORT_DYLIB:
|
||||
case LC_LOAD_DYLIB:
|
||||
loadCommand = new DylibCommand(header, DylibCommand::DependencyNormal);
|
||||
break;
|
||||
case LC_ID_DYLIB:
|
||||
loadCommand = new DylibCommand(header, DylibCommand::DependencyId);
|
||||
break;
|
||||
case LC_SYMTAB:
|
||||
loadCommand = new SymbolTableCommand(header);
|
||||
break;
|
||||
case LC_RPATH:
|
||||
loadCommand = new RPathCommand(header);
|
||||
break;
|
||||
default:
|
||||
loadCommand = new GenericCommand(header);
|
||||
break;
|
||||
}
|
||||
return loadCommand;
|
||||
}
|
||||
|
||||
|
||||
LoadCommand::LoadCommand(MachOHeader* header) :
|
||||
header(header), file(header->getFile()), offset(file.getPosition()), lcData(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LoadCommand::~LoadCommand() {
|
||||
delete[] lcData;
|
||||
}
|
||||
|
||||
void LoadCommand::readLcData() const {
|
||||
file.seek(offset + getStructureSize());
|
||||
unsigned int size = getSize() - getStructureSize();
|
||||
lcData = new char[size];
|
||||
file.readBytes(lcData, size);
|
||||
}
|
||||
|
||||
// the offset is not yet in correct byte order here
|
||||
const char* LoadCommand::getLcDataString(unsigned int offset) const {
|
||||
if (!lcData)
|
||||
readLcData();
|
||||
|
||||
return lcData+file.getUint32(offset)-getStructureSize();
|
||||
}
|
32
MachO/loadcommand.h
Normal file
32
MachO/loadcommand.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef LOADCOMMAND_H
|
||||
#define LOADCOMMAND_H
|
||||
#include "macho_global.h"
|
||||
#include "/usr/include/mach-o/loader.h"
|
||||
|
||||
class MachOHeader;
|
||||
class MachOFile;
|
||||
|
||||
class MACHOSHARED_EXPORT LoadCommand
|
||||
{
|
||||
protected:
|
||||
LoadCommand(MachOHeader* header);
|
||||
public:
|
||||
static LoadCommand* getLoadCommand(unsigned int cmd, MachOHeader* header);
|
||||
virtual ~LoadCommand();
|
||||
virtual unsigned int getSize() const = 0;
|
||||
// is smaller than getSize() because it only returns the size of the command structure
|
||||
// only necessary if command uses getLcData()
|
||||
virtual unsigned int getStructureSize() const { return 0; }
|
||||
protected:
|
||||
MachOHeader* header;
|
||||
MachOFile& file;
|
||||
const unsigned int offset;
|
||||
|
||||
const char* getLcDataString(unsigned int offset) const;
|
||||
private:
|
||||
mutable char* lcData;
|
||||
|
||||
void readLcData() const;
|
||||
};
|
||||
|
||||
#endif // LOADCOMMAND_H
|
218
MachO/macho.cpp
Normal file
218
MachO/macho.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
#include "macho.h"
|
||||
#include "machoexception.h"
|
||||
#include "machofile.h"
|
||||
#include "machoarchitecture.h"
|
||||
#include "demangler.h"
|
||||
#include "dynamicloader.h"
|
||||
#include "machoheader.h"
|
||||
|
||||
#include <mach-o/fat.h>
|
||||
|
||||
|
||||
// static variables
|
||||
Demangler* MachO::demangler = 0;
|
||||
DynamicLoader* MachO::dynamicLoader = 0;
|
||||
int MachO::referenceCounter = 0;
|
||||
|
||||
MachO::MachO(const string& filename, const MachO* parent) : parent(parent), bundle(NULL)
|
||||
{
|
||||
// check if filename is bundle
|
||||
string appFilename = getApplicationInBundle(filename);
|
||||
init(appFilename, parent);
|
||||
|
||||
if (referenceCounter == 0) {
|
||||
demangler = new Demangler();
|
||||
dynamicLoader = new DynamicLoader();
|
||||
}
|
||||
referenceCounter++;
|
||||
}
|
||||
|
||||
string MachO::getApplicationInBundle(const string& filename) {
|
||||
CFURLRef bundleUrl = 0;
|
||||
string appFilename = filename;
|
||||
bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)filename.c_str() , filename.length(), true);
|
||||
if (bundleUrl != NULL) {
|
||||
bundle = CFBundleCreate(NULL, bundleUrl);
|
||||
CFRelease(bundleUrl);
|
||||
if (bundle != NULL) {
|
||||
CFURLRef executableUrl = CFBundleCopyExecutableURL(bundle);
|
||||
if (executableUrl != 0) {
|
||||
char executableFile[FILENAME_MAX];
|
||||
CFURLGetFileSystemRepresentation(executableUrl, true, (UInt8 *)executableFile, FILENAME_MAX);
|
||||
appFilename = executableFile;
|
||||
CFRelease(executableUrl);
|
||||
//this->bundlePath = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
return appFilename;
|
||||
}
|
||||
|
||||
void MachO::init(const string& fileName, const MachO* parent)
|
||||
{
|
||||
MachOFile* parentFile = 0;
|
||||
if (parent) {
|
||||
parentFile = parent->file;
|
||||
}
|
||||
file = new MachOFile(fileName, parentFile);
|
||||
// read out magic number
|
||||
uint32_t magic = file->readUint32();
|
||||
|
||||
// Universal magic is always BE
|
||||
if (file->getUint32BE(magic) == FAT_MAGIC) {
|
||||
// this is an universal file
|
||||
|
||||
// get number of architecture headers (BE)
|
||||
uint32_t numberOfArchitectures = file->readUint32BE();
|
||||
|
||||
// read out all architecture headers
|
||||
fat_arch fatArch[numberOfArchitectures];
|
||||
file->readBytes((char*)fatArch, sizeof(fat_arch)*numberOfArchitectures);
|
||||
|
||||
// go through all architectures
|
||||
for (unsigned int n=0; n < numberOfArchitectures; n++) {
|
||||
unsigned int offset = file->getUint32BE(fatArch[n].offset);
|
||||
file->seek(offset);
|
||||
// read out magic number
|
||||
uint32_t magic = file->readUint32();
|
||||
file->seek(offset);
|
||||
MachOArchitecture* architecture = new MachOArchitecture(*file, magic, file->getUint32BE(fatArch[n].size));
|
||||
if (parent)
|
||||
architecture->initParentArchitecture(parent->getCompatibleArchitecture(architecture));
|
||||
architectures.push_back(architecture);
|
||||
}
|
||||
} else {
|
||||
// seek back to beginning
|
||||
file->seek(0);
|
||||
MachOArchitecture* architecture = new MachOArchitecture(*file, magic, getSize());
|
||||
if (parent)
|
||||
architecture->initParentArchitecture(parent->getCompatibleArchitecture(architecture));
|
||||
architectures.push_back(architecture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MachO::~MachO() {
|
||||
referenceCounter--;
|
||||
if (referenceCounter == 0) {
|
||||
delete demangler;
|
||||
demangler = 0;
|
||||
delete dynamicLoader;
|
||||
dynamicLoader = 0;
|
||||
}
|
||||
|
||||
for (MachOArchitecturesIterator it = architectures.begin();
|
||||
it != architectures.end();
|
||||
++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
delete file;
|
||||
if (bundle)
|
||||
CFRelease(bundle);
|
||||
}
|
||||
|
||||
// choose an architecture which is compatible to the given architecture
|
||||
MachOArchitecture* MachO::getCompatibleArchitecture(MachOArchitecture* destArchitecture) const {
|
||||
MachOHeader::CpuType destCpuType = destArchitecture->getHeader()->getCpuType();
|
||||
|
||||
// go through all architectures
|
||||
for (MachOArchitecturesConstIterator it = architectures.begin();
|
||||
it != architectures.end();
|
||||
++it)
|
||||
{
|
||||
// TODO: match subtypes (only for PowerPC necessary)
|
||||
if ((*it)->getHeader()->getCpuType() == destCpuType)
|
||||
return *it;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
MachOArchitecture* MachO::getHostCompatibleArchitecture() const {
|
||||
|
||||
unsigned int destCpuType = MachOHeader::getHostCpuType();
|
||||
// go through all architectures
|
||||
for (MachOArchitecturesConstIterator it = architectures.begin();
|
||||
it != architectures.end();
|
||||
++it)
|
||||
{
|
||||
// TODO: match subtypes (only for PowerPC necessary)
|
||||
if ((*it)->getHeader()->getCpuType() == destCpuType)
|
||||
return *it;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
long long int MachO::getSize() const {
|
||||
return file->getSize();
|
||||
}
|
||||
|
||||
|
||||
time_t MachO::getLastModificationTime() const {
|
||||
return file->getLastModificationTime();
|
||||
}
|
||||
|
||||
// return bundle version if available, otherwise NULL string
|
||||
string MachO::getVersion() const {
|
||||
string version;
|
||||
if (bundle != 0) {
|
||||
CFStringRef cfVersion = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
|
||||
// is version available at all?
|
||||
if (cfVersion) {
|
||||
version = extractStringFromCFStringRef(cfVersion);
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
string MachO::getName() const {
|
||||
string name;
|
||||
if (bundle != 0) {
|
||||
CFStringRef cfBundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
|
||||
// is version available at all?
|
||||
if (cfBundleName) {
|
||||
name = extractStringFromCFStringRef(cfBundleName);
|
||||
} else {
|
||||
// take bundle executable name
|
||||
CFURLRef bundleUrl = CFBundleCopyExecutableURL(bundle);
|
||||
cfBundleName = CFURLCopyLastPathComponent(bundleUrl);
|
||||
name = extractStringFromCFStringRef(cfBundleName);
|
||||
CFRelease(cfBundleName);
|
||||
CFRelease(bundleUrl);
|
||||
|
||||
}
|
||||
} else {
|
||||
name = file->getTitle();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
string MachO::extractStringFromCFStringRef(CFStringRef cfStringRef) {
|
||||
string string;
|
||||
const char* szString = CFStringGetCStringPtr(cfStringRef, kCFStringEncodingASCII);
|
||||
if (szString == NULL) {
|
||||
CFIndex stringLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfStringRef), kCFStringEncodingASCII);
|
||||
|
||||
char szStringNew[stringLength + 1];
|
||||
if (CFStringGetCString(cfStringRef,
|
||||
szStringNew,
|
||||
stringLength+1,
|
||||
kCFStringEncodingASCII
|
||||
))
|
||||
string = szStringNew;
|
||||
delete[] szString;
|
||||
} else {
|
||||
string = szString;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
string MachO::getPath() const {
|
||||
return file->getPath();
|
||||
}
|
||||
|
||||
string MachO::getFileName() const { return file->getName(); }
|
||||
|
||||
MachO::MachOArchitecturesIterator MachO::getArchitecturesBegin() { return architectures.begin(); }
|
||||
|
||||
MachO::MachOArchitecturesIterator MachO::getArchitecturesEnd() { return architectures.end(); }
|
50
MachO/macho.h
Normal file
50
MachO/macho.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef MACHO_H
|
||||
#define MACHO_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <list>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
class MachOFile;
|
||||
class MachOArchitecture;
|
||||
class Demangler;
|
||||
class DynamicLoader;
|
||||
|
||||
class MACHOSHARED_EXPORT MachO {
|
||||
private:
|
||||
typedef std::list<MachOArchitecture*> MachOArchitectures;
|
||||
public:
|
||||
MachO(const string& fileName, const MachO* parent = 0);
|
||||
~MachO();
|
||||
|
||||
string getFileName() const;
|
||||
|
||||
typedef MachOArchitectures::iterator MachOArchitecturesIterator;
|
||||
typedef MachOArchitectures::const_iterator MachOArchitecturesConstIterator;
|
||||
MachOArchitecturesIterator getArchitecturesBegin();
|
||||
MachOArchitecturesIterator getArchitecturesEnd();
|
||||
MachOArchitecture* getCompatibleArchitecture(MachOArchitecture* destArchitecture) const;
|
||||
MachOArchitecture* getHostCompatibleArchitecture() const;
|
||||
long long int getSize() const;
|
||||
time_t getLastModificationTime() const;
|
||||
string getVersion() const;
|
||||
string getName() const;
|
||||
//sQIcon getIcon() const;*/
|
||||
const MachO* getParent() { return parent;}
|
||||
string getPath() const;
|
||||
static Demangler* demangler;
|
||||
static DynamicLoader* dynamicLoader;
|
||||
static int referenceCounter;
|
||||
private:
|
||||
const MachO* parent;
|
||||
MachOFile* file;
|
||||
MachOArchitectures architectures;
|
||||
CFBundleRef bundle;
|
||||
|
||||
string getApplicationInBundle(const string& bundlePath);
|
||||
static string extractStringFromCFStringRef(CFStringRef cfStringRef);
|
||||
void init(const string& fileName, const MachO* parent);
|
||||
|
||||
};
|
||||
|
||||
#endif // MACHO_H
|
32
MachO/macho32header.cpp
Normal file
32
MachO/macho32header.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "macho32header.h"
|
||||
#include "machoexception.h"
|
||||
|
||||
MachO32Header::MachO32Header(const MachOFile& file, bool reversedBO) :
|
||||
MachOHeader(file, reversedBO)
|
||||
{
|
||||
this->file.readBytes((char*)&header, sizeof(header));
|
||||
}
|
||||
|
||||
MachOHeader::CpuType MachO32Header::getCpuType() const {
|
||||
return MachOHeader::getCpuType(file.getUint32(header.cputype));
|
||||
}
|
||||
|
||||
unsigned int MachO32Header::getInternalFileType() const {
|
||||
return file.getUint32(header.filetype);
|
||||
}
|
||||
|
||||
bool MachO32Header::is64Bit() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int MachO32Header::getNumberOfLoadCommands() const {
|
||||
return file.getUint32(header.ncmds);
|
||||
}
|
||||
|
||||
unsigned int MachO32Header::getLoadCommandSize() const {
|
||||
return file.getUint32(header.sizeofcmds);;
|
||||
}
|
||||
|
||||
unsigned int MachO32Header::getSize() const {
|
||||
return sizeof(header);
|
||||
}
|
22
MachO/macho32header.h
Normal file
22
MachO/macho32header.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef MACHO32HEADER_H
|
||||
#define MACHO32HEADER_H
|
||||
|
||||
|
||||
#include "machoheader.h"
|
||||
|
||||
class MachO32Header : public MachOHeader
|
||||
{
|
||||
public:
|
||||
MachO32Header(const MachOFile& file, bool reversedBO);
|
||||
virtual unsigned int getNumberOfLoadCommands() const;
|
||||
virtual bool is64Bit() const;
|
||||
virtual unsigned int getLoadCommandSize() const; // size of load command following the header
|
||||
virtual unsigned int getSize() const; // size of header only
|
||||
virtual MachOHeader::CpuType getCpuType() const;
|
||||
protected:
|
||||
virtual unsigned int getInternalFileType() const;
|
||||
private:
|
||||
mach_header header;
|
||||
};
|
||||
|
||||
#endif // MACHO32HEADER_H
|
33
MachO/macho64header.cpp
Normal file
33
MachO/macho64header.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "macho64header.h"
|
||||
#include "machoexception.h"
|
||||
|
||||
MachO64Header::MachO64Header(const MachOFile& file, bool reversedBO) :
|
||||
MachOHeader(file, reversedBO)
|
||||
{
|
||||
|
||||
this->file.readBytes((char*)&header, sizeof(header));
|
||||
}
|
||||
|
||||
MachOHeader::CpuType MachO64Header::getCpuType() const {
|
||||
return MachOHeader::getCpuType(file.getUint32(header.cputype));
|
||||
}
|
||||
|
||||
unsigned int MachO64Header::getInternalFileType() const {
|
||||
return file.getUint32(header.filetype);
|
||||
}
|
||||
|
||||
bool MachO64Header::is64Bit() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int MachO64Header::getNumberOfLoadCommands() const {
|
||||
return file.getUint32(header.ncmds);
|
||||
}
|
||||
|
||||
unsigned int MachO64Header::getLoadCommandSize() const {
|
||||
return file.getUint32(header.sizeofcmds);
|
||||
}
|
||||
|
||||
unsigned int MachO64Header::getSize() const {
|
||||
return sizeof(header);
|
||||
}
|
21
MachO/macho64header.h
Normal file
21
MachO/macho64header.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MACHO64HEADER_H
|
||||
#define MACHO64HEADER_H
|
||||
|
||||
#include "machoheader.h"
|
||||
|
||||
class MachO64Header : public MachOHeader
|
||||
{
|
||||
public:
|
||||
MachO64Header(const MachOFile& file, bool reversedBO);
|
||||
virtual unsigned int getNumberOfLoadCommands() const;
|
||||
virtual bool is64Bit() const;
|
||||
virtual unsigned int getLoadCommandSize() const; // size of load command following the header
|
||||
virtual unsigned int getSize() const; // size of header only
|
||||
virtual MachOHeader::CpuType getCpuType() const;
|
||||
protected:
|
||||
virtual unsigned int getInternalFileType() const;
|
||||
private:
|
||||
mach_header_64 header;
|
||||
};
|
||||
|
||||
#endif // MACHO64HEADER_H
|
14
MachO/macho_global.h
Normal file
14
MachO/macho_global.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef MACHO_GLOBAL_H
|
||||
#define MACHO_GLOBAL_H
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std; // macht alles aus dem Namensraum std bekannt
|
||||
|
||||
#if defined(MACHO_LIBRARY)
|
||||
# define MACHOSHARED_EXPORT
|
||||
#else
|
||||
# define MACHOSHARED_EXPORT
|
||||
#endif
|
||||
|
||||
#endif // MACHO_GLOBAL_H
|
90
MachO/machoarchitecture.cpp
Normal file
90
MachO/machoarchitecture.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "machoarchitecture.h"
|
||||
#include "machofile.h"
|
||||
#include "machoheader.h"
|
||||
#include "loadcommand.h"
|
||||
#include "dylibcommand.h"
|
||||
#include "machoexception.h"
|
||||
#include "rpathcommand.h"
|
||||
#include "macho.h"
|
||||
#include "dynamicloader.h"
|
||||
|
||||
|
||||
MachOArchitecture::MachOArchitecture(MachOFile& file, uint32_t magic, unsigned int size) :
|
||||
header(MachOHeader::getHeader(file, magic)), file(header->getFile()), size(size), hasReadLoadCommands(false), parent(0), dynamicLibIdCommand(0)
|
||||
{
|
||||
}
|
||||
|
||||
void MachOArchitecture::initParentArchitecture(const MachOArchitecture* parent) {
|
||||
this->parent = parent;
|
||||
}
|
||||
|
||||
string MachOArchitecture::getResolvedName(const string& name, const string& workingPath) const {
|
||||
string absoluteFileName = MachO::dynamicLoader->getPathname(name, this, workingPath);
|
||||
if (!absoluteFileName.empty())
|
||||
return absoluteFileName;
|
||||
// return unresolved name if it cannot be resolved to a valid absolute name
|
||||
return name;
|
||||
}
|
||||
|
||||
std::vector<string*> MachOArchitecture::getRPaths() const {
|
||||
// try to get it from the parent (recursively)
|
||||
std::vector<string*> prevRPaths = parent?(std::vector<string*>(parent->getRPaths())):(std::vector<string*>());
|
||||
// add own rpaths to the end
|
||||
prevRPaths.insert(prevRPaths.end(), rPaths.begin(), rPaths.end());
|
||||
return prevRPaths;
|
||||
}
|
||||
|
||||
void MachOArchitecture::readLoadCommands() const {
|
||||
// read out number of commands
|
||||
unsigned int numberOfCommands = header->getNumberOfLoadCommands();
|
||||
// read out command identifiers
|
||||
for (unsigned int n=0; n<numberOfCommands; n++) {
|
||||
unsigned int commandOffset = file.getPosition();
|
||||
unsigned int cmd = file.readUint32();
|
||||
|
||||
file.seek(commandOffset);
|
||||
LoadCommand* loadCommand = LoadCommand::getLoadCommand(cmd, header);
|
||||
DylibCommand* dylibCommand = dynamic_cast<DylibCommand*>(loadCommand);
|
||||
if (dylibCommand != 0 && dylibCommand->isId()) {
|
||||
dynamicLibIdCommand = dylibCommand;
|
||||
}
|
||||
|
||||
RPathCommand* rPathCommand = dynamic_cast<RPathCommand*>(loadCommand);
|
||||
if (rPathCommand != 0) {
|
||||
// try to replace placeholder
|
||||
string resolvedRPath = MachO::dynamicLoader->replacePlaceholder(rPathCommand->getPath(), this);
|
||||
if (resolvedRPath.empty()) {
|
||||
resolvedRPath = rPathCommand->getPath();
|
||||
}
|
||||
rPaths.push_back(new string(resolvedRPath));
|
||||
}
|
||||
loadCommands.push_back(loadCommand);
|
||||
file.seek(commandOffset + loadCommand->getSize());
|
||||
}
|
||||
hasReadLoadCommands = true;
|
||||
}
|
||||
|
||||
MachOArchitecture::~MachOArchitecture() {
|
||||
delete header;
|
||||
|
||||
for (LoadCommandsIterator it = loadCommands.begin();
|
||||
it != loadCommands.end();
|
||||
++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
|
||||
for (std::vector<string*>::iterator it2 = rPaths.begin();
|
||||
it2 != rPaths.end();
|
||||
++it2)
|
||||
{
|
||||
delete *it2;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int MachOArchitecture::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
48
MachO/machoarchitecture.h
Normal file
48
MachO/machoarchitecture.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef MACHOARCHITECTURE_H
|
||||
#define MACHOARCHITECTURE_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
class MachOFile;
|
||||
class MachOHeader;
|
||||
class LoadCommand;
|
||||
class DylibCommand;
|
||||
|
||||
class MACHOSHARED_EXPORT MachOArchitecture
|
||||
{
|
||||
private:
|
||||
typedef std::list<LoadCommand*> LoadCommands;
|
||||
typedef LoadCommands::iterator LoadCommandsIterator;
|
||||
public:
|
||||
typedef LoadCommands::const_iterator LoadCommandsConstIterator;
|
||||
|
||||
MachOArchitecture(MachOFile& file, uint32_t magic, unsigned int size);
|
||||
~MachOArchitecture();
|
||||
|
||||
const MachOHeader* getHeader() { return header; }
|
||||
LoadCommandsConstIterator getLoadCommandsBegin() const { if(!hasReadLoadCommands) { readLoadCommands(); } return loadCommands.begin(); }
|
||||
LoadCommandsConstIterator getLoadCommandsEnd() const { if(!hasReadLoadCommands) { readLoadCommands(); } return loadCommands.end(); }
|
||||
DylibCommand* getDynamicLibIdCommand() const { if(!hasReadLoadCommands) { readLoadCommands(); } return dynamicLibIdCommand; }
|
||||
unsigned int getSize() const;
|
||||
void initParentArchitecture(const MachOArchitecture* parent);
|
||||
const MachOFile* getFile() const { return &file; }
|
||||
|
||||
std::vector<string*> getRPaths() const;
|
||||
string getResolvedName(const string& name, const string& workingPath) const;
|
||||
|
||||
private:
|
||||
MachOHeader* header;
|
||||
MachOFile& file;
|
||||
const unsigned int size;
|
||||
mutable bool hasReadLoadCommands;
|
||||
void readLoadCommands() const;
|
||||
const MachOArchitecture* parent; // architecture from which this architecture was loaded
|
||||
|
||||
mutable LoadCommands loadCommands;
|
||||
mutable DylibCommand* dynamicLibIdCommand;
|
||||
mutable std::vector<string*> rPaths;
|
||||
};
|
||||
|
||||
#endif // MACHOARCHITECTURE_H
|
40
MachO/machocache.cpp
Normal file
40
MachO/machocache.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* machocache.cpp
|
||||
* MachO
|
||||
*
|
||||
* Created by Konrad Windszus on 13.07.09.
|
||||
* Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "machocache.h"
|
||||
#include "macho.h"
|
||||
|
||||
MachOCache::MachOCache() {
|
||||
|
||||
}
|
||||
|
||||
MachOCache::~MachOCache() {
|
||||
// remove all cache entries
|
||||
for (CacheMapIterator it = cache.begin(); it != cache.end(); it++) {
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
|
||||
MachO* MachOCache::getFile(const string& filename, const MachO* parent) {
|
||||
CacheMapIterator it = cache.find(filename);
|
||||
|
||||
// check if already in cache?
|
||||
if (it == cache.end()) {
|
||||
MachO* file = new MachO(filename, parent);
|
||||
cache[filename] = file;
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int MachOCache::getNumEntries() {
|
||||
return cache.size();
|
||||
}
|
21
MachO/machocache.h
Normal file
21
MachO/machocache.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef MACHOCACHE_H
|
||||
#define MACHOCACHE_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <map>
|
||||
|
||||
class MachO;
|
||||
class MachOCache
|
||||
{
|
||||
public:
|
||||
MachOCache();
|
||||
~MachOCache();
|
||||
MachO* getFile(const string& filename, const MachO* parent);
|
||||
unsigned int getNumEntries();
|
||||
private:
|
||||
typedef map<string, MachO*> CacheMap;
|
||||
typedef CacheMap::iterator CacheMapIterator;
|
||||
CacheMap cache;
|
||||
};
|
||||
|
||||
#endif // MACHOCACHE_H
|
6
MachO/machodemangleexception.cpp
Normal file
6
MachO/machodemangleexception.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "machodemangleexception.h"
|
||||
|
||||
MachODemangleException::MachODemangleException(const string& cause) :
|
||||
MachOException(cause)
|
||||
{
|
||||
}
|
17
MachO/machodemangleexception.h
Normal file
17
MachO/machodemangleexception.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MACHODEMANGLEEXCEPTION_H
|
||||
#define MACHODEMANGLEEXCEPTION_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include "machoexception.h"
|
||||
|
||||
|
||||
class MACHOSHARED_EXPORT MachODemangleException : public MachOException
|
||||
{
|
||||
|
||||
public:
|
||||
MachODemangleException(const string&);
|
||||
};
|
||||
|
||||
#endif // MACHODEMANGLEEXCEPTION_H
|
||||
|
||||
|
6
MachO/machoexception.cpp
Normal file
6
MachO/machoexception.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "machoexception.h"
|
||||
MachOException::MachOException(const string& cause) :
|
||||
cause(cause)
|
||||
{
|
||||
}
|
||||
|
15
MachO/machoexception.h
Normal file
15
MachO/machoexception.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef MACHOEXCEPTION_H
|
||||
#define MACHOEXCEPTION_H
|
||||
|
||||
#include "macho_global.h"
|
||||
|
||||
class MACHOSHARED_EXPORT MachOException
|
||||
{
|
||||
public:
|
||||
MachOException(const string&);
|
||||
const string& getCause() { return cause; }
|
||||
private:
|
||||
const string cause;
|
||||
};
|
||||
|
||||
#endif // MACHOEXCEPTION_H
|
96
MachO/machofile.cpp
Normal file
96
MachO/machofile.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "machofile.h"
|
||||
#include "machoexception.h"
|
||||
#include "internalfile.h"
|
||||
|
||||
MachOFile::MachOFile(const string& filename,const MachOFile* parent, bool reversedByteOrder) :
|
||||
file(InternalFile::create(filename)), position(0), reversedByteOrder(reversedByteOrder), parent(parent)
|
||||
{
|
||||
if (parent) {
|
||||
executablePath = parent->executablePath;
|
||||
} else {
|
||||
executablePath = getPath();
|
||||
}
|
||||
}
|
||||
|
||||
MachOFile::MachOFile(const MachOFile& file, bool reversedByteOrder) :
|
||||
file(InternalFile::create(file.file)), position(file.position), reversedByteOrder(reversedByteOrder), parent(file.parent), executablePath(file.executablePath)
|
||||
{
|
||||
}
|
||||
|
||||
MachOFile::~MachOFile() {
|
||||
file->release();
|
||||
}
|
||||
|
||||
string MachOFile::getPath() const { return file->getPath(); }
|
||||
string MachOFile::getName() const { return file->getName(); }
|
||||
string MachOFile::getTitle() const { return file->getTitle(); }
|
||||
long long int MachOFile::getSize() const { return file->getSize(); }
|
||||
time_t MachOFile::getLastModificationTime() const { return file->getLastModificationTime(); }
|
||||
|
||||
uint32_t MachOFile::readUint32() {
|
||||
unsigned int temp;
|
||||
readBytes((char*)&temp, sizeof(temp));
|
||||
return getUint32(temp);
|
||||
}
|
||||
|
||||
uint32_t MachOFile::readUint32LE() {
|
||||
unsigned int temp;
|
||||
readBytes((char*)&temp, sizeof(temp));
|
||||
return getUint32LE(temp);
|
||||
}
|
||||
|
||||
uint32_t MachOFile::readUint32BE() {
|
||||
unsigned int temp;
|
||||
readBytes((char*)&temp, sizeof(temp));
|
||||
return getUint32BE(temp);
|
||||
}
|
||||
|
||||
uint32_t MachOFile::getUint32BE(uint32_t data) {
|
||||
return convertByteOrder((char*)&data, true, sizeof(data));
|
||||
}
|
||||
|
||||
uint32_t MachOFile::getUint32LE(uint32_t data) {
|
||||
return convertByteOrder((char*)&data, false, sizeof(data));
|
||||
}
|
||||
|
||||
void MachOFile::readBytes(char* result, size_t size) {
|
||||
if (file->getPosition() != position) {
|
||||
file->seek(position);
|
||||
}
|
||||
if (file->read(result, size) != size)
|
||||
throw MachOException("File '" + file->getName() + "' not big enough. Probably no valid Mach-O!");
|
||||
position += size;
|
||||
}
|
||||
|
||||
// convert from big endian or little endian to native format (Intel=little endian) and return as unsigned int (32bit)
|
||||
unsigned int MachOFile::convertByteOrder(char* data, bool isBigEndian, unsigned int numberOfBytes) {
|
||||
|
||||
assert(numberOfBytes> 0);
|
||||
assert(numberOfBytes <= 4); // max 4 byte
|
||||
|
||||
unsigned int result = 0;
|
||||
|
||||
// big endian extract (most significant byte first) (will work on little and big-endian computers)
|
||||
unsigned int numberOfShifts = isBigEndian ? numberOfBytes - 1 : 0;
|
||||
|
||||
for (unsigned int n = 0; n < numberOfBytes; n++) {
|
||||
result |= static_cast<unsigned char>(data[n]) << (8 * numberOfShifts); // the bit shift will do the correct byte order for you
|
||||
numberOfShifts += isBigEndian ? -1 : +1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t MachOFile::reverseByteOrder(uint32_t data) {
|
||||
char* sourceData = (char*)&data;
|
||||
uint32_t result;
|
||||
char* destData = (char*)&result;
|
||||
|
||||
destData[3] = sourceData[0];
|
||||
destData[2] = sourceData[1];
|
||||
destData[1] = sourceData[2];
|
||||
destData[0] = sourceData[3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
44
MachO/machofile.h
Normal file
44
MachO/machofile.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef MACHOFILE_H
|
||||
#define MACHOFILE_H
|
||||
|
||||
#include "macho_global.h"
|
||||
|
||||
class InternalFile;
|
||||
class MachOFile
|
||||
{
|
||||
public:
|
||||
MachOFile(const string& filename, const MachOFile* parent, bool reversedByteOrder = false);
|
||||
MachOFile(const MachOFile& file, bool reversedByteOrder);
|
||||
~MachOFile();
|
||||
|
||||
uint32_t readUint32();
|
||||
uint32_t readUint32LE();
|
||||
uint32_t readUint32BE();
|
||||
|
||||
void readBytes(char* result, size_t size);
|
||||
|
||||
uint32_t getUint32(unsigned int data) const {return (reversedByteOrder?reverseByteOrder(data):data);}
|
||||
static uint32_t getUint32LE(uint32_t data);
|
||||
static uint32_t getUint32BE(uint32_t data);
|
||||
string getPath() const;
|
||||
string getName() const;
|
||||
string getTitle() const;
|
||||
long long int getSize() const;
|
||||
void seek(long long int offset) { position = offset; }
|
||||
long long int getPosition() const { return position; }
|
||||
const string& getExecutablePath() const { return executablePath; }
|
||||
time_t getLastModificationTime() const;
|
||||
|
||||
private:
|
||||
static unsigned int convertByteOrder(char* data, bool isBigEndian, unsigned int numberOfBytes);
|
||||
static unsigned int reverseByteOrder(unsigned int data);
|
||||
|
||||
InternalFile* file;
|
||||
long long int position;
|
||||
const bool reversedByteOrder;
|
||||
const MachOFile* parent;
|
||||
string executablePath;
|
||||
|
||||
};
|
||||
|
||||
#endif // MACHOFILE_H
|
80
MachO/machoheader.cpp
Normal file
80
MachO/machoheader.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "machoheader.h"
|
||||
#include "machofile.h"
|
||||
#include "macho32header.h"
|
||||
#include "macho64header.h"
|
||||
#include "machoexception.h"
|
||||
#include <mach/mach.h>
|
||||
|
||||
MachOHeader* MachOHeader::getHeader(MachOFile& file, uint32_t magic) {
|
||||
MachOHeader* header;
|
||||
switch (magic) {
|
||||
case MH_MAGIC:
|
||||
header = new MachO32Header(file, false);
|
||||
break;
|
||||
case MH_CIGAM:
|
||||
header = new MachO32Header(file, true);
|
||||
break;
|
||||
case MH_MAGIC_64:
|
||||
header = new MachO64Header(file, false);
|
||||
break;
|
||||
case MH_CIGAM_64:
|
||||
header = new MachO64Header(file, true);
|
||||
break;
|
||||
default:
|
||||
throw MachOException("Invalid magic number. No Mach-O file.");
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
// for every header create a new filehandler with correct byte order
|
||||
MachOHeader::MachOHeader(const MachOFile& file, bool reversedBO) :
|
||||
file(file, reversedBO), offset(this->file.getPosition())
|
||||
{
|
||||
}
|
||||
|
||||
MachOHeader::~MachOHeader() {
|
||||
|
||||
}
|
||||
|
||||
// taken over from dyld.cpp (V 97.1)
|
||||
MachOHeader::CpuType MachOHeader::getHostCpuType() {
|
||||
struct host_basic_info info;
|
||||
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
|
||||
mach_port_t hostPort = mach_host_self();
|
||||
kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
|
||||
mach_port_deallocate(mach_task_self(), hostPort);
|
||||
if ( result != KERN_SUCCESS )
|
||||
throw "host_info() failed";
|
||||
//sHostCPUsubtype = info.cpu_subtype;
|
||||
return getCpuType(info.cpu_type);
|
||||
}
|
||||
|
||||
MachOHeader::CpuType MachOHeader::getCpuType(unsigned int cpu) {
|
||||
MachOHeader::CpuType cpuType;
|
||||
switch(cpu) {
|
||||
case CPU_TYPE_POWERPC:
|
||||
cpuType = MachOHeader::CpuTypePowerPc;
|
||||
break;
|
||||
case CPU_TYPE_I386:
|
||||
cpuType = MachOHeader::CpuTypeI386;
|
||||
break;
|
||||
case CPU_TYPE_POWERPC64:
|
||||
cpuType = MachOHeader::CpuTypePowerPc64;
|
||||
break;
|
||||
case CPU_TYPE_X86_64:
|
||||
cpuType = MachOHeader::CpuTypeX8664;
|
||||
break;
|
||||
default:
|
||||
cpuType = MachOHeader::CpuTypeOther;
|
||||
}
|
||||
return cpuType;
|
||||
}
|
||||
|
||||
MachOHeader::FileType MachOHeader::getFileType() const {
|
||||
unsigned int fileType = getInternalFileType();
|
||||
if (fileType > NumFileTypes || fileType < 1) {
|
||||
throw MachOException("Invalid file type");
|
||||
}
|
||||
return static_cast<FileType>(fileType-1);
|
||||
}
|
||||
|
55
MachO/machoheader.h
Normal file
55
MachO/machoheader.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef MACHOHEADER_H
|
||||
#define MACHOHEADER_H
|
||||
|
||||
#include "MachO_global.h"
|
||||
#include "machofile.h"
|
||||
#include "/usr/include/mach-o/loader.h"
|
||||
|
||||
class MACHOSHARED_EXPORT MachOHeader
|
||||
{
|
||||
public:
|
||||
static MachOHeader* getHeader(MachOFile& file, uint32_t magic);
|
||||
virtual ~MachOHeader();
|
||||
|
||||
enum CpuType {
|
||||
CpuTypePowerPc,
|
||||
CpuTypeI386,
|
||||
CpuTypePowerPc64,
|
||||
CpuTypeX8664,
|
||||
CpuTypeOther,
|
||||
NumCpuTypes
|
||||
};
|
||||
|
||||
enum FileType {
|
||||
|
||||
FileTypeObject, /* relocatable object file */
|
||||
FileTypeExecutable, /* demand paged executable file */
|
||||
FileTypeVmLib, /* fixed VM shared library file */
|
||||
FileTypeCore, /* core file */
|
||||
FileTypePreload, /* preloaded executable file */
|
||||
FileTypeDylib, /* dynamically bound shared library */
|
||||
FileTypeDylinker, /* dynamic link editor */
|
||||
FileTypeBundle, /* dynamically bound bundle file */
|
||||
FileTypeDylibStub, /* shared library stub for static linking only, no section contents */
|
||||
FileTypeDsym, /* companion file with only debug sections */
|
||||
NumFileTypes
|
||||
};
|
||||
FileType getFileType() const;
|
||||
virtual CpuType getCpuType() const = 0;
|
||||
static CpuType getHostCpuType();
|
||||
static CpuType getCpuType(unsigned int internalCpuType);
|
||||
virtual unsigned int getNumberOfLoadCommands() const = 0;
|
||||
virtual bool is64Bit() const = 0;
|
||||
MachOFile& getFile() { return file;}
|
||||
unsigned int getOffset() const { return offset; }
|
||||
virtual unsigned int getLoadCommandSize() const = 0; // size of load command following the header
|
||||
virtual unsigned int getSize() const = 0; // size of header only
|
||||
|
||||
protected:
|
||||
MachOHeader(const MachOFile& file, bool reversedBO);
|
||||
MachOFile file;
|
||||
const unsigned int offset;
|
||||
virtual unsigned int getInternalFileType() const = 0;
|
||||
};
|
||||
|
||||
#endif // MACHOHEADER_H
|
20
MachO/rpathcommand.cpp
Normal file
20
MachO/rpathcommand.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "rpathcommand.h"
|
||||
#include "machofile.h"
|
||||
#include "machoheader.h"
|
||||
|
||||
RPathCommand::RPathCommand(MachOHeader* header) :
|
||||
LoadCommand(header)
|
||||
{
|
||||
file.readBytes((char*)&command, sizeof(command));
|
||||
}
|
||||
|
||||
RPathCommand::~RPathCommand() {
|
||||
}
|
||||
|
||||
unsigned int RPathCommand::getSize() const {
|
||||
return file.getUint32(command.cmdsize);
|
||||
}
|
||||
|
||||
const char* RPathCommand::getPath() const {
|
||||
return getLcDataString(command.path.offset);
|
||||
}
|
20
MachO/rpathcommand.h
Normal file
20
MachO/rpathcommand.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef RPATHCOMMAND_H
|
||||
#define RPATHCOMMAND_H
|
||||
|
||||
#include "MachO_global.h"
|
||||
#include "loadcommand.h"
|
||||
|
||||
class MACHOSHARED_EXPORT RPathCommand : public LoadCommand
|
||||
{
|
||||
public:
|
||||
RPathCommand(MachOHeader* header);
|
||||
virtual ~RPathCommand();
|
||||
|
||||
virtual unsigned int getSize() const;
|
||||
virtual unsigned int getStructureSize() const { return sizeof(command); }
|
||||
const char* getPath() const;
|
||||
private:
|
||||
rpath_command command;
|
||||
};
|
||||
|
||||
#endif // RPATHCOMMAND_H
|
79
MachO/symboltablecommand.cpp
Normal file
79
MachO/symboltablecommand.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "symboltablecommand.h"
|
||||
#include "symboltableentry.h"
|
||||
#include "symboltableentry32.h"
|
||||
#include "symboltableentry64.h"
|
||||
#include "machofile.h"
|
||||
#include "machoheader.h"
|
||||
|
||||
SymbolTableCommand::SymbolTableCommand(MachOHeader* header) :
|
||||
LoadCommand(header), symbols32(0), symbols64(0), stringTable(0)
|
||||
{
|
||||
file.readBytes((char*)&command, sizeof(command));
|
||||
// delay loading symbol table
|
||||
}
|
||||
|
||||
void SymbolTableCommand::readSymbolTable() const {
|
||||
unsigned int stringTableLength = file.getUint32(command.strsize);
|
||||
stringTable = new char[stringTableLength];
|
||||
|
||||
// offset relative to beginning of architecture
|
||||
file.seek(header->getOffset() + file.getUint32(command.stroff));
|
||||
file.readBytes(stringTable, file.getUint32(command.strsize));
|
||||
|
||||
unsigned int numberOfSymbols = file.getUint32(command.nsyms);
|
||||
// offset relative to beginning of architecture
|
||||
if (header->is64Bit()) {
|
||||
symbols64 = new struct nlist_64[numberOfSymbols];
|
||||
file.seek(header->getOffset() + file.getUint32(command.symoff));
|
||||
file.readBytes((char*)symbols64, sizeof(*symbols64) * numberOfSymbols);
|
||||
for (unsigned int n = 0; n < numberOfSymbols; n++) {
|
||||
SymbolTableEntry* entry = new SymbolTableEntry64(file, &symbols64[n], stringTable);
|
||||
symbolTableEntries.push_back(entry);
|
||||
}
|
||||
} else {
|
||||
symbols32 = new struct nlist[numberOfSymbols];
|
||||
file.seek(header->getOffset() + file.getUint32(command.symoff));
|
||||
file.readBytes((char*)symbols32, sizeof(*symbols32) * numberOfSymbols);
|
||||
for (unsigned int n = 0; n < numberOfSymbols; n++) {
|
||||
SymbolTableEntry* entry = new SymbolTableEntry32(file, &symbols32[n], stringTable);
|
||||
symbolTableEntries.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SymbolTableCommand::~SymbolTableCommand()
|
||||
{
|
||||
for (SymbolTableEntriesIterator it = symbolTableEntries.begin();
|
||||
it != symbolTableEntries.end();
|
||||
++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
delete[] symbols32;
|
||||
delete[] symbols64;
|
||||
delete[] stringTable;
|
||||
}
|
||||
|
||||
unsigned int SymbolTableCommand::getSize() const {
|
||||
return file.getUint32(command.cmdsize);
|
||||
}
|
||||
|
||||
SymbolTableCommand::SymbolTableEntriesConstIterator SymbolTableCommand::getSymbolTableEntryBegin() const {
|
||||
if (stringTable == 0)
|
||||
readSymbolTable();
|
||||
return symbolTableEntries.begin();
|
||||
}
|
||||
|
||||
SymbolTableCommand::SymbolTableEntriesConstIterator SymbolTableCommand::getSymbolTableEntryEnd() const {
|
||||
if (stringTable == 0)
|
||||
readSymbolTable();
|
||||
return symbolTableEntries.end();
|
||||
}
|
||||
|
||||
/*
|
||||
const std::vector<const SymbolTableEntry*>* SymbolTableCommand::getSymbolTableEntries() const {
|
||||
if (stringTable == 0)
|
||||
readSymbolTable();
|
||||
return &symbolTableEntries;
|
||||
}*/
|
||||
|
34
MachO/symboltablecommand.h
Normal file
34
MachO/symboltablecommand.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef SYMBOLTABLECOMMAND_H
|
||||
#define SYMBOLTABLECOMMAND_H
|
||||
|
||||
#include "loadcommand.h"
|
||||
#include <mach-o/nlist.h>
|
||||
#include <list>
|
||||
|
||||
class SymbolTableEntry;
|
||||
class SymbolTableCommand : public LoadCommand
|
||||
{
|
||||
private:
|
||||
typedef list<const SymbolTableEntry*> SymbolTableEntries;
|
||||
typedef SymbolTableEntries::iterator SymbolTableEntriesIterator;
|
||||
public:
|
||||
typedef SymbolTableEntries::const_iterator SymbolTableEntriesConstIterator;
|
||||
|
||||
SymbolTableCommand(MachOHeader* header);
|
||||
virtual ~SymbolTableCommand();
|
||||
virtual unsigned int getSize() const;
|
||||
|
||||
SymbolTableEntriesConstIterator getSymbolTableEntryBegin() const;
|
||||
SymbolTableEntriesConstIterator getSymbolTableEntryEnd() const;
|
||||
//const std::vector<const SymbolTableEntry*>* getSymbolTableEntries() const;
|
||||
private:
|
||||
symtab_command command;
|
||||
mutable struct nlist* symbols32;
|
||||
mutable struct nlist_64* symbols64;
|
||||
mutable char* stringTable;
|
||||
mutable SymbolTableEntries symbolTableEntries;
|
||||
|
||||
void readSymbolTable() const;
|
||||
};
|
||||
|
||||
#endif // SYMBOLTABLECOMMAND_H
|
48
MachO/symboltableentry.cpp
Normal file
48
MachO/symboltableentry.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "symboltableentry.h"
|
||||
#include "macho.h"
|
||||
#include "machofile.h"
|
||||
#include "demangler.h"
|
||||
|
||||
SymbolTableEntry::SymbolTableEntry(MachOFile& file, char* stringTable) :
|
||||
file(file), stringTable(stringTable)
|
||||
{
|
||||
}
|
||||
|
||||
SymbolTableEntry::~SymbolTableEntry() {
|
||||
|
||||
}
|
||||
|
||||
string SymbolTableEntry::getName(bool shouldDemangle) const {
|
||||
const char* internalName = getInternalName();
|
||||
if (shouldDemangle) {
|
||||
return MachO::demangler->demangleName(internalName);
|
||||
}
|
||||
return internalName;
|
||||
}
|
||||
|
||||
SymbolTableEntry::Type SymbolTableEntry::getType() const {
|
||||
unsigned int type = getInternalType();
|
||||
|
||||
if (type & N_STAB) {
|
||||
return TypeDebug;
|
||||
}
|
||||
if (type & N_PEXT) {
|
||||
return TypePrivateExtern;
|
||||
}
|
||||
if (type & N_EXT) {
|
||||
if ((type & N_TYPE) == N_UNDF)
|
||||
return TypeImported;
|
||||
else
|
||||
return TypeExported;
|
||||
}
|
||||
else
|
||||
return TypeLocal;
|
||||
}
|
||||
|
||||
/*
|
||||
QDebug& operator<<(QDebug& dbg, const SymbolTableEntry& symbolTable) {
|
||||
unsigned int type = symbolTable.getInternalType();
|
||||
dbg.nospace() << "Name:" << symbolTable.getInternalName() << "Type" << (type & N_TYPE) << "STAB" << (type & N_STAB) << "PEXT" << (type & N_PEXT) << "NTYPE" << (type & N_TYPE) << "NEXT" << (type & N_EXT);
|
||||
return dbg.space();
|
||||
}
|
||||
*/
|
36
MachO/symboltableentry.h
Normal file
36
MachO/symboltableentry.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef SYMBOLTABLEENTRY_H
|
||||
#define SYMBOLTABLEENTRY_H
|
||||
|
||||
#include "macho_global.h"
|
||||
#include <mach-o/nlist.h>
|
||||
class MachOFile;
|
||||
|
||||
class SymbolTableEntry
|
||||
{
|
||||
public:
|
||||
SymbolTableEntry(MachOFile& file, char* stringTable);
|
||||
virtual ~SymbolTableEntry();
|
||||
string getName(bool shouldDemangle) const;
|
||||
|
||||
enum Type {
|
||||
TypeExported = 0,
|
||||
TypeImported,
|
||||
TypeLocal,
|
||||
TypeDebug,
|
||||
TypePrivateExtern,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
Type getType() const;
|
||||
virtual unsigned int getInternalType() const = 0;
|
||||
virtual const char* getInternalName() const = 0;
|
||||
|
||||
protected:
|
||||
MachOFile& file;
|
||||
char* stringTable;
|
||||
};
|
||||
|
||||
|
||||
//QDebug &operator<<(QDebug &dbg, const SymbolTableEntry &symbolTable);
|
||||
|
||||
#endif // SYMBOLTABLEENTRY_H
|
17
MachO/symboltableentry32.cpp
Normal file
17
MachO/symboltableentry32.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "symboltableentry32.h"
|
||||
#include "machofile.h"
|
||||
|
||||
SymbolTableEntry32::SymbolTableEntry32(MachOFile& file, struct nlist* entry, char* stringTable) :
|
||||
SymbolTableEntry(file, stringTable), entry(entry)
|
||||
{
|
||||
}
|
||||
|
||||
const char* SymbolTableEntry32::getInternalName() const {
|
||||
return &stringTable[file.getUint32(entry->n_un.n_strx)];
|
||||
}
|
||||
|
||||
unsigned int SymbolTableEntry32::getInternalType() const {
|
||||
return entry->n_type;
|
||||
}
|
||||
|
||||
|
17
MachO/symboltableentry32.h
Normal file
17
MachO/symboltableentry32.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef SYMBOLTABLEENTRY32_H
|
||||
#define SYMBOLTABLEENTRY32_H
|
||||
|
||||
#include "symboltableentry.h"
|
||||
|
||||
class SymbolTableEntry32 : public SymbolTableEntry
|
||||
{
|
||||
public:
|
||||
SymbolTableEntry32(MachOFile& file, struct nlist* entry, char* stringTable);
|
||||
virtual const char* getInternalName() const;
|
||||
virtual unsigned int getInternalType() const;
|
||||
private:
|
||||
struct nlist* entry;
|
||||
};
|
||||
|
||||
|
||||
#endif // SYMBOLTABLEENTRY32_H
|
16
MachO/symboltableentry64.cpp
Normal file
16
MachO/symboltableentry64.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "symboltableentry64.h"
|
||||
#include "machofile.h"
|
||||
|
||||
SymbolTableEntry64::SymbolTableEntry64(MachOFile& file, struct nlist_64* entry, char* stringTable) :
|
||||
SymbolTableEntry(file, stringTable), entry(entry)
|
||||
{
|
||||
}
|
||||
|
||||
const char* SymbolTableEntry64::getInternalName() const {
|
||||
return &stringTable[file.getUint32(entry->n_un.n_strx)];
|
||||
}
|
||||
|
||||
unsigned int SymbolTableEntry64::getInternalType() const {
|
||||
return entry->n_type;
|
||||
}
|
||||
|
16
MachO/symboltableentry64.h
Normal file
16
MachO/symboltableentry64.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef SYMBOLTABLEENTRY64_H
|
||||
#define SYMBOLTABLEENTRY64_H
|
||||
|
||||
#include "symboltableentry.h"
|
||||
|
||||
class SymbolTableEntry64 : public SymbolTableEntry
|
||||
{
|
||||
public:
|
||||
SymbolTableEntry64(MachOFile& file, struct nlist_64* entry, char* stringTable);
|
||||
virtual const char* getInternalName() const;
|
||||
virtual unsigned int getInternalType() const;
|
||||
private:
|
||||
struct nlist_64* entry;
|
||||
};
|
||||
|
||||
#endif // SYMBOLTABLEENTRY64_H
|
Loading…
x
Reference in New Issue
Block a user