1 //=--------- MachOAtomGraphBuilder.cpp - MachO AtomGraph builder ----------===//
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
7 //===----------------------------------------------------------------------===//
9 // Generic MachO AtomGraph buliding code.
11 //===----------------------------------------------------------------------===//
13 #include "MachOAtomGraphBuilder.h"
15 #define DEBUG_TYPE "jitlink"
20 MachOAtomGraphBuilder::~MachOAtomGraphBuilder() {}
22 Expected
<std::unique_ptr
<AtomGraph
>> MachOAtomGraphBuilder::buildGraph() {
23 if (auto Err
= parseSections())
24 return std::move(Err
);
26 if (auto Err
= addAtoms())
27 return std::move(Err
);
29 if (auto Err
= addRelocations())
30 return std::move(Err
);
35 MachOAtomGraphBuilder::MachOAtomGraphBuilder(const object::MachOObjectFile
&Obj
)
37 G(llvm::make_unique
<AtomGraph
>(Obj
.getFileName(), getPointerSize(Obj
),
38 getEndianness(Obj
))) {}
40 void MachOAtomGraphBuilder::addCustomAtomizer(StringRef SectionName
,
41 CustomAtomizeFunction Atomizer
) {
42 assert(!CustomAtomizeFunctions
.count(SectionName
) &&
43 "Custom atomizer for this section already exists");
44 CustomAtomizeFunctions
[SectionName
] = std::move(Atomizer
);
47 bool MachOAtomGraphBuilder::areLayoutLocked(const Atom
&A
, const Atom
&B
) {
48 // If these atoms are the same then they're trivially "locked".
52 // If A and B are different, check whether either is undefined. (in which
53 // case they are not locked).
54 if (!A
.isDefined() || !B
.isDefined())
57 // A and B are different, but they're both defined atoms. We need to check
58 // whether they're part of the same alt_entry chain.
59 auto &DA
= static_cast<const DefinedAtom
&>(A
);
60 auto &DB
= static_cast<const DefinedAtom
&>(B
);
62 auto AStartItr
= AltEntryStarts
.find(&DA
);
63 if (AStartItr
== AltEntryStarts
.end()) // If A is not in a chain bail out.
66 auto BStartItr
= AltEntryStarts
.find(&DB
);
67 if (BStartItr
== AltEntryStarts
.end()) // If B is not in a chain bail out.
70 // A and B are layout locked if they're in the same chain.
71 return AStartItr
->second
== BStartItr
->second
;
75 MachOAtomGraphBuilder::getPointerSize(const object::MachOObjectFile
&Obj
) {
76 return Obj
.is64Bit() ? 8 : 4;
80 MachOAtomGraphBuilder::getEndianness(const object::MachOObjectFile
&Obj
) {
81 return Obj
.isLittleEndian() ? support::little
: support::big
;
84 MachOAtomGraphBuilder::MachOSection
&MachOAtomGraphBuilder::getCommonSection() {
85 if (!CommonSymbolsSection
) {
86 auto Prot
= static_cast<sys::Memory::ProtectionFlags
>(
87 sys::Memory::MF_READ
| sys::Memory::MF_WRITE
);
88 auto &GenericSection
= G
->createSection("<common>", 1, Prot
, true);
89 CommonSymbolsSection
= MachOSection(GenericSection
);
91 return *CommonSymbolsSection
;
94 Error
MachOAtomGraphBuilder::parseSections() {
95 for (auto &SecRef
: Obj
.sections()) {
96 assert((SecRef
.getAlignment() <= std::numeric_limits
<uint32_t>::max()) &&
97 "Section alignment does not fit in 32 bits");
100 if (auto EC
= SecRef
.getName(Name
))
101 return errorCodeToError(EC
);
103 unsigned SectionIndex
= SecRef
.getIndex() + 1;
105 uint32_t Align
= SecRef
.getAlignment();
106 if (!isPowerOf2_32(Align
))
107 return make_error
<JITLinkError
>("Section " + Name
+
108 " has non-power-of-2 "
111 // FIXME: Get real section permissions
112 // How, exactly, on MachO?
113 sys::Memory::ProtectionFlags Prot
;
115 Prot
= static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
116 sys::Memory::MF_EXEC
);
118 Prot
= static_cast<sys::Memory::ProtectionFlags
>(sys::Memory::MF_READ
|
119 sys::Memory::MF_WRITE
);
121 auto &GenericSection
= G
->createSection(Name
, Align
, Prot
, SecRef
.isBSS());
124 dbgs() << "Adding section " << Name
<< ": "
125 << format("0x%016" PRIx64
, SecRef
.getAddress())
126 << ", align: " << SecRef
.getAlignment() << "\n";
129 assert(!Sections
.count(SectionIndex
) && "Section index already in use");
133 .try_emplace(SectionIndex
, GenericSection
, SecRef
.getAddress(),
134 SecRef
.getAlignment())
137 if (!SecRef
.isVirtual()) {
138 // If this section has content then record it.
139 Expected
<StringRef
> Content
= SecRef
.getContents();
141 return Content
.takeError();
142 if (Content
->size() != SecRef
.getSize())
143 return make_error
<JITLinkError
>("Section content size does not match "
144 "declared size for " +
146 MachOSec
.setContent(*Content
);
148 // If this is a zero-fill section then just record the size.
149 MachOSec
.setZeroFill(SecRef
.getSize());
152 uint32_t SectionFlags
=
153 Obj
.is64Bit() ? Obj
.getSection64(SecRef
.getRawDataRefImpl()).flags
154 : Obj
.getSection(SecRef
.getRawDataRefImpl()).flags
;
156 MachOSec
.setNoDeadStrip(SectionFlags
& MachO::S_ATTR_NO_DEAD_STRIP
);
159 return Error::success();
162 // Adds atoms with identified start addresses (but not lengths) for all named
164 // Also, for every section that contains named atoms, but does not have an
165 // atom at offset zero of that section, constructs an anonymous atom covering
167 Error
MachOAtomGraphBuilder::addNonCustomAtoms() {
168 using AddrToAtomMap
= std::map
<JITTargetAddress
, DefinedAtom
*>;
169 DenseMap
<MachOSection
*, AddrToAtomMap
> SecToAtoms
;
171 DenseMap
<MachOSection
*, unsigned> FirstOrdinal
;
172 std::vector
<DefinedAtom
*> AltEntryAtoms
;
174 DenseSet
<StringRef
> ProcessedSymbols
; // Used to check for duplicate defs.
176 for (auto SymI
= Obj
.symbol_begin(), SymE
= Obj
.symbol_end(); SymI
!= SymE
;
178 object::SymbolRef
Sym(SymI
->getRawDataRefImpl(), &Obj
);
180 auto Name
= Sym
.getName();
182 return Name
.takeError();
184 // Bail out on duplicate definitions: There should never be more than one
185 // definition for a symbol in a given object file.
186 if (ProcessedSymbols
.count(*Name
))
187 return make_error
<JITLinkError
>("Duplicate definition within object: " +
190 ProcessedSymbols
.insert(*Name
);
192 auto Addr
= Sym
.getAddress();
194 return Addr
.takeError();
196 auto SymType
= Sym
.getType();
198 return SymType
.takeError();
200 auto Flags
= Sym
.getFlags();
202 if (Flags
& object::SymbolRef::SF_Undefined
) {
203 LLVM_DEBUG(dbgs() << "Adding undef atom \"" << *Name
<< "\"\n");
204 G
->addExternalAtom(*Name
);
206 } else if (Flags
& object::SymbolRef::SF_Absolute
) {
207 LLVM_DEBUG(dbgs() << "Adding absolute \"" << *Name
<< "\" addr: "
208 << format("0x%016" PRIx64
, *Addr
) << "\n");
209 auto &A
= G
->addAbsoluteAtom(*Name
, *Addr
);
210 A
.setGlobal(Flags
& object::SymbolRef::SF_Global
);
211 A
.setExported(Flags
& object::SymbolRef::SF_Exported
);
212 A
.setWeak(Flags
& object::SymbolRef::SF_Weak
);
214 } else if (Flags
& object::SymbolRef::SF_Common
) {
216 dbgs() << "Adding common \"" << *Name
217 << "\" addr: " << format("0x%016" PRIx64
, *Addr
) << "\n";
220 G
->addCommonAtom(getCommonSection().getGenericSection(), *Name
, *Addr
,
221 std::max(Sym
.getAlignment(), 1U),
222 Obj
.getCommonSymbolSize(Sym
.getRawDataRefImpl()));
223 A
.setGlobal(Flags
& object::SymbolRef::SF_Global
);
224 A
.setExported(Flags
& object::SymbolRef::SF_Exported
);
228 LLVM_DEBUG(dbgs() << "Adding defined atom \"" << *Name
<< "\"\n");
230 // This atom is neither undefined nor absolute, so it must be defined in
231 // this object. Get its section index.
232 auto SecItr
= Sym
.getSection();
234 return SecItr
.takeError();
236 uint64_t SectionIndex
= (*SecItr
)->getIndex() + 1;
238 LLVM_DEBUG(dbgs() << " to section index " << SectionIndex
<< "\n");
240 auto SecByIndexItr
= Sections
.find(SectionIndex
);
241 if (SecByIndexItr
== Sections
.end())
242 return make_error
<JITLinkError
>("Unrecognized section index in macho");
244 auto &Sec
= SecByIndexItr
->second
;
246 auto &DA
= G
->addDefinedAtom(Sec
.getGenericSection(), *Name
, *Addr
,
247 std::max(Sym
.getAlignment(), 1U));
249 DA
.setGlobal(Flags
& object::SymbolRef::SF_Global
);
250 DA
.setExported(Flags
& object::SymbolRef::SF_Exported
);
251 DA
.setWeak(Flags
& object::SymbolRef::SF_Weak
);
253 DA
.setCallable(*SymType
& object::SymbolRef::ST_Function
);
255 // Check NDesc flags.
259 NDesc
= Obj
.getSymbol64TableEntry(SymI
->getRawDataRefImpl()).n_desc
;
261 NDesc
= Obj
.getSymbolTableEntry(SymI
->getRawDataRefImpl()).n_desc
;
263 // Record atom for alt-entry post-processing (where the layout-next
264 // constraints will be added).
265 if (NDesc
& MachO::N_ALT_ENTRY
)
266 AltEntryAtoms
.push_back(&DA
);
268 // If this atom has a no-dead-strip attr attached then mark it live.
269 if (NDesc
& MachO::N_NO_DEAD_STRIP
)
274 dbgs() << " Added " << *Name
275 << " addr: " << format("0x%016" PRIx64
, *Addr
)
276 << ", align: " << DA
.getAlignment()
277 << ", section: " << Sec
.getGenericSection().getName() << "\n";
280 auto &SecAtoms
= SecToAtoms
[&Sec
];
281 SecAtoms
[DA
.getAddress() - Sec
.getAddress()] = &DA
;
284 // Add anonymous atoms.
285 for (auto &KV
: Sections
) {
288 // Skip empty sections.
292 // Skip sections with custom handling.
293 if (CustomAtomizeFunctions
.count(S
.getName()))
296 auto SAI
= SecToAtoms
.find(&S
);
298 // If S is not in the SecToAtoms map then it contained no named atom. Add
299 // one anonymous atom to cover the whole section.
300 if (SAI
== SecToAtoms
.end()) {
301 SecToAtoms
[&S
][0] = &G
->addAnonymousAtom(
302 S
.getGenericSection(), S
.getAddress(), S
.getAlignment());
306 // Otherwise, check whether this section had an atom covering offset zero.
308 auto &SecAtoms
= SAI
->second
;
309 if (!SecAtoms
.count(0))
310 SecAtoms
[0] = &G
->addAnonymousAtom(S
.getGenericSection(), S
.getAddress(),
314 LLVM_DEBUG(dbgs() << "MachOGraphBuilder setting atom content\n");
316 // Set atom contents and any section-based flags.
317 for (auto &KV
: SecToAtoms
) {
319 auto &SecAtoms
= KV
.second
;
321 // Iterate the atoms in reverse order and set up their contents.
322 JITTargetAddress LastAtomAddr
= S
.getSize();
323 for (auto I
= SecAtoms
.rbegin(), E
= SecAtoms
.rend(); I
!= E
; ++I
) {
324 auto Offset
= I
->first
;
325 auto &A
= *I
->second
;
327 dbgs() << " " << A
<< " to [ " << S
.getAddress() + Offset
<< " .. "
328 << S
.getAddress() + LastAtomAddr
<< " ]\n";
332 A
.setZeroFill(LastAtomAddr
- Offset
);
334 A
.setContent(S
.getContent().substr(Offset
, LastAtomAddr
- Offset
));
336 // If the section has no-dead-strip set then mark the atom as live.
337 if (S
.isNoDeadStrip())
340 LastAtomAddr
= Offset
;
344 LLVM_DEBUG(dbgs() << "Adding alt-entry starts\n");
346 // Sort alt-entry atoms by address in ascending order.
347 llvm::sort(AltEntryAtoms
.begin(), AltEntryAtoms
.end(),
348 [](const DefinedAtom
*LHS
, const DefinedAtom
*RHS
) {
349 return LHS
->getAddress() < RHS
->getAddress();
352 // Process alt-entry atoms in address order to build the table of alt-entry
353 // atoms to alt-entry chain starts.
354 for (auto *DA
: AltEntryAtoms
) {
355 assert(!AltEntryStarts
.count(DA
) && "Duplicate entry in AltEntryStarts");
357 // DA is an alt-entry atom. Look for the predecessor atom that it is locked
358 // to, bailing out if we do not find one.
359 auto AltEntryPred
= G
->findAtomByAddress(DA
->getAddress() - 1);
361 return AltEntryPred
.takeError();
363 // Add a LayoutNext edge from the predecessor to this atom.
364 AltEntryPred
->setLayoutNext(*DA
);
366 // Check to see whether the predecessor itself is an alt-entry atom.
367 auto AltEntryStartItr
= AltEntryStarts
.find(&*AltEntryPred
);
368 if (AltEntryStartItr
!= AltEntryStarts
.end()) {
369 // If the predecessor was an alt-entry atom then re-use its value.
371 dbgs() << " " << *DA
<< " -> " << *AltEntryStartItr
->second
372 << " (based on existing entry for " << *AltEntryPred
<< ")\n";
374 AltEntryStarts
[DA
] = AltEntryStartItr
->second
;
376 // If the predecessor does not have an entry then add an entry for this
377 // atom (i.e. the alt_entry atom) and a self-reference entry for the
378 /// predecessory atom that is the start of this chain.
380 dbgs() << " " << *AltEntryPred
<< " -> " << *AltEntryPred
<< "\n"
381 << " " << *DA
<< " -> " << *AltEntryPred
<< "\n";
383 AltEntryStarts
[&*AltEntryPred
] = &*AltEntryPred
;
384 AltEntryStarts
[DA
] = &*AltEntryPred
;
388 return Error::success();
391 Error
MachOAtomGraphBuilder::addAtoms() {
392 // Add all named atoms.
393 if (auto Err
= addNonCustomAtoms())
396 // Process special sections.
397 for (auto &KV
: Sections
) {
399 auto HI
= CustomAtomizeFunctions
.find(S
.getGenericSection().getName());
400 if (HI
!= CustomAtomizeFunctions
.end()) {
401 auto &Atomize
= HI
->second
;
402 if (auto Err
= Atomize(S
))
407 return Error::success();
410 } // end namespace jitlink
411 } // end namespace llvm