diff options
author | Daniel Bertalan <dani@danielbertalan.dev> | 2022-08-12 15:10:58 +0200 |
---|---|---|
committer | Daniel Bertalan <dani@danielbertalan.dev> | 2022-08-18 09:29:27 +0200 |
commit | 11443ef85dbe4f3930f726b1be4d7f90f99d1021 (patch) | |
tree | 9e0c385d02e25463d41933e15024b6f1da52244c /llvm/lib/Object/MachOObjectFile.cpp | |
parent | ed342d9d29898952e2d1fcd40716e83ed5b9f202 (diff) | |
download | llvm-11443ef85dbe4f3930f726b1be4d7f90f99d1021.zip llvm-11443ef85dbe4f3930f726b1be4d7f90f99d1021.tar.gz llvm-11443ef85dbe4f3930f726b1be4d7f90f99d1021.tar.bz2 |
[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
Diffstat (limited to 'llvm/lib/Object/MachOObjectFile.cpp')
-rw-r--r-- | llvm/lib/Object/MachOObjectFile.cpp | 118 |
1 files changed, 111 insertions, 7 deletions
diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index 2f463a1..d7e9b57 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -4756,7 +4756,7 @@ ArrayRef<uint8_t> MachOObjectFile::getDyldInfoLazyBindOpcodes() const { return None; auto DyldInfoOrErr = - getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); + getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); if (!DyldInfoOrErr) return None; MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); @@ -4765,8 +4765,8 @@ ArrayRef<uint8_t> MachOObjectFile::getDyldInfoLazyBindOpcodes() const { return makeArrayRef(Ptr, DyldInfo.lazy_bind_size); } -Expected<Optional<MachO::dyld_chained_fixups_header>> -MachOObjectFile::getChainedFixupsHeader() const { +Expected<Optional<MachO::linkedit_data_command>> +MachOObjectFile::getChainedFixupsLoadCommand() const { // Load the dyld chained fixups load command. if (!DyldChainedFixupsLoadCmd) return llvm::None; @@ -4774,13 +4774,28 @@ MachOObjectFile::getChainedFixupsHeader() const { *this, DyldChainedFixupsLoadCmd); if (!DyldChainedFixupsOrErr) 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, // 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; - if (CFHeaderOffset == 0) - return DyldChainedFixupsOrErr.takeError(); + uint64_t CFSize = DyldChainedFixups.datasize; // Load the dyld chained fixups header. const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset); @@ -4808,7 +4823,7 @@ MachOObjectFile::getChainedFixupsHeader() const { Twine(CFHeader.starts_offset) + " 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) > EndOffset) { return malformedError(Twine("bad chained fixups: image starts end ") + @@ -4820,6 +4835,95 @@ MachOObjectFile::getChainedFixupsHeader() const { 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>> MachOObjectFile::getDyldChainedFixupTargets() const { auto CFHeaderOrErr = getChainedFixupsHeader(); |