| 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 | |