#include <errno.h>
#include <vector>
+#include <tuple>
#include <set>
-#include <ext/hash_set>
+#include <unordered_set>
+#include "configure.h"
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
#include "MachOTrie.hpp"
+#include "../ld/code-sign-blobs/superblob.h"
static bool printRebase = false;
static bool printBind = false;
static bool printSharedRegion = false;
static bool printFunctionStarts = false;
static bool printDylibs = false;
+static bool printDRs = false;
+static bool printDataCode = false;
static cpu_type_t sPreferredArch = 0;
static cpu_type_t sPreferredSubArch = 0;
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
-
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
-
DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch);
void printRebaseInfo();
void printRebaseInfoOpcodes();
void printClassicLazyBindingInfo();
void printClassicBindingInfo();
void printSharedRegionInfo();
+ const char* sharedRegionKindName(uint8_t kind);
void printFunctionStartsInfo();
void printDylibsInfo();
+ void printDRInfo();
+ void printDataInCode();
void printFunctionStartLine(uint64_t addr);
- const uint8_t* printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind);
+ const uint8_t* printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind);
+ const uint8_t* printSharedRegionV2InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end);
+ const uint8_t* printSharedRegionV2InfoForEachULEB128AddressAndAdj(const uint8_t* p, const uint8_t* end);
+ const uint8_t* printSharedRegionV2SectionPair(const uint8_t* p, const uint8_t* end);
+ const uint8_t* printSharedRegionV2ToSectionOffset(const uint8_t* p, const uint8_t* end);
+ const uint8_t* printSharedRegionV2Kind(const uint8_t* p, const uint8_t* end);
+
pint_t relocBase();
const char* relocTypeName(uint8_t r_type);
uint8_t segmentIndexForAddress(pint_t addr);
const char* classicOrdinalName(int libraryOrdinal);
pint_t* mappedAddressForVMAddress(pint_t vmaddress);
const char* symbolNameForAddress(uint64_t);
+ const char* closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex=0);
const char* fPath;
const macho_dyld_info_command<P>* fInfo;
const macho_linkedit_data_command<P>* fSharedRegionInfo;
const macho_linkedit_data_command<P>* fFunctionStartsInfo;
+ const macho_linkedit_data_command<P>* fDataInCode;
+ const macho_linkedit_data_command<P>* fDRInfo;
uint64_t fBaseAddress;
const macho_dysymtab_command<P>* fDynamicSymbolTable;
const macho_segment_command<P>* fFirstSegment;
const macho_segment_command<P>* fFirstWritableSegment;
bool fWriteableSegmentWithAddrOver4G;
std::vector<const macho_segment_command<P>*>fSegments;
+ std::vector<const macho_section<P>*> fSections;
std::vector<const char*> fDylibs;
std::vector<const macho_dylib_command<P>*> fDylibLoadCommands;
+ macho_section<P> fMachHeaderPseudoSection;
};
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
return true;
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_KEXT_BUNDLE:
return true;
}
return false;
}
+#if SUPPORT_ARCH_arm_any
template <>
bool DyldInfoPrinter<arm>::validFile(const uint8_t* fileContent)
{
switch (header->filetype()) {
case MH_EXECUTE:
case MH_DYLIB:
+ case MH_DYLIB_STUB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_KEXT_BUNDLE:
return true;
}
return false;
}
+#endif
+
+#if SUPPORT_ARCH_arm64
+template <>
+bool DyldInfoPrinter<arm64>::validFile(const uint8_t* fileContent)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ if ( header->magic() != MH_MAGIC_64 )
+ return false;
+ if ( header->cputype() != CPU_TYPE_ARM64 )
+ return false;
+ switch (header->filetype()) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_DYLIB_STUB:
+ case MH_BUNDLE:
+ case MH_DYLINKER:
+ case MH_KEXT_BUNDLE:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+#endif
+
template <typename A>
DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch)
: fHeader(NULL), fLength(fileLength),
fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL),
- fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL),
+ fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), fDataInCode(NULL), fDRInfo(NULL),
fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL),
fWriteableSegmentWithAddrOver4G(false)
{
fPath = strdup(path);
fHeader = (const macho_header<P>*)fileContent;
-
+
+ fMachHeaderPseudoSection.set_segname("__TEXT");
+ fMachHeaderPseudoSection.set_sectname("");
+ fMachHeaderPseudoSection.set_addr(0);
+ fSections.push_back(&fMachHeaderPseudoSection);
+
// get LC_DYLD_INFO
const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
if ( segCmd->vmaddr() > 0x100000000ULL )
fWriteableSegmentWithAddrOver4G = true;
}
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)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)
+ fSections.push_back(sect);
}
break;
case LC_LOAD_DYLIB:
case LC_FUNCTION_STARTS:
fFunctionStartsInfo = (macho_linkedit_data_command<P>*)cmd;
break;
+ case LC_DATA_IN_CODE:
+ fDataInCode = (macho_linkedit_data_command<P>*)cmd;
+ break;
+ case LC_DYLIB_CODE_SIGN_DRS:
+ fDRInfo = (macho_linkedit_data_command<P>*)cmd;
+ break;
}
cmd = (const macho_load_command<P>*)endOfCmd;
}
if ( printArch ) {
- switch ( fHeader->cputype() ) {
- case CPU_TYPE_I386:
- printf("for arch i386:\n");
- break;
- case CPU_TYPE_X86_64:
- printf("for arch x86_64:\n");
- break;
- case CPU_TYPE_POWERPC:
- printf("for arch ppc:\n");
- break;
- case CPU_TYPE_ARM:
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( (cpu_subtype_t)fHeader->cpusubtype() == t->subType) {
- printf("for arch %s:\n", t->subTypeName);
- break;
- }
- }
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( (cpu_type_t)fHeader->cputype() == t->cpuType ) {
+ if ( t->isSubType && ((cpu_subtype_t)fHeader->cpusubtype() != t->cpuSubType) )
+ continue;
+ printf("for arch %s:\n", t->archName);
+ }
}
}
-
if ( printRebase ) {
if ( fInfo != NULL )
printRebaseInfo();
printFunctionStartsInfo();
if ( printDylibs )
printDylibsInfo();
+ if ( printDRs )
+ printDRInfo();
+ if ( printDataCode )
+ printDataInCode();
}
static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end)
if (p == end)
throwf("malformed sleb128");
byte = *p++;
- result |= ((byte & 0x7f) << bit);
+ result |= (((int64_t)(byte & 0x7f)) << bit);
bit += 7;
} while (byte & 0x80);
// sign extend negative numbers
return "main-executable";
case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
return "flat-namespace";
+ case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
+ return "weak";
}
if ( libraryOrdinal < BIND_SPECIAL_DYLIB_FLAT_LOOKUP )
throw "unknown special ordinal";
template <typename A>
void DyldInfoPrinter<A>::printRebaseInfo()
{
+ bool seenThreadedRebase = false;
if ( (fInfo == NULL) || (fInfo->rebase_off() == 0) ) {
- printf("no compressed rebase info\n");
+ // If we have no rebase opcodes, then we may be using the threaded rebase/bind combined
+ // format and need to parse the bind opcodes instead.
+ if ( (fInfo->rebase_size() == 0) && (fInfo->bind_size() != 0) ) {
+ const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off();
+ const uint8_t* end = &p[fInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint8_t flags = 0;
+ uint8_t segIndex = 0;
+ uint64_t segOffset = 0;
+ const char* symbolName = NULL;
+ const char* fromDylib = "??";
+ int libraryOrdinal = 0;
+ int64_t addend = 0;
+ uint32_t count;
+ uint32_t skip;
+ pint_t segStartAddr = 0;
+ const char* segName = "??";
+ const char* typeName = "??";
+ const char* weak_import = "";
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ fromDylib = ordinalName(libraryOrdinal);
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ flags = immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT;
+ if ( flags != 0 )
+ weak_import = " (weak import)";
+ else
+ weak_import = "";
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ typeName = bindTypeName(type);
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segName = segmentName(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ printf("rebase information (from compressed dyld info):\n");
+ printf("segment section address type value\n");
+ count = read_uleb128(p, end);
+ seenThreadedRebase = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ const uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+
+#if SUPPORT_ARCH_arm64e
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ if (isAuthenticated) {
+ uint64_t targetValue = value & 0xFFFFFFFFULL;
+ targetValue += fBaseAddress;
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX (JOP: diversity %d, address %s, %s)\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, targetValue, diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, targetValue);
+ }
+ }
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0);
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
+
+ if (!seenThreadedRebase)
+ printf("no compressed rebase info\n");
}
else {
printf("rebase information (from compressed dyld info):\n");
- printf("segment section address type\n");
+ printf("segment section address type value\n");
const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off();
const uint8_t* end = &p[fInfo->rebase_size()];
uint64_t segOffset = 0;
uint32_t count;
uint32_t skip;
- int segIndex;
+ int segIndex = 0;
pint_t segStartAddr = 0;
const char* segName = "??";
const char* typeName = "??";
+ const uint8_t* pointerLocation = nullptr;
+ uint64_t value = 0;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ value = P::getP(*(uint64_t*)pointerLocation);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, value);
segOffset += skip + sizeof(pint_t);
}
break;
default:
throwf("bad rebase opcode %d", *p);
}
- }
+ }
}
-
}
}
else {
printf("rebase opcodes:\n");
- const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off();
- const uint8_t* end = &p[fInfo->rebase_size()];
-
+ const uint8_t* start = (uint8_t*)fHeader + fInfo->rebase_off();
+ const uint8_t* end = &start[fInfo->rebase_size()];
+ const uint8_t* p = start;
+
uint8_t type = 0;
uint64_t address = fBaseAddress;
uint32_t count;
while ( !done && (p < end) ) {
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ uint32_t opcodeOffset = p-start;
++p;
switch (opcode) {
case REBASE_OPCODE_DONE:
done = true;
- printf("REBASE_OPCODE_DONE()\n");
+ printf("0x%04X REBASE_OPCODE_DONE()\n", opcodeOffset);
break;
case REBASE_OPCODE_SET_TYPE_IMM:
type = immediate;
- printf("REBASE_OPCODE_SET_TYPE_IMM(%d)\n", type);
+ printf("0x%04X REBASE_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type);
break;
case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segmentIndex = immediate;
address = read_uleb128(p, end);
- printf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", segmentIndex, address);
+ printf("0x%04X REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", opcodeOffset, segmentIndex, address);
break;
case REBASE_OPCODE_ADD_ADDR_ULEB:
address = read_uleb128(p, end);
- printf("REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", address);
+ printf("0x%04X REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", opcodeOffset, address);
break;
case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
address = immediate*sizeof(pint_t);
- printf("REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", address);
+ printf("0x%04X REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", opcodeOffset, address);
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
- printf("REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", immediate);
+ printf("0x%04X REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", opcodeOffset, immediate);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
- printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", count);
+ printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", opcodeOffset, count);
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
skip = read_uleb128(p, end) + sizeof(pint_t);
- printf("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", skip);
+ printf("0x%04X REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", opcodeOffset, skip);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
- printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", count, skip);
+ printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", opcodeOffset, count, skip);
break;
default:
throwf("bad rebase opcode %d", *p);
}
-
-
-
-
-
template <typename A>
void DyldInfoPrinter<A>::printBindingInfoOpcodes(bool weakbinding)
{
skip = read_uleb128(p, end);
printf("0x%04X BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%d, 0x%08X)\n", opcodeOffset, count, skip);
break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ printf("0x%04X BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB(%d)\n", opcodeOffset, count);
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY:
+ printf("0x%04X BIND_SUBOPCODE_THREADED_APPLY\n", opcodeOffset);
+ break;
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
default:
throwf("unknown bind opcode %d", *p);
}
}
+struct ThreadedBindData {
+ ThreadedBindData(const char* symbolName, int64_t addend, int libraryOrdinal, uint8_t flags, uint8_t type)
+ : symbolName(symbolName), addend(addend), libraryOrdinal(libraryOrdinal), flags(flags), type(type) { }
+ std::tuple<const char*, int64_t, int, uint8_t, uint8_t> pack() const {
+ return std::make_tuple(symbolName, addend, libraryOrdinal, flags, type);
+ }
+
+ const char* symbolName = nullptr;
+ int64_t addend = 0;
+ int libraryOrdinal = 0;
+ uint8_t flags = 0;
+ uint8_t type = 0;
+};
template <typename A>
void DyldInfoPrinter<A>::printBindingInfo()
printf("segment section address type addend dylib symbol\n");
const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off();
const uint8_t* end = &p[fInfo->bind_size()];
-
+
uint8_t type = 0;
+ uint8_t flags = 0;
uint8_t segIndex = 0;
uint64_t segOffset = 0;
const char* symbolName = NULL;
const char* segName = "??";
const char* typeName = "??";
const char* weak_import = "";
+ std::vector<ThreadedBindData> ordinalTable;
+ bool useThreadedRebaseBind = false;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
while (*p != '\0')
++p;
++p;
- if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 )
+ flags = immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT;
+ if ( flags != 0 )
weak_import = " (weak import)";
else
weak_import = "";
segOffset += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
- segOffset += sizeof(pint_t);
+ if (!useThreadedRebaseBind) {
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
+ segOffset += sizeof(pint_t);
+ } else {
+ ordinalTable.push_back(ThreadedBindData(symbolName, addend, libraryOrdinal, flags, type));
+ }
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import );
segOffset += skip + sizeof(pint_t);
}
break;
+ case BIND_OPCODE_THREADED:
+ // Note the immediate is a sub opcode
+ switch (immediate) {
+ case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+ count = read_uleb128(p, end);
+ ordinalTable.clear();
+ ordinalTable.reserve(count);
+ useThreadedRebaseBind = true;
+ break;
+ case BIND_SUBOPCODE_THREADED_APPLY: {
+ uint64_t delta = 0;
+ do {
+ const uint8_t* pointerLocation = (uint8_t*)fHeader + fSegments[segIndex]->fileoff() + segOffset;
+ uint64_t value = P::getP(*(uint64_t*)pointerLocation);
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+
+ if (isRebase) {
+ //printf("(rebase): %-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ } else {
+ // the ordinal is bits [0..15]
+ uint16_t ordinal = value & 0xFFFF;
+ if (ordinal >= ordinalTable.size()) {
+ fprintf(stderr, "bind ordinal is out of range\n");
+ break;
+ }
+ std::tie(symbolName, addend, libraryOrdinal, flags, type) = ordinalTable[ordinal].pack();
+ if ( (flags & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 )
+ weak_import = " (weak import)";
+ else
+ weak_import = "";
+ fromDylib = ordinalName(libraryOrdinal);
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s with value 0x%016llX (JOP: diversity %d, address %s, %s)\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import, value, diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s with value 0x%016llX\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import, value );
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ segOffset += delta * sizeof(pint_t);
+ } while ( delta != 0);
+ break;
+ }
+ default:
+ throwf("unknown threaded bind subopcode %d", immediate);
+ }
+ break;
default:
throwf("bad bind opcode %d", *p);
}
parseTrie(start, end, list);
//std::sort(list.begin(), list.end(), SortExportsByAddress());
for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) {
- if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
- if ( it->importName[0] == '\0' )
- fprintf(stdout, "[re-export] %s from dylib=%llu\n", it->name, it->other);
- else
- fprintf(stdout, "[re-export] %s from dylib=%llu named=%s\n", it->name, it->other, it->importName);
- }
- else {
- const char* flags = "";
- if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
- flags = "[weak_def] ";
- else if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL )
- flags = "[per-thread] ";
- if ( it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
- flags = "[resolver] ";
- fprintf(stdout, "0x%08llX %s%s (resolver=0x%08llX)\n", fBaseAddress+it->address, flags, it->name, it->other);
+ const bool reExport = (it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
+ const bool weakDef = (it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
+ const bool threadLocal = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
+ const bool abs = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
+ const bool resolver = (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
+ if ( reExport )
+ printf("[re-export] ");
+ else
+ printf("0x%08llX ", fBaseAddress+it->address);
+ printf("%s", it->name);
+ if ( weakDef || threadLocal || resolver || abs ) {
+ bool needComma = false;
+ printf(" [");
+ if ( weakDef ) {
+ printf("weak_def");
+ needComma = true;
}
- else {
- fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name);
+ if ( threadLocal ) {
+ if ( needComma )
+ printf(", ");
+ printf("per-thread");
+ needComma = true;
+ }
+ if ( abs ) {
+ if ( needComma )
+ printf(", ");
+ printf("absolute");
+ needComma = true;
}
+ if ( resolver ) {
+ if ( needComma )
+ printf(", ");
+ printf("resolver=0x%08llX", it->other);
+ needComma = true;
+ }
+ printf("]");
+ }
+ if ( reExport ) {
+ if ( it->importName[0] == '\0' )
+ printf(" (from %s)", fDylibs[it->other - 1]);
+ else
+ printf(" (%s from %s)", it->importName, fDylibs[it->other - 1]);
}
+ printf("\n");
}
}
}
char* cummulativeString, int curStrOffset)
{
const uint8_t* const me = p;
- const uint8_t terminalSize = read_uleb128(p, end);
+ const uint64_t terminalSize = read_uleb128(p, end);
const uint8_t* children = p + terminalSize;
if ( terminalSize != 0 ) {
uint32_t flags = read_uleb128(p, end);
printf("[addr=0x%06llX] ", address);
else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL)
printf("[flags=THREAD_LOCAL addr=0x%06llX] ", address);
+ else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE)
+ printf("[flags=ABSOLUTE addr=0x%06llX] ", address);
else
printf("[flags=0x%llX addr=0x%06llX] ", flags, address);
}
template <typename A>
-const uint8_t* DyldInfoPrinter<A>::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind)
+const uint8_t* DyldInfoPrinter<A>::printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind)
{
const char* kindStr = "??";
- switch (kind) {
- case 1:
+ switch (kind ) {
+ case DYLD_CACHE_ADJ_V1_POINTER_32:
kindStr = "32-bit pointer";
break;
- case 2:
+ case DYLD_CACHE_ADJ_V1_POINTER_64:
kindStr = "64-bit pointer";
break;
- case 3:
- kindStr = "ppc hi16";
+ case DYLD_CACHE_ADJ_V1_ADRP:
+ kindStr = "arm64 ADRP";
break;
- case 4:
- kindStr = "32-bit offset to IMPORT";
- break;
- case 5:
- kindStr = "thumb2 movw";
- break;
- case 6:
- kindStr = "ARM movw";
- break;
- case 0x10:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+0:
kindStr = "thumb2 movt low high 4 bits=0";
break;
- case 0x11:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+1:
kindStr = "thumb2 movt low high 4 bits=1";
break;
- case 0x12:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+2:
kindStr = "thumb2 movt low high 4 bits=2";
break;
- case 0x13:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+3:
kindStr = "thumb2 movt low high 4 bits=3";
break;
- case 0x14:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+4:
kindStr = "thumb2 movt low high 4 bits=4";
break;
- case 0x15:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+5:
kindStr = "thumb2 movt low high 4 bits=5";
break;
- case 0x16:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+6:
kindStr = "thumb2 movt low high 4 bits=6";
break;
- case 0x17:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+7:
kindStr = "thumb2 movt low high 4 bits=7";
break;
- case 0x18:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+8:
kindStr = "thumb2 movt low high 4 bits=8";
break;
- case 0x19:
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+9:
kindStr = "thumb2 movt low high 4 bits=9";
break;
- case 0x1A:
- kindStr = "thumb2 movt low high 4 bits=0xA";
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+10:
+ kindStr = "thumb2 movt low high 4 bits=10";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+11:
+ kindStr = "thumb2 movt low high 4 bits=11";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+12:
+ kindStr = "thumb2 movt low high 4 bits=12";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+13:
+ kindStr = "thumb2 movt low high 4 bits=13";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+14:
+ kindStr = "thumb2 movt low high 4 bits=14";
break;
- case 0x1B:
- kindStr = "thumb2 movt low high 4 bits=0xB";
+ case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+15:
+ kindStr = "thumb2 movt low high 4 bits=15";
break;
- case 0x1C:
- kindStr = "thumb2 movt low high 4 bits=0xC";
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+0:
+ kindStr = "arm movt low high 4 bits=0";
break;
- case 0x1D:
- kindStr = "thumb2 movt low high 4 bits=0xD";
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+1:
+ kindStr = "arm movt low high 4 bits=1";
break;
- case 0x1E:
- kindStr = "thumb2 movt low high 4 bits=0xE";
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+2:
+ kindStr = "arm movt low high 4 bits=2";
break;
- case 0x1F:
- kindStr = "thumb2 movt low high 4 bits=0xF";
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+3:
+ kindStr = "arm movt low high 4 bits=3";
break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+4:
+ kindStr = "arm movt low high 4 bits=4";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+5:
+ kindStr = "arm movt low high 4 bits=5";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+6:
+ kindStr = "arm movt low high 4 bits=6";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+7:
+ kindStr = "arm movt low high 4 bits=7";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+8:
+ kindStr = "arm movt low high 4 bits=8";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+9:
+ kindStr = "arm movt low high 4 bits=9";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+10:
+ kindStr = "arm movt low high 4 bits=10";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+11:
+ kindStr = "arm movt low high 4 bits=11";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+12:
+ kindStr = "arm movt low high 4 bits=12";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+13:
+ kindStr = "arm movt low high 4 bits=13";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+14:
+ kindStr = "arm movt low high 4 bits=14";
+ break;
+ case DYLD_CACHE_ADJ_V1_ARM_MOVT+15:
+ kindStr = "arm movt low high 4 bits=15";
+ break;
+ default:
+ kindStr = "<<unknown>>";
}
uint64_t address = 0;
uint64_t delta = 0;
- uint32_t shift = 0;
- bool more = true;
do {
- uint8_t byte = *p++;
- delta |= ((byte & 0x7F) << shift);
- shift += 7;
- if ( byte < 0x80 ) {
- if ( delta != 0 ) {
- address += delta;
- printf("0x%0llX %s\n", address+fBaseAddress, kindStr);
- delta = 0;
- shift = 0;
- }
- else {
- more = false;
- }
- }
- } while (more);
+ delta = read_uleb128(p, end);
+ address += delta;
+ printf("0x%0llX %s\n", address+fBaseAddress, kindStr);
+ } while (delta);
+
return p;
}
else {
const uint8_t* infoStart = (uint8_t*)fHeader + fSharedRegionInfo->dataoff();
const uint8_t* infoEnd = &infoStart[fSharedRegionInfo->datasize()];
- for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
- uint8_t kind = *p++;
- p = this->printSharedRegionInfoForEachULEB128Address(p, kind);
+ if ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ) {
+ ++infoStart;
+ // Whole :== <count> FromToSection+
+ // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+ // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+ const uint8_t* p = infoStart;
+ uint64_t sectionCount = read_uleb128(p, infoEnd);
+ for (uint64_t i=0; i < sectionCount; ++i) {
+ uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toSectionIndex = read_uleb128(p, infoEnd);
+ uint64_t toOffsetCount = read_uleb128(p, infoEnd);
+ const macho_section<P>* fromSection = fSections[fromSectionIndex];
+ const macho_section<P>* toSection = fSections[toSectionIndex];
+ char fromSectionName[20];
+ strncpy(fromSectionName, fromSection->sectname(), 16);
+ fromSectionName[16] = '\0';
+ printf("from sect=%s/%s, to sect=%s/%s, count=%lld:\n", fromSection->segname(), fromSectionName, toSection->segname(), toSection->sectname(), toOffsetCount);
+ uint64_t toSectionOffset = 0;
+ const char* lastFromSymbol = NULL;
+ for (uint64_t j=0; j < toOffsetCount; ++j) {
+ uint64_t toSectionDelta = read_uleb128(p, infoEnd);
+ uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
+ toSectionOffset += toSectionDelta;
+ for (uint64_t k=0; k < fromOffsetCount; ++k) {
+ uint64_t kind = read_uleb128(p, infoEnd);
+ uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
+ uint64_t fromSectionOffset = 0;
+ for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
+ uint64_t delta = read_uleb128(p, infoEnd);
+ fromSectionOffset += delta;
+ uint64_t symbolOffset;
+ const char* s = closestSymbolNameForAddress(fromSection->addr()+fromSectionOffset, &symbolOffset, fromSectionIndex);
+ if ( (s != lastFromSymbol) && (s != NULL) )
+ printf(" %s:\n", s);
+ const char* toSymbol = closestSymbolNameForAddress(toSection->addr()+toSectionOffset, &symbolOffset, toSectionIndex);
+ printf(" from addr=0x%0llX %s to addr=0x%0llX", fromSection->addr()+fromSectionOffset, sharedRegionKindName(kind), toSection->addr()+toSectionOffset);
+ if ( toSymbol != NULL ) {
+ if ( symbolOffset == 0 )
+ printf(" (%s)", toSymbol);
+ else
+ printf(" (%s + %lld)", toSymbol, symbolOffset);
+ }
+ printf("\n");
+ lastFromSymbol = s;
+ }
+ }
+ }
+ }
}
+ else {
+ for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
+ uint8_t kind = *p++;
+ p = this->printSharedRegionV1InfoForEachULEB128Address(p, infoEnd, kind);
+ }
+ }
+ }
+}
+template <typename A>
+const char* DyldInfoPrinter<A>::sharedRegionKindName(uint8_t kind)
+{
+ switch (kind) {
+ default:
+ return "<<unknown>>";
+ case DYLD_CACHE_ADJ_V2_POINTER_32:
+ return "pointer32";
+ case DYLD_CACHE_ADJ_V2_POINTER_64:
+ return "pointer64";
+ case DYLD_CACHE_ADJ_V2_DELTA_32:
+ return "delta32";
+ case DYLD_CACHE_ADJ_V2_DELTA_64:
+ return "delta64";
+ case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
+ return "adrp";
+ case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
+ return "off12";
+ case DYLD_CACHE_ADJ_V2_ARM64_BR26:
+ return "br26";
+ case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT:
+ return "movw/movt";
+ case DYLD_CACHE_ADJ_V2_ARM_BR24:
+ return "br24";
+ case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT:
+ return "movw/movt";
+ case DYLD_CACHE_ADJ_V2_THUMB_BR22:
+ return "br22";
+ case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
+ return "off32";
+ case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
+ return "theaded-pointer64";
}
}
+
+#if SUPPORT_ARCH_arm_any
template <>
void DyldInfoPrinter<arm>::printFunctionStartLine(uint64_t addr)
{
else
printf("0x%0llX %s\n", addr, symbolNameForAddress(addr));
}
+#endif
template <typename A>
void DyldInfoPrinter<A>::printFunctionStartLine(uint64_t addr)
}
}
+template <typename A>
+void DyldInfoPrinter<A>::printDRInfo()
+{
+ if ( fDRInfo == NULL ) {
+ printf("no Designated Requirements info\n");
+ }
+ else {
+ printf("dylibs DRs\n");
+ const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff());
+ //const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize());
+ typedef Security::SuperBlob<Security::kSecCodeMagicDRList> DRListSuperBlob;
+ const DRListSuperBlob* topBlob = (DRListSuperBlob*)start;
+ if ( topBlob->validateBlob(fDRInfo->datasize()) ) {
+ if ( topBlob->count() == fDylibLoadCommands.size() ) {
+ for(unsigned i=0; i < topBlob->count(); ++i) {
+ printf(" %-20s ", fDylibs[i]);
+ const Security::BlobCore* item = topBlob->find(i);
+ if ( item != NULL ) {
+ const uint8_t* itemStart = (uint8_t*)item;
+ const uint8_t* itemEnd = itemStart + item->length();
+ for(const uint8_t* p=itemStart; p < itemEnd; ++p)
+ printf("%02X ", *p);
+ }
+ else {
+ printf("no DR info");
+ }
+ printf("\n");
+ }
+ }
+ else {
+ fprintf(stderr, "superblob of DRs has a different number of elements than dylib load commands\n");
+ }
+ }
+ else {
+ fprintf(stderr, "superblob of DRs invalid\n");
+ }
+ }
+}
+
+
+
+
+
+template <typename A>
+void DyldInfoPrinter<A>::printDataInCode()
+{
+ if ( fDataInCode == NULL ) {
+ printf("no data-in-code info\n");
+ }
+ else {
+ printf("offset length data-kind\n");
+ const macho_data_in_code_entry<P>* start = (macho_data_in_code_entry<P>*)((uint8_t*)fHeader + fDataInCode->dataoff());
+ const macho_data_in_code_entry<P>* end = (macho_data_in_code_entry<P>*)((uint8_t*)fHeader + fDataInCode->dataoff() + fDataInCode->datasize());
+ for (const macho_data_in_code_entry<P>* p=start; p < end; ++p) {
+ const char* kindStr = "???";
+ switch ( p->kind() ) {
+ case 1:
+ kindStr = "data";
+ break;
+ case 2:
+ kindStr = "jumptable8";
+ break;
+ case 3:
+ kindStr = "jumptable16";
+ break;
+ case 4:
+ kindStr = "jumptable32";
+ break;
+ case 5:
+ kindStr = "jumptable32absolute";
+ break;
+ }
+ printf("0x%08X 0x%04X %s\n", p->offset(), p->length(), kindStr);
+ }
+ }
+}
+
+
template <>
ppc::P::uint_t DyldInfoPrinter<ppc>::relocBase()
template <>
x86_64::P::uint_t DyldInfoPrinter<x86_64>::relocBase()
{
- // check for split-seg
return fFirstWritableSegment->vmaddr();
}
+#if SUPPORT_ARCH_arm_any
template <>
arm::P::uint_t DyldInfoPrinter<arm>::relocBase()
{
else
return fFirstSegment->vmaddr();
}
+#endif
+
+#if SUPPORT_ARCH_arm64
+template <>
+arm64::P::uint_t DyldInfoPrinter<arm64>::relocBase()
+{
+ return fFirstWritableSegment->vmaddr();
+}
+#endif
template <>
{
if ( r_type == GENERIC_RELOC_VANILLA )
return "pointer";
- else if ( r_type == PPC_RELOC_PB_LA_PTR )
- return "pb pointer";
else
return "??";
}
return "??";
}
+#if SUPPORT_ARCH_arm_any
template <>
const char* DyldInfoPrinter<arm>::relocTypeName(uint8_t r_type)
{
else
return "??";
}
-
+#endif
+
+#if SUPPORT_ARCH_arm64
+template <>
+const char* DyldInfoPrinter<arm64>::relocTypeName(uint8_t r_type)
+{
+ if ( r_type == ARM64_RELOC_UNSIGNED )
+ return "pointer";
+ return "??";
+}
+#endif
+
template <typename A>
void DyldInfoPrinter<A>::printRelocRebaseInfo()
{
+ // First check if we can find a magic section for threaded rebase
+ {
+ auto rebaseChain = [this](uintptr_t chainStartMappedAddress, uintptr_t chainStartVMAddress, uint64_t stepMultiplier, uintptr_t baseAddress) {
+ uint64_t delta = 0;
+ uintptr_t mappedAddress = chainStartMappedAddress;
+ uintptr_t vmAddress = chainStartVMAddress;
+ do {
+ uint64_t value = *(uint64_t*)mappedAddress;
+
+ uint8_t segIndex = segmentIndexForAddress(vmAddress);
+ const char* segName = segmentName(segIndex);
+ const char* sectName = sectionName(segIndex, vmAddress);
+
+#if SUPPORT_ARCH_arm64e
+ uint16_t diversity = (uint16_t)(value >> 32);
+ bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+ ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+ bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+ bool isRebase = (value & (1ULL << 62)) == 0;
+ if (isRebase) {
+
+#if SUPPORT_ARCH_arm64e
+ if (isAuthenticated) {
+ uint64_t slide = 0;
+ static const char* keyNames[] = {
+ "IA", "IB", "DA", "DB"
+ };
+ // The new value for a rebase is the low 32-bits of the threaded value plus the slide.
+ uint64_t newValue = (value & 0xFFFFFFFF) + slide;
+ // Add in the offset from the mach_header
+ newValue += baseAddress;
+ // We have bits to merge in to the discriminator
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX (JOP: diversity %d, address %s, %s)\n",
+ segName, sectName, (uint64_t)vmAddress, "pointer", newValue,
+ diversity, hasAddressDiversity ? "true" : "false", keyNames[key]);
+ } else
+#endif
+ {
+ // Regular pointer which needs to fit in 51-bits of value.
+ // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+ // and the bottom 43-bits to be fit in to 51-bits.
+ uint64_t top8Bits = value & 0x0007F80000000000ULL;
+ uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+ uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+ printf("%-7s %-16s 0x%08llX %s 0x%08llX\n", segName, sectName, (uint64_t)vmAddress, "pointer", targetValue);
+ }
+ }
+
+ // The delta is bits [51..61]
+ // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+ value &= ~(1ULL << 62);
+ delta = ( value & 0x3FF8000000000000 ) >> 51;
+ mappedAddress += delta * stepMultiplier;
+ vmAddress += delta * stepMultiplier;
+ } while ( delta != 0 );
+ };
+ for(const macho_segment_command<P>* segCmd : fSegments) {
+ if (strcmp(segCmd->segname(), "__TEXT") != 0)
+ continue;
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if (strcmp(sect->sectname(), "__thread_starts") != 0)
+ continue;
+ printf("rebase information (from __TEXT,__thread_starts):\n");
+ printf("segment section address type\n");
+ const uint8_t* sectionContent = (uint8_t*)fHeader + segCmd->fileoff() + sect->offset();
+ uint32_t *threadStarts = (uint32_t*)sectionContent;
+ uint32_t *threadEnds = (uint32_t*)(sectionContent + sect->size());
+ uint32_t threadStartsHeader = threadStarts[0];
+ uint64_t stepMultiplier = (threadStartsHeader & 1) == 1 ? 8 : 4;
+ for (uint32_t* threadOffset = threadStarts + 1; threadOffset != threadEnds; ++threadOffset) {
+ //printf("Thread start offset: 0x%016X\n", *threadOffset);
+ // If we get a 0xFFFFFFFF offset then ld64 overestimated the size required. So skip the remainder
+ // of the entries.
+ if (*threadOffset == 0xFFFFFFFF)
+ break;
+ uint64_t chainStartVMAddr = fBaseAddress + *threadOffset;
+ uintptr_t chainStartMappedAddress = (uintptr_t)mappedAddressForVMAddress(chainStartVMAddr);
+ rebaseChain(chainStartMappedAddress, chainStartVMAddr, stepMultiplier, fBaseAddress);
+ }
+ return;
+ }
+ }
+ }
+
if ( fDynamicSymbolTable == NULL ) {
printf("no classic dynamic symbol table");
}
}
template <typename A>
-const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr)
+const char* DyldInfoPrinter<A>::closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex)
{
+ const macho_nlist<P>* bestSymbol = NULL;
if ( fDynamicSymbolTable != NULL ) {
- // find exact match in globals
- const macho_nlist<P>* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
- for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+ // find closest match in globals
+ const macho_nlist<P>* const globalsStart = &fSymbols[fDynamicSymbolTable->iextdefsym()];
+ const macho_nlist<P>* const globalsEnd = &globalsStart[fDynamicSymbolTable->nextdefsym()];
+ for (const macho_nlist<P>* s = globalsStart; s < globalsEnd; ++s) {
+ if ( (s->n_type() & N_TYPE) == N_SECT ) {
+ if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) {
+ if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) )
+ bestSymbol = s;
+ }
}
}
- // find exact match in local symbols
- const macho_nlist<P>* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
- for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+
+ // find closest match in locals
+ const macho_nlist<P>* const localsStart = &fSymbols[fDynamicSymbolTable->ilocalsym()];
+ const macho_nlist<P>* const localsEnd = &localsStart[fDynamicSymbolTable->nlocalsym()];
+ for (const macho_nlist<P>* s = localsStart; s < localsEnd; ++s) {
+ if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) {
+ if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) {
+ if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) )
+ bestSymbol = s;
+ }
}
}
}
else {
- // find exact match in all symbols
- const macho_nlist<P>* lastSym = &fSymbols[fSymbolCount];
- for (const macho_nlist<P>* sym = &fSymbols[0]; sym < lastSym; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+ // find closest match in locals
+ const macho_nlist<P>* const allStart = &fSymbols[0];
+ const macho_nlist<P>* const allEnd = &fSymbols[fSymbolCount];
+ for (const macho_nlist<P>* s = allStart; s < allEnd; ++s) {
+ if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) {
+ if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) {
+ if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) )
+ bestSymbol = s;
+ }
}
}
}
+ if ( bestSymbol != NULL ) {
+ *offset = addr - bestSymbol->n_value();
+ return &fStrings[bestSymbol->n_strx()];
+ }
+ *offset = 0;
+ return NULL;
+}
+template <typename A>
+const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr)
+{
+ uint64_t offset;
+ const char* s = closestSymbolNameForAddress(addr, &offset);
+ if ( (offset == 0) && (s != NULL) )
+ return s;
return "?";
}
-
+
template <typename A>
void DyldInfoPrinter<A>::printClassicBindingInfo()
{
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
+#if SUPPORT_ARCH_arm_any
case CPU_TYPE_ARM:
if ( DyldInfoPrinter<arm>::validFile(p + offset) )
DyldInfoPrinter<arm>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, arm slice does not contain arm mach-o";
break;
+#endif
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ if ( DyldInfoPrinter<arm64>::validFile(p + offset) )
+ DyldInfoPrinter<arm64>::make(p + offset, size, path, (sPreferredArch == 0));
+ else
+ throw "in universal file, arm64 slice does not contain arm64 mach-o";
+ break;
+#endif
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( DyldInfoPrinter<x86_64>::validFile(p) ) {
DyldInfoPrinter<x86_64>::make(p, length, path, false);
}
+#if SUPPORT_ARCH_arm_any
else if ( DyldInfoPrinter<arm>::validFile(p) ) {
DyldInfoPrinter<arm>::make(p, length, path, false);
}
+#endif
+#if SUPPORT_ARCH_arm64
+ else if ( DyldInfoPrinter<arm64>::validFile(p) ) {
+ DyldInfoPrinter<arm64>::make(p, length, path, false);
+ }
+#endif
else {
throw "not a known file type";
}
{
fprintf(stderr, "Usage: dyldinfo [-arch <arch>] <options> <mach-o file>\n"
"\t-dylibs print dependent dylibs\n"
+ "\t-dr print dependent dylibs and show any recorded DR info\n"
"\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n"
"\t-bind print addresses dyld will set based on symbolic lookups\n"
"\t-weak_bind print symbols which dyld must coalesce\n"
"\t-opcodes print opcodes used to generate the rebase and binding information\n"
"\t-function_starts print table of function start addresses\n"
"\t-export_dot print a GraphViz .dot file of the exported symbols trie\n"
+ "\t-data_in_code print any data-in-code information\n"
);
}
sPreferredArch = CPU_TYPE_POWERPC64;
else if ( strcmp(arch, "ppc") == 0 )
sPreferredArch = CPU_TYPE_POWERPC;
- else if ( strcmp(arch, "i386") == 0 )
- sPreferredArch = CPU_TYPE_I386;
- else if ( strcmp(arch, "x86_64") == 0 )
- sPreferredArch = CPU_TYPE_X86_64;
else {
+ if ( arch == NULL )
+ throw "-arch missing architecture name";
bool found = false;
- for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
- if ( strcmp(t->subTypeName,arch) == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = t->subType;
+ for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
+ if ( strcmp(t->archName,arch) == 0 ) {
+ sPreferredArch = t->cpuType;
+ if ( t->isSubType )
+ sPreferredSubArch = t->cpuSubType;
found = true;
break;
}
else if ( strcmp(arg, "-dylibs") == 0 ) {
printDylibs = true;
}
+ else if ( strcmp(arg, "-dr") == 0 ) {
+ printDRs = true;
+ }
+ else if ( strcmp(arg, "-data_in_code") == 0 ) {
+ printDataCode = true;
+ }
else {
throwf("unknown option: %s\n", arg);
}