xref: /freebsd/contrib/llvm-project/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
XCOFFLinkGraphBuilder(const object::XCOFFObjectFile & Obj,std::shared_ptr<orc::SymbolStringPool> SSP,Triple TT,SubtargetFeatures Features,LinkGraph::GetEdgeKindNameFunction GetEdgeKindName)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           std::string(Obj.getFileName()), std::move(SSP), std::move(TT),
41           std::move(Features), std::move(GetEdgeKindName))) {}
42 
43 #ifndef NDEBUG
getStorageClassString(XCOFF::StorageClass SC)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 
processSections()151 Error XCOFFLinkGraphBuilder::processSections() {
152   LLVM_DEBUG(dbgs() << "  Creating graph sections...\n");
153 
154   UndefSection = &G->createSection("*UND*", 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(*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()] = {GraphSec, Section};
185   }
186 
187   return Error::success();
188 }
189 
190 static std::optional<object::XCOFFSymbolRef>
getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile & Obj,const object::SymbolRef & Sym)191 getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj,
192                                   const object::SymbolRef &Sym) {
193   const object::XCOFFSymbolRef SymRef =
194       Obj.toSymbolRef(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
printSymbolEntry(raw_ostream & OS,const object::XCOFFObjectFile & Obj,const object::XCOFFSymbolRef & Sym)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 
processCsectsAndSymbols()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(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(*SymbolName, Symbol.getSize(), 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, Symbol);
277     object::XCOFFSymbolRef CsectSymbol = ParentSym ? *ParentSym : Symbol;
278 
279     auto CsectSymbolIndex = Obj.getSymbolIndex(CsectSymbol.getEntryAddress());
280     auto ParentSectionNumber = CsectSymbol.getSectionNumber();
281 
282     bool IsUndefinedSection = !SectionTable.contains(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(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           *ParentSection, SectionBuffer.slice(Offset, CsectSymbol.getSize()),
310           orc::ExecutorAddr(*CsectSymbolAddr), CsectSymbol.getAlignment(), 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(*B, BlockOffset, *SymbolName, Symbol.getSize(), L,
341                              S, *IsCallableOrErr, true);
342   }
343 
344   return Error::success();
345 }
346 
processRelocations()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(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(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(G->blocks(),
377                         [Target = orc::ExecutorAddr(Section.getAddress() +
378                                                     Relocation.getOffset())](
379                             const Block *B) -> bool {
380                           return B->getRange().contains(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(ppc64::EdgeKind_ppc64::Pointer64, TargetBlockOffset, *S, 0);
391         break;
392       default:
393         SmallString<16> RelocType;
394         Relocation.getTypeName(RelocType);
395         return make_error<StringError>(
396             "Unsupported Relocation Type: " + RelocType, std::error_code());
397       }
398     }
399   }
400 
401   return Error::success();
402 }
403 
buildGraph()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