mirror of
https://github.com/QuasarApp/macdependency.git
synced 2025-04-27 21:04:31 +00:00
- Applied all recommended XCode (8) settings. - Removed boost and replaced that code by standard functions. - Implemented name mangling via abi::__cxa_demangle instead of running an external process to use c++filt. - Enabled C++11. Min deployment target is now OSX 10.7. - Some code clean up (e.g. formatting, std namespace).
219 lines
6.9 KiB
C++
219 lines
6.9 KiB
C++
#include "macho.h"
|
|
#include "machoexception.h"
|
|
#include "machofile.h"
|
|
#include "machoarchitecture.h"
|
|
#include "dynamicloader.h"
|
|
#include "machoheader.h"
|
|
|
|
#include <mach-o/fat.h>
|
|
|
|
// see http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
|
// for MachO specification
|
|
|
|
// static variables
|
|
DynamicLoader* MachO::dynamicLoader = 0;
|
|
int MachO::referenceCounter = 0;
|
|
|
|
MachO::MachO(const std::string& filename, const MachO* parent) : parent(parent), bundle(NULL)
|
|
{
|
|
// check if filename is bundle
|
|
std::string appFilename = getApplicationInBundle(filename);
|
|
init(appFilename, parent);
|
|
|
|
if (referenceCounter == 0) {
|
|
dynamicLoader = new DynamicLoader();
|
|
}
|
|
referenceCounter++;
|
|
}
|
|
|
|
std::string MachO::getApplicationInBundle(const std::string& filename) {
|
|
CFURLRef bundleUrl = 0;
|
|
std::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 std::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 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;
|
|
}
|
|
|
|
unsigned long long MachO::getSize() const {
|
|
return file->getSize();
|
|
}
|
|
|
|
|
|
time_t MachO::getLastModificationTime() const {
|
|
return file->getLastModificationTime();
|
|
}
|
|
|
|
// return bundle version if available, otherwise NULL string
|
|
std::string MachO::getVersion() const {
|
|
std::string version;
|
|
if (bundle != 0) {
|
|
CFStringRef cfVersion = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
|
|
// is version available at all?
|
|
if (cfVersion) {
|
|
version = extractStringFromCFStringRef(cfVersion);
|
|
}
|
|
}
|
|
return version;
|
|
}
|
|
|
|
std::string MachO::getName() const {
|
|
std::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;
|
|
}
|
|
|
|
std::string MachO::extractStringFromCFStringRef(CFStringRef cfStringRef) {
|
|
std::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;
|
|
}
|
|
|
|
std::string MachO::getPath() const {
|
|
return file->getPath();
|
|
}
|
|
|
|
std::string MachO::getFileName() const {
|
|
std::string filename = file->getName();
|
|
return filename;
|
|
}
|
|
|
|
MachO::MachOArchitecturesIterator MachO::getArchitecturesBegin() { return architectures.begin(); }
|
|
|
|
MachO::MachOArchitecturesIterator MachO::getArchitecturesEnd() { return architectures.end(); }
|