Files
wbenny-pdbex/Source/PDBExtractor.cpp
T
MeeSong be7a72fe8f Support binary file path (#1)
* [ADD] direct export call msdia140.dll
* [MDF] Copy msdia140.dll cmd
* [ADD] Support binary file path
2018-11-10 13:17:30 +01:00

696 lines
16 KiB
C++

#include "PDBExtractor.h"
#include "PDBHeaderReconstructor.h"
#include "PDBSymbolVisitor.h"
#include "PDBSymbolSorter.h"
#include "PDBSymbolSorterAlphabetical.h"
#include "UdtFieldDefinition.h"
#include <iostream>
#include <fstream>
#include <stdexcept>
namespace
{
//
// Headers & footers for test file and reconstructed header.
//
static const char TEST_FILE_HEADER[] =
"#include <stdio.h>\n"
"#include <stddef.h>\n"
"#include <stdint.h>\n"
"\n"
"typedef long HRESULT;\n"
"\n"
"#include \"%s\"\n"
"\n"
"int main()\n"
"{\n";
static const char TEST_FILE_FOOTER[] =
"\n"
"\treturn 0;\n"
"}\n"
"\n";
static const char HEADER_FILE_HEADER[] =
"/*\n"
" * PDB file: %s\n"
" * Image architecture: %s (0x%04x)\n"
" *\n"
" * Dumped by pdbex tool v" PDBEX_VERSION_STRING ", by wbenny\n"
" */\n\n";
static const char DEFINITIONS_PRAGMA_PACK_BEGIN[] =
"#include <pshpack1.h>";
static const char DEFINITIONS_PRAGMA_PACK_END[] =
"#include <poppack.h>";
//
// Error messages.
//
static const char* MESSAGE_INVALID_PARAMETERS =
"Invalid parameters";
static const char* MESSAGE_FILE_NOT_FOUND =
"File not found";
static const char* MESSAGE_SYMBOL_NOT_FOUND =
"Symbol not found";
//
// Our exception class.
//
class PDBDumperException
: public std::runtime_error
{
public:
PDBDumperException(const char* Message)
: std::runtime_error(Message)
{
}
};
}
int
PDBExtractor::Run(
int argc,
char** argv
)
{
int Result = ERROR_SUCCESS;
try
{
ParseParameters(argc, argv);
OpenPDBFile();
PrintTestHeader();
if (m_Settings.SymbolName == "*")
{
DumpAllSymbols();
}
else if (m_Settings.SymbolName == "%")
{
DumpAllSymbolsOneByOne();
}
else
{
DumpOneSymbol();
}
PrintTestFooter();
}
catch (const PDBDumperException& e)
{
std::cerr << e.what() << std::endl;
Result = EXIT_FAILURE;
}
CloseOpenedFiles();
return Result;
}
void
PDBExtractor::PrintUsage()
{
printf("Extracts types and structures from PDB (Program database).\n");
printf("Version v%s\n", PDBEX_VERSION_STRING);
printf("\n");
printf("pdbex <symbol> <path> [-o <filename>] [-t <filename>] [-e <type>]\n");
printf(" [-u <prefix>] [-s prefix] [-r prefix] [-g suffix]\n");
printf(" [-p] [-x] [-m] [-b] [-d] [-i] [-l]\n");
printf("\n");
printf("<symbol> Symbol name to extract\n");
printf(" Use '*' if all symbols should be extracted.\n");
printf(" Use '%%' if all symbols should be extracted separately.\n");
printf("<path> Path to the PDB file.\n");
printf(" -o filename Specifies the output file. (stdout)\n");
printf(" -t filename Specifies the output test file. (off)\n");
printf(" -e [n,i,a] Specifies expansion of nested structures/unions. (i)\n");
printf(" n = none Only top-most type is printed.\n");
printf(" i = inline unnamed Unnamed types are nested.\n");
printf(" a = inline all All types are nested.\n");
printf(" -u prefix Unnamed union prefix (in combination with -d).\n");
printf(" -s prefix Unnamed struct prefix (in combination with -d).\n");
printf(" -r prefix Prefix for all symbols.\n");
printf(" -g suffix Suffix for all symbols.\n");
printf("\n");
printf("Following options can be explicitly turned off by adding trailing '-'.\n");
printf("Example: -p-\n");
printf(" -p Create padding members. (T)\n");
printf(" -x Show offsets. (T)\n");
printf(" -m Create Microsoft typedefs. (T)\n");
printf(" -b Allow bitfields in union. (F)\n");
printf(" -d Allow unnamed data types. (T)\n");
printf(" -i Use types from stdint.h instead of native types. (F)\n");
printf(" -j Print definitions of referenced types. (T)\n");
printf(" -k Print header. (T)\n");
printf(" -n Print declarations. (T)\n");
printf(" -l Print definitions. (T)\n");
printf(" -f Print functions. (F)\n");
printf(" -z Print #pragma pack directives. (T)\n");
printf(" -y Sort declarations and definitions. (F)\n");
printf("\n");
}
void
PDBExtractor::ParseParameters(
int argc,
char** argv
)
{
//
// Early check for help parameter.
//
if ( argc == 1 ||
(argc == 2 && strcmp(argv[1], "-h") == 0) ||
(argc == 2 && strcmp(argv[1], "--help") == 0))
{
PrintUsage();
//
// Kitten died when I wrote this.
//
exit(EXIT_SUCCESS);
}
int ArgumentPointer = 0;
m_Settings.SymbolName = argv[++ArgumentPointer];
m_Settings.PdbPath = argv[++ArgumentPointer];
while (++ArgumentPointer < argc)
{
const char* CurrentArgument = argv[ArgumentPointer];
size_t CurrentArgumentLength = strlen(CurrentArgument);
const char* NextArgument = ArgumentPointer < argc
? argv[ArgumentPointer + 1]
: nullptr;
size_t NextArgumentLength = NextArgument
? strlen(CurrentArgument)
: 0;
//
// Handling of -X- switches.
//
if ((CurrentArgumentLength != 2 && CurrentArgumentLength != 3) ||
(CurrentArgumentLength == 2 && CurrentArgument[0] != '-') ||
(CurrentArgumentLength == 3 && CurrentArgument[0] != '-' && CurrentArgument[2] != '-'))
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
bool OffSwitch = CurrentArgumentLength == 3 && CurrentArgument[2] == '-';
//
// Handling of options.
//
switch (CurrentArgument[1])
{
case 'o':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.OutputFilename = NextArgument;
if (m_Settings.SymbolName != "%")
{
m_Settings.PdbHeaderReconstructorSettings.OutputFile = new std::ofstream(
NextArgument,
std::ios::out
);
}
else
{
m_Settings.PdbHeaderReconstructorSettings.OutputFile = nullptr;
}
break;
case 't':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.TestFilename = NextArgument;
m_Settings.PdbHeaderReconstructorSettings.TestFile = new std::ofstream(
m_Settings.TestFilename,
std::ios::out
);
break;
case 'e':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
switch (NextArgument[0])
{
case 'n':
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
PDBHeaderReconstructor::MemberStructExpansionType::None;
break;
case 'i':
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed;
break;
case 'a':
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
PDBHeaderReconstructor::MemberStructExpansionType::InlineAll;
break;
default:
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion =
PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed;
break;
}
break;
case 'u':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.PdbHeaderReconstructorSettings.AnonymousUnionPrefix = NextArgument;
break;
case 's':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.PdbHeaderReconstructorSettings.AnonymousStructPrefix = NextArgument;
break;
case 'r':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.PdbHeaderReconstructorSettings.SymbolPrefix = NextArgument;
break;
case 'g':
if (!NextArgument)
{
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
++ArgumentPointer;
m_Settings.PdbHeaderReconstructorSettings.SymbolSuffix = NextArgument;
break;
case 'p':
m_Settings.PdbHeaderReconstructorSettings.CreatePaddingMembers = !OffSwitch;
break;
case 'x':
m_Settings.PdbHeaderReconstructorSettings.ShowOffsets = !OffSwitch;
break;
case 'm':
m_Settings.PdbHeaderReconstructorSettings.MicrosoftTypedefs = !OffSwitch;
break;
case 'b':
m_Settings.PdbHeaderReconstructorSettings.AllowBitFieldsInUnion = !OffSwitch;
break;
case 'd':
m_Settings.PdbHeaderReconstructorSettings.AllowAnonymousDataTypes = !OffSwitch;
break;
case 'i':
m_Settings.UdtFieldDefinitionSettings.UseStdInt = !OffSwitch;
break;
case 'j':
m_Settings.PrintReferencedTypes = !OffSwitch;
break;
case 'k':
m_Settings.PrintHeader = !OffSwitch;
break;
case 'n':
m_Settings.PrintDeclarations = !OffSwitch;
break;
case 'l':
m_Settings.PrintDefinitions = !OffSwitch;
break;
case 'f':
m_Settings.PrintFunctions = !OffSwitch;
break;
case 'z':
m_Settings.PrintPragmaPack = !OffSwitch;
break;
case 'y':
m_Settings.Sort = !OffSwitch;
break;
default:
throw PDBDumperException(MESSAGE_INVALID_PARAMETERS);
}
}
m_HeaderReconstructor = std::make_unique<PDBHeaderReconstructor>(
&m_Settings.PdbHeaderReconstructorSettings
);
m_SymbolVisitor = std::make_unique<PDBSymbolVisitor<UdtFieldDefinition>>(
m_HeaderReconstructor.get(),
&m_Settings.UdtFieldDefinitionSettings
);
if (m_Settings.Sort)
{
m_SymbolSorter = std::make_unique<PDBSymbolSorterAlphabetical>();
}
else
{
m_SymbolSorter = std::make_unique<PDBSymbolSorter>();
}
}
void
PDBExtractor::OpenPDBFile()
{
if (m_PDB.Open(m_Settings.PdbPath.c_str()) == FALSE)
{
throw PDBDumperException(MESSAGE_FILE_NOT_FOUND);
}
}
void
PDBExtractor::PrintTestHeader()
{
if (m_Settings.PdbHeaderReconstructorSettings.TestFile != nullptr)
{
static char TEST_FILE_HEADER_FORMATTED[16 * 1024];
sprintf_s(
TEST_FILE_HEADER_FORMATTED, TEST_FILE_HEADER,
m_Settings.OutputFilename
);
(*m_Settings.PdbHeaderReconstructorSettings.TestFile) << TEST_FILE_HEADER_FORMATTED;
}
}
void
PDBExtractor::PrintTestFooter()
{
if (m_Settings.PdbHeaderReconstructorSettings.TestFile != nullptr)
{
(*m_Settings.PdbHeaderReconstructorSettings.TestFile) << TEST_FILE_FOOTER;
}
}
void
PDBExtractor::PrintPDBHeader()
{
if (m_Settings.PrintHeader)
{
static const char* const ArchitectureString =
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_I386 ? "i386" :
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_AMD64 ? "AMD64" :
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_IA64 ? "IA64" :
m_PDB.GetMachineType() == IMAGE_FILE_MACHINE_ARMNT ? "ArmNT" :
"Unknown";
static char HEADER_FILE_HEADER_FORMATTED[16 * 1024];
sprintf_s(
HEADER_FILE_HEADER_FORMATTED, HEADER_FILE_HEADER,
m_Settings.PdbPath.c_str(),
ArchitectureString,
m_PDB.GetMachineType()
);
*m_Settings.PdbHeaderReconstructorSettings.OutputFile << HEADER_FILE_HEADER_FORMATTED;
}
}
void
PDBExtractor::PrintPDBDeclarations()
{
//
// Write declarations.
//
if (m_Settings.PrintDeclarations)
{
for (auto&& e : m_SymbolSorter->GetSortedSymbols())
{
if (e->Tag == SymTagUDT && !PDB::IsUnnamedSymbol(e))
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< PDB::GetUdtKindString(e->u.Udt.Kind)
<< " " << m_HeaderReconstructor->GetCorrectedSymbolName(e) << ";"
<< std::endl;
}
else if (e->Tag == SymTagEnum)
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< "enum"
<< " " << m_HeaderReconstructor->GetCorrectedSymbolName(e) << ";"
<< std::endl;
}
}
*m_Settings.PdbHeaderReconstructorSettings.OutputFile << std::endl;
}
}
void
PDBExtractor::PrintPDBDefinitions()
{
//
// Write definitions.
//
if (m_Settings.PrintDefinitions)
{
if (m_Settings.PrintPragmaPack)
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< DEFINITIONS_PRAGMA_PACK_BEGIN
<< std::endl;
}
for (auto&& e : m_SymbolSorter->GetSortedSymbols())
{
bool Expand = true;
//
// Do not expand unnamed types, if they will be inlined.
//
if (m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion == PDBHeaderReconstructor::MemberStructExpansionType::InlineUnnamed &&
e->Tag == SymTagUDT &&
PDB::IsUnnamedSymbol(e))
{
Expand = false;
}
if (Expand)
{
m_SymbolVisitor->Run(e);
}
}
if (m_Settings.PrintPragmaPack)
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< DEFINITIONS_PRAGMA_PACK_END
<< std::endl;
}
}
}
void
PDBExtractor::PrintPDBFunctions()
{
//
// Write definitions.
//
if (m_Settings.PrintFunctions)
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< "/*"
<< std::endl;
for (auto&& e : m_PDB.GetFunctionSet())
{
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< e
<< std::endl;
}
*m_Settings.PdbHeaderReconstructorSettings.OutputFile
<< "*/"
<< std::endl;
}
}
void
PDBExtractor::DumpAllSymbols()
{
//
// We are going to print all symbols.
//
PrintPDBHeader();
for (auto&& e : m_PDB.GetSymbolMap())
{
m_SymbolSorter->Visit(e.second);
}
PrintPDBDeclarations();
PrintPDBDefinitions();
PrintPDBFunctions();
}
void
PDBExtractor::DumpOneSymbol()
{
const SYMBOL* Symbol = m_PDB.GetSymbolByName(m_Settings.SymbolName.c_str());
if (Symbol == nullptr)
{
throw PDBDumperException(MESSAGE_SYMBOL_NOT_FOUND);
}
PrintPDBHeader();
//
// InlineAll supresses PrintReferencedTypes.
//
if (m_Settings.PrintReferencedTypes &&
m_Settings.PdbHeaderReconstructorSettings.MemberStructExpansion != PDBHeaderReconstructor::MemberStructExpansionType::InlineAll)
{
m_SymbolSorter->Visit(Symbol);
//
// Print header only when PrintReferencedTypes == true.
//
PrintPDBDefinitions();
}
else
{
//
// Print only the specified symbol.
//
m_SymbolVisitor->Run(Symbol);
}
}
void
PDBExtractor::DumpAllSymbolsOneByOne()
{
//
// Copy all symbols locally.
//
for (auto&& e : m_PDB.GetSymbolMap())
{
m_SymbolSorter->Visit(e.second);
}
auto Symbols = m_SymbolSorter->GetSortedSymbols();
m_SymbolSorter->Clear();
//
// Create output directory.
//
std::string OutputDirectory = m_Settings.OutputFilename
? m_Settings.OutputFilename
: ".";
if (OutputDirectory[OutputDirectory.length() - 1] != '\\')
{
OutputDirectory += '\\';
}
BOOL Result = CreateDirectory(OutputDirectory.c_str(), NULL);
if (!Result && GetLastError() != ERROR_ALREADY_EXISTS)
{
throw PDBDumperException("Cannot create directory");
}
for (auto&& e : Symbols)
{
if (!PDB::IsUnnamedSymbol(e))
{
m_Settings.PdbHeaderReconstructorSettings.OutputFile = new std::ofstream(
OutputDirectory + std::string(e->Name) + ".h",
std::ios::out
);
m_Settings.SymbolName = e->Name;
DumpOneSymbol();
delete m_Settings.PdbHeaderReconstructorSettings.OutputFile;
m_SymbolSorter->Clear();
}
}
m_Settings.PdbHeaderReconstructorSettings.OutputFile = nullptr;
}
void
PDBExtractor::CloseOpenedFiles()
{
//
// We want to free the memory only if the filename was specified,
// because OutputFile or TestFile may be std::cout.
//
if (m_Settings.TestFilename)
{
delete m_Settings.PdbHeaderReconstructorSettings.TestFile;
}
if (m_Settings.OutputFilename)
{
delete m_Settings.PdbHeaderReconstructorSettings.OutputFile;
}
}