1 //===------- DebuggerSupportPlugin.cpp - Utils for debugger support -------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // 10 //===----------------------------------------------------------------------===// 11 12 #include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h" 13 14 #include "llvm/ADT/SmallSet.h" 15 #include "llvm/ADT/SmallVector.h" 16 #include "llvm/ADT/StringSet.h" 17 #include "llvm/BinaryFormat/MachO.h" 18 19 #define DEBUG_TYPE "orc" 20 21 using namespace llvm; 22 using namespace llvm::jitlink; 23 using namespace llvm::orc; 24 25 static const char *SynthDebugSectionName = "__jitlink_synth_debug_object"; 26 27 namespace { 28 29 struct MachO64LE { 30 using UIntPtr = uint64_t; 31 32 using Header = MachO::mach_header_64; 33 using SegmentLC = MachO::segment_command_64; 34 using Section = MachO::section_64; 35 using NList = MachO::nlist_64; 36 37 static constexpr support::endianness Endianness = support::little; 38 static constexpr const uint32_t Magic = MachO::MH_MAGIC_64; 39 static constexpr const uint32_t SegmentCmd = MachO::LC_SEGMENT_64; 40 }; 41 42 class MachODebugObjectSynthesizerBase 43 : public GDBJITDebugInfoRegistrationPlugin::DebugSectionSynthesizer { 44 public: 45 static bool isDebugSection(Section &Sec) { 46 return Sec.getName().startswith("__DWARF,"); 47 } 48 49 MachODebugObjectSynthesizerBase(LinkGraph &G, ExecutorAddr RegisterActionAddr) 50 : G(G), RegisterActionAddr(RegisterActionAddr) {} 51 virtual ~MachODebugObjectSynthesizerBase() = default; 52 53 Error preserveDebugSections() { 54 if (G.findSectionByName(SynthDebugSectionName)) { 55 LLVM_DEBUG({ 56 dbgs() << "MachODebugObjectSynthesizer skipping graph " << G.getName() 57 << " which contains an unexpected existing " 58 << SynthDebugSectionName << " section.\n"; 59 }); 60 return Error::success(); 61 } 62 63 LLVM_DEBUG({ 64 dbgs() << "MachODebugObjectSynthesizer visiting graph " << G.getName() 65 << "\n"; 66 }); 67 for (auto &Sec : G.sections()) { 68 if (!isDebugSection(Sec)) 69 continue; 70 // Preserve blocks in this debug section by marking one existing symbol 71 // live for each block, and introducing a new live, anonymous symbol for 72 // each currently unreferenced block. 73 LLVM_DEBUG({ 74 dbgs() << " Preserving debug section " << Sec.getName() << "\n"; 75 }); 76 SmallSet<Block *, 8> PreservedBlocks; 77 for (auto *Sym : Sec.symbols()) { 78 bool NewPreservedBlock = 79 PreservedBlocks.insert(&Sym->getBlock()).second; 80 if (NewPreservedBlock) 81 Sym->setLive(true); 82 } 83 for (auto *B : Sec.blocks()) 84 if (!PreservedBlocks.count(B)) 85 G.addAnonymousSymbol(*B, 0, 0, false, true); 86 } 87 return Error::success(); 88 } 89 90 protected: 91 LinkGraph &G; 92 ExecutorAddr RegisterActionAddr; 93 }; 94 95 template <typename MachOTraits> 96 class MachODebugObjectSynthesizer : public MachODebugObjectSynthesizerBase { 97 private: 98 class MachOStructWriter { 99 public: 100 MachOStructWriter(MutableArrayRef<char> Buffer) : Buffer(Buffer) {} 101 102 size_t getOffset() const { return Offset; } 103 104 template <typename MachOStruct> void write(MachOStruct S) { 105 assert(Offset + sizeof(S) <= Buffer.size() && 106 "Container block overflow while constructing debug MachO"); 107 if (MachOTraits::Endianness != support::endian::system_endianness()) 108 MachO::swapStruct(S); 109 memcpy(Buffer.data() + Offset, &S, sizeof(S)); 110 Offset += sizeof(S); 111 } 112 113 private: 114 MutableArrayRef<char> Buffer; 115 size_t Offset = 0; 116 }; 117 118 public: 119 using MachODebugObjectSynthesizerBase::MachODebugObjectSynthesizerBase; 120 121 Error startSynthesis() override { 122 LLVM_DEBUG({ 123 dbgs() << "Creating " << SynthDebugSectionName << " for " << G.getName() 124 << "\n"; 125 }); 126 auto &SDOSec = G.createSection(SynthDebugSectionName, MemProt::Read); 127 128 struct DebugSectionInfo { 129 Section *Sec = nullptr; 130 StringRef SegName; 131 StringRef SecName; 132 uint64_t Alignment = 0; 133 orc::ExecutorAddr StartAddr; 134 uint64_t Size = 0; 135 }; 136 137 SmallVector<DebugSectionInfo, 12> DebugSecInfos; 138 size_t NumSections = 0; 139 for (auto &Sec : G.sections()) { 140 if (llvm::empty(Sec.blocks())) 141 continue; 142 143 ++NumSections; 144 if (isDebugSection(Sec)) { 145 size_t SepPos = Sec.getName().find(','); 146 if (SepPos > 16 || (Sec.getName().size() - (SepPos + 1) > 16)) { 147 LLVM_DEBUG({ 148 dbgs() << "Skipping debug object synthesis for graph " 149 << G.getName() 150 << ": encountered non-standard DWARF section name \"" 151 << Sec.getName() << "\"\n"; 152 }); 153 return Error::success(); 154 } 155 DebugSecInfos.push_back({&Sec, Sec.getName().substr(0, SepPos), 156 Sec.getName().substr(SepPos + 1), 0, 157 orc::ExecutorAddr(), 0}); 158 } else { 159 NonDebugSections.push_back(&Sec); 160 161 // If the first block in the section has a non-zero alignment offset 162 // then we need to add a padding block, since the section command in 163 // the header doesn't allow for aligment offsets. 164 SectionRange R(Sec); 165 if (!R.empty()) { 166 auto &FB = *R.getFirstBlock(); 167 if (FB.getAlignmentOffset() != 0) { 168 auto Padding = G.allocateBuffer(FB.getAlignmentOffset()); 169 memset(Padding.data(), 0, Padding.size()); 170 G.createContentBlock(Sec, Padding, 171 FB.getAddress() - FB.getAlignmentOffset(), 172 FB.getAlignment(), 0); 173 } 174 } 175 } 176 } 177 178 // Create container block. 179 size_t SectionsCmdSize = 180 sizeof(typename MachOTraits::Section) * NumSections; 181 size_t SegmentLCSize = 182 sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize; 183 size_t ContainerBlockSize = 184 sizeof(typename MachOTraits::Header) + SegmentLCSize; 185 auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize); 186 MachOContainerBlock = &G.createMutableContentBlock( 187 SDOSec, ContainerBlockContent, orc::ExecutorAddr(), 8, 0); 188 189 // Copy debug section blocks and symbols. 190 orc::ExecutorAddr NextBlockAddr(MachOContainerBlock->getSize()); 191 for (auto &SI : DebugSecInfos) { 192 assert(!llvm::empty(SI.Sec->blocks()) && "Empty debug info section?"); 193 194 // Update addresses in debug section. 195 LLVM_DEBUG({ 196 dbgs() << " Appending " << SI.Sec->getName() << " (" 197 << SI.Sec->blocks_size() << " block(s)) at " 198 << formatv("{0:x8}", NextBlockAddr) << "\n"; 199 }); 200 for (auto *B : SI.Sec->blocks()) { 201 NextBlockAddr = alignToBlock(NextBlockAddr, *B); 202 B->setAddress(NextBlockAddr); 203 NextBlockAddr += B->getSize(); 204 } 205 206 auto &FirstBlock = **SI.Sec->blocks().begin(); 207 if (FirstBlock.getAlignmentOffset() != 0) 208 return make_error<StringError>( 209 "First block in " + SI.Sec->getName() + 210 " section has non-zero alignment offset", 211 inconvertibleErrorCode()); 212 if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max()) 213 return make_error<StringError>("First block in " + SI.Sec->getName() + 214 " has alignment >4Gb", 215 inconvertibleErrorCode()); 216 217 SI.Alignment = FirstBlock.getAlignment(); 218 SI.StartAddr = FirstBlock.getAddress(); 219 SI.Size = NextBlockAddr - SI.StartAddr; 220 G.mergeSections(SDOSec, *SI.Sec); 221 SI.Sec = nullptr; 222 } 223 size_t DebugSectionsSize = 224 NextBlockAddr - orc::ExecutorAddr(MachOContainerBlock->getSize()); 225 226 // Write MachO header and debug section load commands. 227 MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent()); 228 typename MachOTraits::Header Hdr; 229 memset(&Hdr, 0, sizeof(Hdr)); 230 Hdr.magic = MachOTraits::Magic; 231 switch (G.getTargetTriple().getArch()) { 232 case Triple::x86_64: 233 Hdr.cputype = MachO::CPU_TYPE_X86_64; 234 Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL; 235 break; 236 case Triple::aarch64: 237 Hdr.cputype = MachO::CPU_TYPE_ARM64; 238 Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL; 239 break; 240 default: 241 llvm_unreachable("Unsupported architecture"); 242 } 243 Hdr.filetype = MachO::MH_OBJECT; 244 Hdr.ncmds = 1; 245 Hdr.sizeofcmds = SegmentLCSize; 246 Hdr.flags = 0; 247 Writer.write(Hdr); 248 249 typename MachOTraits::SegmentLC SegLC; 250 memset(&SegLC, 0, sizeof(SegLC)); 251 SegLC.cmd = MachOTraits::SegmentCmd; 252 SegLC.cmdsize = SegmentLCSize; 253 SegLC.vmaddr = ContainerBlockSize; 254 SegLC.vmsize = DebugSectionsSize; 255 SegLC.fileoff = ContainerBlockSize; 256 SegLC.filesize = DebugSectionsSize; 257 SegLC.maxprot = 258 MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE; 259 SegLC.initprot = 260 MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE; 261 SegLC.nsects = NumSections; 262 SegLC.flags = 0; 263 Writer.write(SegLC); 264 265 StringSet<> ExistingLongNames; 266 for (auto &SI : DebugSecInfos) { 267 typename MachOTraits::Section Sec; 268 memset(&Sec, 0, sizeof(Sec)); 269 memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size()); 270 memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size()); 271 Sec.addr = SI.StartAddr.getValue(); 272 Sec.size = SI.Size; 273 Sec.offset = SI.StartAddr.getValue(); 274 Sec.align = SI.Alignment; 275 Sec.reloff = 0; 276 Sec.nreloc = 0; 277 Sec.flags = MachO::S_ATTR_DEBUG; 278 Writer.write(Sec); 279 } 280 281 // Set MachOContainerBlock to indicate success to 282 // completeSynthesisAndRegister. 283 NonDebugSectionsStart = Writer.getOffset(); 284 return Error::success(); 285 } 286 287 Error completeSynthesisAndRegister() override { 288 if (!MachOContainerBlock) { 289 LLVM_DEBUG({ 290 dbgs() << "Not writing MachO debug object header for " << G.getName() 291 << " since createDebugSection failed\n"; 292 }); 293 return Error::success(); 294 } 295 296 LLVM_DEBUG({ 297 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n"; 298 }); 299 300 MachOStructWriter Writer( 301 MachOContainerBlock->getAlreadyMutableContent().drop_front( 302 NonDebugSectionsStart)); 303 304 unsigned LongSectionNameIdx = 0; 305 for (auto *Sec : NonDebugSections) { 306 size_t SepPos = Sec->getName().find(','); 307 StringRef SegName, SecName; 308 std::string CustomSecName; 309 310 if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) { 311 // No embedded segment name, short section name. 312 SegName = "__JITLINK_CUSTOM"; 313 SecName = Sec->getName(); 314 } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) { 315 // Canonical embedded segment and section name. 316 SegName = Sec->getName().substr(0, SepPos); 317 SecName = Sec->getName().substr(SepPos + 1); 318 } else { 319 // Long section name that needs to be truncated. 320 assert(Sec->getName().size() > 16 && 321 "Short section name should have been handled above"); 322 SegName = "__JITLINK_CUSTOM"; 323 auto IdxStr = std::to_string(++LongSectionNameIdx); 324 CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str(); 325 CustomSecName += "."; 326 CustomSecName += IdxStr; 327 SecName = StringRef(CustomSecName.data(), 16); 328 } 329 330 SectionRange R(*Sec); 331 if (R.getFirstBlock()->getAlignmentOffset() != 0) 332 return make_error<StringError>( 333 "While building MachO debug object for " + G.getName() + 334 " first block has non-zero alignment offset", 335 inconvertibleErrorCode()); 336 337 typename MachOTraits::Section SecCmd; 338 memset(&SecCmd, 0, sizeof(SecCmd)); 339 memcpy(SecCmd.sectname, SecName.data(), SecName.size()); 340 memcpy(SecCmd.segname, SegName.data(), SegName.size()); 341 SecCmd.addr = R.getStart().getValue(); 342 SecCmd.size = R.getSize(); 343 SecCmd.offset = 0; 344 SecCmd.align = R.getFirstBlock()->getAlignment(); 345 SecCmd.reloff = 0; 346 SecCmd.nreloc = 0; 347 SecCmd.flags = 0; 348 Writer.write(SecCmd); 349 } 350 351 SectionRange R(MachOContainerBlock->getSection()); 352 G.allocActions().push_back({cantFail(shared::WrapperFunctionCall::Create< 353 SPSArgList<SPSExecutorAddrRange>>( 354 RegisterActionAddr, R.getRange())), 355 {}}); 356 return Error::success(); 357 } 358 359 private: 360 Block *MachOContainerBlock = nullptr; 361 SmallVector<Section *, 16> NonDebugSections; 362 size_t NonDebugSectionsStart = 0; 363 }; 364 365 } // end anonymous namespace 366 367 namespace llvm { 368 namespace orc { 369 370 Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>> 371 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES, 372 JITDylib &ProcessJD, 373 const Triple &TT) { 374 auto RegisterActionAddr = 375 TT.isOSBinFormatMachO() 376 ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction") 377 : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction"); 378 379 if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr)) 380 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>( 381 ExecutorAddr(Addr->getAddress())); 382 else 383 return Addr.takeError(); 384 } 385 386 Error GDBJITDebugInfoRegistrationPlugin::notifyFailed( 387 MaterializationResponsibility &MR) { 388 return Error::success(); 389 } 390 391 Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources( 392 ResourceKey K) { 393 return Error::success(); 394 } 395 396 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources( 397 ResourceKey DstKey, ResourceKey SrcKey) {} 398 399 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig( 400 MaterializationResponsibility &MR, LinkGraph &LG, 401 PassConfiguration &PassConfig) { 402 403 if (LG.getTargetTriple().getObjectFormat() == Triple::MachO) 404 modifyPassConfigForMachO(MR, LG, PassConfig); 405 else { 406 LLVM_DEBUG({ 407 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph " 408 << LG.getName() << "(triple = " << LG.getTargetTriple().str() 409 << "\n"; 410 }); 411 } 412 } 413 414 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO( 415 MaterializationResponsibility &MR, jitlink::LinkGraph &LG, 416 jitlink::PassConfiguration &PassConfig) { 417 418 switch (LG.getTargetTriple().getArch()) { 419 case Triple::x86_64: 420 case Triple::aarch64: 421 // Supported, continue. 422 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size"); 423 assert(LG.getEndianness() == support::little && 424 "Graph has incorrect endianness"); 425 break; 426 default: 427 // Unsupported. 428 LLVM_DEBUG({ 429 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported " 430 << "MachO graph " << LG.getName() 431 << "(triple = " << LG.getTargetTriple().str() 432 << ", pointer size = " << LG.getPointerSize() << ", endianness = " 433 << (LG.getEndianness() == support::big ? "big" : "little") 434 << ")\n"; 435 }); 436 return; 437 } 438 439 // Scan for debug sections. If we find one then install passes. 440 bool HasDebugSections = false; 441 for (auto &Sec : LG.sections()) 442 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) { 443 HasDebugSections = true; 444 break; 445 } 446 447 if (HasDebugSections) { 448 LLVM_DEBUG({ 449 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() 450 << " contains debug info. Installing debugger support passes.\n"; 451 }); 452 453 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>( 454 LG, RegisterActionAddr); 455 PassConfig.PrePrunePasses.push_back( 456 [=](LinkGraph &G) { return MDOS->preserveDebugSections(); }); 457 PassConfig.PostPrunePasses.push_back( 458 [=](LinkGraph &G) { return MDOS->startSynthesis(); }); 459 PassConfig.PreFixupPasses.push_back( 460 [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); }); 461 } else { 462 LLVM_DEBUG({ 463 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() 464 << " contains no debug info. Skipping.\n"; 465 }); 466 } 467 } 468 469 } // namespace orc 470 } // namespace llvm 471