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
26using namespace llvm;
27
28#define DEBUG_TYPE "jitlink"
29
30namespace llvm {
31namespace jitlink {
32
33XCOFFLinkGraphBuilder::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
44static 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
151Error 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
190static std::optional<object::XCOFFSymbolRef>
191getXCOFFSymbolContainingSymbolRef(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
210static 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
240Error 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
347Error 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
404Expected<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