feat: complete MakePDB.
This commit is contained in:
45
MakePDB/.clang-format
Normal file
45
MakePDB/.clang-format
Normal 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
9
MakePDB/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# XMake
|
||||
.xmake
|
||||
build
|
||||
|
||||
# ClangD
|
||||
.cache
|
||||
@@ -1,2 +1 @@
|
||||
# MakePDB
|
||||
WIP.
|
||||
# cxx-project-template
|
||||
|
||||
59
MakePDB/src/binary/COFF.cpp
Normal file
59
MakePDB/src/binary/COFF.cpp
Normal 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
20
MakePDB/src/binary/COFF.h
Normal 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
|
||||
98
MakePDB/src/binary/PDB.cpp
Normal file
98
MakePDB/src/binary/PDB.cpp
Normal 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
34
MakePDB/src/binary/PDB.h
Normal 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
27
MakePDB/src/data.cpp
Normal 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
35
MakePDB/src/data.h
Normal 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
54
MakePDB/src/main.cpp
Normal 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
6
MakePDB/src/nonstd.h
Normal 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
20
MakePDB/src/pch.h
Normal 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
25
MakePDB/xmake.lua
Normal 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
|
||||
Reference in New Issue
Block a user