Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp
35271 views
1
//=--------- MachOLinkGraphBuilder.cpp - MachO LinkGraph builder ----------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Generic MachO LinkGraph building code.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "MachOLinkGraphBuilder.h"
14
#include <optional>
15
16
#define DEBUG_TYPE "jitlink"
17
18
static const char *CommonSectionName = "__common";
19
20
namespace llvm {
21
namespace jitlink {
22
23
MachOLinkGraphBuilder::~MachOLinkGraphBuilder() = default;
24
25
Expected<std::unique_ptr<LinkGraph>> MachOLinkGraphBuilder::buildGraph() {
26
27
// We only operate on relocatable objects.
28
if (!Obj.isRelocatableObject())
29
return make_error<JITLinkError>("Object is not a relocatable MachO");
30
31
if (auto Err = createNormalizedSections())
32
return std::move(Err);
33
34
if (auto Err = createNormalizedSymbols())
35
return std::move(Err);
36
37
if (auto Err = graphifyRegularSymbols())
38
return std::move(Err);
39
40
if (auto Err = graphifySectionsWithCustomParsers())
41
return std::move(Err);
42
43
if (auto Err = addRelocations())
44
return std::move(Err);
45
46
return std::move(G);
47
}
48
49
MachOLinkGraphBuilder::MachOLinkGraphBuilder(
50
const object::MachOObjectFile &Obj, Triple TT, SubtargetFeatures Features,
51
LinkGraph::GetEdgeKindNameFunction GetEdgeKindName)
52
: Obj(Obj),
53
G(std::make_unique<LinkGraph>(std::string(Obj.getFileName()),
54
std::move(TT), std::move(Features),
55
getPointerSize(Obj), getEndianness(Obj),
56
std::move(GetEdgeKindName))) {
57
auto &MachHeader = Obj.getHeader64();
58
SubsectionsViaSymbols = MachHeader.flags & MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
59
}
60
61
void MachOLinkGraphBuilder::addCustomSectionParser(
62
StringRef SectionName, SectionParserFunction Parser) {
63
assert(!CustomSectionParserFunctions.count(SectionName) &&
64
"Custom parser for this section already exists");
65
CustomSectionParserFunctions[SectionName] = std::move(Parser);
66
}
67
68
Linkage MachOLinkGraphBuilder::getLinkage(uint16_t Desc) {
69
if ((Desc & MachO::N_WEAK_DEF) || (Desc & MachO::N_WEAK_REF))
70
return Linkage::Weak;
71
return Linkage::Strong;
72
}
73
74
Scope MachOLinkGraphBuilder::getScope(StringRef Name, uint8_t Type) {
75
if (Type & MachO::N_EXT) {
76
if ((Type & MachO::N_PEXT) || Name.starts_with("l"))
77
return Scope::Hidden;
78
else
79
return Scope::Default;
80
}
81
return Scope::Local;
82
}
83
84
bool MachOLinkGraphBuilder::isAltEntry(const NormalizedSymbol &NSym) {
85
return NSym.Desc & MachO::N_ALT_ENTRY;
86
}
87
88
bool MachOLinkGraphBuilder::isDebugSection(const NormalizedSection &NSec) {
89
return (NSec.Flags & MachO::S_ATTR_DEBUG &&
90
strcmp(NSec.SegName, "__DWARF") == 0);
91
}
92
93
bool MachOLinkGraphBuilder::isZeroFillSection(const NormalizedSection &NSec) {
94
switch (NSec.Flags & MachO::SECTION_TYPE) {
95
case MachO::S_ZEROFILL:
96
case MachO::S_GB_ZEROFILL:
97
case MachO::S_THREAD_LOCAL_ZEROFILL:
98
return true;
99
default:
100
return false;
101
}
102
}
103
104
unsigned
105
MachOLinkGraphBuilder::getPointerSize(const object::MachOObjectFile &Obj) {
106
return Obj.is64Bit() ? 8 : 4;
107
}
108
109
llvm::endianness
110
MachOLinkGraphBuilder::getEndianness(const object::MachOObjectFile &Obj) {
111
return Obj.isLittleEndian() ? llvm::endianness::little
112
: llvm::endianness::big;
113
}
114
115
Section &MachOLinkGraphBuilder::getCommonSection() {
116
if (!CommonSection)
117
CommonSection = &G->createSection(CommonSectionName,
118
orc::MemProt::Read | orc::MemProt::Write);
119
return *CommonSection;
120
}
121
122
Error MachOLinkGraphBuilder::createNormalizedSections() {
123
// Build normalized sections. Verifies that section data is in-range (for
124
// sections with content) and that address ranges are non-overlapping.
125
126
LLVM_DEBUG(dbgs() << "Creating normalized sections...\n");
127
128
for (auto &SecRef : Obj.sections()) {
129
NormalizedSection NSec;
130
uint32_t DataOffset = 0;
131
132
auto SecIndex = Obj.getSectionIndex(SecRef.getRawDataRefImpl());
133
134
if (Obj.is64Bit()) {
135
const MachO::section_64 &Sec64 =
136
Obj.getSection64(SecRef.getRawDataRefImpl());
137
138
memcpy(&NSec.SectName, &Sec64.sectname, 16);
139
NSec.SectName[16] = '\0';
140
memcpy(&NSec.SegName, Sec64.segname, 16);
141
NSec.SegName[16] = '\0';
142
143
NSec.Address = orc::ExecutorAddr(Sec64.addr);
144
NSec.Size = Sec64.size;
145
NSec.Alignment = 1ULL << Sec64.align;
146
NSec.Flags = Sec64.flags;
147
DataOffset = Sec64.offset;
148
} else {
149
const MachO::section &Sec32 = Obj.getSection(SecRef.getRawDataRefImpl());
150
151
memcpy(&NSec.SectName, &Sec32.sectname, 16);
152
NSec.SectName[16] = '\0';
153
memcpy(&NSec.SegName, Sec32.segname, 16);
154
NSec.SegName[16] = '\0';
155
156
NSec.Address = orc::ExecutorAddr(Sec32.addr);
157
NSec.Size = Sec32.size;
158
NSec.Alignment = 1ULL << Sec32.align;
159
NSec.Flags = Sec32.flags;
160
DataOffset = Sec32.offset;
161
}
162
163
LLVM_DEBUG({
164
dbgs() << " " << NSec.SegName << "," << NSec.SectName << ": "
165
<< formatv("{0:x16}", NSec.Address) << " -- "
166
<< formatv("{0:x16}", NSec.Address + NSec.Size)
167
<< ", align: " << NSec.Alignment << ", index: " << SecIndex
168
<< "\n";
169
});
170
171
// Get the section data if any.
172
if (!isZeroFillSection(NSec)) {
173
if (DataOffset + NSec.Size > Obj.getData().size())
174
return make_error<JITLinkError>(
175
"Section data extends past end of file");
176
177
NSec.Data = Obj.getData().data() + DataOffset;
178
}
179
180
// Get prot flags.
181
// FIXME: Make sure this test is correct (it's probably missing cases
182
// as-is).
183
orc::MemProt Prot;
184
if (NSec.Flags & MachO::S_ATTR_PURE_INSTRUCTIONS)
185
Prot = orc::MemProt::Read | orc::MemProt::Exec;
186
else
187
Prot = orc::MemProt::Read | orc::MemProt::Write;
188
189
auto FullyQualifiedName =
190
G->allocateContent(StringRef(NSec.SegName) + "," + NSec.SectName);
191
NSec.GraphSection = &G->createSection(
192
StringRef(FullyQualifiedName.data(), FullyQualifiedName.size()), Prot);
193
194
// TODO: Are there any other criteria for NoAlloc lifetime?
195
if (NSec.Flags & MachO::S_ATTR_DEBUG)
196
NSec.GraphSection->setMemLifetime(orc::MemLifetime::NoAlloc);
197
198
IndexToSection.insert(std::make_pair(SecIndex, std::move(NSec)));
199
}
200
201
std::vector<NormalizedSection *> Sections;
202
Sections.reserve(IndexToSection.size());
203
for (auto &KV : IndexToSection)
204
Sections.push_back(&KV.second);
205
206
// If we didn't end up creating any sections then bail out. The code below
207
// assumes that we have at least one section.
208
if (Sections.empty())
209
return Error::success();
210
211
llvm::sort(Sections,
212
[](const NormalizedSection *LHS, const NormalizedSection *RHS) {
213
assert(LHS && RHS && "Null section?");
214
if (LHS->Address != RHS->Address)
215
return LHS->Address < RHS->Address;
216
return LHS->Size < RHS->Size;
217
});
218
219
for (unsigned I = 0, E = Sections.size() - 1; I != E; ++I) {
220
auto &Cur = *Sections[I];
221
auto &Next = *Sections[I + 1];
222
if (Next.Address < Cur.Address + Cur.Size)
223
return make_error<JITLinkError>(
224
"Address range for section " +
225
formatv("\"{0}/{1}\" [ {2:x16} -- {3:x16} ] ", Cur.SegName,
226
Cur.SectName, Cur.Address, Cur.Address + Cur.Size) +
227
"overlaps section \"" + Next.SegName + "/" + Next.SectName + "\"" +
228
formatv("\"{0}/{1}\" [ {2:x16} -- {3:x16} ] ", Next.SegName,
229
Next.SectName, Next.Address, Next.Address + Next.Size));
230
}
231
232
return Error::success();
233
}
234
235
Error MachOLinkGraphBuilder::createNormalizedSymbols() {
236
LLVM_DEBUG(dbgs() << "Creating normalized symbols...\n");
237
238
for (auto &SymRef : Obj.symbols()) {
239
240
unsigned SymbolIndex = Obj.getSymbolIndex(SymRef.getRawDataRefImpl());
241
uint64_t Value;
242
uint32_t NStrX;
243
uint8_t Type;
244
uint8_t Sect;
245
uint16_t Desc;
246
247
if (Obj.is64Bit()) {
248
const MachO::nlist_64 &NL64 =
249
Obj.getSymbol64TableEntry(SymRef.getRawDataRefImpl());
250
Value = NL64.n_value;
251
NStrX = NL64.n_strx;
252
Type = NL64.n_type;
253
Sect = NL64.n_sect;
254
Desc = NL64.n_desc;
255
} else {
256
const MachO::nlist &NL32 =
257
Obj.getSymbolTableEntry(SymRef.getRawDataRefImpl());
258
Value = NL32.n_value;
259
NStrX = NL32.n_strx;
260
Type = NL32.n_type;
261
Sect = NL32.n_sect;
262
Desc = NL32.n_desc;
263
}
264
265
// Skip stabs.
266
// FIXME: Are there other symbols we should be skipping?
267
if (Type & MachO::N_STAB)
268
continue;
269
270
std::optional<StringRef> Name;
271
if (NStrX) {
272
if (auto NameOrErr = SymRef.getName())
273
Name = *NameOrErr;
274
else
275
return NameOrErr.takeError();
276
} else if (Type & MachO::N_EXT)
277
return make_error<JITLinkError>("Symbol at index " +
278
formatv("{0}", SymbolIndex) +
279
" has no name (string table index 0), "
280
"but N_EXT bit is set");
281
282
LLVM_DEBUG({
283
dbgs() << " ";
284
if (!Name)
285
dbgs() << "<anonymous symbol>";
286
else
287
dbgs() << *Name;
288
dbgs() << ": value = " << formatv("{0:x16}", Value)
289
<< ", type = " << formatv("{0:x2}", Type)
290
<< ", desc = " << formatv("{0:x4}", Desc) << ", sect = ";
291
if (Sect)
292
dbgs() << static_cast<unsigned>(Sect - 1);
293
else
294
dbgs() << "none";
295
dbgs() << "\n";
296
});
297
298
// If this symbol has a section, verify that the addresses line up.
299
if (Sect != 0) {
300
auto NSec = findSectionByIndex(Sect - 1);
301
if (!NSec)
302
return NSec.takeError();
303
304
if (orc::ExecutorAddr(Value) < NSec->Address ||
305
orc::ExecutorAddr(Value) > NSec->Address + NSec->Size)
306
return make_error<JITLinkError>("Address " + formatv("{0:x}", Value) +
307
" for symbol " + *Name +
308
" does not fall within section");
309
310
if (!NSec->GraphSection) {
311
LLVM_DEBUG({
312
dbgs() << " Skipping: Symbol is in section " << NSec->SegName << "/"
313
<< NSec->SectName
314
<< " which has no associated graph section.\n";
315
});
316
continue;
317
}
318
}
319
320
IndexToSymbol[SymbolIndex] =
321
&createNormalizedSymbol(*Name, Value, Type, Sect, Desc,
322
getLinkage(Desc), getScope(*Name, Type));
323
}
324
325
return Error::success();
326
}
327
328
void MachOLinkGraphBuilder::addSectionStartSymAndBlock(
329
unsigned SecIndex, Section &GraphSec, orc::ExecutorAddr Address,
330
const char *Data, orc::ExecutorAddrDiff Size, uint32_t Alignment,
331
bool IsLive) {
332
Block &B =
333
Data ? G->createContentBlock(GraphSec, ArrayRef<char>(Data, Size),
334
Address, Alignment, 0)
335
: G->createZeroFillBlock(GraphSec, Size, Address, Alignment, 0);
336
auto &Sym = G->addAnonymousSymbol(B, 0, Size, false, IsLive);
337
auto SecI = IndexToSection.find(SecIndex);
338
assert(SecI != IndexToSection.end() && "SecIndex invalid");
339
auto &NSec = SecI->second;
340
assert(!NSec.CanonicalSymbols.count(Sym.getAddress()) &&
341
"Anonymous block start symbol clashes with existing symbol address");
342
NSec.CanonicalSymbols[Sym.getAddress()] = &Sym;
343
}
344
345
Error MachOLinkGraphBuilder::graphifyRegularSymbols() {
346
347
LLVM_DEBUG(dbgs() << "Creating graph symbols...\n");
348
349
/// We only have 256 section indexes: Use a vector rather than a map.
350
std::vector<std::vector<NormalizedSymbol *>> SecIndexToSymbols;
351
SecIndexToSymbols.resize(256);
352
353
// Create commons, externs, and absolutes, and partition all other symbols by
354
// section.
355
for (auto &KV : IndexToSymbol) {
356
auto &NSym = *KV.second;
357
358
switch (NSym.Type & MachO::N_TYPE) {
359
case MachO::N_UNDF:
360
if (NSym.Value) {
361
if (!NSym.Name)
362
return make_error<JITLinkError>("Anonymous common symbol at index " +
363
Twine(KV.first));
364
NSym.GraphSymbol = &G->addDefinedSymbol(
365
G->createZeroFillBlock(getCommonSection(),
366
orc::ExecutorAddrDiff(NSym.Value),
367
orc::ExecutorAddr(),
368
1ull << MachO::GET_COMM_ALIGN(NSym.Desc), 0),
369
0, *NSym.Name, orc::ExecutorAddrDiff(NSym.Value), Linkage::Strong,
370
NSym.S, false, NSym.Desc & MachO::N_NO_DEAD_STRIP);
371
} else {
372
if (!NSym.Name)
373
return make_error<JITLinkError>("Anonymous external symbol at "
374
"index " +
375
Twine(KV.first));
376
NSym.GraphSymbol = &G->addExternalSymbol(
377
*NSym.Name, 0, (NSym.Desc & MachO::N_WEAK_REF) != 0);
378
}
379
break;
380
case MachO::N_ABS:
381
if (!NSym.Name)
382
return make_error<JITLinkError>("Anonymous absolute symbol at index " +
383
Twine(KV.first));
384
NSym.GraphSymbol = &G->addAbsoluteSymbol(
385
*NSym.Name, orc::ExecutorAddr(NSym.Value), 0, Linkage::Strong,
386
getScope(*NSym.Name, NSym.Type), NSym.Desc & MachO::N_NO_DEAD_STRIP);
387
break;
388
case MachO::N_SECT:
389
SecIndexToSymbols[NSym.Sect - 1].push_back(&NSym);
390
break;
391
case MachO::N_PBUD:
392
return make_error<JITLinkError>(
393
"Unupported N_PBUD symbol " +
394
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
395
" at index " + Twine(KV.first));
396
case MachO::N_INDR:
397
return make_error<JITLinkError>(
398
"Unupported N_INDR symbol " +
399
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
400
" at index " + Twine(KV.first));
401
default:
402
return make_error<JITLinkError>(
403
"Unrecognized symbol type " + Twine(NSym.Type & MachO::N_TYPE) +
404
" for symbol " +
405
(NSym.Name ? ("\"" + *NSym.Name + "\"") : Twine("<anon>")) +
406
" at index " + Twine(KV.first));
407
}
408
}
409
410
// Loop over sections performing regular graphification for those that
411
// don't have custom parsers.
412
for (auto &KV : IndexToSection) {
413
auto SecIndex = KV.first;
414
auto &NSec = KV.second;
415
416
if (!NSec.GraphSection) {
417
LLVM_DEBUG({
418
dbgs() << " " << NSec.SegName << "/" << NSec.SectName
419
<< " has no graph section. Skipping.\n";
420
});
421
continue;
422
}
423
424
// Skip sections with custom parsers.
425
if (CustomSectionParserFunctions.count(NSec.GraphSection->getName())) {
426
LLVM_DEBUG({
427
dbgs() << " Skipping section " << NSec.GraphSection->getName()
428
<< " as it has a custom parser.\n";
429
});
430
continue;
431
} else if ((NSec.Flags & MachO::SECTION_TYPE) ==
432
MachO::S_CSTRING_LITERALS) {
433
if (auto Err = graphifyCStringSection(
434
NSec, std::move(SecIndexToSymbols[SecIndex])))
435
return Err;
436
continue;
437
} else
438
LLVM_DEBUG({
439
dbgs() << " Graphifying regular section "
440
<< NSec.GraphSection->getName() << "...\n";
441
});
442
443
bool SectionIsNoDeadStrip = NSec.Flags & MachO::S_ATTR_NO_DEAD_STRIP;
444
bool SectionIsText = NSec.Flags & MachO::S_ATTR_PURE_INSTRUCTIONS;
445
446
auto &SecNSymStack = SecIndexToSymbols[SecIndex];
447
448
// If this section is non-empty but there are no symbols covering it then
449
// create one block and anonymous symbol to cover the entire section.
450
if (SecNSymStack.empty()) {
451
if (NSec.Size > 0) {
452
LLVM_DEBUG({
453
dbgs() << " Section non-empty, but contains no symbols. "
454
"Creating anonymous block to cover "
455
<< formatv("{0:x16}", NSec.Address) << " -- "
456
<< formatv("{0:x16}", NSec.Address + NSec.Size) << "\n";
457
});
458
addSectionStartSymAndBlock(SecIndex, *NSec.GraphSection, NSec.Address,
459
NSec.Data, NSec.Size, NSec.Alignment,
460
SectionIsNoDeadStrip);
461
} else
462
LLVM_DEBUG({
463
dbgs() << " Section empty and contains no symbols. Skipping.\n";
464
});
465
continue;
466
}
467
468
// Sort the symbol stack in by address, alt-entry status, scope, and name.
469
// We sort in reverse order so that symbols will be visited in the right
470
// order when we pop off the stack below.
471
llvm::sort(SecNSymStack, [](const NormalizedSymbol *LHS,
472
const NormalizedSymbol *RHS) {
473
if (LHS->Value != RHS->Value)
474
return LHS->Value > RHS->Value;
475
if (isAltEntry(*LHS) != isAltEntry(*RHS))
476
return isAltEntry(*RHS);
477
if (LHS->S != RHS->S)
478
return static_cast<uint8_t>(LHS->S) < static_cast<uint8_t>(RHS->S);
479
return LHS->Name < RHS->Name;
480
});
481
482
// The first symbol in a section can not be an alt-entry symbol.
483
if (!SecNSymStack.empty() && isAltEntry(*SecNSymStack.back()))
484
return make_error<JITLinkError>(
485
"First symbol in " + NSec.GraphSection->getName() + " is alt-entry");
486
487
// If the section is non-empty but there is no symbol covering the start
488
// address then add an anonymous one.
489
if (orc::ExecutorAddr(SecNSymStack.back()->Value) != NSec.Address) {
490
auto AnonBlockSize =
491
orc::ExecutorAddr(SecNSymStack.back()->Value) - NSec.Address;
492
LLVM_DEBUG({
493
dbgs() << " Section start not covered by symbol. "
494
<< "Creating anonymous block to cover [ " << NSec.Address
495
<< " -- " << (NSec.Address + AnonBlockSize) << " ]\n";
496
});
497
addSectionStartSymAndBlock(SecIndex, *NSec.GraphSection, NSec.Address,
498
NSec.Data, AnonBlockSize, NSec.Alignment,
499
SectionIsNoDeadStrip);
500
}
501
502
// Visit section symbols in order by popping off the reverse-sorted stack,
503
// building graph symbols as we go.
504
//
505
// If MH_SUBSECTIONS_VIA_SYMBOLS is set we'll build a block for each
506
// alt-entry chain.
507
//
508
// If MH_SUBSECTIONS_VIA_SYMBOLS is not set then we'll just build one block
509
// for the whole section.
510
while (!SecNSymStack.empty()) {
511
SmallVector<NormalizedSymbol *, 8> BlockSyms;
512
513
// Get the symbols in this alt-entry chain, or the whole section (if
514
// !SubsectionsViaSymbols).
515
BlockSyms.push_back(SecNSymStack.back());
516
SecNSymStack.pop_back();
517
while (!SecNSymStack.empty() &&
518
(isAltEntry(*SecNSymStack.back()) ||
519
SecNSymStack.back()->Value == BlockSyms.back()->Value ||
520
!SubsectionsViaSymbols)) {
521
BlockSyms.push_back(SecNSymStack.back());
522
SecNSymStack.pop_back();
523
}
524
525
// BlockNSyms now contains the block symbols in reverse canonical order.
526
auto BlockStart = orc::ExecutorAddr(BlockSyms.front()->Value);
527
orc::ExecutorAddr BlockEnd =
528
SecNSymStack.empty() ? NSec.Address + NSec.Size
529
: orc::ExecutorAddr(SecNSymStack.back()->Value);
530
orc::ExecutorAddrDiff BlockOffset = BlockStart - NSec.Address;
531
orc::ExecutorAddrDiff BlockSize = BlockEnd - BlockStart;
532
533
LLVM_DEBUG({
534
dbgs() << " Creating block for " << formatv("{0:x16}", BlockStart)
535
<< " -- " << formatv("{0:x16}", BlockEnd) << ": "
536
<< NSec.GraphSection->getName() << " + "
537
<< formatv("{0:x16}", BlockOffset) << " with "
538
<< BlockSyms.size() << " symbol(s)...\n";
539
});
540
541
Block &B =
542
NSec.Data
543
? G->createContentBlock(
544
*NSec.GraphSection,
545
ArrayRef<char>(NSec.Data + BlockOffset, BlockSize),
546
BlockStart, NSec.Alignment, BlockStart % NSec.Alignment)
547
: G->createZeroFillBlock(*NSec.GraphSection, BlockSize,
548
BlockStart, NSec.Alignment,
549
BlockStart % NSec.Alignment);
550
551
std::optional<orc::ExecutorAddr> LastCanonicalAddr;
552
auto SymEnd = BlockEnd;
553
while (!BlockSyms.empty()) {
554
auto &NSym = *BlockSyms.back();
555
BlockSyms.pop_back();
556
557
bool SymLive =
558
(NSym.Desc & MachO::N_NO_DEAD_STRIP) || SectionIsNoDeadStrip;
559
560
auto &Sym = createStandardGraphSymbol(
561
NSym, B, SymEnd - orc::ExecutorAddr(NSym.Value), SectionIsText,
562
SymLive, LastCanonicalAddr != orc::ExecutorAddr(NSym.Value));
563
564
if (LastCanonicalAddr != Sym.getAddress()) {
565
if (LastCanonicalAddr)
566
SymEnd = *LastCanonicalAddr;
567
LastCanonicalAddr = Sym.getAddress();
568
}
569
}
570
}
571
}
572
573
return Error::success();
574
}
575
576
Symbol &MachOLinkGraphBuilder::createStandardGraphSymbol(NormalizedSymbol &NSym,
577
Block &B, size_t Size,
578
bool IsText,
579
bool IsNoDeadStrip,
580
bool IsCanonical) {
581
582
LLVM_DEBUG({
583
dbgs() << " " << formatv("{0:x16}", NSym.Value) << " -- "
584
<< formatv("{0:x16}", NSym.Value + Size) << ": ";
585
if (!NSym.Name)
586
dbgs() << "<anonymous symbol>";
587
else
588
dbgs() << NSym.Name;
589
if (IsText)
590
dbgs() << " [text]";
591
if (IsNoDeadStrip)
592
dbgs() << " [no-dead-strip]";
593
if (!IsCanonical)
594
dbgs() << " [non-canonical]";
595
dbgs() << "\n";
596
});
597
598
auto SymOffset = orc::ExecutorAddr(NSym.Value) - B.getAddress();
599
auto &Sym =
600
NSym.Name
601
? G->addDefinedSymbol(B, SymOffset, *NSym.Name, Size, NSym.L, NSym.S,
602
IsText, IsNoDeadStrip)
603
: G->addAnonymousSymbol(B, SymOffset, Size, IsText, IsNoDeadStrip);
604
NSym.GraphSymbol = &Sym;
605
606
if (IsCanonical)
607
setCanonicalSymbol(getSectionByIndex(NSym.Sect - 1), Sym);
608
609
return Sym;
610
}
611
612
Error MachOLinkGraphBuilder::graphifySectionsWithCustomParsers() {
613
// Graphify special sections.
614
for (auto &KV : IndexToSection) {
615
auto &NSec = KV.second;
616
617
// Skip non-graph sections.
618
if (!NSec.GraphSection)
619
continue;
620
621
auto HI = CustomSectionParserFunctions.find(NSec.GraphSection->getName());
622
if (HI != CustomSectionParserFunctions.end()) {
623
auto &Parse = HI->second;
624
if (auto Err = Parse(NSec))
625
return Err;
626
}
627
}
628
629
return Error::success();
630
}
631
632
Error MachOLinkGraphBuilder::graphifyCStringSection(
633
NormalizedSection &NSec, std::vector<NormalizedSymbol *> NSyms) {
634
assert(NSec.GraphSection && "C string literal section missing graph section");
635
assert(NSec.Data && "C string literal section has no data");
636
637
LLVM_DEBUG({
638
dbgs() << " Graphifying C-string literal section "
639
<< NSec.GraphSection->getName() << "\n";
640
});
641
642
if (NSec.Data[NSec.Size - 1] != '\0')
643
return make_error<JITLinkError>("C string literal section " +
644
NSec.GraphSection->getName() +
645
" does not end with null terminator");
646
647
/// Sort into reverse order to use as a stack.
648
llvm::sort(NSyms,
649
[](const NormalizedSymbol *LHS, const NormalizedSymbol *RHS) {
650
if (LHS->Value != RHS->Value)
651
return LHS->Value > RHS->Value;
652
if (LHS->L != RHS->L)
653
return LHS->L > RHS->L;
654
if (LHS->S != RHS->S)
655
return LHS->S > RHS->S;
656
if (RHS->Name) {
657
if (!LHS->Name)
658
return true;
659
return *LHS->Name > *RHS->Name;
660
}
661
return false;
662
});
663
664
bool SectionIsNoDeadStrip = NSec.Flags & MachO::S_ATTR_NO_DEAD_STRIP;
665
bool SectionIsText = NSec.Flags & MachO::S_ATTR_PURE_INSTRUCTIONS;
666
orc::ExecutorAddrDiff BlockStart = 0;
667
668
// Scan section for null characters.
669
for (size_t I = 0; I != NSec.Size; ++I) {
670
if (NSec.Data[I] == '\0') {
671
size_t BlockSize = I + 1 - BlockStart;
672
// Create a block for this null terminated string.
673
auto &B = G->createContentBlock(*NSec.GraphSection,
674
{NSec.Data + BlockStart, BlockSize},
675
NSec.Address + BlockStart, NSec.Alignment,
676
BlockStart % NSec.Alignment);
677
678
LLVM_DEBUG({
679
dbgs() << " Created block " << B.getRange()
680
<< ", align = " << B.getAlignment()
681
<< ", align-ofs = " << B.getAlignmentOffset() << " for \"";
682
for (size_t J = 0; J != std::min(B.getSize(), size_t(16)); ++J)
683
switch (B.getContent()[J]) {
684
case '\0': break;
685
case '\n': dbgs() << "\\n"; break;
686
case '\t': dbgs() << "\\t"; break;
687
default: dbgs() << B.getContent()[J]; break;
688
}
689
if (B.getSize() > 16)
690
dbgs() << "...";
691
dbgs() << "\"\n";
692
});
693
694
// If there's no symbol at the start of this block then create one.
695
if (NSyms.empty() ||
696
orc::ExecutorAddr(NSyms.back()->Value) != B.getAddress()) {
697
auto &S = G->addAnonymousSymbol(B, 0, BlockSize, false, false);
698
setCanonicalSymbol(NSec, S);
699
LLVM_DEBUG({
700
dbgs() << " Adding symbol for c-string block " << B.getRange()
701
<< ": <anonymous symbol> at offset 0\n";
702
});
703
}
704
705
// Process any remaining symbols that point into this block.
706
auto LastCanonicalAddr = B.getAddress() + BlockSize;
707
while (!NSyms.empty() && orc::ExecutorAddr(NSyms.back()->Value) <
708
B.getAddress() + BlockSize) {
709
auto &NSym = *NSyms.back();
710
size_t SymSize = (B.getAddress() + BlockSize) -
711
orc::ExecutorAddr(NSyms.back()->Value);
712
bool SymLive =
713
(NSym.Desc & MachO::N_NO_DEAD_STRIP) || SectionIsNoDeadStrip;
714
715
bool IsCanonical = false;
716
if (LastCanonicalAddr != orc::ExecutorAddr(NSym.Value)) {
717
IsCanonical = true;
718
LastCanonicalAddr = orc::ExecutorAddr(NSym.Value);
719
}
720
721
auto &Sym = createStandardGraphSymbol(NSym, B, SymSize, SectionIsText,
722
SymLive, IsCanonical);
723
(void)Sym;
724
LLVM_DEBUG({
725
dbgs() << " Adding symbol for c-string block " << B.getRange()
726
<< ": "
727
<< (Sym.hasName() ? Sym.getName() : "<anonymous symbol>")
728
<< " at offset " << formatv("{0:x}", Sym.getOffset()) << "\n";
729
});
730
731
NSyms.pop_back();
732
}
733
734
BlockStart += BlockSize;
735
}
736
}
737
738
assert(llvm::all_of(NSec.GraphSection->blocks(),
739
[](Block *B) { return isCStringBlock(*B); }) &&
740
"All blocks in section should hold single c-strings");
741
742
return Error::success();
743
}
744
745
Error CompactUnwindSplitter::operator()(LinkGraph &G) {
746
auto *CUSec = G.findSectionByName(CompactUnwindSectionName);
747
if (!CUSec)
748
return Error::success();
749
750
if (!G.getTargetTriple().isOSBinFormatMachO())
751
return make_error<JITLinkError>(
752
"Error linking " + G.getName() +
753
": compact unwind splitting not supported on non-macho target " +
754
G.getTargetTriple().str());
755
756
unsigned CURecordSize = 0;
757
unsigned PersonalityEdgeOffset = 0;
758
unsigned LSDAEdgeOffset = 0;
759
switch (G.getTargetTriple().getArch()) {
760
case Triple::aarch64:
761
case Triple::x86_64:
762
// 64-bit compact-unwind record format:
763
// Range start: 8 bytes.
764
// Range size: 4 bytes.
765
// CU encoding: 4 bytes.
766
// Personality: 8 bytes.
767
// LSDA: 8 bytes.
768
CURecordSize = 32;
769
PersonalityEdgeOffset = 16;
770
LSDAEdgeOffset = 24;
771
break;
772
default:
773
return make_error<JITLinkError>(
774
"Error linking " + G.getName() +
775
": compact unwind splitting not supported on " +
776
G.getTargetTriple().getArchName());
777
}
778
779
std::vector<Block *> OriginalBlocks(CUSec->blocks().begin(),
780
CUSec->blocks().end());
781
LLVM_DEBUG({
782
dbgs() << "In " << G.getName() << " splitting compact unwind section "
783
<< CompactUnwindSectionName << " containing "
784
<< OriginalBlocks.size() << " initial blocks...\n";
785
});
786
787
while (!OriginalBlocks.empty()) {
788
auto *B = OriginalBlocks.back();
789
OriginalBlocks.pop_back();
790
791
if (B->getSize() == 0) {
792
LLVM_DEBUG({
793
dbgs() << " Skipping empty block at "
794
<< formatv("{0:x16}", B->getAddress()) << "\n";
795
});
796
continue;
797
}
798
799
LLVM_DEBUG({
800
dbgs() << " Splitting block at " << formatv("{0:x16}", B->getAddress())
801
<< " into " << (B->getSize() / CURecordSize)
802
<< " compact unwind record(s)\n";
803
});
804
805
if (B->getSize() % CURecordSize)
806
return make_error<JITLinkError>(
807
"Error splitting compact unwind record in " + G.getName() +
808
": block at " + formatv("{0:x}", B->getAddress()) + " has size " +
809
formatv("{0:x}", B->getSize()) +
810
" (not a multiple of CU record size of " +
811
formatv("{0:x}", CURecordSize) + ")");
812
813
unsigned NumBlocks = B->getSize() / CURecordSize;
814
LinkGraph::SplitBlockCache C;
815
816
for (unsigned I = 0; I != NumBlocks; ++I) {
817
auto &CURec = G.splitBlock(*B, CURecordSize, &C);
818
bool AddedKeepAlive = false;
819
820
for (auto &E : CURec.edges()) {
821
if (E.getOffset() == 0) {
822
LLVM_DEBUG({
823
dbgs() << " Updating compact unwind record at "
824
<< formatv("{0:x16}", CURec.getAddress()) << " to point to "
825
<< (E.getTarget().hasName() ? E.getTarget().getName()
826
: StringRef())
827
<< " (at " << formatv("{0:x16}", E.getTarget().getAddress())
828
<< ")\n";
829
});
830
831
if (E.getTarget().isExternal())
832
return make_error<JITLinkError>(
833
"Error adding keep-alive edge for compact unwind record at " +
834
formatv("{0:x}", CURec.getAddress()) + ": target " +
835
E.getTarget().getName() + " is an external symbol");
836
auto &TgtBlock = E.getTarget().getBlock();
837
auto &CURecSym =
838
G.addAnonymousSymbol(CURec, 0, CURecordSize, false, false);
839
TgtBlock.addEdge(Edge::KeepAlive, 0, CURecSym, 0);
840
AddedKeepAlive = true;
841
} else if (E.getOffset() != PersonalityEdgeOffset &&
842
E.getOffset() != LSDAEdgeOffset)
843
return make_error<JITLinkError>("Unexpected edge at offset " +
844
formatv("{0:x}", E.getOffset()) +
845
" in compact unwind record at " +
846
formatv("{0:x}", CURec.getAddress()));
847
}
848
849
if (!AddedKeepAlive)
850
return make_error<JITLinkError>(
851
"Error adding keep-alive edge for compact unwind record at " +
852
formatv("{0:x}", CURec.getAddress()) +
853
": no outgoing target edge at offset 0");
854
}
855
}
856
return Error::success();
857
}
858
859
} // end namespace jitlink
860
} // end namespace llvm
861
862