macdependency/MachO/macho.cpp
Mike Lischke 972fcb3094 Overhaul of the application to avoid it crashing on macOS Sierra.
- 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).
2016-11-19 16:23:39 +01:00

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(); }