Handle debug search paths on D3D12 correctly

* We also need to unwrap the needless PDB wrapper on MS's separate debug info
  setup.
This commit is contained in:
baldurk
2020-06-29 16:33:24 +01:00
parent dfc82b3bd2
commit 767f123c73
9 changed files with 282 additions and 299 deletions
-9
View File
@@ -25,7 +25,6 @@
#include "d3d11_device.h"
#include "core/core.h"
#include "core/settings.h"
#include "driver/dxgi/dxgi_wrapped.h"
#include "jpeg-compressor/jpge.h"
#include "maths/formatpacking.h"
@@ -39,9 +38,6 @@
#include "d3d11_resources.h"
#include "d3d11_shader_cache.h"
RDOC_CONFIG(rdcarray<rdcstr>, DXBC_Debug_SearchDirPaths, {},
"Paths to search for separated shader debug PDBs.");
WRAPPED_POOL_INST(WrappedID3D11Device);
WrappedID3D11Device *WrappedID3D11Device::m_pCurrentWrappedDevice = NULL;
@@ -790,11 +786,6 @@ rdcstr WrappedID3D11Device::GetChunkName(uint32_t idx)
return ToStr((D3D11Chunk)idx);
}
const rdcarray<rdcstr> &WrappedID3D11Device::GetShaderDebugInfoSearchPaths()
{
return DXBC_Debug_SearchDirPaths();
}
void WrappedID3D11Device::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src,
rdcstr d)
{
-1
View File
@@ -454,7 +454,6 @@ public:
return m_LayoutDescs[layout];
}
const rdcarray<rdcstr> &GetShaderDebugInfoSearchPaths();
template <typename SerialiserType>
bool Serialise_CaptureScope(SerialiserType &ser);
-129
View File
@@ -27,7 +27,6 @@
#include "api/app/renderdoc_app.h"
#include "driver/dxgi/dxgi_wrapped.h"
#include "driver/shaders/dxbc/dxbc_reflect.h"
#include "lz4/lz4.h"
#include "d3d11_context.h"
#include "d3d11_renderstate.h"
@@ -72,134 +71,6 @@ Threading::CriticalSection WrappedID3DDeviceContextState::m_Lock;
const GUID RENDERDOC_ID3D11ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
void WrappedShader::ShaderEntry::TryReplaceOriginalByteCode()
{
if(!DXBC::DXBCContainer::CheckForDebugInfo((const void *)&m_Bytecode[0], m_Bytecode.size()))
{
rdcstr originalPath = m_DebugInfoPath;
if(originalPath.empty())
originalPath =
DXBC::DXBCContainer::GetDebugBinaryPath((const void *)&m_Bytecode[0], m_Bytecode.size());
if(!originalPath.empty())
{
bool lz4 = false;
if(!strncmp(originalPath.c_str(), "lz4#", 4))
{
originalPath = originalPath.substr(4);
lz4 = true;
}
// could support more if we're willing to compile in the decompressor
FILE *originalShaderFile = NULL;
size_t numSearchPaths = m_DebugInfoSearchPaths ? m_DebugInfoSearchPaths->size() : 0;
rdcstr foundPath;
// keep searching until we've exhausted all possible path options, or we've found a file that
// opens
while(originalShaderFile == NULL && !originalPath.empty())
{
// while we haven't found a file, keep trying through the search paths. For i==0
// check the path on its own, in case it's an absolute path.
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
{
if(i == 0)
{
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
foundPath = originalPath;
continue;
}
else
{
const rdcstr &searchPath = (*m_DebugInfoSearchPaths)[i - 1];
foundPath = searchPath + "/" + originalPath;
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
}
}
if(originalShaderFile == NULL)
{
// the "documented" behaviour for D3D debug info names is that when presented with a
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
// append it to all search paths as-is, then strip off the top-level subdirectory to get
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
// and didn't open a file, try to strip off the the top directory and continue.
int32_t offs = originalPath.find_first_of("\\/");
// if we couldn't find a directory separator there's nothing to do, stop looking
if(offs == -1)
break;
// otherwise strip up to there and keep going
originalPath.erase(0, offs + 1);
}
}
if(originalShaderFile == NULL)
return;
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
if(lz4 || originalShaderSize >= m_Bytecode.size())
{
rdcarray<byte> originalBytecode;
originalBytecode.resize((size_t)originalShaderSize);
FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize,
originalShaderFile);
if(lz4)
{
rdcarray<byte> decompressed;
// first try decompressing to 1MB flat
decompressed.resize(100 * 1024);
int ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
// if it failed, either source is corrupt or we didn't allocate enough space.
// Just allocate 255x compressed size since it can't need any more than that.
decompressed.resize(255 * originalBytecode.size());
ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
return;
}
}
RDCASSERT(ret > 0, ret);
// we resize and memcpy instead of just doing .swap() because that would
// transfer over the over-large pessimistic capacity needed for decompression
originalBytecode.resize(ret);
memcpy(&originalBytecode[0], &decompressed[0], originalBytecode.size());
}
if(DXBC::DXBCContainer::CheckForDebugInfo((const void *)&originalBytecode[0],
originalBytecode.size()))
{
m_Bytecode.swap(originalBytecode);
}
}
FileIO::fclose(originalShaderFile);
}
}
}
void WrappedShader::ShaderEntry::BuildReflection()
{
RDCCOMPILE_ASSERT(
+4 -6
View File
@@ -911,12 +911,11 @@ public:
class ShaderEntry
{
public:
ShaderEntry() : m_DebugInfoSearchPaths(NULL), m_DXBCFile(NULL) {}
ShaderEntry() : m_DXBCFile(NULL) {}
ShaderEntry(WrappedID3D11Device *device, ResourceId id, const byte *code, size_t codeLen)
{
m_ID = id;
m_Bytecode.assign(code, codeLen);
m_DebugInfoSearchPaths = &device->GetShaderDebugInfoSearchPaths();
m_DXBCFile = NULL;
}
~ShaderEntry()
@@ -930,8 +929,8 @@ public:
{
if(m_DXBCFile == NULL && !m_Bytecode.empty())
{
TryReplaceOriginalByteCode();
m_DXBCFile = new DXBC::DXBCContainer((const void *)&m_Bytecode[0], m_Bytecode.size());
m_DXBCFile = new DXBC::DXBCContainer(m_Bytecode, m_DebugInfoPath);
m_Bytecode.clear();
}
return m_DXBCFile;
}
@@ -962,9 +961,8 @@ public:
ResourceId m_ID;
rdcstr m_DebugInfoPath;
const rdcarray<rdcstr> *m_DebugInfoSearchPaths;
rdcarray<byte> m_Bytecode;
bytebuf m_Bytecode;
bool m_Built = false;
DXBC::DXBCContainer *m_DXBCFile;
-129
View File
@@ -24,7 +24,6 @@
#include "d3d12_resources.h"
#include "driver/shaders/dxbc/dxbc_reflect.h"
#include "lz4/lz4.h"
#include "d3d12_command_list.h"
#include "d3d12_command_queue.h"
@@ -34,134 +33,6 @@ bool WrappedID3D12Shader::m_InternalResources = false;
const GUID RENDERDOC_ID3D12ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
void WrappedID3D12Shader::TryReplaceOriginalByteCode()
{
if(!DXBC::DXBCContainer::CheckForDebugInfo((const void *)&m_Bytecode[0], m_Bytecode.size()))
{
rdcstr originalPath = m_DebugInfoPath;
if(originalPath.empty())
originalPath =
DXBC::DXBCContainer::GetDebugBinaryPath((const void *)&m_Bytecode[0], m_Bytecode.size());
if(!originalPath.empty())
{
bool lz4 = false;
if(!strncmp(originalPath.c_str(), "lz4#", 4))
{
originalPath = originalPath.substr(4);
lz4 = true;
}
// could support more if we're willing to compile in the decompressor
FILE *originalShaderFile = NULL;
size_t numSearchPaths = m_DebugInfoSearchPaths ? m_DebugInfoSearchPaths->size() : 0;
rdcstr foundPath;
// keep searching until we've exhausted all possible path options, or we've found a file that
// opens
while(originalShaderFile == NULL && !originalPath.empty())
{
// while we haven't found a file, keep trying through the search paths. For i==0
// check the path on its own, in case it's an absolute path.
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
{
if(i == 0)
{
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
foundPath = originalPath;
continue;
}
else
{
const rdcstr &searchPath = (*m_DebugInfoSearchPaths)[i - 1];
foundPath = searchPath + "/" + originalPath;
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
}
}
if(originalShaderFile == NULL)
{
// the "documented" behaviour for D3D debug info names is that when presented with a
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
// append it to all search paths as-is, then strip off the top-level subdirectory to get
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
// and didn't open a file, try to strip off the the top directory and continue.
int32_t offs = originalPath.find_first_of("\\/");
// if we couldn't find a directory separator there's nothing to do, stop looking
if(offs == -1)
break;
// otherwise strip up to there and keep going
originalPath.erase(0, offs + 1);
}
}
if(originalShaderFile == NULL)
return;
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
if(lz4 || originalShaderSize >= m_Bytecode.size())
{
rdcarray<byte> originalBytecode;
originalBytecode.resize((size_t)originalShaderSize);
FileIO::fread(&originalBytecode[0], sizeof(byte), (size_t)originalShaderSize,
originalShaderFile);
if(lz4)
{
rdcarray<byte> decompressed;
// first try decompressing to 1MB flat
decompressed.resize(100 * 1024);
int ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
// if it failed, either source is corrupt or we didn't allocate enough space.
// Just allocate 255x compressed size since it can't need any more than that.
decompressed.resize(255 * originalBytecode.size());
ret = LZ4_decompress_safe((const char *)&originalBytecode[0], (char *)&decompressed[0],
(int)originalBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
return;
}
}
RDCASSERT(ret > 0, ret);
// we resize and memcpy instead of just doing .swap() because that would
// transfer over the over-large pessimistic capacity needed for decompression
originalBytecode.resize(ret);
memcpy(&originalBytecode[0], &decompressed[0], originalBytecode.size());
}
if(DXBC::DXBCContainer::CheckForDebugInfo((const void *)&originalBytecode[0],
originalBytecode.size()))
{
m_Bytecode.swap(originalBytecode);
}
}
FileIO::fclose(originalShaderFile);
}
}
}
#undef D3D12_TYPE_MACRO
#define D3D12_TYPE_MACRO(iface) WRAPPED_POOL_INST(CONCAT(Wrapped, iface));
+2 -13
View File
@@ -681,7 +681,6 @@ public:
: WrappedDeviceChild12(NULL, device), m_Key(byteCode)
{
m_Bytecode.assign((const byte *)byteCode.pShaderBytecode, byteCode.BytecodeLength);
m_DebugInfoSearchPaths = NULL;
m_DXBCFile = NULL;
device->GetResourceManager()->AddLiveResource(GetResourceID(), this);
@@ -733,12 +732,6 @@ public:
}
DXBCKey GetKey() { return m_Key; }
void SetDebugInfoPath(rdcarray<rdcstr> *searchPaths, const rdcstr &path)
{
m_DebugInfoSearchPaths = searchPaths;
m_DebugInfoPath = path;
}
D3D12_SHADER_BYTECODE GetDesc()
{
D3D12_SHADER_BYTECODE ret;
@@ -751,8 +744,7 @@ public:
{
if(m_DXBCFile == NULL && !m_Bytecode.empty())
{
TryReplaceOriginalByteCode();
m_DXBCFile = new DXBC::DXBCContainer((const void *)&m_Bytecode[0], m_Bytecode.size());
m_DXBCFile = new DXBC::DXBCContainer(m_Bytecode, rdcstr());
}
return m_DXBCFile;
}
@@ -781,10 +773,7 @@ public:
DXBCKey m_Key;
rdcstr m_DebugInfoPath;
rdcarray<rdcstr> *m_DebugInfoSearchPaths;
rdcarray<byte> m_Bytecode;
bytebuf m_Bytecode;
bool m_Built;
DXBC::DXBCContainer *m_DXBCFile;
@@ -27,13 +27,18 @@
#include <algorithm>
#include "api/app/renderdoc_app.h"
#include "common/common.h"
#include "core/settings.h"
#include "driver/shaders/dxil/dxil_bytecode.h"
#include "lz4/lz4.h"
#include "serialise/serialiser.h"
#include "strings/string_utils.h"
#include "dxbc_bytecode.h"
#include "driver/dx/official/d3dcompiler.h"
RDOC_CONFIG(rdcarray<rdcstr>, DXBC_Debug_SearchDirPaths, {},
"Paths to search for separated shader debug PDBs.");
namespace DXBC
{
struct RDEFCBufferVariable
@@ -774,25 +779,166 @@ rdcstr DXBCContainer::GetDebugBinaryPath(const void *ByteCode, size_t ByteCodeLe
return debugPath;
}
DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
void DXBCContainer::TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath)
{
RDCASSERT(ByteCodeLength < UINT32_MAX);
if(!CheckForDebugInfo((const void *)&byteCode[0], byteCode.size()))
{
rdcstr originalPath = debugInfoPath;
if(originalPath.empty())
originalPath = GetDebugBinaryPath((const void *)&byteCode[0], byteCode.size());
if(!originalPath.empty())
{
bool lz4 = false;
if(!strncmp(originalPath.c_str(), "lz4#", 4))
{
originalPath = originalPath.substr(4);
lz4 = true;
}
// could support more if we're willing to compile in the decompressor
FILE *originalShaderFile = NULL;
const rdcarray<rdcstr> &searchPaths = DXBC_Debug_SearchDirPaths();
size_t numSearchPaths = searchPaths.size();
rdcstr foundPath;
// keep searching until we've exhausted all possible path options, or we've found a file that
// opens
while(originalShaderFile == NULL && !originalPath.empty())
{
// while we haven't found a file, keep trying through the search paths. For i==0
// check the path on its own, in case it's an absolute path.
for(size_t i = 0; originalShaderFile == NULL && i <= numSearchPaths; i++)
{
if(i == 0)
{
originalShaderFile = FileIO::fopen(originalPath.c_str(), "rb");
foundPath = originalPath;
continue;
}
else
{
const rdcstr &searchPath = searchPaths[i - 1];
foundPath = searchPath + "/" + originalPath;
originalShaderFile = FileIO::fopen(foundPath.c_str(), "rb");
}
}
if(originalShaderFile == NULL)
{
// the "documented" behaviour for D3D debug info names is that when presented with a
// relative path containing subfolders like foo/bar/blah.pdb then we should first try to
// append it to all search paths as-is, then strip off the top-level subdirectory to get
// bar/blah.pdb and try that in all search directories, and keep going. So if we got here
// and didn't open a file, try to strip off the the top directory and continue.
int32_t offs = originalPath.find_first_of("\\/");
// if we couldn't find a directory separator there's nothing to do, stop looking
if(offs == -1)
break;
// otherwise strip up to there and keep going
originalPath.erase(0, offs + 1);
}
}
if(originalShaderFile == NULL)
return;
FileIO::fseek64(originalShaderFile, 0L, SEEK_END);
uint64_t originalShaderSize = FileIO::ftell64(originalShaderFile);
FileIO::fseek64(originalShaderFile, 0, SEEK_SET);
if(lz4 || originalShaderSize >= byteCode.size())
{
bytebuf debugBytecode;
debugBytecode.resize((size_t)originalShaderSize);
FileIO::fread(&debugBytecode[0], sizeof(byte), (size_t)originalShaderSize,
originalShaderFile);
if(lz4)
{
rdcarray<byte> decompressed;
// first try decompressing to 1MB flat
decompressed.resize(100 * 1024);
int ret = LZ4_decompress_safe((const char *)&debugBytecode[0], (char *)&decompressed[0],
(int)debugBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
// if it failed, either source is corrupt or we didn't allocate enough space.
// Just allocate 255x compressed size since it can't need any more than that.
decompressed.resize(255 * debugBytecode.size());
ret = LZ4_decompress_safe((const char *)&debugBytecode[0], (char *)&decompressed[0],
(int)debugBytecode.size(), (int)decompressed.size());
if(ret < 0)
{
RDCERR("Failed to decompress LZ4 data from %s", foundPath.c_str());
return;
}
}
RDCASSERT(ret > 0, ret);
// we resize and memcpy instead of just doing .swap() because that would
// transfer over the over-large pessimistic capacity needed for decompression
debugBytecode.resize(ret);
memcpy(&debugBytecode[0], &decompressed[0], debugBytecode.size());
}
if(IsPDBFile(&debugBytecode[0], debugBytecode.size()))
{
UnwrapEmbeddedPDBData(debugBytecode);
m_DebugShaderBlob = debugBytecode;
}
else if(CheckForDebugInfo((const void *)&debugBytecode[0], debugBytecode.size()))
{
byteCode.swap(debugBytecode);
}
}
FileIO::fclose(originalShaderFile);
}
}
}
DXBCContainer::DXBCContainer(bytebuf &ByteCode, const rdcstr &debugInfoPath)
{
RDCEraseEl(m_ShaderStats);
m_ShaderBlob.resize(ByteCodeLength);
memcpy(&m_ShaderBlob[0], ByteCode, m_ShaderBlob.size());
TryFetchSeparateDebugInfo(ByteCode, debugInfoPath);
char *data = (char *)&m_ShaderBlob[0]; // just for convenience
m_ShaderBlob = ByteCode;
FileHeader *header = (FileHeader *)&m_ShaderBlob[0];
// just for convenience
char *data = (char *)m_ShaderBlob.data();
char *debugData = (char *)m_DebugShaderBlob.data();
FileHeader *header = (FileHeader *)data;
FileHeader *debugHeader = (FileHeader *)debugData;
if(header->fourcc != FOURCC_DXBC)
return;
if(header->fileLength != (uint32_t)ByteCodeLength)
if(header->fileLength != (uint32_t)ByteCode.size())
return;
if(debugHeader && debugHeader->fourcc != FOURCC_DXBC)
debugHeader = NULL;
if(debugHeader && debugHeader->fileLength != m_DebugShaderBlob.size())
debugHeader = NULL;
memcpy(m_Hash, header->hashValue, sizeof(m_Hash));
// default to vertex shader to support blobs without RDEF chunks (e.g. used with
@@ -800,6 +946,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
m_Type = DXBC::ShaderType::Vertex;
uint32_t *chunkOffsets = (uint32_t *)(header + 1); // right after the header
uint32_t *debugChunkOffsets = debugHeader ? (uint32_t *)(debugHeader + 1) : NULL;
for(uint32_t chunkIdx = 0; chunkIdx < header->numChunks; chunkIdx++)
{
@@ -989,7 +1136,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
RDEFCBufferVariable *var =
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + varStride);
if(var->nameOffset > ByteCodeLength)
if(var->nameOffset > ByteCode.size())
{
varStride += extraData;
}
@@ -1001,7 +1148,7 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
RDEFCBufferVariable *var =
(RDEFCBufferVariable *)(chunkContents + cbuf->variables.offset + vi * varStride);
RDCASSERT(var->nameOffset < ByteCodeLength);
RDCASSERT(var->nameOffset < ByteCode.size());
CBufferVariable v;
@@ -1131,6 +1278,19 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
}
}
// next search the debug file if it exists
for(uint32_t chunkIdx = 0;
debugHeader && m_DXILByteCode == NULL && chunkIdx < debugHeader->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx]);
uint32_t *chunkSize = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx] + sizeof(uint32_t));
char *chunkContents = (char *)(debugData + debugChunkOffsets[chunkIdx] + sizeof(uint32_t) * 2);
if(*fourcc == FOURCC_ILDB)
m_DXILByteCode = new DXIL::Program((const byte *)chunkContents, *chunkSize);
}
// if we didn't find ILDB then we have to get the bytecode from DXIL. However we look for the
// STAT chunk and if we find it get reflection from there, since it will have better
// information. What a mess.
@@ -1390,6 +1550,18 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength)
}
}
// try to find SPDB in the separate debug info pdb now
for(uint32_t chunkIdx = 0;
debugHeader && m_DebugInfo == NULL && chunkIdx < debugHeader->numChunks; chunkIdx++)
{
uint32_t *fourcc = (uint32_t *)(debugData + debugChunkOffsets[chunkIdx]);
if(*fourcc == FOURCC_SPDB)
{
m_DebugInfo = MakeSPDBChunk(fourcc);
}
}
if(m_DXILByteCode)
m_DebugInfo = m_DXILByteCode;
@@ -40,6 +40,8 @@ class IDebugInfo;
struct Reflection;
IDebugInfo *MakeSDBGChunk(void *data);
IDebugInfo *MakeSPDBChunk(void *data);
bool IsPDBFile(void *data, size_t length);
void UnwrapEmbeddedPDBData(bytebuf &bytes);
};
// many thanks to winehq for information of format of RDEF, STAT and SIGN chunks:
@@ -140,7 +142,7 @@ ShaderCompileFlags EncodeFlags(const uint32_t flags, const rdcstr &profile);
class DXBCContainer
{
public:
DXBCContainer(const void *ByteCode, size_t ByteCodeLength);
DXBCContainer(bytebuf &ByteCode, const rdcstr &debugInfoPath);
~DXBCContainer();
DXBC::ShaderType m_Type = DXBC::ShaderType::Max;
struct
@@ -171,6 +173,10 @@ private:
DXBCContainer(const DXBCContainer &o);
DXBCContainer &operator=(const DXBCContainer &o);
void TryFetchSeparateDebugInfo(bytebuf &byteCode, const rdcstr &debugInfoPath);
bytebuf m_DebugShaderBlob;
rdcstr m_Disassembly;
D3D_PRIMITIVE_TOPOLOGY m_OutputTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
+88 -2
View File
@@ -42,6 +42,20 @@ namespace DXBC
{
static const uint32_t FOURCC_SPDB = MAKE_FOURCC('S', 'P', 'D', 'B');
bool IsPDBFile(void *data, size_t length)
{
FileHeaderPage *header = (FileHeaderPage *)data;
if(length < sizeof(FileHeaderPage))
return false;
if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0",
sizeof(header->identifier)) != 0)
return false;
return true;
}
SPDBChunk::SPDBChunk(void *chunk)
{
m_HasDebugInfo = false;
@@ -64,8 +78,7 @@ SPDBChunk::SPDBChunk(void *chunk)
FileHeaderPage *header = (FileHeaderPage *)data;
if(memcmp(header->identifier, "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0",
sizeof(header->identifier)) != 0)
if(!IsPDBFile(data, spdblength))
{
RDCWARN("Unexpected SPDB type");
return;
@@ -1822,4 +1835,77 @@ IDebugInfo *MakeSPDBChunk(void *data)
return new SPDBChunk(data);
}
void UnwrapEmbeddedPDBData(bytebuf &bytes)
{
if(!IsPDBFile(bytes.data(), bytes.size()))
return;
FileHeaderPage *header = (FileHeaderPage *)bytes.data();
uint32_t pageCount = header->PageCount;
if(pageCount * header->PageSize != bytes.size())
{
RDCWARN("Corrupt header/pdb. %u pages of %u size doesn't match %zu file size", pageCount,
header->PageSize, bytes.size());
// some DXC versions write the wrong page count, just count ourselves from the file size.
if((bytes.size() % header->PageSize) == 0)
{
header->PageCount = (uint32_t)bytes.size() / header->PageSize;
RDCWARN("Correcting page count to %u by dividing file size %zu by page size %u.",
header->PageCount, bytes.size(), header->PageSize);
}
}
const byte **pages = new const byte *[header->PageCount];
for(uint32_t i = 0; i < header->PageCount; i++)
pages[i] = &bytes[i * header->PageSize];
uint32_t rootdirCount = header->PagesForByteSize(header->RootDirSize);
uint32_t rootDirIndicesCount = header->PagesForByteSize(rootdirCount * sizeof(uint32_t));
PageMapping rootdirIndicesMapping(pages, header->PageSize, header->RootDirectory,
rootDirIndicesCount);
const byte *rootdirIndices = rootdirIndicesMapping.Data();
PageMapping directoryMapping(pages, header->PageSize, (uint32_t *)rootdirIndices, rootdirCount);
const uint32_t *dirContents = (const uint32_t *)directoryMapping.Data();
rdcarray<PDBStream> streams;
streams.resize(*dirContents);
dirContents++;
SPDBLOG("SPDB contains %zu streams", streams.size());
for(size_t i = 0; i < streams.size(); i++)
{
streams[i].byteLength = *dirContents;
SPDBLOG("Stream[%zu] is %u bytes", i, streams[i].byteLength);
dirContents++;
}
for(size_t i = 0; i < streams.size(); i++)
{
if(streams[i].byteLength == 0)
continue;
for(uint32_t p = 0; p < header->PagesForByteSize(streams[i].byteLength); p++)
{
streams[i].pageIndices.push_back(*dirContents);
dirContents++;
}
}
if(streams.size() < 5)
return;
// stream 5 is expected to contain the embedded data
PageMapping embeddedData(pages, header->PageSize, &streams[5].pageIndices[0],
(uint32_t)streams[5].pageIndices.size());
bytes.assign(embeddedData.Data(), streams[5].byteLength);
}
}; // namespace DXBC