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