#include <vector>
#include <set>
-#include <ext/hash_map>
+#include <unordered_map>
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
struct Segment
{
public:
- Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size,
+ Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align,
uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot),
- fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fPermissions(prot),
- fNewAddress(0), fMappedAddress(NULL) {
+ fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align),
+ fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) {
strlcpy(fOrigName, segName, 16);
}
bool readable() const { return fPermissions & VM_PROT_READ; }
bool writable() const { return fPermissions & VM_PROT_WRITE; }
bool executable() const { return fPermissions & VM_PROT_EXECUTE; }
+ uint64_t alignment() const { return fAlignment; }
const char* name() const { return fOrigName; }
uint64_t newAddress() const { return fNewAddress; }
void* mappedAddress() const { return fMappedAddress; }
uint64_t fSize;
uint64_t fFileOffset;
uint64_t fFileSize;
+ uint64_t fAlignment;
uint32_t fPermissions;
uint64_t fNewAddress;
void* fMappedAddress;
virtual bool hasMainExecutableLookupLinkage() const = 0;
virtual bool isTwoLevelNamespace() const = 0;
virtual bool hasDyldInfo() const = 0;
+ virtual bool hasMultipleReadWriteSegments() const = 0;
virtual uint32_t getNameFileOffset() const = 0;
virtual time_t getLastModTime() const = 0;
virtual ino_t getInode() const = 0;
virtual std::vector<Segment>& getSegments() = 0;
virtual const std::vector<Segment>& getSegments() const = 0;
+ virtual const Segment* getSegment(const char* name) const = 0;
virtual const std::vector<Library>& getLibraries() const = 0;
virtual uint64_t getBaseAddress() const = 0;
virtual uint64_t getVMSize() const = 0;
// need getDyldInfoExports because export info uses ULEB encoding and size could grow
virtual const uint8_t* getDyldInfoExports() const = 0;
virtual void setDyldInfoExports(const uint8_t* newExports) const = 0;
+ virtual void uuid(uuid_t u) const = 0;
};
virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); }
virtual bool hasDyldInfo() const { return fHasDyldInfo; }
+ virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; }
virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; }
virtual time_t getLastModTime() const { return fMTime; }
virtual ino_t getInode() const { return fInode; }
virtual std::vector<Segment>& getSegments() { return fSegments; }
virtual const std::vector<Segment>& getSegments() const { return fSegments; }
+ virtual const Segment* getSegment(const char* name) const;
virtual const std::vector<Library>& getLibraries() const { return fLibraries; }
virtual uint64_t getBaseAddress() const { return fLowSegment->address(); }
virtual uint64_t getVMSize() const { return fVMSize; }
virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; }
virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; }
virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; }
+ virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); }
private:
typedef typename A::P P;
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
+ uint64_t segmentSize(const macho_segment_command<typename A::P>* segCmd) const;
+ uint64_t segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
+ uint64_t segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
+ bool validReadWriteSeg(const Segment& seg) const;
+
static cpu_type_t arch();
const char* fPath;
bool fMainExecutableLookupLinkage;
bool fIsDylib;
bool fHasDyldInfo;
+ bool fHasTooManyWritableSegments;
mutable const uint8_t* fDyldInfoExports;
+ uuid_t fUUID;
};
const std::vector<MachOLayoutAbstraction*>& allLayouts() const { return fLayouts; }
private:
+ class CStringHash {
+ public:
+ size_t operator()(const char* __s) const {
+ size_t __h = 0;
+ for ( ; *__s; ++__s)
+ __h = 5 * __h + *__s;
+ return __h;
+ };
+ };
struct CStringEquals {
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
};
- typedef __gnu_cxx::hash_map<const char*, const UniversalMachOLayout*, __gnu_cxx::hash<const char*>, CStringEquals> PathToNode;
+ typedef std::unordered_map<const char*, const UniversalMachOLayout*, CStringHash, CStringEquals> PathToNode;
static bool requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
if ( layout->getArchPair().arch == ap.arch ) {
switch ( ap.arch ) {
case CPU_TYPE_ARM:
- if ( layout->getArchPair().subtype == ap.subtype )
+ case CPU_TYPE_X86_64:
+ if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) )
return layout;
break;
- default:
+ default:
return layout;
}
}
}
+ // if requesting x86_64h and it did not exist, try x86_64 as a fallback
+ if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) {
+ ap.subtype = CPU_SUBTYPE_X86_64_ALL;
+ return this->getSlice(ap);
+ }
return NULL;
}
}
try {
switch ( OSSwapBigToHostInt32(slices[i].cputype) ) {
- case CPU_TYPE_POWERPC:
- fLayouts.push_back(new MachOLayout<ppc>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- break;
case CPU_TYPE_I386:
fLayouts.push_back(new MachOLayout<x86>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
break;
case CPU_TYPE_ARM:
fLayouts.push_back(new MachOLayout<arm>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
break;
- case CPU_TYPE_POWERPC64:
- // ignore ppc64 slices
+ case CPU_TYPE_ARM64:
+ fLayouts.push_back(new MachOLayout<arm64>(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
break;
default:
throw "unknown slice in fat file";
}
else {
try {
- if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
- if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(mh->cputype), OSSwapBigToHostInt32(mh->cpusubtype)) )
- fLayouts.push_back(new MachOLayout<ppc>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
- }
- else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
+ if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<arm>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
- else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
- // ignore ppc64 slices
+ else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) {
+ if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
+ fLayouts.push_back(new MachOLayout<arm64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
else {
throw "unknown file format";
}
+template <typename A>
+uint64_t MachOLayout<A>::segmentSize(const macho_segment_command<typename A::P>* segCmd) const
+{
+ // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
+ if ( segCmd->nsects() > 0 ) {
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const lastSection = §ionsStart[segCmd->nsects()-1];
+ uint64_t endSectAddr = lastSection->addr() + lastSection->size();
+ uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096);
+ if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) {
+ uint64_t size = endSectAddrPage - segCmd->vmaddr();
+ //if ( size != segCmd->vmsize() )
+ // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n",
+ // segCmd->segname(), size, segCmd->vmsize(), getFilePath());
+ return size;
+ }
+ }
+ return segCmd->vmsize();
+}
+
+template <typename A>
+uint64_t MachOLayout<A>::segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const
+{
+ int p2align = 12;
+ if ( segCmd->nsects() > 0 ) {
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()-1];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->align() > p2align )
+ p2align = sect->align();
+ }
+ }
+ return (1 << p2align);
+}
+
+template <typename A>
+uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const
+{
+ // <rdar://problem/13089366> segments may have 16KB alignment padding at end, if so we can remove that in cache
+ if ( segCmd->nsects() > 0 ) {
+ uint64_t endOffset = segCmd->fileoff();
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->offset() != 0 )
+ endOffset = sect->offset() + sect->size();
+ }
+ uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096);
+ //if ( size != segCmd->filesize() )
+ // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n",
+ // segCmd->segname(), size, segCmd->filesize(), getFilePath());
+ return size;
+ }
+ return segCmd->filesize();
+}
+
+
template <typename A>
MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
: fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0),
fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
- fHasDyldInfo(false), fDyldInfoExports(NULL)
+ fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
{
fDylibID.name = NULL;
fDylibID.currentVersion = 0;
fDylibID.compatibilityVersion = 0;
-
+ bzero(fUUID, sizeof(fUUID));
+
const macho_header<P>* mh = (const macho_header<P>*)machHeader;
if ( mh->cputype() != arch() )
throw "Layout object is wrong architecture";
break;
case macho_segment_command<P>::CMD:
{
- macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- fSegments.push_back(Segment(segCmd->vmaddr(), segCmd->vmsize(), segCmd->fileoff(),
- segCmd->filesize(), segCmd->initprot(), segCmd->segname()));
+ const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(),
+ segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname()));
}
break;
case LC_SYMTAB:
fHasDyldInfo = true;
dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
break;
+ case LC_UUID:
+ {
+ const macho_uuid_command<P>* uc = (macho_uuid_command<P>*)cmd;
+ memcpy(&fUUID, uc->uuid(), 16);
+ }
+ break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) )
fLowWritableSegment = &seg;
fVMWritablSize += seg.size();
+ if ( !validReadWriteSeg(seg) ) {
+ fHasTooManyWritableSegments = true;
+ }
}
else {
if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
}
if ( (highSegment != NULL) && (fLowSegment != NULL) )
fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
-
+
// scan undefines looking, for magic ordinals
if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) {
const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)machHeader + symbolTableCmd->symoff());
}
-template <> cpu_type_t MachOLayout<ppc>::arch() { return CPU_TYPE_POWERPC; }
template <> cpu_type_t MachOLayout<x86>::arch() { return CPU_TYPE_I386; }
template <> cpu_type_t MachOLayout<x86_64>::arch() { return CPU_TYPE_X86_64; }
template <> cpu_type_t MachOLayout<arm>::arch() { return CPU_TYPE_ARM; }
-
+template <> cpu_type_t MachOLayout<arm64>::arch() { return CPU_TYPE_ARM64; }
template <>
-bool MachOLayout<ppc>::isSplitSeg() const
+bool MachOLayout<x86>::validReadWriteSeg(const Segment& seg) const
{
- return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 );
+ return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0);
}
+template <typename A>
+bool MachOLayout<A>::validReadWriteSeg(const Segment& seg) const
+{
+ return (strcmp(seg.name(), "__DATA") == 0);
+}
+
+
template <>
bool MachOLayout<x86>::isSplitSeg() const
{
return false;
}
+template <typename A>
+const MachOLayoutAbstraction::Segment* MachOLayout<A>::getSegment(const char* name) const
+{
+ for(std::vector<Segment>::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) {
+ const Segment& seg = *it;
+ if ( strcmp(seg.name(), name) == 0 )
+ return &seg;
+ }
+ return NULL;
+}
+
+
#endif // __MACHO_LAYOUT__