feat: complete MakePDB.

This commit is contained in:
2025-01-20 03:39:05 +08:00
parent 339c931e7b
commit 3f0bfb58a3
13 changed files with 433 additions and 2 deletions

45
MakePDB/.clang-format Normal file
View File

@@ -0,0 +1,45 @@
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Left
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: true
PadOperators: true
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowAllArgumentsOnNextLine: false
AlignOperands: AlignAfterOperator
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AllowShortLambdasOnASingleLine: All
AllowShortBlocksOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakTemplateDeclarations: "Yes"
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Custom
BreakBeforeBinaryOperators: NonAssignment
CommentPragmas: "^ IWYU pragma:"
ConstructorInitializerIndentWidth: 0
IndentWidth: 4
Language: Cpp
MaxEmptyLinesToKeep: 2
PackConstructorInitializers: CurrentLine
PointerAlignment: Left
TabWidth: 4
UseTab: Never
SortIncludes: CaseSensitive

9
MakePDB/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# VSCode
.vscode
# XMake
.xmake
build
# ClangD
.cache

View File

@@ -1,2 +1 @@
# MakePDB
WIP.
# cxx-project-template

View File

@@ -0,0 +1,59 @@
#include "binary/COFF.h"
namespace makepdb::binary {
COFF::COFF(std::string_view Path) {
using namespace object;
auto ObjOrErr = ObjectFile::createObjectFile(Path);
if (!ObjOrErr) {
throw std::runtime_error("Failed to create object file.");
}
if (!isa<COFFObjectFile>(ObjOrErr->getBinary())) {
throw std::runtime_error("Is not a valid PE file.");
}
auto Bin = ObjOrErr->takeBinary();
OwningBinary = object::OwningBinary(
static_unique_ptr_cast<COFFObjectFile>(std::move(Bin.first)),
std::move(Bin.second)
);
}
codeview::PDB70DebugInfo COFF::DebugInfo() const {
const codeview::DebugInfo* DebugInfo;
StringRef PDBFileName;
if (OwningCOFF().getDebugPDBInfo(DebugInfo, PDBFileName) || !DebugInfo) {
throw std::runtime_error("Failed to get pdb info from coff file.");
}
if (DebugInfo->Signature.CVSignature != OMF::Signature::PDB70) {
throw std::runtime_error("Unsupported PDB format.");
}
return DebugInfo->PDB70;
}
size_t COFF::SectionIndex(uint64_t Offset) const {
using namespace object;
uint64_t CurrentIndex = 0;
for (const SectionRef& Sec : OwningCOFF().sections()) {
const coff_section* Section = OwningCOFF().getCOFFSection(Sec);
if (Offset >= Section->VirtualAddress
&& Offset < Section->VirtualAddress + Section->VirtualSize) {
return CurrentIndex;
}
CurrentIndex++;
}
throw std::runtime_error("Offset is not in any section.");
}
object::COFFObjectFile const& COFF::OwningCOFF() const {
return *OwningBinary.getBinary();
}
} // namespace makepdb::binary

20
MakePDB/src/binary/COFF.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <llvm/Object/COFF.h>
namespace makepdb::binary {
class COFF {
public:
explicit COFF(std::string_view Path);
codeview::PDB70DebugInfo DebugInfo() const;
size_t SectionIndex(uint64_t Offset) const;
object::COFFObjectFile const& OwningCOFF() const;
private:
object::OwningBinary<object::COFFObjectFile> OwningBinary;
};
} // namespace makepdb::binary

View File

@@ -0,0 +1,98 @@
#include "binary/PDB.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include <llvm/DebugInfo/PDB/Native/RawConstants.h>
using namespace llvm::pdb;
namespace makepdb::binary {
PDB::PDB(COFF&& COFF, Data&& SymbolData)
: OwningCOFF(std::move(COFF)),
OwningSymbolData(std::move(SymbolData)),
Builder(Allocator) {
constexpr uint32_t BlockSize = 4096;
if (Builder.initialize(BlockSize)) {
throw std::runtime_error("Failed to initialize pdb file builder.");
}
for (uint32_t I = 0; I < pdb::kSpecialStreamCount; ++I) {
if (!Builder.getMsfBuilder().addStream(0)) {
throw std::runtime_error("Failed to add initial stream.");
}
}
ImageBase = OwningCOFF.OwningCOFF().getImageBase();
}
void PDB::WriteTo(std::string_view Path) {
Build();
auto Guid = Builder.getInfoBuilder().getGuid();
if (Builder.commit(Path, &Guid)) {
throw std::runtime_error("Failed to create pdb!");
}
}
void PDB::Build() {
BuildInfo();
BuildDBI();
BuildTPI();
BuildGSI();
}
void PDB::BuildInfo() {
auto PDBInfo = OwningCOFF.DebugInfo();
auto& InfoBuilder = Builder.getInfoBuilder();
InfoBuilder.setVersion(PdbRaw_ImplVer::PdbImplVC70);
InfoBuilder.setAge(PDBInfo.Age);
InfoBuilder.setGuid(*reinterpret_cast<codeview::GUID*>(PDBInfo.Signature));
InfoBuilder.addFeature(PdbRaw_FeatureSig::VC140);
}
void PDB::BuildDBI() {
auto PDBInfo = OwningCOFF.DebugInfo();
auto& DbiBuilder = Builder.getDbiBuilder();
DbiBuilder.setVersionHeader(PdbRaw_DbiVer::PdbDbiV70);
DbiBuilder.setAge(PDBInfo.Age);
DbiBuilder.setMachineType(PDB_Machine::Amd64);
DbiBuilder.setFlags(DbiFlags::FlagStrippedMask);
DbiBuilder.setBuildNumber(14, 11); // LLVM is compatible with LINK 14.11
}
void PDB::BuildTPI() {
Builder.getTpiBuilder().setVersionHeader(PdbRaw_TpiVer::PdbTpiV80);
Builder.getIpiBuilder().setVersionHeader(PdbRaw_TpiVer::PdbTpiV80);
}
void PDB::BuildGSI() {
std::vector<BulkPublic> PublicsIn;
OwningSymbolData.forEach([&PublicsIn, this](const DataEntity& E) {
BulkPublic Symbol;
auto SectionIndex = OwningCOFF.SectionIndex(E.RVA - ImageBase);
auto SectionOrErr =
OwningCOFF.OwningCOFF().getSection(SectionIndex + 1);
if (!SectionOrErr) {
throw std::runtime_error("Invalid section.");
}
Symbol.Name = strdup(E.SymbolName.c_str());
Symbol.NameLen = E.SymbolName.size();
Symbol.Segment = SectionIndex + 1;
Symbol.Offset = E.RVA - ImageBase - SectionOrErr.get()->VirtualAddress;
if (E.IsFunction) Symbol.setFlags(codeview::PublicSymFlags::Function);
PublicsIn.emplace_back(Symbol);
});
Builder.getGsiBuilder().addPublicSymbols(std::move(PublicsIn));
}
} // namespace makepdb::binary

34
MakePDB/src/binary/PDB.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "binary/COFF.h"
#include "data.h"
#include <llvm/DebugInfo/PDB/Native/PDBFileBuilder.h>
#include <llvm/Support/Allocator.h>
namespace makepdb::binary {
class PDB {
public:
explicit PDB(COFF&& COFF, Data&& SymbolData);
void WriteTo(std::string_view Path);
private:
void Build();
inline void BuildInfo();
inline void BuildDBI();
inline void BuildTPI();
inline void BuildGSI();
COFF OwningCOFF;
Data OwningSymbolData;
uint64_t ImageBase;
BumpPtrAllocator Allocator;
pdb::PDBFileBuilder Builder;
};
} // namespace makepdb::binary

27
MakePDB/src/data.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include "data.h"
#include <nlohmann/json.hpp>
namespace makepdb {
Data::Data(std::string_view Path) {
std::ifstream IFS(Path.data());
if (!IFS) {
throw std::runtime_error("Failed to open data path.");
}
auto Data = nlohmann::json::parse(IFS);
if (Data["version"] != 1) {
throw std::runtime_error("Unsupported data version.");
}
for (const auto& E : Data["data"]) {
Entities.emplace(DataEntity{E["symbol"], E["rva"], E["is_function"]});
}
}
void Data::forEach(const std::function<void(DataEntity)> Callback) {
for (const auto& E : Entities) Callback(E);
}
} // namespace makepdb

35
MakePDB/src/data.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
namespace makepdb {
struct DataEntity {
std::string SymbolName;
uint64_t RVA;
bool IsFunction;
bool operator==(const DataEntity& other) const {
return SymbolName == other.SymbolName && RVA == other.RVA
&& IsFunction == other.IsFunction;
}
struct H {
size_t operator()(const DataEntity E) const {
size_t h1 = std::hash<std::string>{}(E.SymbolName);
size_t h2 = std::hash<uint64_t>{}(E.RVA);
size_t h3 = std::hash<bool>{}(E.IsFunction);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
};
class Data {
public:
explicit Data(std::string_view Path);
void forEach(const std::function<void(DataEntity)> Callback);
private:
std::unordered_set<DataEntity, DataEntity::H> Entities;
};
} // namespace makepdb

54
MakePDB/src/main.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include <argparse/argparse.hpp>
#include "binary/COFF.h"
#include "binary/PDB.h"
#include "data.h"
using namespace makepdb;
[[nodiscard]] auto load_args(int argc, char* argv[]) {
argparse::ArgumentParser program("MakePDB", "1.0.0");
struct {
std::string server_program_path;
std::string data_path;
std::string output_path;
} args;
program.add_argument("--program")
.help("Path to bedrock_server.exe")
.store_into(args.server_program_path)
.required();
program.add_argument("--symbol-data")
.help("Path to symbol data.")
.store_into(args.data_path)
.required();
program.add_argument("--output", "-o")
.help("Path to output PDB.")
.store_into(args.output_path)
.required();
program.parse_args(argc, argv);
return args;
}
int main(int argc, char* argv[]) try {
auto args = load_args(argc, argv);
binary::COFF server_program(args.server_program_path);
Data symbol_data(args.data_path);
binary::PDB pdb(std::move(server_program), std::move(symbol_data));
pdb.WriteTo(args.output_path);
return 0;
} catch (const std::exception& e) {
std::println("E: {}", e.what());
return -1;
}

6
MakePDB/src/nonstd.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
template <typename To, typename From>
constexpr auto static_unique_ptr_cast(std::unique_ptr<From>&& F) {
return std::unique_ptr<To>(static_cast<To*>(F.release()));
}

20
MakePDB/src/pch.h Normal file
View File

@@ -0,0 +1,20 @@
// Work on llvm namespace.
namespace llvm {}
using namespace llvm;
// Standard Libraries
#include <fstream>
#include <print>
#include <functional>
#include <memory>
#include <string>
#include <unordered_set>
// Helper
#include "nonstd.h"

25
MakePDB/xmake.lua Normal file
View File

@@ -0,0 +1,25 @@
add_rules('mode.debug', 'mode.release')
add_requires('llvm')
add_requires('nlohmann_json 3.11.3')
add_requires('argparse 3.1')
target('makepdb')
set_kind('binary')
add_files('src/**.cpp')
add_includedirs('src')
set_warnings('all')
set_languages('c23', 'c++23')
set_pcxxheader('src/pch.h')
add_packages(
'llvm',
'nlohmann_json',
'argparse'
)
add_links('LLVM')
if is_mode('debug') then
add_defines('DEBUG')
end