Index: llvm/include/llvm/BinaryFormat/MachO.h =================================================================== --- llvm/include/llvm/BinaryFormat/MachO.h +++ llvm/include/llvm/BinaryFormat/MachO.h @@ -295,6 +295,88 @@ EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02u }; +// header of the LC_DYLD_CHAINED_FIXUPS payload +struct dyld_chained_fixups_header { + uint32_t fixups_version; // 0 + uint32_t starts_offset; // offset of dyld_chained_starts_in_image + // in chain_data + uint32_t imports_offset; // offset of imports table in chain_data + uint32_t symbols_offset; // offset of symbol strings in chain_data + uint32_t imports_count; // number of imported symbol names + uint32_t imports_format; // DYLD_CHAINED_IMPORT* + uint32_t symbols_format; // 0 => uncompressed, 1 => zlib compressed +}; + +// values for dyld_chained_fixups_header.imports_format +enum { + DYLD_CHAINED_IMPORT = 1, + DYLD_CHAINED_IMPORT_ADDEND = 2, + DYLD_CHAINED_IMPORT_ADDEND64 = 3, +}; + +// This struct is embedded in LC_DYLD_CHAINED_FIXUPS payload +struct dyld_chained_starts_in_image { + uint32_t seg_count; + uint32_t seg_info_offset[1]; // each entry is offset into this struct for that + // segment followed by pool of + // dyld_chain_starts_in_segment data +}; + +// This struct is embedded in dyld_chain_starts_in_image +// and passed down to the kernel for page-in linking +struct dyld_chained_starts_in_segment { + uint32_t size; // size of this (amount kernel needs to copy) + uint16_t page_size; // 0x1000 or 0x4000 + uint16_t pointer_format; // DYLD_CHAINED_PTR_* + uint64_t segment_offset; // offset in memory to start of segment + uint32_t max_valid_pointer; // for 32-bit OS, any value beyond this is not a + // pointer + uint16_t page_count; // how many pages are in array + uint16_t page_start[1]; // each entry is offset in each page of first + // element in chain or DYLD_CHAINED_PTR_START_NONE + // if no fixups on page uint16_t chain_starts[1]; + // some 32-bit formats may require multiple starts + // per page. for those, if high bit is set in + // page_starts[], then it is index into + // chain_starts[] which is a list of starts the + // last of which has the high bit set +}; + +// DYLD_CHAINED_IMPORT +struct dyld_chained_import { + uint32_t lib_ordinal : 8, weak_import : 1, name_offset : 23; +}; + +// DYLD_CHAINED_IMPORT_ADDEND +struct dyld_chained_import_addend { + uint32_t lib_ordinal : 8, weak_import : 1, name_offset : 23; + int32_t addend; +}; + +// DYLD_CHAINED_IMPORT_ADDEND64 +struct dyld_chained_import_addend64 { + uint64_t lib_ordinal : 16, weak_import : 1, reserved : 15, name_offset : 32; + uint64_t addend; +}; + +// values for dyld_chained_starts_in_segment.pointer_format +enum { + DYLD_CHAINED_PTR_ARM64E = 1, // stride 8, unauth target is vmaddr + DYLD_CHAINED_PTR_64 = 2, // target is vmaddr + DYLD_CHAINED_PTR_32 = 3, + DYLD_CHAINED_PTR_32_CACHE = 4, + DYLD_CHAINED_PTR_32_FIRMWARE = 5, + DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset + DYLD_CHAINED_PTR_ARM64E_OFFSET = 7, // old name + DYLD_CHAINED_PTR_ARM64E_KERNEL = 7, // stride 4, unauth target is vm offset + DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8, + DYLD_CHAINED_PTR_ARM64E_USERLAND = 9, // stride 8, unauth target is vm offset + DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10, // stride 4, unauth target is vmaddr + DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches + DYLD_CHAINED_PTR_ARM64E_USERLAND24 = + 12, // stride 8, unauth target is vm offset, 24-bit bind +}; + enum { // Constant masks for the "n_type" field in llvm::MachO::nlist and // llvm::MachO::nlist_64 Index: llvm/test/tools/llvm-objdump/MachO/chained_fixups_dump.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objdump/MachO/chained_fixups_dump.test @@ -0,0 +1,50 @@ +# RUN: llvm-objdump --macho --chained_fixups %p/Inputs/macho-fixup-chain | FileCheck %s + +# CHECK: macho_fixup_chain: +# CHECK: chained fixups header (LC_DYLD_CHAINED_FIXUPS) +# CHECK: fixups_version = 0 +# CHECK: starts_offset = 32 +# CHECK: imports_offset = 80 +# CHECK: symbols_offset = 104 +# CHECK: imports_count = 6 +# CHECK: imports_format = 1 (DYLD_CHAINED_IMPORT) +# CHECK: symbols_format = 0 +# CHECK: chained starts in image +# CHECK: seg_count = 5 +# CHECK: seg_offset[0] = 0 (__PAGEZERO) +# CHECK: seg_offset[1] = 0 (__TEXT) +# CHECK: seg_offset[2] = 24 (__DATA_CONST) +# CHECK: seg_offset[3] = 0 (__DATA) +# CHECK: seg_offset[4] = 0 (__LINKEDIT) +# CHECK: chained starts in segment 2 (__DATA_CONST) +# CHECK: size = 24 +# CHECK: page_size = 0x0000000000004000 +# CHECK: pointer_format = 6 (DYLD_CHAINED_PTR_64_OFFSET) +# CHECK: segment_offset = 0x0000000000004000 +# CHECK: max_valid_pointer = 0 +# CHECK: page_count = 1 +# CHECK: page_start[0] = 0 +# CHECK: dyld chained import[0] = 0x00000204 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 1 (_$sSSN) +# CHECK: dyld chained import[1] = 0x00001004 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 8 (_$ss23_ContiguousArrayStorageCMn) +# CHECK: dyld chained import[2] = 0x00005204 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 41 (_$ss5print_9separator10terminatoryypd_S2StF) +# CHECK: dyld chained import[3] = 0x0000aa04 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 85 (_swift_allocObject) +# CHECK: dyld chained import[4] = 0x0000d004 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 104 (_swift_getTypeByMangledNameInContext) +# CHECK: dyld chained import[5] = 0x00011a04 +# CHECK: lib_ordinal = 4 (/usr/lib/swift/libswiftCore.dylib) +# CHECK: weak_import = 0 +# CHECK: name_offset = 141 (_swift_release) \ No newline at end of file Index: llvm/tools/llvm-objdump/MachODump.h =================================================================== --- llvm/tools/llvm-objdump/MachODump.h +++ llvm/tools/llvm-objdump/MachODump.h @@ -34,6 +34,7 @@ // MachO specific options extern bool Bind; +extern bool Chained_fixups; extern bool DataInCode; extern std::string DisSymName; extern bool DylibId; @@ -71,6 +72,7 @@ void printBindTable(object::ObjectFile *O); void printLazyBindTable(object::ObjectFile *O); void printWeakBindTable(object::ObjectFile *O); +void printChainedFixups(object::ObjectFile *O); } // namespace objdump } // namespace llvm Index: llvm/tools/llvm-objdump/MachODump.cpp =================================================================== --- llvm/tools/llvm-objdump/MachODump.cpp +++ llvm/tools/llvm-objdump/MachODump.cpp @@ -67,6 +67,7 @@ bool objdump::ExportsTrie; bool objdump::Rebase; bool objdump::Rpaths; +bool objdump::Chained_fixups; bool objdump::Bind; bool objdump::LazyBind; bool objdump::WeakBind; @@ -100,6 +101,7 @@ Bind = InputArgs.hasArg(OBJDUMP_bind); LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind); WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind); + Chained_fixups = InputArgs.hasArg(OBJDUMP_chained_fixups); UseDbg = InputArgs.hasArg(OBJDUMP_g); DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str(); FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr); @@ -1994,6 +1996,8 @@ printLazyBindTable(MachOOF); if (WeakBind) printWeakBindTable(MachOOF); + if (Chained_fixups) + printChainedFixups(MachOOF); if (DwarfDumpType != DIDT_Null) { std::unique_ptr DICtx = DWARFContext::create(*MachOOF); @@ -10448,6 +10452,249 @@ reportError(std::move(Err), Obj->getFileName()); } +//===----------------------------------------------------------------------===// +// fixup chain dumping +//===----------------------------------------------------------------------===// + +static void printMachOChainedFixups(object::MachOObjectFile *Obj) { + MachO::linkedit_data_command Ld = MachO::linkedit_data_command{0, 0, 0, 0}; + Error Err = Error::success(); + + // TODO: add support for segment_command + + SmallVector segments; + SmallVector dylibs; + SmallVector dylibInfo; + + // find LC_DYLD_CHAINED_FIXUPS Load Command & obtain loaded dylib info + for (const auto &Command : Obj->load_commands()) { + if (Command.C.cmd == MachO::LC_DYLD_CHAINED_FIXUPS) { + Ld = Obj->getLinkeditDataLoadCommand(Command); + } + if (Command.C.cmd == MachO::LC_SEGMENT_64) { + segments.emplace_back(Obj->getSegment64LoadCommand(Command)); + } + if (Command.C.cmd == MachO::LC_LOAD_DYLIB || + Command.C.cmd == MachO::LC_ID_DYLIB || + Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + Command.C.cmd == MachO::LC_REEXPORT_DYLIB || + Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + dylibInfo.emplace_back(Command); + dylibs.emplace_back(Obj->getDylibIDLoadCommand(Command)); + continue; + } + } + // no LC_DYLD_CHAINED_FIXUPS found + if (Ld.dataoff == 0) { + if (Err) { + reportError(std::move(Err), Obj->getFileName()); + } + return; + } + + uint32_t HeaderOffset = Ld.dataoff; + + auto memcpyAtOffset = [&](uint32_t Offset, auto &T) -> void { + const char *Ptr = Obj->getData().substr(Offset, sizeof(T)).data(); + memcpy(&T, Ptr, sizeof(T)); + }; + + outs() << "macho_fixup_chain:\n"; + + // parse dyld_chained_fixups_header + MachO::dyld_chained_fixups_header Header; + memcpyAtOffset(HeaderOffset, Header); + + std::string ImportsFormat; + switch (Header.imports_format) { + case MachO::DYLD_CHAINED_IMPORT: + ImportsFormat = "(DYLD_CHAINED_IMPORT)"; + break; + case MachO::DYLD_CHAINED_IMPORT_ADDEND: + ImportsFormat = "(DYLD_CHAINED_IMPORT_ADDEND)"; + break; + case MachO::DYLD_CHAINED_IMPORT_ADDEND64: + ImportsFormat = "(DYLD_CHAINED_IMPORT_ADDEND64)"; + break; + default: + ImportsFormat = "UNKNOWN"; + break; + } + outs() << "chained fixups header (LC_DYLD_CHAINED_FIXUPS)\n" + << " fixups_version = " << Header.fixups_version << "\n" + << " starts_offset = " << Header.starts_offset << "\n" + << " imports_offset = " << Header.imports_offset << "\n" + << " symbols_offset = " << Header.symbols_offset << "\n" + << " imports_count = " << Header.imports_count << "\n" + << " imports_format = " << Header.imports_format << " " + << ImportsFormat << "\n" + << " symbols_format = " << Header.symbols_format << "\n"; + + // parse dyld_chained_starts_in_image + MachO::dyld_chained_starts_in_image Image; + uint32_t ImageOffset = HeaderOffset + Header.starts_offset; + memcpyAtOffset(ImageOffset, Image); + outs() << "chained starts in image\n" + << " seg_count = " << Image.seg_count << "\n"; + SmallVector SegOffsets; + for (uint32_t SegIdx = 0; SegIdx < Image.seg_count; SegIdx++) { + uint32_t SegOffset; + memcpyAtOffset(ImageOffset + sizeof(uint32_t) * (SegIdx + 1), SegOffset); + SegOffsets.push_back(SegOffset); + outs() << " seg_offset[" << SegIdx << "] = " << SegOffset << " (" + << segments[SegIdx].segname << ")\n"; + } + + // parse dyld_chained_starts_in_segment + for (uint32_t SegIdx = 0; SegIdx < Image.seg_count; SegIdx++) { + uint32_t SegOffset = SegOffsets[SegIdx]; + if (SegOffset == 0) + continue; + MachO::dyld_chained_starts_in_segment Seg; + memcpyAtOffset(ImageOffset + SegOffset, Seg); + std::string PointerFormat; + switch (Seg.pointer_format) { + case MachO::DYLD_CHAINED_PTR_ARM64E: + PointerFormat = "DYLD_CHAINED_PTR_ARM64E"; + break; + case MachO::DYLD_CHAINED_PTR_64: + PointerFormat = "DYLD_CHAINED_PTR_64"; + break; + case MachO::DYLD_CHAINED_PTR_32: + PointerFormat = "DYLD_CHAINED_PTR_32"; + break; + case MachO::DYLD_CHAINED_PTR_32_CACHE: + PointerFormat = "DYLD_CHAINED_PTR_32_CACHE"; + break; + case MachO::DYLD_CHAINED_PTR_32_FIRMWARE: + PointerFormat = "DYLD_CHAINED_PTR_32_FIRMWARE"; + break; + case MachO::DYLD_CHAINED_PTR_64_OFFSET: + PointerFormat = "DYLD_CHAINED_PTR_64_OFFSET"; + break; + // TODO: handle DYLD_CHAINED_PTR_ARM64E_OFFSET + // case MachO::DYLD_CHAINED_PTR_ARM64E_OFFSET: + // PointerFormat = "DYLD_CHAINED_PTR_ARM64E_OFFSET"; + // break; + case MachO::DYLD_CHAINED_PTR_ARM64E_KERNEL: + PointerFormat = "DYLD_CHAINED_PTR_ARM64E_KERNEL"; + break; + case MachO::DYLD_CHAINED_PTR_64_KERNEL_CACHE: + PointerFormat = "DYLD_CHAINED_PTR_64_KERNEL_CACHE"; + break; + case MachO::DYLD_CHAINED_PTR_ARM64E_USERLAND: + PointerFormat = "DYLD_CHAINED_PTR_ARM64E_USERLAND"; + break; + case MachO::DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + PointerFormat = "DYLD_CHAINED_PTR_ARM64E_FIRMWARE"; + break; + case MachO::DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + PointerFormat = "DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE"; + break; + case MachO::DYLD_CHAINED_PTR_ARM64E_USERLAND24: + PointerFormat = "DYLD_CHAINED_PTR_ARM64E_USERLAND24"; + break; + default: + PointerFormat = "UNKNOWN"; + break; + } + outs() << "chained starts in segment " << SegIdx << " (" + << segments[SegIdx].segname << ")\n" + << " size = " << Seg.size << "\n" + << " page_size = " << format("0x%016" PRIx64, Seg.page_size) << "\n" + << " pointer_format = " << Seg.pointer_format << " (" + << PointerFormat << ")\n" + << " segment_offset = " + << format("0x%016" PRIx64, Seg.segment_offset) << "\n" + << " max_valid_pointer = " << Seg.max_valid_pointer << "\n" + << " page_count = " << Seg.page_count << "\n"; + for (uint16_t PageIdx = 0; PageIdx < Seg.page_count; PageIdx++) { + uint16_t PageStart; + memcpyAtOffset(ImageOffset + SegOffset + + sizeof(uint16_t) * (PageIdx + 11), + PageStart); + outs() << " page_start[" << PageIdx << "] = " << PageStart << "\n"; + } + } + + // parse dyld_chained_import + // TODO: handle zlib compressed symbol names + uint32_t ImportsOffset = HeaderOffset + Header.imports_offset; + uint32_t SymbolOffset = HeaderOffset + Header.symbols_offset; + auto dylibName = [&](uint32_t lib_ordinal) { + return (const char *)(dylibInfo[lib_ordinal - 1].Ptr) + + dylibs[lib_ordinal - 1].dylib.name; + }; + for (uint32_t ImportIdx = 0; ImportIdx < Header.imports_count; ImportIdx++) { + switch (Header.imports_format) { + case MachO::DYLD_CHAINED_IMPORT: + MachO::dyld_chained_import Import; + memcpyAtOffset(ImportsOffset + ImportIdx * sizeof(Import), Import); + uint32_t ImportValues[1]; + memcpy(ImportValues, &Import, sizeof(Import)); + outs() << "dyld chained import[" << ImportIdx + << "] = " << format("0x%08" PRIx64, ImportValues[0]) << "\n" + << " lib_ordinal = " << Import.lib_ordinal << " (" + << dylibName(Import.lib_ordinal) << ")\n" + << " weak_import = " << Import.weak_import << "\n" + << " name_offset = " << Import.name_offset << " (" + << StringRef(Obj->getData().data() + + (SymbolOffset + Import.name_offset)) + << ")" + << "\n"; + break; + case MachO::DYLD_CHAINED_IMPORT_ADDEND: + MachO::dyld_chained_import_addend ImportAddend; + memcpyAtOffset(ImportsOffset + ImportIdx * sizeof(ImportAddend), + ImportAddend); + uint32_t ImportAddendValues[2]; + memcpy(ImportAddendValues, &ImportAddend, sizeof(ImportAddend)); + outs() << "dyld chained import addend[" << ImportIdx + << "] = " << format("0x%08" PRIx64, ImportAddendValues[0]) + << format("0x%08" PRIx64, ImportAddendValues[1]) << "\n" + << " lib_ordinal = " << ImportAddend.lib_ordinal << " (" + << dylibName(ImportAddend.lib_ordinal) << ")\n" + << " weak_import = " << ImportAddend.weak_import << "\n" + << " name_offset = " << ImportAddend.name_offset << " (" + << StringRef(Obj->getData().data() + + (SymbolOffset + ImportAddend.name_offset)) + << ")" + << "\n" + << " addend = " << ImportAddend.addend << "\n"; + break; + case MachO::DYLD_CHAINED_IMPORT_ADDEND64: + MachO::dyld_chained_import_addend64 ImportAddend64; + memcpyAtOffset(ImportsOffset + ImportIdx * sizeof(ImportAddend64), + ImportAddend64); + uint64_t ImportAddend64Values[2]; + memcpy(ImportAddend64Values, &ImportAddend64Values, + sizeof(ImportAddend64)); + outs() << "dyld chained import addend64[" << ImportIdx + << "] = " << format("0x%016" PRIx64, ImportAddend64Values[0]) + << format("0x%016" PRIx64, ImportAddend64Values[1]) << "\n" + << " lib_ordinal = " << ImportAddend64.lib_ordinal << " (" + << dylibName(ImportAddend64.lib_ordinal) << ")\n" + << " weak_import = " << ImportAddend64.weak_import << "\n" + << " reserved =" << ImportAddend64.reserved << "\n" + << " name_offset = " << ImportAddend64.name_offset << " (" + << StringRef(Obj->getData().data() + + (SymbolOffset + ImportAddend64.name_offset)) + << ")" + << "\n" + << " addend = " << ImportAddend64.addend << "\n"; + break; + default: + outs() << "unknown format\n"; + break; + } + } + + if (Err) { + reportError(std::move(Err), Obj->getFileName()); + } +} + // get_dyld_bind_info_symbolname() is used for disassembly and passed an // address, ReferenceValue, in the Mach-O file and looks in the dyld bind // information for that address. If the address is found its binding symbol @@ -10519,3 +10766,11 @@ << "This operation is only currently supported " "for Mach-O executable files.\n"; } + +void objdump::printChainedFixups(ObjectFile *o) { + if (MachOObjectFile *MachO = dyn_cast(o)) + printMachOChainedFixups(MachO); + else + WithColor::error() << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} Index: llvm/tools/llvm-objdump/ObjdumpOpts.td =================================================================== --- llvm/tools/llvm-objdump/ObjdumpOpts.td +++ llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -245,6 +245,10 @@ HelpText<"Display mach-o weak binding info">, Group; +def chained_fixups : Flag<["--"], "chained_fixups">, + HelpText<"Print raw chained fixup data present in a final linked binary built with chained fixups. (requires --macho)">, + Group; + def g : Flag<["-"], "g">, HelpText<"Print line information from debug info if available">, Group; Index: llvm/tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- llvm/tools/llvm-objdump/llvm-objdump.cpp +++ llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -2757,11 +2757,11 @@ !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && - !(MachOOpt && - (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || - FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist || - LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths || - UniversalHeaders || WeakBind || !FilterSections.empty()))) { + !(MachOOpt && (Bind || DataInCode || DylibId || DylibsUsed || + ExportsTrie || FirstPrivateHeader || FunctionStarts || + IndirectSymbols || InfoPlist || LazyBind || LinkOptHints || + ObjcMetaData || Rebase || Rpaths || UniversalHeaders || + WeakBind || Chained_fixups || !FilterSections.empty()))) { T->printHelp(ToolName); return 2; }