aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Object/MachOObjectFile.cpp
diff options
context:
space:
mode:
authorDaniel Bertalan <dani@danielbertalan.dev>2022-08-12 15:10:58 +0200
committerDaniel Bertalan <dani@danielbertalan.dev>2022-08-18 09:29:27 +0200
commit11443ef85dbe4f3930f726b1be4d7f90f99d1021 (patch)
tree9e0c385d02e25463d41933e15024b6f1da52244c /llvm/lib/Object/MachOObjectFile.cpp
parented342d9d29898952e2d1fcd40716e83ed5b9f202 (diff)
downloadllvm-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.cpp118
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();