#include <errno.h>
#include <vector>
+#include <tuple>
#include <set>
#include <unordered_set>
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()];
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);
}
- }
+ }
}
-
}
}
-
-
-
-
-
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);
}
return "br22";
case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
return "off32";
+ case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
+ return "theaded-pointer64";
}
}
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");
}
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;
-#if SUPPORT_ARCH_arm64
- else if ( strcmp(arch, "arm64") == 0 )
- sPreferredArch = CPU_TYPE_ARM64;
-#endif
else {
if ( arch == NULL )
throw "-arch missing architecture name";