refactor: new project structure.

This commit is contained in:
2025-02-27 23:07:54 +08:00
committed by GitHub
parent 44d0911320
commit 5f4a912e30
81 changed files with 816 additions and 842 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
# vscode
.vscode
# c++/xmake
.xmake
build
# c++/clangd
.cache
.clangd
# python
__pycache__
.venv

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "DumpSYM"]
path = DumpSYM
url = https://github.com/Redbeanw44602/dumpsym.git

View File

@@ -1,46 +0,0 @@
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
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerIndentWidth: 0
IndentWidth: 4
Language: Cpp
MaxEmptyLinesToKeep: 2
PackConstructorInitializers: CurrentLine
PointerAlignment: Left
TabWidth: 4
UseTab: Never
SortIncludes: CaseSensitive

9
AskRVA/.gitignore vendored
View File

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

View File

@@ -1,3 +0,0 @@
#pragma once
#include "symbol.h"

View File

@@ -1,37 +0,0 @@
#include "format/input/decl_type.h"
#include "util/string.h"
namespace format::input {
DeclType::DeclType(std::string_view str) {
using namespace util::string;
// clang-format off
switch (H(str)) {
#define HSTR(x) \
case H(#x): \
m_data = x; \
break
HSTR(Function);
HSTR(CXXDeductionGuide);
HSTR(CXXMethod);
HSTR(CXXConstructor);
HSTR(CXXConversion);
HSTR(CXXDestructor);
HSTR(Var);
HSTR(Decomposition);
HSTR(ImplicitParam);
HSTR(OMPCapturedExpr);
HSTR(ParamVar);
HSTR(VarTemplateSpecialization);
#undef HSTR
default:
throw std::invalid_argument("Unexpected decl type.");
}
// clang-format on
}
} // namespace format::input

View File

@@ -1,55 +0,0 @@
#pragma once
namespace format::input {
class DeclType {
public:
enum Enum {
Function,
CXXDeductionGuide,
CXXMethod,
CXXConstructor,
CXXConversion,
CXXDestructor,
Var,
Decomposition,
ImplicitParam,
OMPCapturedExpr,
ParamVar,
VarTemplateSpecialization,
COUNT
};
explicit DeclType(Enum value) : m_data(value) {}
explicit DeclType(std::string_view str);
bool isFunction() const { return m_data >= Function && m_data < Var; }
bool isVar() const { return m_data >= Var && m_data < COUNT; }
Enum data() const { return m_data; }
bool operator==(const DeclType& other) const { return m_data == other.m_data; }
private:
Enum m_data;
};
struct Symbol {
std::string m_name;
DeclType m_type;
bool operator==(const Symbol& other) const { return m_name == other.m_name; }
};
} // namespace format::input
namespace std {
template <>
struct hash<format::input::Symbol> {
size_t operator()(const format::input::Symbol& symbol) const { return hash<string>{}(symbol.m_name); }
};
} // namespace std

View File

@@ -1,20 +0,0 @@
#pragma once
#include "format/input/decl_type.h"
namespace format::input {
class SymbolListFile {
public:
[[nodiscard]] static SymbolListFile load(std::string_view path);
[[nodiscard]] static SymbolListFile load(const std::vector<std::string>& path);
void for_each(const std::function<void(Symbol)>& callback);
private:
SymbolListFile() = default;
std::unordered_set<Symbol> m_data;
};
} // namespace format::input

View File

@@ -1,35 +0,0 @@
#include "format/output/all.h"
#include "format/output/fakepdb.h"
#include "format/output/makepdb.h"
#include "format/output/text.h"
namespace format::output {
void DefaultOutputFile::record_failure(std::string_view symbol) { m_failed_symbols.emplace(symbol); }
void DefaultOutputFile::save_failure(std::string_view path) const {
std::ofstream ofs(path.data());
if (!ofs) {
throw std::runtime_error("Failed to open save file.");
}
for (const auto& symbol : m_failed_symbols) {
ofs << symbol << "\n";
}
}
std::unique_ptr<IOutputFile> create(OutputFormat format) {
switch (format) {
case OutputFormat::Text:
return std::make_unique<OutputTextFile>();
case OutputFormat::FakePDB:
return std::make_unique<OutputFakePDBFile>();
case OutputFormat::MakePDB:
return std::make_unique<OutputMakePDBFile>();
default:
throw std::invalid_argument("Invalid output file format.");
}
}
} // namespace format::output

View File

@@ -1,33 +0,0 @@
#pragma once
namespace format {
enum class OutputFormat { Text, FakePDB, MakePDB };
namespace output {
class IOutputFile {
public:
virtual ~IOutputFile() = default;
virtual void record(std::string_view symbol, uint64_t rva, bool is_function) = 0;
virtual void save(std::string_view path) const = 0;
virtual void record_failure(std::string_view symbol) = 0;
virtual void save_failure(std::string_view path) const = 0;
};
class DefaultOutputFile : public IOutputFile {
public:
void record_failure(std::string_view symbol) override;
void save_failure(std::string_view path) const override;
protected:
std::unordered_set<std::string> m_failed_symbols;
};
std::unique_ptr<IOutputFile> create(OutputFormat format);
} // namespace output
} // namespace format

View File

@@ -1,21 +0,0 @@
#include "format/output/fakepdb.h"
#include <nlohmann/json.hpp>
namespace format::output {
void OutputFakePDBFile::save(std::string_view path) const {
std::ofstream ofs(path.data());
if (!ofs) {
throw std::runtime_error("Failed to open save file.");
}
nlohmann::json data;
for (const auto& [symbol, rva] : m_symbol_rva_map) {
data[symbol] = std::format("{:#x}", rva);
}
ofs << data.dump(4);
}
} // namespace format::output

View File

@@ -1,12 +0,0 @@
#pragma once
#include "format/output/text.h"
namespace format::output {
class OutputFakePDBFile : public OutputTextFile {
public:
void save(std::string_view path) const override;
};
} // namespace format::output

View File

@@ -1,33 +0,0 @@
#include "format/output/makepdb.h"
#include <nlohmann/json.hpp>
namespace format::output {
void OutputMakePDBFile::record(std::string_view symbol, uint64_t rva, bool is_function) {
m_records.emplace(std::string(symbol), rva, is_function);
}
void OutputMakePDBFile::save(std::string_view path) const {
std::ofstream ofs(path.data());
if (!ofs) {
throw std::runtime_error("Failed to open save file.");
}
nlohmann::json data;
// MakePDB - Format V1
data["version"] = 1;
for (const auto& [symbol, rva, is_fun] : m_records) {
data["data"].emplace_back(nlohmann::json{
{"symbol", symbol},
{"rva", rva },
{"is_function", is_fun}
});
}
ofs << data.dump(4);
}
} // namespace format::output

View File

@@ -1,16 +0,0 @@
#pragma once
#include "format/output/all.h"
namespace format::output {
class OutputMakePDBFile : public DefaultOutputFile {
public:
void record(std::string_view symbol, uint64_t rva, bool is_function) override;
void save(std::string_view path) const override;
private:
std::unordered_set<std::tuple<std::string, uint64_t, bool>> m_records;
};
} // namespace format::output

View File

@@ -1,16 +0,0 @@
#pragma once
#include "format/output/all.h"
namespace format::output {
class OutputTextFile : public DefaultOutputFile {
public:
void record(std::string_view symbol, uint64_t rva, bool is_function) override;
void save(std::string_view path) const override;
protected:
std::unordered_map<std::string, uint64_t> m_symbol_rva_map;
};
} // namespace format::output

View File

@@ -1,31 +0,0 @@
add_rules('mode.debug', 'mode.release')
set_allowedplats('windows')
set_allowedarchs('x64')
add_repositories('liteldev-repo https://github.com/LiteLDev/xmake-repo.git')
-- from xmake-repo
add_requires('argparse 3.1')
add_requires('nlohmann_json 3.11.3')
-- from liteldev-repo
add_requires('preloader 1.12.0')
target('askrva')
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(
'argparse',
'nlohmann_json',
'preloader'
)
if is_mode('debug') then
add_defines('DEBUG')
end

View File

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

View File

@@ -1,14 +0,0 @@
add_rules('mode.debug', 'mode.release')
add_requires('xxhash')
target('blob-extractor')
set_kind('binary')
add_files('src/**.cpp')
add_includedirs('src')
set_warnings('all')
set_languages('c23', 'c++23')
if is_mode('debug') then
add_defines('DEBUG')
end

View File

@@ -1,5 +0,0 @@
# Python
.venv
# VSCode
.vscode

6
DeThunk/.gitignore vendored
View File

@@ -1,6 +0,0 @@
# Python
__pycache__
.venv
# VSCode
.vscode

View File

@@ -1,17 +0,0 @@
# Same as Black.
line-length = 100
indent-width = 4
# Assume Python 3.12
target-version = "py312"
[lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E", "F", "W"]
ignore = ["E501"]
[format]
# Like Black, use double quotes for strings.
quote-style = "single"

Submodule DumpSYM deleted from 6cf0947308

View File

@@ -1,45 +0,0 @@
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

View File

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

View File

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

View File

@@ -1,45 +0,0 @@
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
View File

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

View File

@@ -1,53 +0,0 @@
#pragma once
#include "binary/COFF.h"
#include "raw_type_data.h"
#include "symbol_data.h"
#include <llvm/DebugInfo/PDB/Native/PDBFileBuilder.h>
#include <llvm/Support/Allocator.h>
namespace makepdb::binary {
class PDB {
public:
using OwningCOFF = std::unique_ptr<COFF>;
using OwningSymbolData = std::unique_ptr<SymbolData>;
using OwningRawTypeData = std::unique_ptr<RawTypeData>;
explicit PDB();
void set_coff_object(OwningCOFF coff_object) {
m_owning_coff = std::move(coff_object);
m_image_base = m_owning_coff->get_owning_coff().getImageBase();
}
void set_symbol_data(OwningSymbolData symbol_data) {
m_owning_symbol_data = std::move(symbol_data);
}
void set_raw_type_data(OwningRawTypeData raw_type_data) {
m_owning_raw_type_data = std::move(raw_type_data);
}
void write(std::string_view path);
private:
void build();
inline void build_Info();
inline void build_DBI();
inline void build_TPI();
inline void build_GSI();
OwningCOFF m_owning_coff;
OwningSymbolData m_owning_symbol_data;
OwningRawTypeData m_owning_raw_type_data;
uint64_t m_image_base;
BumpPtrAllocator m_allocator;
pdb::PDBFileBuilder m_builder;
};
} // namespace makepdb::binary

View File

@@ -1,6 +0,0 @@
#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()));
}

View File

@@ -1,20 +0,0 @@
// 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"

View File

@@ -1,32 +0,0 @@
#include "symbol_data.h"
#include <nlohmann/json.hpp>
namespace makepdb {
SymbolData::SymbolData(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& entity : data["data"]) {
m_entities.emplace(SymbolDataEntity{
entity["symbol"],
entity["rva"],
entity["is_function"]
});
}
}
void SymbolData::for_each(const std::function<void(SymbolDataEntity)> callback
) const {
for (const auto& entity : m_entities) callback(entity);
}
} // namespace makepdb

View File

@@ -1,35 +0,0 @@
#pragma once
namespace makepdb {
struct SymbolDataEntity {
std::string symbol_name;
uint64_t rva;
bool is_function;
bool operator==(const SymbolDataEntity& other) const {
return symbol_name == other.symbol_name && rva == other.rva
&& is_function == other.is_function;
}
struct Hash {
size_t operator()(const SymbolDataEntity entity) const {
size_t h1 = std::hash<std::string>{}(entity.symbol_name);
size_t h2 = std::hash<uint64_t>{}(entity.rva);
size_t h3 = std::hash<bool>{}(entity.is_function);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
};
class SymbolData {
public:
explicit SymbolData(std::string_view path);
void for_each(const std::function<void(SymbolDataEntity)> callback) const;
private:
std::unordered_set<SymbolDataEntity, SymbolDataEntity::Hash> m_entities;
};
} // namespace makepdb

View File

@@ -1,25 +0,0 @@
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

View File

@@ -1,60 +1,78 @@
# DebugInfo # DebugInfo
This repository regenerates debug information (like PDB) from LeviLamina's public data. This repository regenerates debug information (like PDB) from LeviLamina's public data.
## Background ## Background
- After 1.21.3.01, Mojang removed debug information from BDS.
- Mojang no longer provides any debugging information (both server and client) to the community. - After 1.21.3.01, Mojang removed debug information from BDS.
- Mojang has an agreement with LiteLDev to provide them with debug data. - Mojang no longer provides any debugging information (both server and client) to the community.
- LiteLDev generates header files and obfuscated symbol to RVA lookup tables from debug data and provides them to the community. - Mojang has an agreement with LiteLDev to provide them with debug data.
- LiteLDev generates header files and obfuscated symbol to RVA lookup tables from debug data and provides them to the community.
### Problems caused by Mojang's collaboration with LiteLDev ### Problems caused by Mojang's collaboration with LiteLDev
- Mojang's collaboration with LiteLDev is opaque and we have no idea what they do.
- LiteLDev has completed its monopoly, and there will no longer be a second mod loader in the community. - Mojang's collaboration with LiteLDev is opaque and we have no idea what they do.
- Due to the obfuscated format, the community can no longer reverse engineer BDS. - LiteLDev has completed its monopoly, and there will no longer be a second mod loader in the community.
- Due to the obfuscated format, the community can no longer reverse engineer BDS.
### Header files, obfuscation format and security ### Header files, obfuscation format and security
- LeviLamina's design necessitates that they publish header files.
- The header file contains all the declaration information so that symbols can be generated. - LeviLamina's design necessitates that they publish header files.
- The RVA of the corresponding symbol can be extracted from the obfuscated format at runtime. - The header file contains all the declaration information so that symbols can be generated.
- The obfuscated format is actually a carrier of the complete "symbol table", which used to be PDB/DWARF. - The RVA of the corresponding symbol can be extracted from the obfuscated format at runtime.
- The obfuscated format is actually a carrier of the complete "symbol table", which used to be PDB/DWARF.
## Tool for restoring original DebugInfo from obfuscated format ## Tool for restoring original DebugInfo from obfuscated format
> [!NOTE] > [!NOTE]
> LiteLDev has not yet released bedrock_runtime_data/magicblob for the Linux server. > LiteLDev has not yet released bedrock_runtime_data/magicblob for the Linux server.
They are dethunk, dumpsym, askrva and makepdb. Each tool is in a directory with the same name as it, and also has a README to help you use it. They are dethunk, dumpsym, askrva and makepdb. Each tool is in a directory with the same name as it, and also has a README to help you use it.
In short, the PDB is generated by the following steps: In short, the PDB is generated by the following steps:
- Preprocess the header files published by LiteLDev by dethunk.
- Preprocess the header files published by LiteLDev by dethunk.
``` ```
python main.py {HEADER_PROJECT_DIR}/src python main.py {HEADER_PROJECT_DIR}/src
``` ```
- Compile the header file and load the dumpsym plugin in the compilation parameters.
- Compile the header file and load the dumpsym plugin in the compilation parameters.
``` ```
xmake f -c -p windows -a x64 -m release --sdk=/opt/msvc --cxflags="-fplugin=/path/to/libdumpsym.so -fplugin-arg-dumpsym-record-decl-name" --toolchain=clang xmake f -c -p windows -a x64 -m release --sdk=/opt/msvc --cxflags="-fplugin=/path/to/libdumpsym.so -fplugin-arg-dumpsym-record-decl-name" --toolchain=clang
xmake -v xmake -v
``` ```
- Find the generated symbols file.
- Find the generated symbols file.
``` ```
{HEADER_PROJECT_DIR}/build/.objs/bdsheader/windows/x64/release/test/__cpp_main.cpp.cpp.symbols {HEADER_PROJECT_DIR}/build/.objs/bdsheader/windows/x64/release/test/__cpp_main.cpp.cpp.symbols
``` ```
- Generate symbol table using askrva.
- Generate symbol table using askrva.
``` ```
./askrva __cpp_main.cpp.cpp.symbols --output succeed.json --output-failed failed.txt --output-format=makepdb ./askrva __cpp_main.cpp.cpp.symbols --output succeed.json --output-failed failed.txt --output-format=makepdb
``` ```
- Generate PDB using makepdb.
- Generate PDB using makepdb.
``` ```
./makepdb --program bedrock_server.exe --symbol-data succeed.json --output bedrock_server.pdb ./makepdb --program bedrock_server.exe --symbol-data succeed.json --output bedrock_server.pdb
``` ```
## TODO ## TODO
- [ ] Tap into more available symbols.
- [ ] Fully open source HeaderGen. - [ ] Tap into more available symbols.
- [ ] Bootstrap. - [ ] Fully open source HeaderGen.
- [ ] Opti project structure. - [ ] Bootstrap.
## Be with us ## Be with us
Our vision is to build an open and inclusive Minecraft: Bedrock Edition ecosystem. Our vision is to build an open and inclusive Minecraft: Bedrock Edition ecosystem.
- [https://t.me/bdsplugins](https://t.me/s/bdsplugins)
- [https://t.me/bdsplugins](https://t.me/s/bdsplugins)
## LICENSE ## LICENSE
All tools are open source under the MIT license. All tools are open source under the MIT license.

View File

@@ -18,7 +18,7 @@ known as magicblob), PreLoader no longer handles PDB. The source code in the cur
### Usage ### Usage
- --output-format can be `auto` / `txt` / `fakepdb` / `makepdb` - --output-format can be `auto` / `txt` / `makepdb`
``` ```
Usage: askrva [--help] [--version] --output VAR [--output-failed VAR] [--output-format VAR] path Usage: askrva [--help] [--version] --output VAR [--output-failed VAR] [--output-format VAR] path

38
docs/dump-sym.md Normal file
View File

@@ -0,0 +1,38 @@
# DumpSYM
Sometimes, we need to extract symbols from declarations. So this compiler plugin was born.
### Build
> As far as I know, there are some problems with the clang plugin executing under Windows, so I recommend that all operations be performed under Linux.
- Building llvm will consume a lot of time and resources, it is recommended to pre-install llvm from your system package manager, xmake can detect system packages.
- Run `xmake` to build.
### Usage
Simply pass `-fplugin=...` to clang and the plugin will run automatically.
#### Optional Arguments
- `record-decl-name` - Add the name of the Decl in the output, reference: [FunctionDecl](https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html), [VarDecl](https://clang.llvm.org/doxygen/classclang_1_1VarDecl.html)
> [!NOTE]
> Because LLVM is used, both ItaniumABI and MicrosoftABI are supported.
- Example:
```
$ clang++ -fplugin=/path/to/plugin/libdumpsym.so -fplugin-arg-dumpsym-record-decl-name test.cpp
```
- The result will be generated in the `<TU>.symbols` file
```
$ cat test.cpp.symbols
Function, main
CXXDestructor, ??_DThreadPool@OS@@QEAAXXZ
CXXConstructor, ??0SpinLockImpl@@QEAA@AEBV0@@Z
CXXMethod, ??4SpinLockImpl@@QEAAAEAV0@AEBV0@@Z
Var, ?Low@OSThreadPriority@Threading@Bedrock@@2V123@B
```

View File

@@ -0,0 +1,55 @@
#include "data_format/bound_symbol_list.h"
#include <nlohmann/json.hpp>
namespace di::data_format {
constexpr int BOUND_SYMBOL_LIST_FORMAT_VERSION = 1;
BoundSymbolList::BoundSymbolList(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"] != BOUND_SYMBOL_LIST_FORMAT_VERSION) {
throw std::runtime_error("Unsupported data version.");
}
for (const auto& entity : data["data"]) {
m_entities.emplace(
BoundSymbol{entity["symbol"], entity["rva"], entity["is_function"]}
);
}
}
void BoundSymbolList::record(
std::string_view symbol,
uint64_t rva,
bool is_function
) {
m_entities.emplace(BoundSymbol{std::string(symbol), rva, is_function});
}
void BoundSymbolList::write_to(const std::string& path) const {
std::ofstream ofs(path);
if (!ofs) {
throw std::runtime_error("Failed to open file!");
}
nlohmann::json data;
data["version"] = BOUND_SYMBOL_LIST_FORMAT_VERSION;
for (const auto& entity : m_entities) {
data["data"].emplace_back(nlohmann::json{
{"symbol", entity.m_symbol_name},
{"rva", entity.m_rva },
{"is_function", entity.m_is_function}
});
}
ofs << data.dump(4);
}
} // namespace di::data_format

View File

@@ -0,0 +1,25 @@
#pragma once
#include "data_format/type/bound_symbol.h"
namespace di::data_format {
class BoundSymbolList {
public:
using for_each_callback_t = std::function<void(BoundSymbol const&)>;
explicit BoundSymbolList() = default;
explicit BoundSymbolList(std::string_view path);
void record(std::string_view symbol, uint64_t rva, bool is_function);
void write_to(const std::string& path) const;
constexpr void for_each(const for_each_callback_t& callback) const {
for (const auto& entity : m_entities) callback(entity);
}
private:
std::unordered_set<BoundSymbol> m_entities;
};
} // namespace di::data_format

View File

@@ -1,12 +1,12 @@
#include "format/output/text.h" #include "data_format/human_readable_symbol_list.h"
namespace format::output { namespace di::data_format {
void OutputTextFile::record(std::string_view symbol, uint64_t rva, bool is_function) { void HumanReadableSymbolList::record(std::string_view symbol, uint64_t rva) {
m_symbol_rva_map.try_emplace(std::string(symbol), rva); m_symbol_rva_map.try_emplace(std::string(symbol), rva);
} }
void OutputTextFile::save(std::string_view path) const { void HumanReadableSymbolList::write_to(std::string_view path) const {
std::ofstream ofs(path.data()); std::ofstream ofs(path.data());
if (!ofs) { if (!ofs) {
throw std::runtime_error("Failed to open save file."); throw std::runtime_error("Failed to open save file.");
@@ -17,4 +17,4 @@ void OutputTextFile::save(std::string_view path) const {
} }
} }
} // namespace format::output } // namespace di::data_format

View File

@@ -0,0 +1,14 @@
#pragma once
namespace di::data_format {
class HumanReadableSymbolList {
public:
void record(std::string_view symbol, uint64_t rva);
void write_to(std::string_view path) const;
protected:
std::unordered_map<std::string, uint64_t> m_symbol_rva_map;
};
} // namespace di::data_format

View File

@@ -0,0 +1,19 @@
#include "data_format/raw_text.h"
namespace di::data_format {
void RawText::record(std::string_view content) {
m_data += content;
m_data += '\n';
}
void RawText::write_to(std::string_view path) const {
std::ofstream ofs(path.data());
if (!ofs) {
throw std::runtime_error("Failed to open save file.");
}
ofs << m_data;
}
} // namespace di::data_format

View File

@@ -0,0 +1,14 @@
#pragma once
namespace di::data_format {
class RawText {
public:
void record(std::string_view line);
void write_to(std::string_view path) const;
private:
std::string m_data;
};
} // namespace di::data_format

View File

@@ -1,5 +1,6 @@
#include "raw_type_data.h" #include "data_format/raw_type_data.h"
#include <llvm/DebugInfo/CodeView/TypeStreamMerger.h>
#include <llvm/DebugInfo/PDB/IPDBSession.h> #include <llvm/DebugInfo/PDB/IPDBSession.h>
#include <llvm/DebugInfo/PDB/Native/NativeSession.h> #include <llvm/DebugInfo/PDB/Native/NativeSession.h>
#include <llvm/DebugInfo/PDB/Native/PDBFile.h> #include <llvm/DebugInfo/PDB/Native/PDBFile.h>
@@ -9,7 +10,7 @@
using namespace llvm::pdb; using namespace llvm::pdb;
namespace makepdb { namespace di::data_format {
RawTypeData::RawTypeData(std::string_view path) RawTypeData::RawTypeData(std::string_view path)
: m_storaged_TPI(m_allocator), : m_storaged_TPI(m_allocator),
@@ -51,4 +52,4 @@ RawTypeData::RawTypeData(std::string_view path)
} }
} }
} // namespace makepdb } // namespace di::data_format

View File

@@ -1,13 +1,12 @@
#pragma once #pragma once
#include <llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h> #include <llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h>
#include <llvm/DebugInfo/CodeView/TypeStreamMerger.h>
namespace makepdb { namespace di::data_format {
class RawTypeData { class RawTypeData {
public: public:
using ForEachTpiCallback = using for_each_callback_t =
std::function<void(codeview::TypeIndex, codeview::CVType)>; std::function<void(codeview::TypeIndex, codeview::CVType)>;
enum TypedStream { TPI, IPI }; enum TypedStream { TPI, IPI };
@@ -15,7 +14,7 @@ public:
explicit RawTypeData(std::string_view path); explicit RawTypeData(std::string_view path);
template <TypedStream Stream> template <TypedStream Stream>
void for_each(const ForEachTpiCallback& callback) /*const*/ { void for_each(const for_each_callback_t& callback) /*const*/ {
if constexpr (Stream == TPI) { if constexpr (Stream == TPI) {
return m_storaged_TPI.ForEachRecord(callback); return m_storaged_TPI.ForEachRecord(callback);
} }
@@ -31,4 +30,4 @@ private:
codeview::MergingTypeTableBuilder m_storaged_IPI; codeview::MergingTypeTableBuilder m_storaged_IPI;
}; };
} // namespace makepdb } // namespace di::data_format

View File

@@ -0,0 +1,33 @@
#pragma once
#include <boost/functional/hash.hpp>
namespace di {
struct BoundSymbol {
std::string m_symbol_name;
uint64_t m_rva;
bool m_is_function;
bool operator==(const BoundSymbol& other) const {
return m_symbol_name == other.m_symbol_name && m_rva == other.m_rva
&& m_is_function == other.m_is_function;
}
};
} // namespace di
namespace std {
template <>
struct hash<di::BoundSymbol> {
constexpr size_t operator()(const di::BoundSymbol& symbol) const {
size_t seed = 0;
boost::hash_combine(seed, symbol.m_symbol_name);
boost::hash_combine(seed, symbol.m_rva);
boost::hash_combine(seed, symbol.m_is_function);
return seed;
}
};
} // namespace std

View File

@@ -0,0 +1,75 @@
#pragma once
#include "util/string.h"
namespace di {
class DeclType {
public:
enum Enum {
Function,
CXXDeductionGuide,
CXXMethod,
CXXConstructor,
CXXConversion,
CXXDestructor,
Var,
Decomposition,
ImplicitParam,
OMPCapturedExpr,
ParamVar,
VarTemplateSpecialization,
COUNT
};
constexpr explicit DeclType(Enum value) : m_data(value) {}
constexpr explicit DeclType(std::string_view str) {
using namespace util::string;
// clang-format off
switch (H(str)) {
#define HSTR(x) \
case H(#x): \
m_data = x; \
break
HSTR(Function);
HSTR(CXXDeductionGuide);
HSTR(CXXMethod);
HSTR(CXXConstructor);
HSTR(CXXConversion);
HSTR(CXXDestructor);
HSTR(Var);
HSTR(Decomposition);
HSTR(ImplicitParam);
HSTR(OMPCapturedExpr);
HSTR(ParamVar);
HSTR(VarTemplateSpecialization);
#undef HSTR
default:
throw std::invalid_argument("Unexpected decl type.");
}
// clang-format on
}
constexpr bool is_function() const {
return m_data >= Function && m_data < Var;
}
constexpr bool is_variable() const {
return m_data >= Var && m_data < COUNT;
}
constexpr Enum data() const { return m_data; }
constexpr bool operator==(const DeclType& other) const {
return m_data == other.m_data;
}
private:
Enum m_data;
};
} // namespace di

View File

@@ -0,0 +1,27 @@
#pragma once
#include "data_format/type/decl_type.h"
namespace di {
struct TypedSymbol {
std::string m_name;
DeclType m_type;
constexpr bool operator==(const TypedSymbol& other) const {
return m_name == other.m_name;
}
};
} // namespace di
namespace std {
template <>
struct hash<di::TypedSymbol> {
constexpr size_t operator()(const di::TypedSymbol& symbol) const {
return std::hash<std::string>{}(symbol.m_name);
}
};
} // namespace std

View File

@@ -1,12 +1,8 @@
#include "format/input/symbol.h" #include "data_format/typed_symbol_list.h"
namespace format::input { namespace di::data_format {
SymbolListFile SymbolListFile::load(std::string_view path) { return load(std::vector<std::string>{path.data()}); }
SymbolListFile SymbolListFile::load(const std::vector<std::string>& paths) {
SymbolListFile result;
TypedSymbolList::TypedSymbolList(const std::vector<std::string>& paths) {
for (const auto& path : paths) { for (const auto& path : paths) {
std::ifstream ifs(path.data()); std::ifstream ifs(path.data());
if (!ifs) { if (!ifs) {
@@ -20,24 +16,19 @@ SymbolListFile SymbolListFile::load(const std::vector<std::string>& paths) {
auto separator_pos = line.find(", "); auto separator_pos = line.find(", ");
if (separator_pos == std::string::npos) { if (separator_pos == std::string::npos) {
throw std::runtime_error( throw std::runtime_error(
"Symbol data is not included declType, please re-generate symlist file with -record-decl-name." "Symbol data is not included declType, please re-generate "
"symlist file with -record-decl-name."
); );
} }
auto declType_s = line.substr(0, separator_pos); auto declType_s = line.substr(0, separator_pos);
auto symbol = line.substr(separator_pos + 2); auto symbol = line.substr(separator_pos + 2);
result.m_data.emplace(symbol, DeclType(declType_s)); m_data.emplace(symbol, DeclType(declType_s));
} }
std::println("Read {} symbols from dumped symlist.", result.m_data.size()); std::println("Read {} symbols from dumped symlist.", m_data.size());
} }
return result;
} }
void SymbolListFile::for_each(const std::function<void(Symbol)>& callback) { } // namespace di::data_format
for (const auto& entity : m_data) callback(entity);
}
} // namespace format::input

View File

@@ -0,0 +1,23 @@
#pragma once
#include "data_format/type/typed_symbol.h"
namespace di::data_format {
class TypedSymbolList {
public:
using for_each_callback_t = std::function<void(TypedSymbol const&)>;
explicit TypedSymbolList(const std::string& path)
: TypedSymbolList(std::vector<std::string>{path}) {};
explicit TypedSymbolList(const std::vector<std::string>& paths);
constexpr void for_each(const for_each_callback_t& callback) const {
for (const auto& entity : m_data) callback(entity);
}
private:
std::unordered_set<TypedSymbol> m_data;
};
} // namespace di::data_format

View File

@@ -0,0 +1,119 @@
#include "frontend_action/dump_symbol.h"
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/CompilerInstance.h>
using namespace clang;
namespace {
bool config_record_decl_name = false;
class Container : private std::unordered_set<std::string> {
public:
void put(const std::string& symbol) { emplace(symbol); }
void write_to(const std::string& path) {
std::ofstream ofs(path);
if (ofs) {
for (const auto& E : *this) {
ofs << E << "\n";
}
}
}
};
class Visitor : public RecursiveASTVisitor<Visitor> {
public:
Visitor(ASTContext& context, Container& container)
: m_namegen(context),
m_symbol_container(container) {}
bool VisitNamedDecl(NamedDecl* decl) {
if (!decl || !decl->getDeclName()) return true;
// FIXME: There are likely other contexts in which it makes
// no sense to ask for a mangled name.
if (isa<RequiresExprBodyDecl>(decl->getDeclContext())) return true;
// If the declaration is dependent or is in a dependent
// context, then the mangling is unlikely to be meaningful
// (and in some cases may cause "don't know how to mangle
// this" assertion failures.
if (decl->isTemplated()) return true;
// Mangled names are not meaningful for locals, and may not
// be well-defined in the case of VLAs.
auto* var_decl = dyn_cast<VarDecl>(decl);
if (var_decl && var_decl->hasLocalStorage()) return true;
// Do not mangle template deduction guides.
if (isa<CXXDeductionGuideDecl>(decl)) return true;
std::string mangled_name = m_namegen.getName(decl);
if (!mangled_name.empty()) {
if (config_record_decl_name) {
mangled_name = std::format(
"{}, {}",
decl->getDeclKindName(),
mangled_name
);
}
m_symbol_container.put(mangled_name);
}
return true;
}
private:
ASTNameGenerator m_namegen;
Container& m_symbol_container;
};
class Consumer : public ASTConsumer {
public:
explicit Consumer(ASTContext& Context) {}
void HandleTranslationUnit(ASTContext& Context) override {
Container Symbols;
Visitor(Context, Symbols)
.TraverseDecl(Context.getTranslationUnitDecl());
// Save
auto& SM = Context.getSourceManager();
auto Loc = SM.getLocForStartOfFile(SM.getMainFileID());
Symbols.write_to(SM.getFilename(Loc).str() + ".symbols");
}
};
} // namespace
namespace di::frontend_action {
std::unique_ptr<ASTConsumer> DumpSymbolFrontendAction::CreateASTConsumer(
CompilerInstance& instance,
llvm::StringRef
) {
return std::make_unique<Consumer>(instance.getASTContext());
}
bool DumpSymbolFrontendAction::ParseArgs(
const CompilerInstance&,
const std::vector<std::string>& args
) {
for (const auto& arg : args) {
if (arg.ends_with("record-decl-name")) {
config_record_decl_name = true;
}
}
return true;
}
PluginASTAction::ActionType DumpSymbolFrontendAction::getActionType() {
return AddAfterMainAction;
}
} // namespace di::frontend_action

View File

@@ -0,0 +1,27 @@
#pragma once
#include <clang/Frontend/FrontendAction.h>
namespace clang {
class ASTConsumer;
class CompilerInstance;
} // namespace clang
namespace di::frontend_action {
class DumpSymbolFrontendAction : public clang::PluginASTAction {
protected:
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance& instance,
llvm::StringRef
) override;
bool ParseArgs(
const clang::CompilerInstance&,
const std::vector<std::string>& args
) override;
ActionType getActionType() override;
};
} // namespace di::frontend_action

View File

@@ -1,5 +1,10 @@
#pragma once #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()));
}
// From: // From:
// https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set // https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set
@@ -29,7 +34,9 @@ struct HashValueImpl {
template <class Tuple> template <class Tuple>
struct HashValueImpl<Tuple, 0> { struct HashValueImpl<Tuple, 0> {
static void apply(size_t& seed, Tuple const& tuple) { hash_combine(seed, std::get<0>(tuple)); } static void apply(size_t& seed, Tuple const& tuple) {
hash_combine(seed, std::get<0>(tuple));
}
}; };
} // namespace } // namespace

View File

@@ -1,6 +1,6 @@
#include "binary/COFF.h" #include "object_file/COFF.h"
namespace makepdb::binary { namespace di::object_file {
COFF::COFF(std::string_view path) { COFF::COFF(std::string_view path) {
using namespace object; using namespace object;
@@ -67,4 +67,4 @@ object::COFFObjectFile const& COFF::get_owning_coff() const {
return *m_owning_binary.getBinary(); return *m_owning_binary.getBinary();
} }
} // namespace makepdb::binary } // namespace di::object_file

View File

@@ -2,7 +2,7 @@
#include <llvm/Object/COFF.h> #include <llvm/Object/COFF.h>
namespace makepdb::binary { namespace di::object_file {
class COFF { class COFF {
public: public:
@@ -20,4 +20,4 @@ private:
object::OwningBinary<object::COFFObjectFile> m_owning_binary; object::OwningBinary<object::COFFObjectFile> m_owning_binary;
}; };
} // namespace makepdb::binary } // namespace di::object_file

View File

@@ -1,15 +1,15 @@
#include "binary/PDB.h" #include "object_file/PDB.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h" #include <llvm/DebugInfo/MSF/MSFBuilder.h>
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" #include <llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h>
#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h" #include <llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h>
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" #include <llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h>
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include <llvm/DebugInfo/PDB/Native/RawConstants.h> #include <llvm/DebugInfo/PDB/Native/RawConstants.h>
#include <llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h>
using namespace llvm::pdb; using namespace llvm::pdb;
namespace makepdb::binary { namespace di::object_file {
PDB::PDB() : m_builder(m_allocator) { PDB::PDB() : m_builder(m_allocator) {
constexpr uint32_t block_size = 4096; constexpr uint32_t block_size = 4096;
@@ -90,12 +90,12 @@ void PDB::build_TPI() {
IPI.setVersionHeader(PdbRaw_TpiVer::PdbTpiV80); IPI.setVersionHeader(PdbRaw_TpiVer::PdbTpiV80);
if (m_owning_raw_type_data) { if (m_owning_raw_type_data) {
m_owning_raw_type_data->for_each<RawTypeData::TPI>( m_owning_raw_type_data->for_each<data_format::RawTypeData::TPI>(
[&TPI](codeview::TypeIndex index, const codeview::CVType& type) { [&TPI](codeview::TypeIndex index, const codeview::CVType& type) {
TPI.addTypeRecord(type.RecordData, std::nullopt); TPI.addTypeRecord(type.RecordData, std::nullopt);
} }
); );
m_owning_raw_type_data->for_each<RawTypeData::IPI>( m_owning_raw_type_data->for_each<data_format::RawTypeData::IPI>(
[&IPI](codeview::TypeIndex index, const codeview::CVType& type) { [&IPI](codeview::TypeIndex index, const codeview::CVType& type) {
IPI.addTypeRecord(type.RecordData, std::nullopt); IPI.addTypeRecord(type.RecordData, std::nullopt);
} }
@@ -105,24 +105,23 @@ void PDB::build_TPI() {
void PDB::build_GSI() { void PDB::build_GSI() {
std::vector<BulkPublic> publics; std::vector<BulkPublic> publics;
m_owning_symbol_data->for_each([&publics, m_owning_symbol_data->for_each([&publics, this](const BoundSymbol& entity) {
this](const SymbolDataEntity& entity) {
BulkPublic symbol; BulkPublic symbol;
auto section_index = auto section_index =
m_owning_coff->get_section_index(entity.rva - m_image_base); m_owning_coff->get_section_index(entity.m_rva - m_image_base);
auto section_or_err = auto section_or_err =
m_owning_coff->get_owning_coff().getSection(section_index + 1); m_owning_coff->get_owning_coff().getSection(section_index + 1);
if (!section_or_err) { if (!section_or_err) {
throw std::runtime_error("Invalid section."); throw std::runtime_error("Invalid section.");
} }
symbol.Name = strdup(entity.symbol_name.c_str()); symbol.Name = strdup(entity.m_symbol_name.c_str());
symbol.NameLen = entity.symbol_name.size(); symbol.NameLen = entity.m_symbol_name.size();
symbol.Segment = section_index + 1; symbol.Segment = section_index + 1;
symbol.Offset = symbol.Offset =
entity.rva - m_image_base - section_or_err.get()->VirtualAddress; entity.m_rva - m_image_base - section_or_err.get()->VirtualAddress;
if (entity.is_function) if (entity.m_is_function)
symbol.setFlags(codeview::PublicSymFlags::Function); symbol.setFlags(codeview::PublicSymFlags::Function);
publics.emplace_back(symbol); publics.emplace_back(symbol);
@@ -131,4 +130,4 @@ void PDB::build_GSI() {
m_builder.getGsiBuilder().addPublicSymbols(std::move(publics)); m_builder.getGsiBuilder().addPublicSymbols(std::move(publics));
} }
} // namespace makepdb::binary } // namespace di::object_file

53
src/object_file/PDB.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "object_file/COFF.h"
#include "data_format/bound_symbol_list.h"
#include "data_format/raw_type_data.h"
#include <llvm/DebugInfo/PDB/Native/PDBFileBuilder.h>
#include <llvm/Support/Allocator.h>
namespace di::object_file {
class PDB {
public:
using owning_coff_t = std::unique_ptr<COFF>;
using owning_symbol_data_t = std::unique_ptr<data_format::BoundSymbolList>;
using owning_type_data_t = std::unique_ptr<data_format::RawTypeData>;
explicit PDB();
void set_coff_object(owning_coff_t coff_object) {
m_owning_coff = std::move(coff_object);
m_image_base = m_owning_coff->get_owning_coff().getImageBase();
}
void set_symbol_data(owning_symbol_data_t symbol_data) {
m_owning_symbol_data = std::move(symbol_data);
}
void set_raw_type_data(owning_type_data_t raw_type_data) {
m_owning_raw_type_data = std::move(raw_type_data);
}
void write(std::string_view path);
private:
void build();
inline void build_Info();
inline void build_DBI();
inline void build_TPI();
inline void build_GSI();
owning_coff_t m_owning_coff;
owning_symbol_data_t m_owning_symbol_data;
owning_type_data_t m_owning_raw_type_data;
uint64_t m_image_base;
BumpPtrAllocator m_allocator;
pdb::PDBFileBuilder m_builder;
};
} // namespace di::object_file

View File

@@ -1,13 +1,18 @@
// Work on llvm namespace.
namespace llvm {}
using namespace llvm;
// Standard Libraries // Standard Libraries
#include <exception>
#include <functional>
#include <fstream> #include <fstream>
#include <print> #include <print>
#include <exception>
#include <functional>
#include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
@@ -16,5 +21,8 @@
#include <type_traits> #include <type_traits>
#include <bitset>
// Helper // Helper
#include "nonstd.h" #include "nonstd.h"

View File

@@ -1,22 +1,26 @@
#include "format/input/all.h"
#include "format/output/all.h"
#include "util/string.h" #include "util/string.h"
#include "data_format/bound_symbol_list.h"
#include "data_format/human_readable_symbol_list.h"
#include "data_format/raw_text.h"
#include "data_format/typed_symbol_list.h"
#include <argparse/argparse.hpp> #include <argparse/argparse.hpp>
#if DI_USE_NATIVE_SYMBOL_RESOLVER
#include <pl/SymbolProvider.h> #include <pl/SymbolProvider.h>
#endif
using namespace format; enum class OutputFormat { Text, MakePDB };
constexpr auto VERSION = "1.0.0"; using namespace di;
[[nodiscard]] auto load_args(int argc, char* argv[]) { [[nodiscard]] auto load_args(int argc, char* argv[]) {
argparse::ArgumentParser program("askrva", VERSION); argparse::ArgumentParser program("askrva");
struct { struct {
OutputFormat m_output_format; OutputFormat m_output_format;
std::vector<std::string> m_input_path; std::vector<std::string> m_input_paths;
std::string m_output_path; std::string m_output_path;
std::optional<std::string> m_output_failed_path; std::optional<std::string> m_output_failed_path;
@@ -28,7 +32,7 @@ constexpr auto VERSION = "1.0.0";
program.add_argument("path") program.add_argument("path")
.help("Path to the symbol list file.") .help("Path to the symbol list file.")
.store_into(args.m_input_path) .store_into(args.m_input_paths)
.nargs(argparse::nargs_pattern::at_least_one) .nargs(argparse::nargs_pattern::at_least_one)
.required(); .required();
@@ -42,7 +46,7 @@ constexpr auto VERSION = "1.0.0";
program.add_argument("--output-format") program.add_argument("--output-format")
.help("Specify output format.") .help("Specify output format.")
.choices("auto", "text", "fakepdb", "makepdb") .choices("auto", "text", "makepdb")
.default_value("auto") .default_value("auto")
.store_into(output_format); .store_into(output_format);
@@ -56,8 +60,6 @@ constexpr auto VERSION = "1.0.0";
switch (H(output_format)) { switch (H(output_format)) {
case H("text"): case H("text"):
return OutputFormat::Text; return OutputFormat::Text;
case H("fakepdb"):
return OutputFormat::FakePDB;
case H("makepdb"): case H("makepdb"):
return OutputFormat::MakePDB; return OutputFormat::MakePDB;
case H("auto"): case H("auto"):
@@ -81,23 +83,36 @@ constexpr auto VERSION = "1.0.0";
int main(int argc, char* argv[]) try { int main(int argc, char* argv[]) try {
auto args = load_args(argc, argv); auto args = load_args(argc, argv);
auto symlist = input::SymbolListFile::load(args.m_input_path); auto symlist = data_format::TypedSymbolList(args.m_input_paths);
auto output_file = output::create(args.m_output_format); data_format::BoundSymbolList bound_symbol_list;
data_format::HumanReadableSymbolList human_readable_symbol_list;
data_format::RawText raw_text;
symlist.for_each([&output_file](const input::Symbol& symbol) { symlist.for_each([&](const TypedSymbol& symbol) {
auto& sym = symbol.m_name; auto& sym = symbol.m_name;
auto rva = pl::symbol_provider::pl_resolve_symbol_silent_n(sym.c_str(), sym.size()); #if DI_USE_NATIVE_SYMBOL_RESOLVER
auto rva = pl::symbol_provider::pl_resolve_symbol_silent_n(
sym.c_str(),
sym.size()
);
#else
auto rva = (void*)nullptr; // TODO
#endif
if (rva) { if (rva) {
output_file->record(symbol.m_name, reinterpret_cast<uint64_t>(rva), symbol.m_type.isFunction()); bound_symbol_list.record(
symbol.m_name,
reinterpret_cast<uint64_t>(rva),
symbol.m_type.is_function()
);
} else { } else {
output_file->record_failure(symbol.m_name); raw_text.record(symbol.m_name);
} }
}); });
output_file->save(args.m_output_path); bound_symbol_list.write_to(args.m_output_path);
if (args.m_output_failed_path) { if (args.m_output_failed_path) {
output_file->save_failure(*args.m_output_failed_path); raw_text.write_to(*args.m_output_failed_path);
} }
std::println("Everything is OK."); std::println("Everything is OK.");

View File

@@ -1,8 +1,3 @@
#include <bitset>
#include <cstdint>
#include <fstream>
#include <print>
#include <unordered_map>
#define XXH_INLINE_ALL #define XXH_INLINE_ALL
#include "xxhash.h" #include "xxhash.h"

View File

@@ -0,0 +1,9 @@
#include "frontend_action/dump_symbol.h"
#include <clang/Frontend/FrontendPluginRegistry.h>
using namespace clang;
static FrontendPluginRegistry::Add<
di::frontend_action::DumpSymbolFrontendAction>
X("dumpsym", "Extract all declared symbols from a TU.");

View File

@@ -9,9 +9,6 @@
#include <argparse/argparse.hpp> #include <argparse/argparse.hpp>
#include <fstream>
#include <print>
using namespace llvm; using namespace llvm;
using namespace llvm::pdb; using namespace llvm::pdb;
using namespace llvm::codeview; using namespace llvm::codeview;

View File

@@ -1,16 +1,16 @@
#include <argparse/argparse.hpp> #include <argparse/argparse.hpp>
#include "binary/COFF.h" #include "object_file/COFF.h"
#include "binary/PDB.h" #include "object_file/PDB.h"
#include "raw_type_data.h" #include "data_format/bound_symbol_list.h"
#include "symbol_data.h" #include "data_format/raw_type_data.h"
using namespace makepdb; using namespace di;
[[nodiscard]] auto load_args(int argc, char* argv[]) { [[nodiscard]] auto load_args(int argc, char* argv[]) {
argparse::ArgumentParser program("makepdb", "1.1.0"); argparse::ArgumentParser program("makepdb");
struct { struct {
std::string server_program_path; std::string server_program_path;
@@ -53,16 +53,18 @@ int main(int argc, char* argv[]) try {
auto args = load_args(argc, argv); auto args = load_args(argc, argv);
auto server_program = auto server_program =
std::make_unique<binary::COFF>(args.server_program_path); std::make_unique<object_file::COFF>(args.server_program_path);
auto symbol_data = std::make_unique<SymbolData>(args.symbol_data_path); auto symbol_data =
std::make_unique<data_format::BoundSymbolList>(args.symbol_data_path);
std::unique_ptr<RawTypeData> raw_type_data; std::unique_ptr<data_format::RawTypeData> raw_type_data;
if (args.typeinfo_pdb_path) { if (args.typeinfo_pdb_path) {
raw_type_data = std::make_unique<RawTypeData>(*args.typeinfo_pdb_path); raw_type_data =
std::make_unique<data_format::RawTypeData>(*args.typeinfo_pdb_path);
} }
binary::PDB pdb; object_file::PDB pdb;
pdb.set_coff_object(std::move(server_program)); pdb.set_coff_object(std::move(server_program));
pdb.set_symbol_data(std::move(symbol_data)); pdb.set_symbol_data(std::move(symbol_data));
pdb.set_raw_type_data(std::move(raw_type_data)); pdb.set_raw_type_data(std::move(raw_type_data));

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
namespace util::string { namespace di::util::string {
constexpr unsigned int H(std::string_view str, unsigned int hash = 0) { constexpr unsigned int H(std::string_view str, unsigned int hash = 0) {
for (char c : str) hash = hash * 31 + static_cast<unsigned int>(c); for (char c : str) hash = hash * 31 + static_cast<unsigned int>(c);
return hash; return hash;
} }
} // namespace util::string } // namespace di::util::string

120
xmake.lua Normal file
View File

@@ -0,0 +1,120 @@
add_rules('mode.debug', 'mode.release')
add_requires('argparse 3.1')
add_requires('nlohmann_json 3.11.3')
add_requires('xxhash 0.8.3')
add_requires('boost 1.87.0')
add_requires('llvm')
--- options
option('symbol-resolver')
set_default('builtin')
set_showmenu(true)
set_description('Select a symbol resolver.')
set_values('builtin', 'native')
before_check(function (option)
-- the native symbol resolution backend is only available under windows, because liteldev
-- has not released a linux version.
if option:value() == 'native' and not is_plat('windows') then
raise('the native symbol resolver does not support this platform.')
end
end)
option_end()
if is_config('symbol-resolver', 'native') then
add_repositories('liteldev-repo https://github.com/LiteLDev/xmake-repo.git')
add_requires('preloader 1.12.0')
end
--- global settings
set_languages('c23', 'c++23')
set_warnings('all')
add_includedirs('src')
set_policy("build.optimization.lto", true)
if is_mode('debug') then
add_defines('DI_DEBUG')
end
--- targets
target('libdi')
set_kind('static')
add_files('src/**.cpp')
set_pcxxheader('src/pch.h')
set_basename('di')
add_packages(
'nlohmann_json'
)
remove_files('src/tools/**')
target('askrva')
set_kind('binary')
add_deps('libdi')
add_files('src/tools/askrva/**.cpp')
set_pcxxheader('src/pch.h')
add_packages(
'argparse',
'nlohmann_json'
)
if is_config('symbol-resolver', 'native') then
add_packages('preloader')
add_defines('DI_USE_NATIVE_SYMBOL_RESOLVER=1')
end
target('blob-extractor')
set_kind('binary')
add_deps('libdi')
add_files('src/tools/blob-extractor/**.cpp')
set_pcxxheader('src/pch.h')
target('dumpsym')
set_kind('shared')
add_deps('libdi')
add_files('src/tools/dumpsym/**.cpp')
set_pcxxheader('src/pch.h')
add_packages(
'llvm'
)
target('extractsym')
set_kind('binary')
add_deps('libdi')
add_files('src/tools/extractsym/**.cpp')
set_pcxxheader('src/pch.h')
add_packages(
'llvm',
'argparse'
)
if is_plat('linux') then -- workaround to fix link problem.
add_links('LLVM')
end
target('makepdb')
set_kind('binary')
add_deps('libdi')
add_files('src/tools/makepdb/**.cpp')
set_pcxxheader('src/pch.h')
add_packages(
'llvm',
'nlohmann_json',
'argparse'
)
if is_plat('linux') then
add_links('LLVM')
end