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() {} 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 JITTargetAddress Alignment = 0; 133 JITTargetAddress StartAddr = 0; 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, 0}); 157 } else { 158 NonDebugSections.push_back(&Sec); 159 160 // If the first block in the section has a non-zero alignment offset 161 // then we need to add a padding block, since the section command in 162 // the header doesn't allow for aligment offsets. 163 SectionRange R(Sec); 164 if (!R.empty()) { 165 auto &FB = *R.getFirstBlock(); 166 if (FB.getAlignmentOffset() != 0) { 167 auto Padding = G.allocateBuffer(FB.getAlignmentOffset()); 168 memset(Padding.data(), 0, Padding.size()); 169 G.createContentBlock(Sec, Padding, 170 FB.getAddress() - FB.getAlignmentOffset(), 171 FB.getAlignment(), 0); 172 } 173 } 174 } 175 } 176 177 // Create container block. 178 size_t SectionsCmdSize = 179 sizeof(typename MachOTraits::Section) * NumSections; 180 size_t SegmentLCSize = 181 sizeof(typename MachOTraits::SegmentLC) + SectionsCmdSize; 182 size_t ContainerBlockSize = 183 sizeof(typename MachOTraits::Header) + SegmentLCSize; 184 auto ContainerBlockContent = G.allocateBuffer(ContainerBlockSize); 185 MachOContainerBlock = 186 &G.createMutableContentBlock(SDOSec, ContainerBlockContent, 0, 8, 0); 187 188 // Copy debug section blocks and symbols. 189 JITTargetAddress NextBlockAddr = MachOContainerBlock->getSize(); 190 for (auto &SI : DebugSecInfos) { 191 assert(!llvm::empty(SI.Sec->blocks()) && "Empty debug info section?"); 192 193 // Update addresses in debug section. 194 LLVM_DEBUG({ 195 dbgs() << " Appending " << SI.Sec->getName() << " (" 196 << SI.Sec->blocks_size() << " block(s)) at " 197 << formatv("{0:x8}", NextBlockAddr) << "\n"; 198 }); 199 for (auto *B : SI.Sec->blocks()) { 200 NextBlockAddr = alignToBlock(NextBlockAddr, *B); 201 B->setAddress(NextBlockAddr); 202 NextBlockAddr += B->getSize(); 203 } 204 205 auto &FirstBlock = **SI.Sec->blocks().begin(); 206 if (FirstBlock.getAlignmentOffset() != 0) 207 return make_error<StringError>( 208 "First block in " + SI.Sec->getName() + 209 " section has non-zero alignment offset", 210 inconvertibleErrorCode()); 211 if (FirstBlock.getAlignment() > std::numeric_limits<uint32_t>::max()) 212 return make_error<StringError>("First block in " + SI.Sec->getName() + 213 " has alignment >4Gb", 214 inconvertibleErrorCode()); 215 216 SI.Alignment = FirstBlock.getAlignment(); 217 SI.StartAddr = FirstBlock.getAddress(); 218 SI.Size = NextBlockAddr - SI.StartAddr; 219 G.mergeSections(SDOSec, *SI.Sec); 220 SI.Sec = nullptr; 221 } 222 size_t DebugSectionsSize = NextBlockAddr - MachOContainerBlock->getSize(); 223 224 // Write MachO header and debug section load commands. 225 MachOStructWriter Writer(MachOContainerBlock->getAlreadyMutableContent()); 226 typename MachOTraits::Header Hdr; 227 memset(&Hdr, 0, sizeof(Hdr)); 228 Hdr.magic = MachOTraits::Magic; 229 switch (G.getTargetTriple().getArch()) { 230 case Triple::x86_64: 231 Hdr.cputype = MachO::CPU_TYPE_X86_64; 232 Hdr.cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL; 233 break; 234 case Triple::aarch64: 235 Hdr.cputype = MachO::CPU_TYPE_ARM64; 236 Hdr.cpusubtype = MachO::CPU_SUBTYPE_ARM64_ALL; 237 break; 238 default: 239 llvm_unreachable("Unsupported architecture"); 240 } 241 Hdr.filetype = MachO::MH_OBJECT; 242 Hdr.ncmds = 1; 243 Hdr.sizeofcmds = SegmentLCSize; 244 Hdr.flags = 0; 245 Writer.write(Hdr); 246 247 typename MachOTraits::SegmentLC SegLC; 248 memset(&SegLC, 0, sizeof(SegLC)); 249 SegLC.cmd = MachOTraits::SegmentCmd; 250 SegLC.cmdsize = SegmentLCSize; 251 SegLC.vmaddr = ContainerBlockSize; 252 SegLC.vmsize = DebugSectionsSize; 253 SegLC.fileoff = ContainerBlockSize; 254 SegLC.filesize = DebugSectionsSize; 255 SegLC.maxprot = 256 MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE; 257 SegLC.initprot = 258 MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE; 259 SegLC.nsects = NumSections; 260 SegLC.flags = 0; 261 Writer.write(SegLC); 262 263 StringSet<> ExistingLongNames; 264 for (auto &SI : DebugSecInfos) { 265 typename MachOTraits::Section Sec; 266 memset(&Sec, 0, sizeof(Sec)); 267 memcpy(Sec.sectname, SI.SecName.data(), SI.SecName.size()); 268 memcpy(Sec.segname, SI.SegName.data(), SI.SegName.size()); 269 Sec.addr = SI.StartAddr; 270 Sec.size = SI.Size; 271 Sec.offset = SI.StartAddr; 272 Sec.align = SI.Alignment; 273 Sec.reloff = 0; 274 Sec.nreloc = 0; 275 Sec.flags = MachO::S_ATTR_DEBUG; 276 Writer.write(Sec); 277 } 278 279 // Set MachOContainerBlock to indicate success to 280 // completeSynthesisAndRegister. 281 NonDebugSectionsStart = Writer.getOffset(); 282 return Error::success(); 283 } 284 285 Error completeSynthesisAndRegister() override { 286 if (!MachOContainerBlock) { 287 LLVM_DEBUG({ 288 dbgs() << "Not writing MachO debug object header for " << G.getName() 289 << " since createDebugSection failed\n"; 290 }); 291 return Error::success(); 292 } 293 294 LLVM_DEBUG({ 295 dbgs() << "Writing MachO debug object header for " << G.getName() << "\n"; 296 }); 297 298 MachOStructWriter Writer( 299 MachOContainerBlock->getAlreadyMutableContent().drop_front( 300 NonDebugSectionsStart)); 301 302 unsigned LongSectionNameIdx = 0; 303 for (auto *Sec : NonDebugSections) { 304 size_t SepPos = Sec->getName().find(','); 305 StringRef SegName, SecName; 306 std::string CustomSecName; 307 308 if ((SepPos == StringRef::npos && Sec->getName().size() <= 16)) { 309 // No embedded segment name, short section name. 310 SegName = "__JITLINK_CUSTOM"; 311 SecName = Sec->getName(); 312 } else if (SepPos < 16 && (Sec->getName().size() - (SepPos + 1) <= 16)) { 313 // Canonical embedded segment and section name. 314 SegName = Sec->getName().substr(0, SepPos); 315 SecName = Sec->getName().substr(SepPos + 1); 316 } else { 317 // Long section name that needs to be truncated. 318 assert(Sec->getName().size() > 16 && 319 "Short section name should have been handled above"); 320 SegName = "__JITLINK_CUSTOM"; 321 auto IdxStr = std::to_string(++LongSectionNameIdx); 322 CustomSecName = Sec->getName().substr(0, 15 - IdxStr.size()).str(); 323 CustomSecName += "."; 324 CustomSecName += IdxStr; 325 SecName = StringRef(CustomSecName.data(), 16); 326 } 327 328 SectionRange R(*Sec); 329 if (R.getFirstBlock()->getAlignmentOffset() != 0) 330 return make_error<StringError>( 331 "While building MachO debug object for " + G.getName() + 332 " first block has non-zero alignment offset", 333 inconvertibleErrorCode()); 334 335 typename MachOTraits::Section SecCmd; 336 memset(&SecCmd, 0, sizeof(SecCmd)); 337 memcpy(SecCmd.sectname, SecName.data(), SecName.size()); 338 memcpy(SecCmd.segname, SegName.data(), SegName.size()); 339 SecCmd.addr = R.getStart(); 340 SecCmd.size = R.getSize(); 341 SecCmd.offset = 0; 342 SecCmd.align = R.getFirstBlock()->getAlignment(); 343 SecCmd.reloff = 0; 344 SecCmd.nreloc = 0; 345 SecCmd.flags = 0; 346 Writer.write(SecCmd); 347 } 348 349 SectionRange R(MachOContainerBlock->getSection()); 350 G.allocActions().push_back( 351 {{RegisterActionAddr.getValue(), R.getStart(), R.getSize()}, {}}); 352 return Error::success(); 353 } 354 355 private: 356 Block *MachOContainerBlock = nullptr; 357 SmallVector<Section *, 16> NonDebugSections; 358 size_t NonDebugSectionsStart = 0; 359 }; 360 361 } // end anonymous namespace 362 363 namespace llvm { 364 namespace orc { 365 366 Expected<std::unique_ptr<GDBJITDebugInfoRegistrationPlugin>> 367 GDBJITDebugInfoRegistrationPlugin::Create(ExecutionSession &ES, 368 JITDylib &ProcessJD, 369 const Triple &TT) { 370 auto RegisterActionAddr = 371 TT.isOSBinFormatMachO() 372 ? ES.intern("_llvm_orc_registerJITLoaderGDBAllocAction") 373 : ES.intern("llvm_orc_registerJITLoaderGDBAllocAction"); 374 375 if (auto Addr = ES.lookup({&ProcessJD}, RegisterActionAddr)) 376 return std::make_unique<GDBJITDebugInfoRegistrationPlugin>( 377 ExecutorAddr(Addr->getAddress())); 378 else 379 return Addr.takeError(); 380 } 381 382 Error GDBJITDebugInfoRegistrationPlugin::notifyFailed( 383 MaterializationResponsibility &MR) { 384 return Error::success(); 385 } 386 387 Error GDBJITDebugInfoRegistrationPlugin::notifyRemovingResources( 388 ResourceKey K) { 389 return Error::success(); 390 } 391 392 void GDBJITDebugInfoRegistrationPlugin::notifyTransferringResources( 393 ResourceKey DstKey, ResourceKey SrcKey) {} 394 395 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfig( 396 MaterializationResponsibility &MR, LinkGraph &LG, 397 PassConfiguration &PassConfig) { 398 399 if (LG.getTargetTriple().getObjectFormat() == Triple::MachO) 400 modifyPassConfigForMachO(MR, LG, PassConfig); 401 else { 402 LLVM_DEBUG({ 403 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unspported graph " 404 << LG.getName() << "(triple = " << LG.getTargetTriple().str() 405 << "\n"; 406 }); 407 } 408 } 409 410 void GDBJITDebugInfoRegistrationPlugin::modifyPassConfigForMachO( 411 MaterializationResponsibility &MR, jitlink::LinkGraph &LG, 412 jitlink::PassConfiguration &PassConfig) { 413 414 switch (LG.getTargetTriple().getArch()) { 415 case Triple::x86_64: 416 case Triple::aarch64: 417 // Supported, continue. 418 assert(LG.getPointerSize() == 8 && "Graph has incorrect pointer size"); 419 assert(LG.getEndianness() == support::little && 420 "Graph has incorrect endianness"); 421 break; 422 default: 423 // Unsupported. 424 LLVM_DEBUG({ 425 dbgs() << "GDBJITDebugInfoRegistrationPlugin skipping unsupported " 426 << "MachO graph " << LG.getName() 427 << "(triple = " << LG.getTargetTriple().str() 428 << ", pointer size = " << LG.getPointerSize() << ", endianness = " 429 << (LG.getEndianness() == support::big ? "big" : "little") 430 << ")\n"; 431 }); 432 return; 433 } 434 435 // Scan for debug sections. If we find one then install passes. 436 bool HasDebugSections = false; 437 for (auto &Sec : LG.sections()) 438 if (MachODebugObjectSynthesizerBase::isDebugSection(Sec)) { 439 HasDebugSections = true; 440 break; 441 } 442 443 if (HasDebugSections) { 444 LLVM_DEBUG({ 445 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() 446 << " contains debug info. Installing debugger support passes.\n"; 447 }); 448 449 auto MDOS = std::make_shared<MachODebugObjectSynthesizer<MachO64LE>>( 450 LG, RegisterActionAddr); 451 PassConfig.PrePrunePasses.push_back( 452 [=](LinkGraph &G) { return MDOS->preserveDebugSections(); }); 453 PassConfig.PostPrunePasses.push_back( 454 [=](LinkGraph &G) { return MDOS->startSynthesis(); }); 455 PassConfig.PreFixupPasses.push_back( 456 [=](LinkGraph &G) { return MDOS->completeSynthesisAndRegister(); }); 457 } else { 458 LLVM_DEBUG({ 459 dbgs() << "GDBJITDebugInfoRegistrationPlugin: Graph " << LG.getName() 460 << " contains no debug info. Skipping.\n"; 461 }); 462 } 463 } 464 465 } // namespace orc 466 } // namespace llvm 467