refactor: new project structure.
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# vscode
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# c++/xmake
|
||||||
|
.xmake
|
||||||
|
build
|
||||||
|
|
||||||
|
# c++/clangd
|
||||||
|
.cache
|
||||||
|
.clangd
|
||||||
|
|
||||||
|
# python
|
||||||
|
__pycache__
|
||||||
|
.venv
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "DumpSYM"]
|
|
||||||
path = DumpSYM
|
|
||||||
url = https://github.com/Redbeanw44602/dumpsym.git
|
|
||||||
@@ -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
9
AskRVA/.gitignore
vendored
@@ -1,9 +0,0 @@
|
|||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# XMake
|
|
||||||
.xmake
|
|
||||||
build
|
|
||||||
|
|
||||||
# ClangD
|
|
||||||
.cache
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "symbol.h"
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
9
BlobExtractor/.gitignore
vendored
9
BlobExtractor/.gitignore
vendored
@@ -1,9 +0,0 @@
|
|||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# XMake
|
|
||||||
.xmake
|
|
||||||
build
|
|
||||||
|
|
||||||
# ClangD
|
|
||||||
.cache
|
|
||||||
@@ -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
|
|
||||||
5
Bootstrap/.gitignore
vendored
5
Bootstrap/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
# Python
|
|
||||||
.venv
|
|
||||||
|
|
||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
6
DeThunk/.gitignore
vendored
6
DeThunk/.gitignore
vendored
@@ -1,6 +0,0 @@
|
|||||||
# Python
|
|
||||||
__pycache__
|
|
||||||
.venv
|
|
||||||
|
|
||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
@@ -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"
|
|
||||||
1
DumpSYM
1
DumpSYM
Submodule DumpSYM deleted from 6cf0947308
@@ -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
ExtractSYM/.gitignore
vendored
9
ExtractSYM/.gitignore
vendored
@@ -1,9 +0,0 @@
|
|||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# XMake
|
|
||||||
.xmake
|
|
||||||
build
|
|
||||||
|
|
||||||
# ClangD
|
|
||||||
.cache
|
|
||||||
@@ -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
|
|
||||||
@@ -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
9
MakePDB/.gitignore
vendored
@@ -1,9 +0,0 @@
|
|||||||
# VSCode
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# XMake
|
|
||||||
.xmake
|
|
||||||
build
|
|
||||||
|
|
||||||
# ClangD
|
|
||||||
.cache
|
|
||||||
@@ -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
|
|
||||||
@@ -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()));
|
|
||||||
}
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
60
README.md
60
README.md
@@ -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.
|
||||||
|
|||||||
@@ -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
38
docs/dump-sym.md
Normal 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
|
||||||
|
```
|
||||||
55
src/data_format/bound_symbol_list.cpp
Normal file
55
src/data_format/bound_symbol_list.cpp
Normal 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
|
||||||
25
src/data_format/bound_symbol_list.h
Normal file
25
src/data_format/bound_symbol_list.h
Normal 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
|
||||||
@@ -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
|
||||||
14
src/data_format/human_readable_symbol_list.h
Normal file
14
src/data_format/human_readable_symbol_list.h
Normal 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
|
||||||
19
src/data_format/raw_text.cpp
Normal file
19
src/data_format/raw_text.cpp
Normal 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
|
||||||
14
src/data_format/raw_text.h
Normal file
14
src/data_format/raw_text.h
Normal 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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
33
src/data_format/type/bound_symbol.h
Normal file
33
src/data_format/type/bound_symbol.h
Normal 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
|
||||||
75
src/data_format/type/decl_type.h
Normal file
75
src/data_format/type/decl_type.h
Normal 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
|
||||||
27
src/data_format/type/typed_symbol.h
Normal file
27
src/data_format/type/typed_symbol.h
Normal 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
|
||||||
@@ -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
|
|
||||||
23
src/data_format/typed_symbol_list.h
Normal file
23
src/data_format/typed_symbol_list.h
Normal 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
|
||||||
119
src/frontend_action/dump_symbol.cpp
Normal file
119
src/frontend_action/dump_symbol.cpp
Normal 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
|
||||||
27
src/frontend_action/dump_symbol.h
Normal file
27
src/frontend_action/dump_symbol.h
Normal 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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
53
src/object_file/PDB.h
Normal 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
|
||||||
@@ -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"
|
||||||
@@ -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.");
|
||||||
@@ -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"
|
||||||
9
src/tools/dumpsym/plugin.cpp
Normal file
9
src/tools/dumpsym/plugin.cpp
Normal 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.");
|
||||||
@@ -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;
|
||||||
@@ -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));
|
||||||
@@ -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
120
xmake.lua
Normal 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
|
||||||
Reference in New Issue
Block a user