[llvm-objdump] Support dumping segment information with -chained_fixups

This commit adds the definitions for `dyld_chained_starts_in_image`,
`dyld_chained_starts_in_segment`, and related enums. Dumping their
contents is possible with the -chained_fixups flag of llvm-otool.

The chained-fixups.yaml test was changed to cover bindings/rebases, as
well as weak imports, weak symbols and flat namespace symbols. Now that
we have actual fixup entries, the __DATA segment contains data that
would need to be hexdumped in YAML. We also test empty pages (to look
for the "DYLD_CHAINED_PTR_START_NONE" annotation), so the YAML would end
up quite large. So instead, this commit includes a binary file.

When Apple's effort to upstream their chained fixups code continues,
we'll replace this code with the then-upstreamed code. But we need
something in the meantime for testing ld64.lld's chained fixups code.

Differential Revision: https://reviews.llvm.org/D131961
This commit is contained in:
Daniel Bertalan 2022-08-12 15:10:58 +02:00
parent ed342d9d29
commit 11443ef85d
7 changed files with 354 additions and 162 deletions

View File

@ -1015,6 +1015,29 @@ enum {
DYLD_CHAINED_SYMBOL_ZLIB = 1, DYLD_CHAINED_SYMBOL_ZLIB = 1,
}; };
// Values for dyld_chained_starts_in_segment::page_start.
enum {
DYLD_CHAINED_PTR_START_NONE = 0xFFFF,
DYLD_CHAINED_PTR_START_MULTI = 0x8000,
DYLD_CHAINED_PTR_START_LAST = 0x8000,
};
// Values for dyld_chained_starts_in_segment::pointer_format.
enum {
DYLD_CHAINED_PTR_ARM64E = 1,
DYLD_CHAINED_PTR_64 = 2,
DYLD_CHAINED_PTR_32 = 3,
DYLD_CHAINED_PTR_32_CACHE = 4,
DYLD_CHAINED_PTR_32_FIRMWARE = 5,
DYLD_CHAINED_PTR_64_OFFSET = 6,
DYLD_CHAINED_PTR_ARM64E_KERNEL = 7,
DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8,
DYLD_CHAINED_PTR_ARM64E_USERLAND = 9,
DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10,
DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11,
DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12,
};
/// Structs for dyld chained fixups. /// Structs for dyld chained fixups.
/// dyld_chained_fixups_header is the data pointed to by LC_DYLD_CHAINED_FIXUPS /// dyld_chained_fixups_header is the data pointed to by LC_DYLD_CHAINED_FIXUPS
/// load command. /// load command.
@ -1036,6 +1059,17 @@ struct dyld_chained_starts_in_image {
uint32_t seg_info_offset[1]; uint32_t seg_info_offset[1];
}; };
struct dyld_chained_starts_in_segment {
uint32_t size; ///< Size of this, including chain_starts entries
uint16_t page_size; ///< Page size in bytes (0x1000 or 0x4000)
uint16_t pointer_format; ///< DYLD_CHAINED_PTR*
uint64_t segment_offset; ///< VM offset from the __TEXT segment
uint32_t max_valid_pointer; ///< Values beyond this are not pointers on 32-bit
uint16_t page_count; ///< Length of the page_start array
uint16_t page_start[1]; ///< Page offset of first fixup on each page, or
///< DYLD_CHAINED_PTR_START_NONE if no fixups
};
// Byte order swapping functions for MachO structs // Byte order swapping functions for MachO structs
inline void swapStruct(fat_header &mh) { inline void swapStruct(fat_header &mh) {
@ -2053,6 +2087,22 @@ inline void swapStruct(dyld_chained_fixups_header &C) {
sys::swapByteOrder(C.symbols_format); sys::swapByteOrder(C.symbols_format);
} }
inline void swapStruct(dyld_chained_starts_in_image &C) {
sys::swapByteOrder(C.seg_count);
// getStructOrErr() cannot copy the variable-length seg_info_offset array.
// Its elements must be byte swapped manually.
}
inline void swapStruct(dyld_chained_starts_in_segment &C) {
sys::swapByteOrder(C.size);
sys::swapByteOrder(C.page_size);
sys::swapByteOrder(C.pointer_format);
sys::swapByteOrder(C.segment_offset);
sys::swapByteOrder(C.max_valid_pointer);
sys::swapByteOrder(C.page_count);
// seg_info_offset entries must be byte swapped manually.
}
/* code signing attributes of a process */ /* code signing attributes of a process */
enum CodeSignAttrs { enum CodeSignAttrs {

View File

@ -685,10 +685,33 @@ public:
ArrayRef<uint8_t> getDyldInfoBindOpcodes() const; ArrayRef<uint8_t> getDyldInfoBindOpcodes() const;
ArrayRef<uint8_t> getDyldInfoWeakBindOpcodes() const; ArrayRef<uint8_t> getDyldInfoWeakBindOpcodes() const;
ArrayRef<uint8_t> getDyldInfoLazyBindOpcodes() const; ArrayRef<uint8_t> getDyldInfoLazyBindOpcodes() const;
/// If the optional is None, no header was found, but the object was well-formed. /// If the optional is None, no header was found, but the object was
/// well-formed.
Expected<Optional<MachO::dyld_chained_fixups_header>> Expected<Optional<MachO::dyld_chained_fixups_header>>
getChainedFixupsHeader() const; getChainedFixupsHeader() const;
Expected<std::vector<ChainedFixupTarget>> getDyldChainedFixupTargets() const; Expected<std::vector<ChainedFixupTarget>> getDyldChainedFixupTargets() const;
// Note: This is a limited, temporary API, which will be removed when Apple
// upstreams their implementation. Please do not rely on this.
Expected<Optional<MachO::linkedit_data_command>>
getChainedFixupsLoadCommand() const;
struct ChainedFixupsSegment {
ChainedFixupsSegment(uint8_t SegIdx, uint32_t Offset,
const MachO::dyld_chained_starts_in_segment &Header,
std::vector<uint16_t> &&PageStarts)
: SegIdx(SegIdx), Offset(Offset), Header(Header),
PageStarts(PageStarts){};
uint32_t SegIdx;
uint32_t Offset; // dyld_chained_starts_in_image::seg_info_offset[SegIdx]
MachO::dyld_chained_starts_in_segment Header;
std::vector<uint16_t> PageStarts; // page_start[] entries, host endianness
};
// Returns the number of sections listed in dyld_chained_starts_in_image, and
// a ChainedFixupsSegment for each segment that has fixups.
Expected<std::pair<size_t, std::vector<ChainedFixupsSegment>>>
getChainedFixupsSegments() const;
ArrayRef<uint8_t> getDyldInfoExportsTrie() const; ArrayRef<uint8_t> getDyldInfoExportsTrie() const;
SmallVector<uint64_t> getFunctionStarts() const; SmallVector<uint64_t> getFunctionStarts() const;
ArrayRef<uint8_t> getUuid() const; ArrayRef<uint8_t> getUuid() const;

View File

@ -4765,8 +4765,8 @@ ArrayRef<uint8_t> MachOObjectFile::getDyldInfoLazyBindOpcodes() const {
return makeArrayRef(Ptr, DyldInfo.lazy_bind_size); return makeArrayRef(Ptr, DyldInfo.lazy_bind_size);
} }
Expected<Optional<MachO::dyld_chained_fixups_header>> Expected<Optional<MachO::linkedit_data_command>>
MachOObjectFile::getChainedFixupsHeader() const { MachOObjectFile::getChainedFixupsLoadCommand() const {
// Load the dyld chained fixups load command. // Load the dyld chained fixups load command.
if (!DyldChainedFixupsLoadCmd) if (!DyldChainedFixupsLoadCmd)
return llvm::None; return llvm::None;
@ -4774,13 +4774,28 @@ MachOObjectFile::getChainedFixupsHeader() const {
*this, DyldChainedFixupsLoadCmd); *this, DyldChainedFixupsLoadCmd);
if (!DyldChainedFixupsOrErr) if (!DyldChainedFixupsOrErr)
return DyldChainedFixupsOrErr.takeError(); return DyldChainedFixupsOrErr.takeError();
MachO::linkedit_data_command DyldChainedFixups = DyldChainedFixupsOrErr.get(); const MachO::linkedit_data_command &DyldChainedFixups =
*DyldChainedFixupsOrErr;
// If the load command is present but the data offset has been zeroed out, // If the load command is present but the data offset has been zeroed out,
// as is the case for dylib stubs, return None (no error). // as is the case for dylib stubs, return None (no error).
if (!DyldChainedFixups.dataoff)
return llvm::None;
return DyldChainedFixups;
}
Expected<Optional<MachO::dyld_chained_fixups_header>>
MachOObjectFile::getChainedFixupsHeader() const {
auto CFOrErr = getChainedFixupsLoadCommand();
if (!CFOrErr)
return CFOrErr.takeError();
if (!CFOrErr->has_value())
return llvm::None;
const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr;
uint64_t CFHeaderOffset = DyldChainedFixups.dataoff; uint64_t CFHeaderOffset = DyldChainedFixups.dataoff;
if (CFHeaderOffset == 0) uint64_t CFSize = DyldChainedFixups.datasize;
return DyldChainedFixupsOrErr.takeError();
// Load the dyld chained fixups header. // Load the dyld chained fixups header.
const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset); const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset);
@ -4808,7 +4823,7 @@ MachOObjectFile::getChainedFixupsHeader() const {
Twine(CFHeader.starts_offset) + Twine(CFHeader.starts_offset) +
" overlaps with chained fixups header"); " overlaps with chained fixups header");
} }
uint32_t EndOffset = DyldChainedFixups.dataoff + DyldChainedFixups.datasize; uint32_t EndOffset = CFHeaderOffset + CFSize;
if (CFImageStartsOffset + sizeof(MachO::dyld_chained_starts_in_image) > if (CFImageStartsOffset + sizeof(MachO::dyld_chained_starts_in_image) >
EndOffset) { EndOffset) {
return malformedError(Twine("bad chained fixups: image starts end ") + return malformedError(Twine("bad chained fixups: image starts end ") +
@ -4820,6 +4835,95 @@ MachOObjectFile::getChainedFixupsHeader() const {
return CFHeader; return CFHeader;
} }
Expected<std::pair<size_t, std::vector<MachOObjectFile::ChainedFixupsSegment>>>
MachOObjectFile::getChainedFixupsSegments() const {
auto CFOrErr = getChainedFixupsLoadCommand();
if (!CFOrErr)
return CFOrErr.takeError();
std::vector<MachOObjectFile::ChainedFixupsSegment> Segments;
if (!CFOrErr->has_value())
return std::make_pair(0, Segments);
const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr;
auto HeaderOrErr = getChainedFixupsHeader();
if (!HeaderOrErr)
return HeaderOrErr.takeError();
if (!HeaderOrErr->has_value())
return std::make_pair(0, Segments);
const MachO::dyld_chained_fixups_header &Header = **HeaderOrErr;
const char *Contents = getPtr(*this, DyldChainedFixups.dataoff);
auto ImageStartsOrErr = getStructOrErr<MachO::dyld_chained_starts_in_image>(
*this, Contents + Header.starts_offset);
if (!ImageStartsOrErr)
return ImageStartsOrErr.takeError();
const MachO::dyld_chained_starts_in_image &ImageStarts = *ImageStartsOrErr;
const char *SegOffsPtr =
Contents + Header.starts_offset +
offsetof(MachO::dyld_chained_starts_in_image, seg_info_offset);
const char *SegOffsEnd =
SegOffsPtr + ImageStarts.seg_count * sizeof(uint32_t);
if (SegOffsEnd > Contents + DyldChainedFixups.datasize)
return malformedError(
"bad chained fixups: seg_info_offset extends past end");
const char *LastSegEnd = nullptr;
for (size_t I = 0, N = ImageStarts.seg_count; I < N; ++I) {
auto OffOrErr =
getStructOrErr<uint32_t>(*this, SegOffsPtr + I * sizeof(uint32_t));
if (!OffOrErr)
return OffOrErr.takeError();
// seg_info_offset == 0 means there is no associated starts_in_segment
// entry.
if (!*OffOrErr)
continue;
auto Fail = [&](Twine Message) {
return malformedError("bad chained fixups: segment info" + Twine(I) +
" at offset " + Twine(*OffOrErr) + Message);
};
const char *SegPtr = Contents + Header.starts_offset + *OffOrErr;
if (LastSegEnd && SegPtr < LastSegEnd)
return Fail(" overlaps with previous segment info");
auto SegOrErr =
getStructOrErr<MachO::dyld_chained_starts_in_segment>(*this, SegPtr);
if (!SegOrErr)
return SegOrErr.takeError();
const MachO::dyld_chained_starts_in_segment &Seg = *SegOrErr;
LastSegEnd = SegPtr + Seg.size;
if (Seg.pointer_format < 1 || Seg.pointer_format > 12)
return Fail(" has unknown pointer format: " + Twine(Seg.pointer_format));
const char *PageStart =
SegPtr + offsetof(MachO::dyld_chained_starts_in_segment, page_start);
const char *PageEnd = PageStart + Seg.page_count * sizeof(uint16_t);
if (PageEnd > SegPtr + Seg.size)
return Fail(" : page_starts extend past seg_info size");
// FIXME: This does not account for multiple offsets on a single page
// (DYLD_CHAINED_PTR_START_MULTI; 32-bit only).
std::vector<uint16_t> PageStarts;
for (size_t PageIdx = 0; PageIdx < Seg.page_count; ++PageIdx) {
uint16_t Start;
memcpy(&Start, PageStart + PageIdx * sizeof(uint16_t), sizeof(uint16_t));
if (isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(Start);
PageStarts.push_back(Start);
}
Segments.emplace_back(I, *OffOrErr, Seg, std::move(PageStarts));
}
return std::make_pair(ImageStarts.seg_count, Segments);
}
Expected<std::vector<ChainedFixupTarget>> Expected<std::vector<ChainedFixupTarget>>
MachOObjectFile::getDyldChainedFixupTargets() const { MachOObjectFile::getDyldChainedFixupTargets() const {
auto CFHeaderOrErr = getChainedFixupsHeader(); auto CFHeaderOrErr = getChainedFixupsHeader();

View File

@ -0,0 +1,86 @@
RUN: llvm-objdump -p %p/Inputs/chained-fixups.macho-x86_64 | FileCheck %s
RUN: llvm-otool -l %p/Inputs/chained-fixups.macho-x86_64 | FileCheck %s
CHECK: LC_DYLD_CHAINED_FIXUPS
CHECK: LC_DYLD_EXPORTS_TRIE
RUN: llvm-objdump --macho --chained-fixups %p/Inputs/chained-fixups.macho-x86_64 | \
RUN: FileCheck --check-prefix=DETAILS -DNAME=%p/Inputs/chained-fixups.macho-x86_64 %s
RUN: llvm-otool -chained_fixups %p/Inputs/chained-fixups.macho-x86_64 | \
RUN: FileCheck --check-prefix=DETAILS -DNAME=%p/Inputs/chained-fixups.macho-x86_64 %s
DETAILS: [[NAME]]:
DETAILS-NEXT: chained fixups header (LC_DYLD_CHAINED_FIXUPS)
DETAILS-NEXT: fixups_version = 0
DETAILS-NEXT: starts_offset = 32
DETAILS-NEXT: imports_offset = 112
DETAILS-NEXT: symbols_offset = 132
DETAILS-NEXT: imports_count = 5
DETAILS-NEXT: imports_format = 1 (DYLD_CHAINED_IMPORT)
DETAILS-NEXT: symbols_format = 0
DETAILS-NEXT: chained starts in image
DETAILS-NEXT: seg_count = 4
DETAILS-NEXT: seg_offset[0] = 0 (__TEXT)
DETAILS-NEXT: seg_offset[1] = 24 (__DATA_CONST)
DETAILS-NEXT: seg_offset[2] = 48 (__DATA)
DETAILS-NEXT: seg_offset[3] = 0 (__LINKEDIT)
DETAILS-NEXT: chained starts in segment 1 (__DATA_CONST)
DETAILS-NEXT: size = 24
DETAILS-NEXT: page_size = 0x1000
DETAILS-NEXT: pointer_format = 6 (DYLD_CHAINED_PTR_64_OFFSET)
DETAILS-NEXT: segment_offset = 0x3e0
DETAILS-NEXT: max_valid_pointer = 0
DETAILS-NEXT: page_count = 1
DETAILS-NEXT: page_start[0] = 0
DETAILS-NEXT: chained starts in segment 2 (__DATA)
DETAILS-NEXT: size = 30
DETAILS-NEXT: page_size = 0x1000
DETAILS-NEXT: pointer_format = 6 (DYLD_CHAINED_PTR_64_OFFSET)
DETAILS-NEXT: segment_offset = 0x3f0
DETAILS-NEXT: max_valid_pointer = 0
DETAILS-NEXT: page_count = 4
DETAILS-NEXT: page_start[0] = 0
DETAILS-NEXT: page_start[1] = 32
DETAILS-NEXT: page_start[2] = 65535 (DYLD_CHAINED_PTR_START_NONE)
DETAILS-NEXT: page_start[3] = 32
## This test checks that the output is identical to that of cctools-1001.2 (XCode 14)
## The input was generated from the following files:
##
## --- dylib.s:
## .data
## .globl _weak, _dylib, _weakImport
## .weak_definition _weak
## _weak:
## _dylib:
## _weakImport:
##
## $ clang --target=x86_64-apple-macos12 -dynamiclib dylib.s -o libdylib.dylib
##
## --- chained-fixups.s:
## .global _local, _weak, _weakLocal, _dylib, _weakImport, _dynamicLookup
## .weak_definition _weakLocal
## .weak_reference _weakImport
##
## .data
## .p2align 4
## _local:
## _weakLocal:
## .quad _local
## .space 8
## .quad _weak
## .space 4096
## .p2align 4
## .quad _weakLocal
## .space 8172
## .p2align 4
## .quad _dylib
## .quad _dylib + 42
##
## .section __DATA_CONST,__const
## .p2align 4
## .quad _weakImport
## .quad _dynamicLookup
##
## $ clang --target=x86_64-apple-macos12 -dynamiclib -L. -ldylib chained-fixups.s \
## -o chained-fixups.macho-x86_64 -Wl,-segalign,0x10 -Wl,-U,_dynamicLookup

View File

@ -1,151 +0,0 @@
# RUN: yaml2obj %s -o %t
# RUN: llvm-objdump -p %t | FileCheck %s
# RUN: llvm-otool -l %t | FileCheck %s
# CHECK: LC_DYLD_CHAINED_FIXUPS
# CHECK: LC_DYLD_EXPORTS_TRIE
# RUN: llvm-objdump --macho --chained-fixups %t | \
# RUN: FileCheck --check-prefix=DETAILS -DNAME=%t %s
# RUN: llvm-otool -chained_fixups %t | \
# RUN: FileCheck --check-prefix=DETAILS -DNAME=%t %s
# DETAILS: [[NAME]]:
# DETAILS-NEXT: chained fixups header (LC_DYLD_CHAINED_FIXUPS)
# DETAILS-NEXT: fixups_version = 0
# DETAILS-NEXT: starts_offset = 32
# DETAILS-NEXT: imports_offset = 44
# DETAILS-NEXT: symbols_offset = 44
# DETAILS-NEXT: imports_count = 0
# DETAILS-NEXT: imports_format = 1 (DYLD_CHAINED_IMPORT)
# DETAILS-NEXT: symbols_format = 0
## This yaml is from a dylib produced by ld64
## echo ".global _foo\n_foo" > dylib.s
## clang -target=x86_64-apple-macos12 -dynamiclib -isysroot Inputs/MacOSX.sdk dylib.s -o libdylib.dylib
## obj2yaml --raw-segment=data libdylib.dylib
--- !mach-o
IsLittleEndian: true
FileHeader:
magic: 0xFEEDFACF
cputype: 0x1000007
cpusubtype: 0x3
filetype: 0x6
ncmds: 13
sizeofcmds: 568
flags: 0x100085
reserved: 0x0
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __TEXT
vmaddr: 0
vmsize: 16384
fileoff: 0
filesize: 16384
maxprot: 5
initprot: 5
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x4000
size: 0
offset: 0x4000
align: 0
reloff: 0x0
nreloc: 0
flags: 0x80000400
reserved1: 0x0
reserved2: 0x0
reserved3: 0x0
content: ''
- cmd: LC_SEGMENT_64
cmdsize: 72
segname: __LINKEDIT
vmaddr: 16384
vmsize: 16384
fileoff: 16384
filesize: 96
maxprot: 1
initprot: 1
nsects: 0
flags: 0
- cmd: LC_ID_DYLIB
cmdsize: 48
dylib:
name: 24
timestamp: 1
current_version: 0
compatibility_version: 0
Content: libdylib.dylib
ZeroPadBytes: 3
- cmd: LC_DYLD_CHAINED_FIXUPS
cmdsize: 16
dataoff: 16384
datasize: 48
- cmd: LC_DYLD_EXPORTS_TRIE
cmdsize: 16
dataoff: 16432
datasize: 16
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 16456
nsyms: 1
stroff: 16472
strsize: 8
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 0
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
- cmd: LC_UUID
cmdsize: 24
uuid: 52409B91-DF59-346A-A63F-D4E6FFDC3E04
- cmd: LC_BUILD_VERSION
cmdsize: 32
platform: 1
minos: 786432
sdk: 851968
ntools: 1
Tools:
- tool: 3
version: 53674242
- cmd: LC_SOURCE_VERSION
cmdsize: 16
version: 0
- cmd: LC_LOAD_DYLIB
cmdsize: 56
dylib:
name: 24
timestamp: 2
current_version: 65793
compatibility_version: 65536
Content: '/usr/lib/libSystem.dylib'
ZeroPadBytes: 8
- cmd: LC_FUNCTION_STARTS
cmdsize: 16
dataoff: 16448
datasize: 8
- cmd: LC_DATA_IN_CODE
cmdsize: 16
dataoff: 16456
datasize: 0
__LINKEDIT: 00000000200000002C0000002C000000000000000100000000000000000000000200000000000000000000000000000000015F666F6F000804008080010000000000000000000000020000000F010000004000000000000020005F666F6F0000
...

View File

@ -1195,6 +1195,20 @@ static void printMachOChainedFixups(object::MachOObjectFile *Obj) {
reportError(std::move(Err), Obj->getFileName()); reportError(std::move(Err), Obj->getFileName());
} }
static SmallVector<std::string> GetSegmentNames(object::MachOObjectFile *O) {
SmallVector<std::string> Ret;
for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
if (Command.C.cmd == MachO::LC_SEGMENT) {
MachO::segment_command SLC = O->getSegmentLoadCommand(Command);
Ret.push_back(SLC.segname);
} else if (Command.C.cmd == MachO::LC_SEGMENT_64) {
MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command);
Ret.push_back(SLC.segname);
}
}
return Ret;
}
static void static void
PrintChainedFixupsHeader(const MachO::dyld_chained_fixups_header &H) { PrintChainedFixupsHeader(const MachO::dyld_chained_fixups_header &H) {
outs() << "chained fixups header (LC_DYLD_CHAINED_FIXUPS)\n"; outs() << "chained fixups header (LC_DYLD_CHAINED_FIXUPS)\n";
@ -1224,6 +1238,50 @@ PrintChainedFixupsHeader(const MachO::dyld_chained_fixups_header &H) {
outs() << '\n'; outs() << '\n';
} }
static constexpr std::array<StringRef, 13> PointerFormats{
"DYLD_CHAINED_PTR_ARM64E",
"DYLD_CHAINED_PTR_64",
"DYLD_CHAINED_PTR_32",
"DYLD_CHAINED_PTR_32_CACHE",
"DYLD_CHAINED_PTR_32_FIRMWARE",
"DYLD_CHAINED_PTR_64_OFFSET",
"DYLD_CHAINED_PTR_ARM64E_KERNEL",
"DYLD_CHAINED_PTR_64_KERNEL_CACHE",
"DYLD_CHAINED_PTR_ARM64E_USERLAND",
"DYLD_CHAINED_PTR_ARM64E_FIRMWARE",
"DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE",
"DYLD_CHAINED_PTR_ARM64E_USERLAND24",
};
static void
PrintChainedFixupsSegment(const MachOObjectFile::ChainedFixupsSegment &Segment,
StringRef SegName) {
outs() << "chained starts in segment " << Segment.SegIdx << " (" << SegName
<< ")\n";
outs() << " size = " << Segment.Header.size << '\n';
outs() << " page_size = " << format("0x%0" PRIx16, Segment.Header.page_size)
<< '\n';
outs() << " pointer_format = " << Segment.Header.pointer_format;
if ((Segment.Header.pointer_format - 1) <
MachO::DYLD_CHAINED_PTR_ARM64E_USERLAND24)
outs() << " (" << PointerFormats[Segment.Header.pointer_format - 1] << ")";
outs() << '\n';
outs() << " segment_offset = "
<< format("0x%0" PRIx64, Segment.Header.segment_offset) << '\n';
outs() << " max_valid_pointer = " << Segment.Header.max_valid_pointer
<< '\n';
outs() << " page_count = " << Segment.Header.page_count << '\n';
for (auto [Index, PageStart] : enumerate(Segment.PageStarts)) {
outs() << " page_start[" << Index << "] = " << PageStart;
// FIXME: Support DYLD_CHAINED_PTR_START_MULTI (32-bit only)
if (PageStart == MachO::DYLD_CHAINED_PTR_START_NONE)
outs() << " (DYLD_CHAINED_PTR_START_NONE)";
outs() << '\n';
}
}
static void PrintChainedFixups(MachOObjectFile *O) { static void PrintChainedFixups(MachOObjectFile *O) {
// MachOObjectFile::getChainedFixupsHeader() reads LC_DYLD_CHAINED_FIXUPS. // MachOObjectFile::getChainedFixupsHeader() reads LC_DYLD_CHAINED_FIXUPS.
// FIXME: Support chained fixups in __TEXT,__chain_starts section too. // FIXME: Support chained fixups in __TEXT,__chain_starts section too.
@ -1234,6 +1292,28 @@ static void PrintChainedFixups(MachOObjectFile *O) {
PrintChainedFixupsHeader(*ChainedFixupHeader); PrintChainedFixupsHeader(*ChainedFixupHeader);
auto [SegCount, Segments] =
unwrapOrError(O->getChainedFixupsSegments(), O->getFileName());
auto SegNames = GetSegmentNames(O);
size_t StartsIdx = 0;
outs() << "chained starts in image\n";
outs() << " seg_count = " << SegCount << '\n';
for (size_t I = 0; I < SegCount; ++I) {
uint64_t SegOffset = 0;
if (StartsIdx < Segments.size() && I == Segments[StartsIdx].SegIdx) {
SegOffset = Segments[StartsIdx].Offset;
++StartsIdx;
}
outs() << " seg_offset[" << I << "] = " << SegOffset << " ("
<< SegNames[I] << ")\n";
}
for (const MachOObjectFile::ChainedFixupsSegment &S : Segments)
PrintChainedFixupsSegment(S, SegNames[S.SegIdx]);
// FIXME: Print more things. // FIXME: Print more things.
} }