| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilDiaDataSource.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| // DIA API implementation for DXIL modules. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "DxilDiaDataSource.h" |
| |
| #include "dxc/DXIL/DxilPDB.h" |
| #include "dxc/DXIL/DxilUtil.h" |
| #include "dxc/DxilContainer/DxilContainer.h" |
| #include "dxc/Support/FileIOHelper.h" |
| #include "dxc/Support/dxcapi.impl.h" |
| |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/MSFileSystem.h" |
| |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| |
| #include "DxilDiaSession.h" |
| |
| dxil_dia::DataSource::DataSource(IMalloc *pMalloc) : m_pMalloc(pMalloc) {} |
| |
| dxil_dia::DataSource::~DataSource() { |
| // These are cross-referenced, so let's be explicit. |
| m_finder.reset(); |
| m_module.reset(); |
| m_context.reset(); |
| } |
| |
| STDMETHODIMP dxil_dia::DataSource::get_lastError(BSTR *pRetVal) { |
| *pRetVal = nullptr; |
| return S_OK; |
| } |
| |
| namespace dxil_dia { |
| std::unique_ptr<llvm::MemoryBuffer> |
| getMemBufferFromBlob(IDxcBlob *pBlob, const llvm::Twine &BufferName) { |
| llvm::StringRef Data((LPSTR)pBlob->GetBufferPointer(), |
| pBlob->GetBufferSize()); |
| return llvm::MemoryBuffer::getMemBufferCopy(Data, BufferName); |
| } |
| |
| std::unique_ptr<llvm::MemoryBuffer> |
| getMemBufferFromStream(IStream *pStream, std::vector<char> &DataContainer, |
| const llvm::Twine &BufferName) { |
| CComPtr<IDxcBlob> pBlob; |
| if (SUCCEEDED(pStream->QueryInterface(&pBlob))) { |
| return getMemBufferFromBlob(pBlob, BufferName); |
| } |
| |
| STATSTG statstg; |
| IFT(pStream->Stat(&statstg, STATFLAG_NONAME)); |
| size_t size = statstg.cbSize.LowPart; |
| |
| DataContainer.resize(size); |
| ULONG read; |
| IFT(pStream->Read(DataContainer.data(), size, &read)); |
| llvm::StringRef Str(DataContainer.data(), size); |
| std::unique_ptr<llvm::MemoryBuffer> result( |
| llvm::MemoryBuffer::getMemBuffer(Str, BufferName.str(), false)); |
| return result; |
| } |
| } // namespace dxil_dia |
| |
| STDMETHODIMP dxil_dia::DataSource::loadDataFromIStream(IStream *pInputIStream) { |
| try { |
| DxcThreadMalloc TM(m_pMalloc); |
| if (m_module.get() != nullptr) { |
| return E_FAIL; |
| } |
| |
| // Setup filesystem because bitcode reader might emit warning |
| ::llvm::sys::fs::MSFileSystem *msfPtr; |
| IFT(CreateMSFileSystemForDisk(&msfPtr)); |
| std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr); |
| |
| ::llvm::sys::fs::AutoPerThreadSystem pts(msf.get()); |
| IFTLLVM(pts.error_code()); |
| |
| CComPtr<IStream> pIStream = pInputIStream; |
| CComPtr<IDxcBlob> pContainer; |
| if (SUCCEEDED(hlsl::pdb::LoadDataFromStream(m_pMalloc, pInputIStream, |
| &pContainer))) { |
| const hlsl::DxilContainerHeader *pContainerHeader = |
| hlsl::IsDxilContainerLike(pContainer->GetBufferPointer(), |
| pContainer->GetBufferSize()); |
| if (!hlsl::IsValidDxilContainer(pContainerHeader, |
| pContainer->GetBufferSize())) |
| return E_FAIL; |
| const hlsl::DxilPartHeader *PartHeader = hlsl::GetDxilPartByType( |
| pContainerHeader, hlsl::DFCC_ShaderDebugInfoDXIL); |
| if (!PartHeader) |
| return E_FAIL; |
| CComPtr<IDxcBlobEncoding> pPinnedBlob; |
| IFR(hlsl::DxcCreateBlobWithEncodingFromPinned( |
| PartHeader + 1, PartHeader->PartSize, CP_ACP, &pPinnedBlob)); |
| pIStream.Release(); |
| IFR(hlsl::CreateReadOnlyBlobStream(pPinnedBlob, &pIStream)); |
| } |
| |
| m_context.reset(); |
| m_finder.reset(); |
| |
| m_context = std::make_shared<llvm::LLVMContext>(); |
| llvm::MemoryBuffer *pBitcodeBuffer; |
| std::unique_ptr<llvm::MemoryBuffer> pEmbeddedBuffer; |
| |
| std::vector<char> DataContainer; |
| std::unique_ptr<llvm::MemoryBuffer> pBuffer = |
| getMemBufferFromStream(pIStream, DataContainer, "data"); |
| size_t bufferSize = pBuffer->getBufferSize(); |
| |
| // The buffer can hold LLVM bitcode for a module, or the ILDB |
| // part from a container. |
| if (bufferSize < sizeof(UINT32)) { |
| return DXC_E_MALFORMED_CONTAINER; |
| } |
| const UINT32 BC_C0DE = ((INT32)(INT8)'B' | (INT32)(INT8)'C' << 8 | |
| (INT32)0xDEC0 << 16); // BC0xc0de in big endian |
| if (BC_C0DE == *(const UINT32 *)pBuffer->getBufferStart()) { |
| pBitcodeBuffer = pBuffer.get(); |
| } else { |
| if (bufferSize <= sizeof(hlsl::DxilProgramHeader)) { |
| return DXC_E_MALFORMED_CONTAINER; |
| } |
| |
| const hlsl::DxilProgramHeader *pDxilProgramHeader = |
| (const hlsl::DxilProgramHeader *)pBuffer->getBufferStart(); |
| if (pDxilProgramHeader->BitcodeHeader.DxilMagic != hlsl::DxilMagicValue) { |
| return DXC_E_MALFORMED_CONTAINER; |
| } |
| |
| UINT32 BlobSize; |
| const char *pBitcode = nullptr; |
| hlsl::GetDxilProgramBitcode(pDxilProgramHeader, &pBitcode, &BlobSize); |
| std::unique_ptr<llvm::MemoryBuffer> p = llvm::MemoryBuffer::getMemBuffer( |
| llvm::StringRef(pBitcode, BlobSize), "data", |
| false /* RequiresNullTerminator */); |
| pEmbeddedBuffer.swap(p); |
| pBitcodeBuffer = pEmbeddedBuffer.get(); |
| } |
| |
| std::string DiagStr; |
| std::unique_ptr<llvm::Module> pModule = |
| hlsl::dxilutil::LoadModuleFromBitcode(pBitcodeBuffer, *m_context.get(), |
| DiagStr); |
| if (!pModule.get()) |
| return E_FAIL; |
| m_finder = std::make_shared<llvm::DebugInfoFinder>(); |
| m_finder->processModule(*pModule.get()); |
| m_module.reset(pModule.release()); |
| } |
| CATCH_CPP_RETURN_HRESULT(); |
| return S_OK; |
| } |
| |
| STDMETHODIMP dxil_dia::DataSource::openSession(IDiaSession **ppSession) { |
| DxcThreadMalloc TM(m_pMalloc); |
| *ppSession = nullptr; |
| if (m_module.get() == nullptr) |
| return E_FAIL; |
| |
| ::llvm::sys::fs::MSFileSystem *msfPtr; |
| IFT(CreateMSFileSystemForDisk(&msfPtr)); |
| std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr); |
| |
| ::llvm::sys::fs::AutoPerThreadSystem pts(msf.get()); |
| IFTLLVM(pts.error_code()); |
| |
| CComPtr<Session> pSession = Session::Alloc(DxcGetThreadMallocNoRef()); |
| IFROOM(pSession.p); |
| pSession->Init(m_context, m_module, m_finder); |
| *ppSession = pSession.Detach(); |
| return S_OK; |
| } |
| |
| HRESULT CreateDxcDiaDataSource(REFIID riid, LPVOID *ppv) { |
| CComPtr<dxil_dia::DataSource> result = |
| CreateOnMalloc<dxil_dia::DataSource>(DxcGetThreadMallocNoRef()); |
| if (result == nullptr) { |
| *ppv = nullptr; |
| return E_OUTOFMEMORY; |
| } |
| |
| return result.p->QueryInterface(riid, ppv); |
| } |