Skip to content

Commit 686d8ce

Browse files
committed
[llvm-objdump] Complete -chained_fixups support
This commit adds definitions for the `dyld_chained_import*` structs. The imports array is now printed with `llvm-otool -chained_fixups`. This completes this option's implementation. A slight difference from cctools otool is that we don't yet dump the raw bytes of the imports entries. 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/D131982
1 parent 096463d commit 686d8ce

File tree

5 files changed

+192
-7
lines changed

5 files changed

+192
-7
lines changed

llvm/include/llvm/BinaryFormat/MachO.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,30 @@ struct dyld_chained_starts_in_segment {
10791079
///< DYLD_CHAINED_PTR_START_NONE if no fixups
10801080
};
10811081

1082+
// DYLD_CHAINED_IMPORT
1083+
struct dyld_chained_import {
1084+
uint32_t lib_ordinal : 8;
1085+
uint32_t weak_import : 1;
1086+
uint32_t name_offset : 23;
1087+
};
1088+
1089+
// DYLD_CHAINED_IMPORT_ADDEND
1090+
struct dyld_chained_import_addend {
1091+
uint32_t lib_ordinal : 8;
1092+
uint32_t weak_import : 1;
1093+
uint32_t name_offset : 23;
1094+
int32_t addend;
1095+
};
1096+
1097+
// DYLD_CHAINED_IMPORT_ADDEND64
1098+
struct dyld_chained_import_addend64 {
1099+
uint64_t lib_ordinal : 16;
1100+
uint64_t weak_import : 1;
1101+
uint64_t reserved : 15;
1102+
uint64_t name_offset : 32;
1103+
uint64_t addend;
1104+
};
1105+
10821106
// Byte order swapping functions for MachO structs
10831107

10841108
inline void swapStruct(fat_header &mh) {

llvm/include/llvm/Object/MachO.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,13 @@ using bind_iterator = content_iterator<MachOBindEntry>;
274274
/// symbol. E.g., C++'s "operator new". This is called a "weak bind."
275275
struct ChainedFixupTarget {
276276
public:
277-
ChainedFixupTarget(int LibOrdinal, StringRef Symbol, uint64_t Addend,
278-
bool WeakImport)
279-
: LibOrdinal(LibOrdinal), SymbolName(Symbol), Addend(Addend),
280-
WeakImport(WeakImport) {}
277+
ChainedFixupTarget(int LibOrdinal, uint32_t NameOffset, StringRef Symbol,
278+
uint64_t Addend, bool WeakImport)
279+
: LibOrdinal(LibOrdinal), NameOffset(NameOffset), SymbolName(Symbol),
280+
Addend(Addend), WeakImport(WeakImport) {}
281281

282282
int libOrdinal() { return LibOrdinal; }
283+
uint32_t nameOffset() { return NameOffset; }
283284
StringRef symbolName() { return SymbolName; }
284285
uint64_t addend() { return Addend; }
285286
bool weakImport() { return WeakImport; }
@@ -289,6 +290,7 @@ struct ChainedFixupTarget {
289290

290291
private:
291292
int LibOrdinal;
293+
uint32_t NameOffset;
292294
StringRef SymbolName;
293295
uint64_t Addend;
294296
bool WeakImport;

llvm/lib/Object/MachOObjectFile.cpp

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/ADT/StringSwitch.h"
2020
#include "llvm/ADT/Triple.h"
2121
#include "llvm/ADT/Twine.h"
22+
#include "llvm/ADT/bit.h"
2223
#include "llvm/BinaryFormat/MachO.h"
2324
#include "llvm/BinaryFormat/Swift.h"
2425
#include "llvm/Object/Error.h"
@@ -4924,15 +4925,122 @@ MachOObjectFile::getChainedFixupsSegments() const {
49244925
return std::make_pair(ImageStarts.seg_count, Segments);
49254926
}
49264927

4928+
// The special library ordinals have a negative value, but they are encoded in
4929+
// an unsigned bitfield, so we need to sign extend the value.
4930+
template <typename T> static int getEncodedOrdinal(T Value) {
4931+
if (Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) ||
4932+
Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ||
4933+
Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_WEAK_LOOKUP))
4934+
return SignExtend32<sizeof(T) * CHAR_BIT>(Value);
4935+
return Value;
4936+
}
4937+
4938+
template <typename T, unsigned N>
4939+
static std::array<T, N> getArray(const MachOObjectFile &O, const void *Ptr) {
4940+
std::array<T, N> RawValue;
4941+
memcpy(RawValue.data(), Ptr, N * sizeof(T));
4942+
if (O.isLittleEndian() != sys::IsLittleEndianHost)
4943+
for (auto &Element : RawValue)
4944+
sys::swapByteOrder(Element);
4945+
return RawValue;
4946+
}
4947+
49274948
Expected<std::vector<ChainedFixupTarget>>
49284949
MachOObjectFile::getDyldChainedFixupTargets() const {
4950+
auto CFOrErr = getChainedFixupsLoadCommand();
4951+
if (!CFOrErr)
4952+
return CFOrErr.takeError();
4953+
4954+
std::vector<ChainedFixupTarget> Targets;
4955+
if (!CFOrErr->has_value())
4956+
return Targets;
4957+
4958+
const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr;
4959+
49294960
auto CFHeaderOrErr = getChainedFixupsHeader();
49304961
if (!CFHeaderOrErr)
49314962
return CFHeaderOrErr.takeError();
4932-
std::vector<ChainedFixupTarget> Targets;
49334963
if (!(*CFHeaderOrErr))
49344964
return Targets;
4935-
return Targets;
4965+
const MachO::dyld_chained_fixups_header &Header = **CFHeaderOrErr;
4966+
4967+
size_t ImportSize = 0;
4968+
if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT)
4969+
ImportSize = sizeof(MachO::dyld_chained_import);
4970+
else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND)
4971+
ImportSize = sizeof(MachO::dyld_chained_import_addend);
4972+
else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND64)
4973+
ImportSize = sizeof(MachO::dyld_chained_import_addend64);
4974+
else
4975+
return malformedError("bad chained fixups: unknown imports format: " +
4976+
Twine(Header.imports_format));
4977+
4978+
const char *Contents = getPtr(*this, DyldChainedFixups.dataoff);
4979+
const char *Imports = Contents + Header.imports_offset;
4980+
size_t ImportsEndOffset =
4981+
Header.imports_offset + ImportSize * Header.imports_count;
4982+
const char *ImportsEnd = Contents + ImportsEndOffset;
4983+
const char *Symbols = Contents + Header.symbols_offset;
4984+
const char *SymbolsEnd = Contents + DyldChainedFixups.datasize;
4985+
4986+
if (ImportsEnd > Symbols)
4987+
return malformedError("bad chained fixups: imports end " +
4988+
Twine(ImportsEndOffset) + " extends past end " +
4989+
Twine(DyldChainedFixups.datasize));
4990+
4991+
if (ImportsEnd > Symbols)
4992+
return malformedError("bad chained fixups: imports end " +
4993+
Twine(ImportsEndOffset) + " overlaps with symbols");
4994+
4995+
// We use bit manipulation to extract data from the bitfields. This is correct
4996+
// for both LE and BE hosts, but we assume that the object is little-endian.
4997+
if (!isLittleEndian())
4998+
return createError("parsing big-endian chained fixups is not implemented");
4999+
for (const char *ImportPtr = Imports; ImportPtr < ImportsEnd;
5000+
ImportPtr += ImportSize) {
5001+
int LibOrdinal;
5002+
bool WeakImport;
5003+
uint32_t NameOffset;
5004+
uint64_t Addend;
5005+
if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT) {
5006+
static_assert(sizeof(uint32_t) == sizeof(MachO::dyld_chained_import));
5007+
auto RawValue = getArray<uint32_t, 1>(*this, ImportPtr);
5008+
5009+
LibOrdinal = getEncodedOrdinal<uint8_t>(RawValue[0] & 0xFF);
5010+
WeakImport = (RawValue[0] >> 8) & 1;
5011+
NameOffset = RawValue[0] >> 9;
5012+
Addend = 0;
5013+
} else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND) {
5014+
static_assert(sizeof(uint64_t) ==
5015+
sizeof(MachO::dyld_chained_import_addend));
5016+
auto RawValue = getArray<uint32_t, 2>(*this, ImportPtr);
5017+
5018+
LibOrdinal = getEncodedOrdinal<uint8_t>(RawValue[0] & 0xFF);
5019+
WeakImport = (RawValue[0] >> 8) & 1;
5020+
NameOffset = RawValue[0] >> 9;
5021+
Addend = bit_cast<int32_t>(RawValue[1]);
5022+
} else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND64) {
5023+
static_assert(2 * sizeof(uint64_t) ==
5024+
sizeof(MachO::dyld_chained_import_addend64));
5025+
auto RawValue = getArray<uint64_t, 2>(*this, ImportPtr);
5026+
5027+
LibOrdinal = getEncodedOrdinal<uint16_t>(RawValue[0] & 0xFFFF);
5028+
NameOffset = (RawValue[0] >> 16) & 1;
5029+
WeakImport = RawValue[0] >> 17;
5030+
Addend = RawValue[1];
5031+
} else {
5032+
llvm_unreachable("Import format should have been checked");
5033+
}
5034+
5035+
const char *Str = Symbols + NameOffset;
5036+
if (Str >= SymbolsEnd)
5037+
return malformedError("bad chained fixups: symbol offset " +
5038+
Twine(NameOffset) + " extends past end " +
5039+
Twine(DyldChainedFixups.datasize));
5040+
Targets.emplace_back(LibOrdinal, NameOffset, Str, Addend, WeakImport);
5041+
}
5042+
5043+
return std::move(Targets);
49365044
}
49375045

49385046
ArrayRef<uint8_t> MachOObjectFile::getDyldInfoExportsTrie() const {

llvm/test/tools/llvm-objdump/MachO/chained-fixups.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,30 @@ DETAILS-NEXT: page_start[0] = 0
4343
DETAILS-NEXT: page_start[1] = 32
4444
DETAILS-NEXT: page_start[2] = 65535 (DYLD_CHAINED_PTR_START_NONE)
4545
DETAILS-NEXT: page_start[3] = 32
46+
DETAILS-NEXT: dyld chained import[0]
47+
DETAILS-NEXT: lib_ordinal = -2 (flat-namespace)
48+
DETAILS-NEXT: weak_import = 0
49+
DETAILS-NEXT: name_offset = 1 (_dynamicLookup)
50+
DETAILS-NEXT: dyld chained import[1]
51+
DETAILS-NEXT: lib_ordinal = 1 (libdylib)
52+
DETAILS-NEXT: weak_import = 1
53+
DETAILS-NEXT: name_offset = 16 (_weakImport)
54+
DETAILS-NEXT: dyld chained import[2]
55+
DETAILS-NEXT: lib_ordinal = 1 (libdylib)
56+
DETAILS-NEXT: weak_import = 0
57+
DETAILS-NEXT: name_offset = 28 (_dylib)
58+
DETAILS-NEXT: dyld chained import[3]
59+
DETAILS-NEXT: lib_ordinal = -3 (weak)
60+
DETAILS-NEXT: weak_import = 0
61+
DETAILS-NEXT: name_offset = 35 (_weakLocal)
62+
DETAILS-NEXT: dyld chained import[4]
63+
DETAILS-NEXT: lib_ordinal = -3 (weak)
64+
DETAILS-NEXT: weak_import = 0
65+
DETAILS-NEXT: name_offset = 46 (_weak)
4666

4767
## This test checks that the output is identical to that of cctools-1001.2 (XCode 14)
68+
## FIXME: Print encoded values of the dyld_chained_import* entries
69+
##
4870
## The input was generated from the following files:
4971
##
5072
## --- dylib.s:

llvm/tools/llvm-objdump/MachODump.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ static std::vector<std::string> ArchFlags;
9494
static bool ArchAll = false;
9595
static std::string ThumbTripleName;
9696

97+
static StringRef ordinalName(const object::MachOObjectFile *, int);
98+
9799
void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) {
98100
FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header);
99101
ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie);
@@ -1282,6 +1284,26 @@ PrintChainedFixupsSegment(const MachOObjectFile::ChainedFixupsSegment &Segment,
12821284
}
12831285
}
12841286

1287+
static void PrintChainedFixupTarget(ChainedFixupTarget &Target, size_t Idx,
1288+
int Format, MachOObjectFile *O) {
1289+
if (Format == MachO::DYLD_CHAINED_IMPORT)
1290+
outs() << "dyld chained import";
1291+
else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND)
1292+
outs() << "dyld chained import addend";
1293+
else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND64)
1294+
outs() << "dyld chained import addend64";
1295+
// FIXME: otool prints the encoded value as well.
1296+
outs() << '[' << Idx << "]\n";
1297+
1298+
outs() << " lib_ordinal = " << Target.libOrdinal() << " ("
1299+
<< ordinalName(O, Target.libOrdinal()) << ")\n";
1300+
outs() << " weak_import = " << Target.weakImport() << '\n';
1301+
outs() << " name_offset = " << Target.nameOffset() << " ("
1302+
<< Target.symbolName() << ")\n";
1303+
if (Format != MachO::DYLD_CHAINED_IMPORT)
1304+
outs() << " addend = " << (int64_t)Target.addend() << '\n';
1305+
}
1306+
12851307
static void PrintChainedFixups(MachOObjectFile *O) {
12861308
// MachOObjectFile::getChainedFixupsHeader() reads LC_DYLD_CHAINED_FIXUPS.
12871309
// FIXME: Support chained fixups in __TEXT,__chain_starts section too.
@@ -1314,7 +1336,12 @@ static void PrintChainedFixups(MachOObjectFile *O) {
13141336
for (const MachOObjectFile::ChainedFixupsSegment &S : Segments)
13151337
PrintChainedFixupsSegment(S, SegNames[S.SegIdx]);
13161338

1317-
// FIXME: Print more things.
1339+
auto FixupTargets =
1340+
unwrapOrError(O->getDyldChainedFixupTargets(), O->getFileName());
1341+
1342+
uint32_t ImportsFormat = ChainedFixupHeader->imports_format;
1343+
for (auto [Idx, Target] : enumerate(FixupTargets))
1344+
PrintChainedFixupTarget(Target, Idx, ImportsFormat, O);
13181345
}
13191346

13201347
static void PrintDyldInfo(MachOObjectFile *O) {
@@ -10508,6 +10535,8 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) {
1050810535
return "main-executable";
1050910536
case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
1051010537
return "flat-namespace";
10538+
case MachO::BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
10539+
return "weak";
1051110540
default:
1051210541
if (Ordinal > 0) {
1051310542
std::error_code EC =

0 commit comments

Comments
 (0)