| //===- DXContainerPDB.cpp - DirectX PDB writer pass -----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DirectX.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/BinaryFormat/DXContainer.h" |
| #include "llvm/DebugInfo/CodeView/GUID.h" |
| #include "llvm/DebugInfo/MSF/MSFBuilder.h" |
| #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" |
| #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/MC/MCDXContainerWriter.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/IOSandbox.h" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| class DXContainerPDB : public ModulePass, MCDXContainerBaseWriter { |
| Module *M = nullptr; |
| SmallVector<MCDXContainerPart> Parts; |
| |
| void reset() { |
| M = nullptr; |
| Parts.clear(); |
| } |
| |
| public: |
| static char ID; |
| DXContainerPDB() : ModulePass(ID) {} |
| |
| StringRef getPassName() const override { return "DirectX PDB Emitter"; } |
| |
| bool runOnModule(Module &M) override; |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.setPreservesAll(); |
| } |
| |
| bool shouldSkipSection(StringRef SectionName, size_t SectionSize) override; |
| ArrayRef<MCDXContainerPart> collectParts() override; |
| }; |
| |
| } // namespace |
| |
| bool DXContainerPDB::shouldSkipSection(StringRef SectionName, |
| size_t SectionSize) { |
| if (MCDXContainerBaseWriter::shouldSkipSection(SectionName, SectionSize)) |
| return true; |
| |
| // Skip sections that are irrelevant for debug info. |
| static const StringSet<> DebugSections{"ILDB", "ILDN", "HASH", "PDBI", |
| "SRCI", "STAT", "RDAT", "VERS"}; |
| return !DebugSections.contains(SectionName); |
| } |
| |
| static StringRef getGlobalData(const GlobalVariable &GV) { |
| if (GV.hasInitializer()) |
| if (const auto *Data = |
| dyn_cast<ConstantDataSequential>(GV.getInitializer())) |
| return Data->getRawDataValues(); |
| return {}; |
| } |
| |
| ArrayRef<MCDXContainerPart> DXContainerPDB::collectParts() { |
| Parts.clear(); |
| for (const GlobalVariable &GV : M->globals()) { |
| StringRef Name = GV.getSection(); |
| StringRef Data = getGlobalData(GV); |
| |
| if (Data.empty()) |
| continue; |
| if (shouldSkipSection(Name, Data.size())) |
| continue; |
| |
| Parts.push_back({Name, Data}); |
| } |
| return Parts; |
| } |
| |
| bool DXContainerPDB::runOnModule(Module &M) { |
| this->M = &M; |
| |
| StringRef DebugFileName; |
| ArrayRef<char> ModuleHash; |
| for (const GlobalVariable &GV : M.globals()) { |
| if (GV.getSection() == PdbFileNameSectionName) { |
| assert(DebugFileName.empty() && "Duplicate PDBNAME section"); |
| DebugFileName = getGlobalData(GV); |
| } else if (GV.getSection() == ModuleHashSectionName) { |
| assert(ModuleHash.empty() && "Duplicate PBDHASH section"); |
| StringRef Data = getGlobalData(GV); |
| ModuleHash = ArrayRef(Data.data(), Data.size()); |
| } |
| } |
| |
| // PDB emission was not requested. |
| if (DebugFileName.empty()) |
| return false; |
| if (ModuleHash.empty()) |
| report_fatal_error("Module hash for PDB not found"); |
| |
| BumpPtrAllocator Allocator; |
| pdb::PDBFileBuilder Builder(Allocator); |
| |
| // DirectXShaderCompiler uses block size 512. |
| if (Error Err = Builder.initialize(512)) |
| reportFatalInternalError(std::move(Err)); |
| |
| // Reserved streams that should be empty. |
| static_assert(pdb::kSpecialStreamCount == 5 && |
| "First 5 streams should be empty in DirectX PDB file"); |
| for (uint32_t I = 0; I < pdb::kSpecialStreamCount; ++I) { |
| if (auto Err = Builder.getMsfBuilder().addStream(0).takeError()) |
| reportFatalInternalError(std::move(Err)); |
| } |
| |
| // Add DXContainer stream. |
| if (auto Err = Builder.getMsfBuilder().addStream(0).takeError()) |
| reportFatalInternalError(std::move(Err)); |
| |
| // InfoStream must be filled. Bitcode hash from HASH part is used for PDB |
| // GUID. |
| codeview::GUID PdbGuid; |
| assert(ModuleHash.size() == std::size(PdbGuid.Guid) && |
| "Module hash length must be match GUID length"); |
| std::copy_n(ModuleHash.begin(), std::size(PdbGuid.Guid), PdbGuid.Guid); |
| |
| auto &InfoBuilder = Builder.getInfoBuilder(); |
| InfoBuilder.setAge(1); |
| InfoBuilder.setGuid(PdbGuid); |
| InfoBuilder.setSignature(0); |
| InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); |
| |
| // Write DXContainer. |
| raw_svector_ostream OS(*Builder.getDXContainerData()); |
| write(OS, M.getTargetTriple()); |
| |
| // Write PDB file. |
| // FIXME(sandboxing): Remove this by routing PDB output through the VFS. |
| auto BypassSandbox = sys::sandbox::scopedDisable(); |
| codeview::GUID IgnoredOutGuid; |
| if (Error Err = Builder.commit(DebugFileName, &IgnoredOutGuid)) |
| reportFatalUsageError("Couldn't write to PDB file: " + |
| Twine(toString(std::move(Err)))); |
| |
| reset(); |
| |
| return false; |
| } |
| |
| char DXContainerPDB::ID = 0; |
| INITIALIZE_PASS(DXContainerPDB, "dxil-pdb", "DirectX PDB Emitter", false, true) |
| |
| ModulePass *llvm::createDXContainerPDBPass() { return new DXContainerPDB(); } |