1 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
2 | // See https://llvm.org/LICENSE.txt for license information. |
3 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
4 | // |
5 | //===----------------------------------------------------------------------===// |
6 | // |
7 | // Generic XCOFF LinkGraph building code. |
8 | // |
9 | //===----------------------------------------------------------------------===// |
10 | |
11 | #include "XCOFFLinkGraphBuilder.h" |
12 | #include "llvm/ADT/STLExtras.h" |
13 | #include "llvm/BinaryFormat/XCOFF.h" |
14 | #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
15 | #include "llvm/ExecutionEngine/JITLink/ppc64.h" |
16 | #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" |
17 | #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h" |
18 | #include "llvm/Object/ObjectFile.h" |
19 | #include "llvm/Object/XCOFFObjectFile.h" |
20 | #include "llvm/Support/Debug.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include "llvm/Support/Format.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | #include <memory> |
25 | |
26 | using namespace llvm; |
27 | |
28 | #define DEBUG_TYPE "jitlink" |
29 | |
30 | namespace llvm { |
31 | namespace jitlink { |
32 | |
33 | XCOFFLinkGraphBuilder::XCOFFLinkGraphBuilder( |
34 | const object::XCOFFObjectFile &Obj, |
35 | std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT, |
36 | SubtargetFeatures Features, |
37 | LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) |
38 | : Obj(Obj), |
39 | G(std::make_unique<LinkGraph>( |
40 | args: std::string(Obj.getFileName()), args: std::move(SSP), args: std::move(TT), |
41 | args: std::move(Features), args: std::move(GetEdgeKindName))) {} |
42 | |
43 | #ifndef NDEBUG |
44 | static llvm::StringRef getStorageClassString(XCOFF::StorageClass SC) { |
45 | switch (SC) { |
46 | case XCOFF::StorageClass::C_FILE: |
47 | return "C_FILE (File name)" ; |
48 | case XCOFF::StorageClass::C_BINCL: |
49 | return "C_BINCL (Beginning of include file)" ; |
50 | case XCOFF::StorageClass::C_EINCL: |
51 | return "C_EINCL (Ending of include file)" ; |
52 | case XCOFF::StorageClass::C_GSYM: |
53 | return "C_GSYM (Global variable)" ; |
54 | case XCOFF::StorageClass::C_STSYM: |
55 | return "C_STSYM (Statically allocated symbol)" ; |
56 | case XCOFF::StorageClass::C_BCOMM: |
57 | return "C_BCOMM (Beginning of common block)" ; |
58 | case XCOFF::StorageClass::C_ECOMM: |
59 | return "C_ECOMM (End of common block)" ; |
60 | case XCOFF::StorageClass::C_ENTRY: |
61 | return "C_ENTRY (Alternate entry)" ; |
62 | case XCOFF::StorageClass::C_BSTAT: |
63 | return "C_BSTAT (Beginning of static block)" ; |
64 | case XCOFF::StorageClass::C_ESTAT: |
65 | return "C_ESTAT (End of static block)" ; |
66 | case XCOFF::StorageClass::C_GTLS: |
67 | return "C_GTLS (Global thread-local variable)" ; |
68 | case XCOFF::StorageClass::C_STTLS: |
69 | return "C_STTLS (Static thread-local variable)" ; |
70 | case XCOFF::StorageClass::C_DWARF: |
71 | return "C_DWARF (DWARF section symbol)" ; |
72 | case XCOFF::StorageClass::C_LSYM: |
73 | return "C_LSYM (Automatic variable allocated on stack)" ; |
74 | case XCOFF::StorageClass::C_PSYM: |
75 | return "C_PSYM (Argument to subroutine allocated on stack)" ; |
76 | case XCOFF::StorageClass::C_RSYM: |
77 | return "C_RSYM (Register variable)" ; |
78 | case XCOFF::StorageClass::C_RPSYM: |
79 | return "C_RPSYM (Argument to function stored in register)" ; |
80 | case XCOFF::StorageClass::C_ECOML: |
81 | return "C_ECOML (Local member of common block)" ; |
82 | case XCOFF::StorageClass::C_FUN: |
83 | return "C_FUN (Function or procedure)" ; |
84 | case XCOFF::StorageClass::C_EXT: |
85 | return "C_EXT (External symbol)" ; |
86 | case XCOFF::StorageClass::C_WEAKEXT: |
87 | return "C_WEAKEXT (Weak external symbol)" ; |
88 | case XCOFF::StorageClass::C_NULL: |
89 | return "C_NULL" ; |
90 | case XCOFF::StorageClass::C_STAT: |
91 | return "C_STAT (Static)" ; |
92 | case XCOFF::StorageClass::C_BLOCK: |
93 | return "C_BLOCK (\".bb\" or \".eb\")" ; |
94 | case XCOFF::StorageClass::C_FCN: |
95 | return "C_FCN (\".bf\" or \".ef\")" ; |
96 | case XCOFF::StorageClass::C_HIDEXT: |
97 | return "C_HIDEXT (Un-named external symbol)" ; |
98 | case XCOFF::StorageClass::C_INFO: |
99 | return "C_INFO (Comment string in .info section)" ; |
100 | case XCOFF::StorageClass::C_DECL: |
101 | return "C_DECL (Declaration of object)" ; |
102 | case XCOFF::StorageClass::C_AUTO: |
103 | return "C_AUTO (Automatic variable)" ; |
104 | case XCOFF::StorageClass::C_REG: |
105 | return "C_REG (Register variable)" ; |
106 | case XCOFF::StorageClass::C_EXTDEF: |
107 | return "C_EXTDEF (External definition)" ; |
108 | case XCOFF::StorageClass::C_LABEL: |
109 | return "C_LABEL (Label)" ; |
110 | case XCOFF::StorageClass::C_ULABEL: |
111 | return "C_ULABEL (Undefined label)" ; |
112 | case XCOFF::StorageClass::C_MOS: |
113 | return "C_MOS (Member of structure)" ; |
114 | case XCOFF::StorageClass::C_ARG: |
115 | return "C_ARG (Function argument)" ; |
116 | case XCOFF::StorageClass::C_STRTAG: |
117 | return "C_STRTAG (Structure tag)" ; |
118 | case XCOFF::StorageClass::C_MOU: |
119 | return "C_MOU (Member of union)" ; |
120 | case XCOFF::StorageClass::C_UNTAG: |
121 | return "C_UNTAG (Union tag)" ; |
122 | case XCOFF::StorageClass::C_TPDEF: |
123 | return "C_TPDEF (Type definition)" ; |
124 | case XCOFF::StorageClass::C_USTATIC: |
125 | return "C_USTATIC (Undefined static)" ; |
126 | case XCOFF::StorageClass::C_ENTAG: |
127 | return "C_ENTAG (Enumeration tag)" ; |
128 | case XCOFF::StorageClass::C_MOE: |
129 | return "C_MOE (Member of enumeration)" ; |
130 | case XCOFF::StorageClass::C_REGPARM: |
131 | return "C_REGPARM (Register parameter)" ; |
132 | case XCOFF::StorageClass::C_FIELD: |
133 | return "C_FIELD (Bit field)" ; |
134 | case XCOFF::StorageClass::C_EOS: |
135 | return "C_EOS (End of structure)" ; |
136 | case XCOFF::StorageClass::C_LINE: |
137 | return "C_LINE" ; |
138 | case XCOFF::StorageClass::C_ALIAS: |
139 | return "C_ALIAS (Duplicate tag)" ; |
140 | case XCOFF::StorageClass::C_HIDDEN: |
141 | return "C_HIDDEN (Special storage class for external)" ; |
142 | case XCOFF::StorageClass::C_EFCN: |
143 | return "C_EFCN (Physical end of function)" ; |
144 | case XCOFF::StorageClass::C_TCSYM: |
145 | return "C_TCSYM (Reserved)" ; |
146 | } |
147 | llvm_unreachable("Unknown XCOFF::StorageClass enum" ); |
148 | } |
149 | #endif |
150 | |
151 | Error XCOFFLinkGraphBuilder::processSections() { |
152 | LLVM_DEBUG(dbgs() << " Creating graph sections...\n" ); |
153 | |
154 | UndefSection = &G->createSection(Name: "*UND*" , Prot: orc::MemProt::None); |
155 | |
156 | for (object::SectionRef Section : Obj.sections()) { |
157 | auto SectionName = Section.getName(); |
158 | if (!SectionName) |
159 | return SectionName.takeError(); |
160 | |
161 | LLVM_DEBUG({ |
162 | dbgs() << " section = " << *SectionName |
163 | << ", idx = " << Section.getIndex() |
164 | << ", size = " << format_hex_no_prefix(Section.getSize(), 8) |
165 | << ", vma = " << format_hex(Section.getAddress(), 16) << "\n" ; |
166 | }); |
167 | |
168 | // We can skip debug (including dawrf) and pad sections |
169 | if (Section.isDebugSection() || *SectionName == "pad" ) |
170 | continue; |
171 | LLVM_DEBUG(dbgs() << " creating graph section\n" ); |
172 | |
173 | orc::MemProt Prot = orc::MemProt::Read; |
174 | if (Section.isText()) |
175 | Prot |= orc::MemProt::Exec; |
176 | if (Section.isData() || Section.isBSS()) |
177 | Prot |= orc::MemProt::Write; |
178 | |
179 | jitlink::Section *GraphSec = &G->createSection(Name: *SectionName, Prot); |
180 | // TODO: Check for no_alloc for certain sections |
181 | |
182 | assert(!SectionTable.contains(Section.getIndex()) && |
183 | "Section with same index already exists" ); |
184 | SectionTable[Section.getIndex()] = {.Section: GraphSec, .SectionData: Section}; |
185 | } |
186 | |
187 | return Error::success(); |
188 | } |
189 | |
190 | static std::optional<object::XCOFFSymbolRef> |
191 | getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj, |
192 | const object::SymbolRef &Sym) { |
193 | const object::XCOFFSymbolRef SymRef = |
194 | Obj.toSymbolRef(Ref: Sym.getRawDataRefImpl()); |
195 | if (!SymRef.isCsectSymbol()) |
196 | return std::nullopt; |
197 | |
198 | Expected<object::XCOFFCsectAuxRef> CsectAuxEntOrErr = |
199 | SymRef.getXCOFFCsectAuxRef(); |
200 | if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) |
201 | return std::nullopt; |
202 | uint32_t Idx = |
203 | static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength()); |
204 | object::DataRefImpl DRI; |
205 | DRI.p = Obj.getSymbolByIndex(Idx); |
206 | return object::XCOFFSymbolRef(DRI, &Obj); |
207 | } |
208 | |
209 | #ifndef NDEBUG |
210 | static void printSymbolEntry(raw_ostream &OS, |
211 | const object::XCOFFObjectFile &Obj, |
212 | const object::XCOFFSymbolRef &Sym) { |
213 | OS << " " << format_hex(cantFail(Sym.getAddress()), 16); |
214 | OS << " " << left_justify(cantFail(Sym.getName()), 10); |
215 | if (Sym.isCsectSymbol()) { |
216 | auto CsectAuxEntry = cantFail(Sym.getXCOFFCsectAuxRef()); |
217 | if (!CsectAuxEntry.isLabel()) { |
218 | std::string MCStr = |
219 | "[" + |
220 | XCOFF::getMappingClassString(CsectAuxEntry.getStorageMappingClass()) |
221 | .str() + |
222 | "]" ; |
223 | OS << left_justify(MCStr, 3); |
224 | } |
225 | } |
226 | OS << " " << format_hex(Sym.getSize(), 8); |
227 | OS << " " << Sym.getSectionNumber(); |
228 | OS << " " << getStorageClassString(Sym.getStorageClass()); |
229 | OS << " (idx: " << Obj.getSymbolIndex(Sym.getRawDataRefImpl().p) << ")" ; |
230 | if (Sym.isCsectSymbol()) { |
231 | if (auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Sym)) { |
232 | OS << " (csect idx: " |
233 | << Obj.getSymbolIndex(ParentSym->getRawDataRefImpl().p) << ")" ; |
234 | } |
235 | } |
236 | OS << "\n" ; |
237 | } |
238 | #endif |
239 | |
240 | Error XCOFFLinkGraphBuilder::processCsectsAndSymbols() { |
241 | LLVM_DEBUG(dbgs() << " Creating graph blocks and symbols...\n" ); |
242 | |
243 | for ([[maybe_unused]] auto [K, V] : SectionTable) { |
244 | LLVM_DEBUG(dbgs() << " section entry(idx: " << K |
245 | << " section: " << V.Section->getName() << ")\n" ); |
246 | } |
247 | |
248 | for (object::XCOFFSymbolRef Symbol : Obj.symbols()) { |
249 | LLVM_DEBUG({ printSymbolEntry(dbgs(), Obj, Symbol); }); |
250 | |
251 | auto Flags = Symbol.getFlags(); |
252 | if (!Flags) |
253 | return Flags.takeError(); |
254 | |
255 | bool External = *Flags & object::SymbolRef::SF_Undefined; |
256 | bool Weak = *Flags & object::SymbolRef::SF_Weak; |
257 | bool Global = *Flags & object::SymbolRef::SF_Global; |
258 | |
259 | auto SymbolIndex = Obj.getSymbolIndex(SymEntPtr: Symbol.getEntryAddress()); |
260 | auto SymbolName = Symbol.getName(); |
261 | if (!SymbolName) |
262 | return SymbolName.takeError(); |
263 | |
264 | if (External) { |
265 | LLVM_DEBUG(dbgs() << " created external symbol\n" ); |
266 | SymbolIndexTable[SymbolIndex] = |
267 | &G->addExternalSymbol(Name: *SymbolName, Size: Symbol.getSize(), IsWeaklyReferenced: Weak); |
268 | continue; |
269 | } |
270 | |
271 | if (!Symbol.isCsectSymbol()) { |
272 | LLVM_DEBUG(dbgs() << " skipped: not a csect symbol\n" ); |
273 | continue; |
274 | } |
275 | |
276 | auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Sym: Symbol); |
277 | object::XCOFFSymbolRef CsectSymbol = ParentSym ? *ParentSym : Symbol; |
278 | |
279 | auto CsectSymbolIndex = Obj.getSymbolIndex(SymEntPtr: CsectSymbol.getEntryAddress()); |
280 | auto ParentSectionNumber = CsectSymbol.getSectionNumber(); |
281 | |
282 | bool IsUndefinedSection = !SectionTable.contains(Val: ParentSectionNumber); |
283 | Section *ParentSection = !IsUndefinedSection |
284 | ? SectionTable[ParentSectionNumber].Section |
285 | : UndefSection; |
286 | Block *B = nullptr; |
287 | |
288 | // TODO: Clean up the logic for handling undefined symbols |
289 | if (!CsectTable.contains(Val: CsectSymbolIndex) && !IsUndefinedSection) { |
290 | object::SectionRef &SectionRef = |
291 | SectionTable[ParentSectionNumber].SectionData; |
292 | auto Data = SectionRef.getContents(); |
293 | if (!Data) |
294 | return Data.takeError(); |
295 | auto CsectSymbolAddr = CsectSymbol.getAddress(); |
296 | if (!CsectSymbolAddr) |
297 | return CsectSymbolAddr.takeError(); |
298 | |
299 | ArrayRef<char> SectionBuffer{*Data}; |
300 | auto Offset = *CsectSymbolAddr - SectionRef.getAddress(); |
301 | |
302 | LLVM_DEBUG(dbgs() << " symbol entry: offset = " << Offset |
303 | << ", size = " << CsectSymbol.getSize() |
304 | << ", storage class = " |
305 | << getStorageClassString(CsectSymbol.getStorageClass()) |
306 | << "\n" ); |
307 | |
308 | B = &G->createContentBlock( |
309 | Parent&: *ParentSection, Content: SectionBuffer.slice(N: Offset, M: CsectSymbol.getSize()), |
310 | Address: orc::ExecutorAddr(*CsectSymbolAddr), Alignment: CsectSymbol.getAlignment(), AlignmentOffset: 0); |
311 | |
312 | CsectTable[CsectSymbolIndex] = B; |
313 | } else { |
314 | B = CsectTable[CsectSymbolIndex]; |
315 | } |
316 | |
317 | Scope S{Scope::Local}; |
318 | if (Symbol.getSymbolType() & XCOFF::SYM_V_HIDDEN || |
319 | Symbol.getSymbolType() & XCOFF::SYM_V_INTERNAL) |
320 | S = Scope::Hidden; |
321 | else if (Global) |
322 | S = Scope::Default; |
323 | // TODO: map all symbols for c++ static initialization to SideEffectOnly |
324 | |
325 | Linkage L = Weak ? Linkage::Weak : Linkage::Strong; |
326 | auto SymbolAddr = Symbol.getAddress(); |
327 | if (!SymbolAddr) |
328 | return SymbolAddr.takeError(); |
329 | auto IsCallableOrErr = Symbol.isFunction(); |
330 | if (!IsCallableOrErr) |
331 | return IsCallableOrErr.takeError(); |
332 | |
333 | auto BlockOffset = *SymbolAddr - B->getAddress().getValue(); |
334 | |
335 | LLVM_DEBUG(dbgs() << " creating with linkage = " << getLinkageName(L) |
336 | << ", scope = " << getScopeName(S) << ", B = " |
337 | << format_hex(B->getAddress().getValue(), 16) << "\n" ); |
338 | |
339 | SymbolIndexTable[SymbolIndex] = |
340 | &G->addDefinedSymbol(Content&: *B, Offset: BlockOffset, Name: *SymbolName, Size: Symbol.getSize(), L, |
341 | S, IsCallable: *IsCallableOrErr, IsLive: true); |
342 | } |
343 | |
344 | return Error::success(); |
345 | } |
346 | |
347 | Error XCOFFLinkGraphBuilder::processRelocations() { |
348 | LLVM_DEBUG(dbgs() << " Creating relocations...\n" ); |
349 | |
350 | for (object::SectionRef Section : Obj.sections()) { |
351 | auto SectionName = Section.getName(); |
352 | if (!SectionName) |
353 | return SectionName.takeError(); |
354 | |
355 | LLVM_DEBUG(dbgs() << " Relocations for section " << *SectionName |
356 | << ":\n" ); |
357 | |
358 | for (object::RelocationRef Relocation : Section.relocations()) { |
359 | SmallString<16> RelocName; |
360 | Relocation.getTypeName(Result&: RelocName); |
361 | object::SymbolRef Symbol = *Relocation.getSymbol(); |
362 | |
363 | auto TargetSymbol = Symbol.getName(); |
364 | if (!TargetSymbol) |
365 | return TargetSymbol.takeError(); |
366 | |
367 | auto SymbolIndex = Obj.getSymbolIndex(SymEntPtr: Symbol.getRawDataRefImpl().p); |
368 | |
369 | LLVM_DEBUG(dbgs() << " " << format_hex(Relocation.getOffset(), 16) |
370 | << " (idx: " << SymbolIndex << ")" |
371 | << " " << RelocName << " " << *TargetSymbol << "\n" ;); |
372 | |
373 | assert(SymbolIndexTable.contains(SymbolIndex) && |
374 | "Relocation needs a record in the symbol table" ); |
375 | auto *S = SymbolIndexTable[SymbolIndex]; |
376 | auto It = find_if(Range: G->blocks(), |
377 | P: [Target = orc::ExecutorAddr(Section.getAddress() + |
378 | Relocation.getOffset())]( |
379 | const Block *B) -> bool { |
380 | return B->getRange().contains(Addr: Target); |
381 | }); |
382 | assert(It != G->blocks().end() && |
383 | "Cannot find the target relocation block" ); |
384 | Block *B = *It; |
385 | |
386 | auto TargetBlockOffset = Section.getAddress() + Relocation.getOffset() - |
387 | B->getAddress().getValue(); |
388 | switch (Relocation.getType()) { |
389 | case XCOFF::R_POS: |
390 | B->addEdge(K: ppc64::EdgeKind_ppc64::Pointer64, Offset: TargetBlockOffset, Target&: *S, Addend: 0); |
391 | break; |
392 | default: |
393 | SmallString<16> RelocType; |
394 | Relocation.getTypeName(Result&: RelocType); |
395 | return make_error<StringError>( |
396 | Args: "Unsupported Relocation Type: " + RelocType, Args: std::error_code()); |
397 | } |
398 | } |
399 | } |
400 | |
401 | return Error::success(); |
402 | } |
403 | |
404 | Expected<std::unique_ptr<LinkGraph>> XCOFFLinkGraphBuilder::buildGraph() { |
405 | LLVM_DEBUG(dbgs() << "Building XCOFFLinkGraph...\n" ); |
406 | |
407 | // FIXME: Check to make sure the object is relocatable |
408 | |
409 | if (auto Err = processSections()) |
410 | return Err; |
411 | if (auto Err = processCsectsAndSymbols()) |
412 | return Err; |
413 | if (auto Err = processRelocations()) |
414 | return Err; |
415 | |
416 | return std::move(G); |
417 | } |
418 | |
419 | } // namespace jitlink |
420 | } // namespace llvm |
421 | |