refactor: unified exception handling for llvm.
This commit is contained in:
@@ -49,7 +49,7 @@ public:
|
||||
HSTR(VarTemplateSpecialization);
|
||||
#undef HSTR
|
||||
default:
|
||||
throw EnumTransformException(str, Enum{});
|
||||
throw ConvertEnumException(str, TypeOnly<Enum>{});
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
HSTR(VarTemplateSpecialization);
|
||||
#undef HSTR
|
||||
default:
|
||||
throw EnumTransformException(m_data, Enum{});
|
||||
throw ConvertEnumException(m_data);
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -40,6 +40,10 @@ public:
|
||||
add_context("path", list_file_path.string());
|
||||
add_context("current_line", current_line);
|
||||
}
|
||||
|
||||
constexpr std::string category() const {
|
||||
return "exception.dataformat.missingdecltype";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace di::data_format
|
||||
|
||||
73
src/error.h
73
src/error.h
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
|
||||
namespace di {
|
||||
|
||||
class BaseException {
|
||||
@@ -30,7 +32,7 @@ public:
|
||||
return static_cast<const Derived*>(this)->category();
|
||||
}
|
||||
|
||||
std::string what() const override {
|
||||
std::string what() const final {
|
||||
// [exception.base] Reason...
|
||||
//
|
||||
// Context Information:
|
||||
@@ -82,6 +84,12 @@ protected:
|
||||
m_context_information[key] = std::format("{}", std::forward<T>(value));
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
constexpr void add_context_v_hex(const std::string& key, T value) {
|
||||
m_context_information[key] =
|
||||
std::format("{:#x}", std::forward<T>(value));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_reason;
|
||||
|
||||
@@ -99,26 +107,79 @@ public:
|
||||
constexpr std::string category() const { return "exception.unix"; }
|
||||
};
|
||||
|
||||
class EnumTransformException : public RuntimeException<EnumTransformException> {
|
||||
class ConvertEnumException : public RuntimeException<ConvertEnumException> {
|
||||
public:
|
||||
// TODO: compile-time reflection.
|
||||
// TODO: remove helper.
|
||||
|
||||
template <typename T>
|
||||
explicit EnumTransformException(int enum_val, T _helper = {})
|
||||
template <Enumerate T>
|
||||
explicit ConvertEnumException(T enum_val)
|
||||
: RuntimeException("Unable to convert string to enumeration value because "
|
||||
"input value is bad.") {
|
||||
add_context("enum_type", typeid(T).name());
|
||||
add_context("value", enum_val);
|
||||
add_context("value", underlying_value(enum_val));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
explicit EnumTransformException(std::string_view enum_str, T _helper = {})
|
||||
explicit ConvertEnumException(std::string_view enum_str, TypeOnly<T>)
|
||||
: RuntimeException("Unable to convert enumeration value to string because "
|
||||
"input value is bad.") {
|
||||
add_context("enum_type", typeid(T).name());
|
||||
add_context("string", enum_str);
|
||||
}
|
||||
|
||||
constexpr std::string category() const { return "exception.enumconvert"; }
|
||||
};
|
||||
|
||||
class LLVMException : public RuntimeException<LLVMException> {
|
||||
public:
|
||||
explicit LLVMException(
|
||||
std::string_view error_message_di = "",
|
||||
std::string_view error_message_llvm = ""
|
||||
)
|
||||
: RuntimeException("There were some problems when calling LLVM.") {
|
||||
if (!error_message_di.empty())
|
||||
add_context("error_message_di", error_message_di);
|
||||
if (!error_message_llvm.empty())
|
||||
add_context("error_message_llvm", error_message_llvm);
|
||||
}
|
||||
|
||||
constexpr std::string category() const { return "exception.llvm"; }
|
||||
};
|
||||
|
||||
constexpr void check_llvm_result(Error err, std::string_view msg = "") {
|
||||
if (err) {
|
||||
std::string err_detail;
|
||||
raw_string_ostream os(err_detail);
|
||||
os << err;
|
||||
throw LLVMException(msg, err_detail);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T
|
||||
check_llvm_result(Expected<T> val_or_err, std::string_view msg = "") {
|
||||
if (val_or_err) return std::move(*val_or_err);
|
||||
else {
|
||||
std::string err_detail;
|
||||
raw_string_ostream os(err_detail);
|
||||
auto err = val_or_err.takeError();
|
||||
os << err;
|
||||
throw LLVMException(msg, err_detail);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T&
|
||||
check_llvm_result(Expected<T&> val_or_err, std::string_view msg = "") {
|
||||
if (val_or_err) return *val_or_err;
|
||||
else {
|
||||
std::string err_detail;
|
||||
raw_string_ostream os(err_detail);
|
||||
auto err = val_or_err.takeError();
|
||||
os << err;
|
||||
throw LLVMException(msg, err_detail);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace di
|
||||
|
||||
@@ -5,6 +5,14 @@ constexpr auto static_unique_ptr_cast(std::unique_ptr<From>&& F) {
|
||||
return std::unique_ptr<To>(static_cast<To*>(F.release()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept Enumerate = std::is_enum_v<T>;
|
||||
|
||||
template <Enumerate T>
|
||||
constexpr auto underlying_value(T v) {
|
||||
return static_cast<std::underlying_type_t<decltype(v)>>(v);
|
||||
}
|
||||
|
||||
// From:
|
||||
// https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
#include "object_file/coff.h"
|
||||
#include "error.h"
|
||||
|
||||
namespace di::object_file {
|
||||
|
||||
COFF::COFF(const fs::path& path) {
|
||||
using namespace object;
|
||||
|
||||
auto obj_or_err = ObjectFile::createObjectFile(path.string());
|
||||
if (!obj_or_err) {
|
||||
throw std::runtime_error("Failed to create object file.");
|
||||
}
|
||||
auto file = check_llvm_result(ObjectFile::createObjectFile(path.string()));
|
||||
|
||||
if (!isa<COFFObjectFile>(obj_or_err->getBinary())) {
|
||||
throw std::runtime_error("Is not a valid PE file.");
|
||||
}
|
||||
if (!isa<COFFObjectFile>(file.getBinary()))
|
||||
throw UnexceptObjectException(path, TypeOnly<COFFObjectFile>{});
|
||||
|
||||
auto bin = obj_or_err->takeBinary();
|
||||
auto bin = file.takeBinary();
|
||||
|
||||
m_owning_binary = object::OwningBinary(
|
||||
static_unique_ptr_cast<COFFObjectFile>(std::move(bin.first)),
|
||||
@@ -28,11 +25,13 @@ codeview::PDB70DebugInfo COFF::get_debug_info() const {
|
||||
|
||||
if (get_owning_coff().getDebugPDBInfo(debug_info, pdb_file_name)
|
||||
|| !debug_info) {
|
||||
throw std::runtime_error("Failed to get pdb info from coff file.");
|
||||
throw MissingPDBInfoException();
|
||||
}
|
||||
|
||||
if (debug_info->Signature.CVSignature != OMF::Signature::PDB70) {
|
||||
throw std::runtime_error("Unsupported PDB format.");
|
||||
|
||||
if (auto signature = debug_info->Signature.CVSignature;
|
||||
signature != OMF::Signature::PDB70) {
|
||||
throw UnsupportPDBFormatException(signature);
|
||||
}
|
||||
|
||||
return debug_info->PDB70;
|
||||
@@ -50,7 +49,7 @@ size_t COFF::get_section_index(size_t offset) const {
|
||||
}
|
||||
current_index++;
|
||||
}
|
||||
throw std::runtime_error("Offset is not in any section.");
|
||||
throw SectionNotFoundException(offset);
|
||||
}
|
||||
|
||||
object::coff_section* COFF::get_section_table() {
|
||||
|
||||
@@ -20,4 +20,52 @@ private:
|
||||
object::OwningBinary<object::COFFObjectFile> m_owning_binary;
|
||||
};
|
||||
|
||||
class UnexceptObjectException : public LLVMException {
|
||||
public:
|
||||
template <typename T>
|
||||
explicit UnexceptObjectException(const fs::path& path, TypeOnly<T>)
|
||||
: LLVMException("Unexpected ObjectFile!") {
|
||||
add_context("path", path.string());
|
||||
add_context("excepted", typeid(T).name());
|
||||
}
|
||||
|
||||
constexpr std::string category() const {
|
||||
return "exception.llvm.invalidobject";
|
||||
}
|
||||
};
|
||||
|
||||
class MissingPDBInfoException : public LLVMException {
|
||||
public:
|
||||
explicit MissingPDBInfoException()
|
||||
: LLVMException("No PDB Info found in COFF file.") {}
|
||||
|
||||
constexpr std::string category() const {
|
||||
return "exception.llvm.missingpdbinfo";
|
||||
}
|
||||
};
|
||||
|
||||
class UnsupportPDBFormatException : public LLVMException {
|
||||
public:
|
||||
explicit UnsupportPDBFormatException(uint32_t signature)
|
||||
: LLVMException("Unsupported PDB file format.") {
|
||||
add_context_v_hex("signature", signature);
|
||||
}
|
||||
|
||||
constexpr std::string category() const {
|
||||
return "exception.llvm.missingpdbinfo";
|
||||
}
|
||||
};
|
||||
|
||||
class SectionNotFoundException : public LLVMException {
|
||||
public:
|
||||
explicit SectionNotFoundException(size_t offset)
|
||||
: LLVMException("The offset is not within any section.") {
|
||||
add_context_v_hex("offset", offset);
|
||||
}
|
||||
|
||||
constexpr std::string category() const {
|
||||
return "exception.llvm.sectionnotfound";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace di::object_file
|
||||
|
||||
@@ -22,58 +22,38 @@ using namespace llvm::codeview;
|
||||
namespace di::object_file {
|
||||
|
||||
void PDB::read(const fs::path& path) {
|
||||
if (loadDataForPDB(PDB_ReaderType::Native, path.string(), m_session)) {
|
||||
throw std::runtime_error("Failed to load PDB.");
|
||||
}
|
||||
check_llvm_result(
|
||||
loadDataForPDB(PDB_ReaderType::Native, path.string(), m_session)
|
||||
);
|
||||
|
||||
std::unique_ptr<IPDBSession> pdb_session;
|
||||
if (llvm::pdb::loadDataForPDB(
|
||||
PDB_ReaderType::Native,
|
||||
path.string(),
|
||||
pdb_session
|
||||
)) {
|
||||
throw std::runtime_error("Failed to load PDB.");
|
||||
}
|
||||
|
||||
auto native_session = static_cast<NativeSession*>(pdb_session.get());
|
||||
auto& pdb_file = native_session->getPDBFile();
|
||||
auto& pdb_file = get_native_session().getPDBFile();
|
||||
|
||||
SmallVector<codeview::TypeIndex, 128> type_map;
|
||||
SmallVector<codeview::TypeIndex, 128> id_map;
|
||||
|
||||
if (auto tpi_stream = pdb_file.getPDBTpiStream()) {
|
||||
if (codeview::mergeTypeRecords(
|
||||
auto& tpi_stream = check_llvm_result(pdb_file.getPDBTpiStream());
|
||||
|
||||
check_llvm_result(codeview::mergeTypeRecords(
|
||||
*m_storaged_Tpi,
|
||||
type_map,
|
||||
(*tpi_stream).typeArray()
|
||||
)) {
|
||||
throw std::runtime_error("Failed to merge type record.");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("TPI is not valid.");
|
||||
}
|
||||
tpi_stream.typeArray()
|
||||
));
|
||||
|
||||
if (auto ipi_stream = pdb_file.getPDBIpiStream()) {
|
||||
if (codeview::mergeIdRecords(
|
||||
auto& ipi_stream = check_llvm_result(pdb_file.getPDBIpiStream());
|
||||
|
||||
check_llvm_result(codeview::mergeIdRecords(
|
||||
*m_storaged_Ipi,
|
||||
type_map,
|
||||
id_map,
|
||||
(*ipi_stream).typeArray()
|
||||
)) {
|
||||
throw std::runtime_error("Failed to merge id record.");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("IPI is not valid.");
|
||||
}
|
||||
ipi_stream.typeArray()
|
||||
));
|
||||
}
|
||||
|
||||
void PDB::_write(const fs::path& path) {
|
||||
build();
|
||||
|
||||
GUID out_guid;
|
||||
if (m_builder->commit(path.string(), &out_guid)) {
|
||||
throw std::runtime_error("Failed to create pdb!");
|
||||
}
|
||||
check_llvm_result(m_builder->commit(path.string(), &out_guid));
|
||||
}
|
||||
|
||||
NativeSession& PDB::get_native_session() {
|
||||
@@ -85,42 +65,28 @@ pdb::PDBFile& PDB::get_pdb_file() { return get_native_session().getPDBFile(); }
|
||||
void PDB::_for_each_public(const for_each_symbol_callback_t& callback) {
|
||||
using namespace codeview;
|
||||
auto& file = get_pdb_file();
|
||||
auto publics_stream = file.getPDBPublicsStream();
|
||||
if (!publics_stream) {
|
||||
throw std::runtime_error("Failed to get public stream from PDB.");
|
||||
}
|
||||
auto& publics_stream = check_llvm_result(file.getPDBPublicsStream());
|
||||
auto& symbol_stream = check_llvm_result(file.getPDBSymbolStream());
|
||||
|
||||
auto publics_symbol_stream = file.getPDBSymbolStream();
|
||||
if (!publics_symbol_stream) {
|
||||
throw std::runtime_error("Failed to get symbol stream from PDB.");
|
||||
}
|
||||
|
||||
auto publics_symbols =
|
||||
publics_symbol_stream->getSymbolArray().getUnderlyingStream();
|
||||
for (auto offset : publics_stream->getPublicsTable()) {
|
||||
auto cv_symbol = readSymbolFromStream(publics_symbols, offset);
|
||||
auto public_sym32 =
|
||||
SymbolDeserializer::deserializeAs<PublicSym32>(cv_symbol.get());
|
||||
if (!public_sym32) {
|
||||
throw std::runtime_error("Unsupported symbol type.");
|
||||
}
|
||||
callback(*public_sym32);
|
||||
auto raw_stream = symbol_stream.getSymbolArray().getUnderlyingStream();
|
||||
for (auto offset : publics_stream.getPublicsTable()) {
|
||||
auto cv_symbol = readSymbolFromStream(raw_stream, offset);
|
||||
auto public_sym32 = check_llvm_result(
|
||||
SymbolDeserializer::deserializeAs<PublicSym32>(cv_symbol.get()),
|
||||
"Unsupported symbol type."
|
||||
);
|
||||
callback(public_sym32);
|
||||
}
|
||||
}
|
||||
|
||||
void PDB::build() {
|
||||
constexpr auto block_size = 4096;
|
||||
|
||||
m_builder.reset(new PDBFileBuilder{m_Alloc});
|
||||
|
||||
if (m_builder->initialize(block_size)) {
|
||||
throw std::runtime_error("Failed to initialize pdb file builder.");
|
||||
}
|
||||
constexpr auto block_size = 4096;
|
||||
check_llvm_result(m_builder->initialize(block_size));
|
||||
|
||||
for (uint32_t idx = 0; idx < pdb::kSpecialStreamCount; ++idx) {
|
||||
if (!m_builder->getMsfBuilder().addStream(0)) {
|
||||
throw std::runtime_error("Failed to add initial stream.");
|
||||
}
|
||||
check_llvm_result(m_builder->getMsfBuilder().addStream(0));
|
||||
}
|
||||
|
||||
// INFO
|
||||
@@ -163,9 +129,9 @@ void PDB::build() {
|
||||
Dbi.createSectionMap(section_table_ref);
|
||||
|
||||
// Add COFF section header stream.
|
||||
if (Dbi.addDbgStream(DbgHeaderType::SectionHdr, section_data_ref)) {
|
||||
throw std::runtime_error("Failed to add dbg stream.");
|
||||
}
|
||||
check_llvm_result(
|
||||
Dbi.addDbgStream(DbgHeaderType::SectionHdr, section_data_ref)
|
||||
);
|
||||
}
|
||||
|
||||
// TPI & IPI
|
||||
@@ -198,17 +164,15 @@ void PDB::build() {
|
||||
|
||||
auto section_index =
|
||||
m_owning_coff->get_section_index(entity.m_rva - m_image_base);
|
||||
auto section_or_err =
|
||||
m_owning_coff->get_owning_coff().getSection(section_index + 1);
|
||||
if (!section_or_err) {
|
||||
throw std::runtime_error("Invalid section.");
|
||||
}
|
||||
auto section = check_llvm_result(
|
||||
m_owning_coff->get_owning_coff().getSection(section_index + 1)
|
||||
);
|
||||
|
||||
symbol.Name = strdup(entity.m_symbol_name.c_str());
|
||||
symbol.NameLen = entity.m_symbol_name.size();
|
||||
symbol.Segment = section_index + 1;
|
||||
symbol.Offset = entity.m_rva - m_image_base
|
||||
- section_or_err.get()->VirtualAddress;
|
||||
symbol.Offset =
|
||||
entity.m_rva - m_image_base - section->VirtualAddress;
|
||||
if (entity.m_is_function) symbol.setFlags(PublicSymFlags::Function);
|
||||
|
||||
publics.emplace_back(symbol);
|
||||
|
||||
@@ -10,3 +10,7 @@ using hash_t = uint64_t;
|
||||
} // namespace di
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// shit!
|
||||
template <typename T>
|
||||
struct TypeOnly {};
|
||||
|
||||
@@ -36,6 +36,15 @@ set_warnings('all')
|
||||
|
||||
add_includedirs('src')
|
||||
|
||||
-- workaround to fix std::stacktrace link problem
|
||||
-- for gcc == 14
|
||||
-- see https://gcc.gnu.org/onlinedocs/gcc-14.2.0/libstdc++/manual/manual/using.html
|
||||
-- for gcc == 13
|
||||
-- see https://gcc.gnu.org/onlinedocs/gcc-13.2.0/libstdc++/manual/manual/using.html
|
||||
if is_plat('linux') then
|
||||
add_links('stdc++exp')
|
||||
end
|
||||
|
||||
if is_mode('debug') then
|
||||
add_defines('DI_DEBUG')
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user