refactor: unified exception handling for llvm.

This commit is contained in:
2025-03-04 16:56:49 +08:00
parent 93087ec1ac
commit af44d604b1
9 changed files with 194 additions and 97 deletions

View File

@@ -49,7 +49,7 @@ public:
HSTR(VarTemplateSpecialization); HSTR(VarTemplateSpecialization);
#undef HSTR #undef HSTR
default: default:
throw EnumTransformException(str, Enum{}); throw ConvertEnumException(str, TypeOnly<Enum>{});
} }
// clang-format on // clang-format on
@@ -87,7 +87,7 @@ public:
HSTR(VarTemplateSpecialization); HSTR(VarTemplateSpecialization);
#undef HSTR #undef HSTR
default: default:
throw EnumTransformException(m_data, Enum{}); throw ConvertEnumException(m_data);
} }
// clang-format on // clang-format on

View File

@@ -40,6 +40,10 @@ public:
add_context("path", list_file_path.string()); add_context("path", list_file_path.string());
add_context("current_line", current_line); add_context("current_line", current_line);
} }
constexpr std::string category() const {
return "exception.dataformat.missingdecltype";
}
}; };
} // namespace di::data_format } // namespace di::data_format

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <llvm/Support/Error.h>
namespace di { namespace di {
class BaseException { class BaseException {
@@ -30,7 +32,7 @@ public:
return static_cast<const Derived*>(this)->category(); return static_cast<const Derived*>(this)->category();
} }
std::string what() const override { std::string what() const final {
// [exception.base] Reason... // [exception.base] Reason...
// //
// Context Information: // Context Information:
@@ -82,6 +84,12 @@ protected:
m_context_information[key] = std::format("{}", std::forward<T>(value)); 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: private:
std::string m_reason; std::string m_reason;
@@ -99,26 +107,79 @@ public:
constexpr std::string category() const { return "exception.unix"; } constexpr std::string category() const { return "exception.unix"; }
}; };
class EnumTransformException : public RuntimeException<EnumTransformException> { class ConvertEnumException : public RuntimeException<ConvertEnumException> {
public: public:
// TODO: compile-time reflection. // TODO: compile-time reflection.
// TODO: remove helper. // TODO: remove helper.
template <typename T> template <Enumerate T>
explicit EnumTransformException(int enum_val, T _helper = {}) explicit ConvertEnumException(T enum_val)
: RuntimeException("Unable to convert string to enumeration value because " : RuntimeException("Unable to convert string to enumeration value because "
"input value is bad.") { "input value is bad.") {
add_context("enum_type", typeid(T).name()); add_context("enum_type", typeid(T).name());
add_context("value", enum_val); add_context("value", underlying_value(enum_val));
} }
template <typename T> 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 " : RuntimeException("Unable to convert enumeration value to string because "
"input value is bad.") { "input value is bad.") {
add_context("enum_type", typeid(T).name()); add_context("enum_type", typeid(T).name());
add_context("string", enum_str); 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 } // namespace di

View File

@@ -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())); 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: // 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

View File

@@ -1,20 +1,17 @@
#include "object_file/coff.h" #include "object_file/coff.h"
#include "error.h"
namespace di::object_file { namespace di::object_file {
COFF::COFF(const fs::path& path) { COFF::COFF(const fs::path& path) {
using namespace object; using namespace object;
auto obj_or_err = ObjectFile::createObjectFile(path.string()); auto file = check_llvm_result(ObjectFile::createObjectFile(path.string()));
if (!obj_or_err) {
throw std::runtime_error("Failed to create object file.");
}
if (!isa<COFFObjectFile>(obj_or_err->getBinary())) { if (!isa<COFFObjectFile>(file.getBinary()))
throw std::runtime_error("Is not a valid PE file."); throw UnexceptObjectException(path, TypeOnly<COFFObjectFile>{});
}
auto bin = obj_or_err->takeBinary(); auto bin = file.takeBinary();
m_owning_binary = object::OwningBinary( m_owning_binary = object::OwningBinary(
static_unique_ptr_cast<COFFObjectFile>(std::move(bin.first)), 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) if (get_owning_coff().getDebugPDBInfo(debug_info, pdb_file_name)
|| !debug_info) { || !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; return debug_info->PDB70;
@@ -50,7 +49,7 @@ size_t COFF::get_section_index(size_t offset) const {
} }
current_index++; current_index++;
} }
throw std::runtime_error("Offset is not in any section."); throw SectionNotFoundException(offset);
} }
object::coff_section* COFF::get_section_table() { object::coff_section* COFF::get_section_table() {

View File

@@ -20,4 +20,52 @@ private:
object::OwningBinary<object::COFFObjectFile> m_owning_binary; 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 } // namespace di::object_file

View File

@@ -22,58 +22,38 @@ using namespace llvm::codeview;
namespace di::object_file { namespace di::object_file {
void PDB::read(const fs::path& path) { void PDB::read(const fs::path& path) {
if (loadDataForPDB(PDB_ReaderType::Native, path.string(), m_session)) { check_llvm_result(
throw std::runtime_error("Failed to load PDB."); loadDataForPDB(PDB_ReaderType::Native, path.string(), m_session)
} );
std::unique_ptr<IPDBSession> pdb_session; auto& pdb_file = get_native_session().getPDBFile();
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();
SmallVector<codeview::TypeIndex, 128> type_map; SmallVector<codeview::TypeIndex, 128> type_map;
SmallVector<codeview::TypeIndex, 128> id_map; SmallVector<codeview::TypeIndex, 128> id_map;
if (auto tpi_stream = pdb_file.getPDBTpiStream()) { auto& tpi_stream = check_llvm_result(pdb_file.getPDBTpiStream());
if (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.");
}
if (auto ipi_stream = pdb_file.getPDBIpiStream()) { check_llvm_result(codeview::mergeTypeRecords(
if (codeview::mergeIdRecords( *m_storaged_Tpi,
*m_storaged_Ipi, type_map,
type_map, tpi_stream.typeArray()
id_map, ));
(*ipi_stream).typeArray()
)) { auto& ipi_stream = check_llvm_result(pdb_file.getPDBIpiStream());
throw std::runtime_error("Failed to merge id record.");
} check_llvm_result(codeview::mergeIdRecords(
} else { *m_storaged_Ipi,
throw std::runtime_error("IPI is not valid."); type_map,
} id_map,
ipi_stream.typeArray()
));
} }
void PDB::_write(const fs::path& path) { void PDB::_write(const fs::path& path) {
build(); build();
GUID out_guid; GUID out_guid;
if (m_builder->commit(path.string(), &out_guid)) { check_llvm_result(m_builder->commit(path.string(), &out_guid));
throw std::runtime_error("Failed to create pdb!");
}
} }
NativeSession& PDB::get_native_session() { 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) { void PDB::_for_each_public(const for_each_symbol_callback_t& callback) {
using namespace codeview; using namespace codeview;
auto& file = get_pdb_file(); auto& file = get_pdb_file();
auto publics_stream = file.getPDBPublicsStream(); auto& publics_stream = check_llvm_result(file.getPDBPublicsStream());
if (!publics_stream) { auto& symbol_stream = check_llvm_result(file.getPDBSymbolStream());
throw std::runtime_error("Failed to get public stream from PDB.");
}
auto publics_symbol_stream = file.getPDBSymbolStream(); auto raw_stream = symbol_stream.getSymbolArray().getUnderlyingStream();
if (!publics_symbol_stream) { for (auto offset : publics_stream.getPublicsTable()) {
throw std::runtime_error("Failed to get symbol stream from PDB."); auto cv_symbol = readSymbolFromStream(raw_stream, offset);
} auto public_sym32 = check_llvm_result(
SymbolDeserializer::deserializeAs<PublicSym32>(cv_symbol.get()),
auto publics_symbols = "Unsupported symbol type."
publics_symbol_stream->getSymbolArray().getUnderlyingStream(); );
for (auto offset : publics_stream->getPublicsTable()) { callback(public_sym32);
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);
} }
} }
void PDB::build() { void PDB::build() {
constexpr auto block_size = 4096;
m_builder.reset(new PDBFileBuilder{m_Alloc}); m_builder.reset(new PDBFileBuilder{m_Alloc});
if (m_builder->initialize(block_size)) { constexpr auto block_size = 4096;
throw std::runtime_error("Failed to initialize pdb file builder."); check_llvm_result(m_builder->initialize(block_size));
}
for (uint32_t idx = 0; idx < pdb::kSpecialStreamCount; ++idx) { for (uint32_t idx = 0; idx < pdb::kSpecialStreamCount; ++idx) {
if (!m_builder->getMsfBuilder().addStream(0)) { check_llvm_result(m_builder->getMsfBuilder().addStream(0));
throw std::runtime_error("Failed to add initial stream.");
}
} }
// INFO // INFO
@@ -163,9 +129,9 @@ void PDB::build() {
Dbi.createSectionMap(section_table_ref); Dbi.createSectionMap(section_table_ref);
// Add COFF section header stream. // Add COFF section header stream.
if (Dbi.addDbgStream(DbgHeaderType::SectionHdr, section_data_ref)) { check_llvm_result(
throw std::runtime_error("Failed to add dbg stream."); Dbi.addDbgStream(DbgHeaderType::SectionHdr, section_data_ref)
} );
} }
// TPI & IPI // TPI & IPI
@@ -198,17 +164,15 @@ void PDB::build() {
auto section_index = auto section_index =
m_owning_coff->get_section_index(entity.m_rva - m_image_base); m_owning_coff->get_section_index(entity.m_rva - m_image_base);
auto section_or_err = auto section = check_llvm_result(
m_owning_coff->get_owning_coff().getSection(section_index + 1); m_owning_coff->get_owning_coff().getSection(section_index + 1)
if (!section_or_err) { );
throw std::runtime_error("Invalid section.");
}
symbol.Name = strdup(entity.m_symbol_name.c_str()); symbol.Name = strdup(entity.m_symbol_name.c_str());
symbol.NameLen = entity.m_symbol_name.size(); symbol.NameLen = entity.m_symbol_name.size();
symbol.Segment = section_index + 1; symbol.Segment = section_index + 1;
symbol.Offset = entity.m_rva - m_image_base symbol.Offset =
- section_or_err.get()->VirtualAddress; entity.m_rva - m_image_base - section->VirtualAddress;
if (entity.m_is_function) symbol.setFlags(PublicSymFlags::Function); if (entity.m_is_function) symbol.setFlags(PublicSymFlags::Function);
publics.emplace_back(symbol); publics.emplace_back(symbol);

View File

@@ -10,3 +10,7 @@ using hash_t = uint64_t;
} // namespace di } // namespace di
namespace fs = std::filesystem; namespace fs = std::filesystem;
// shit!
template <typename T>
struct TypeOnly {};

View File

@@ -36,6 +36,15 @@ set_warnings('all')
add_includedirs('src') 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 if is_mode('debug') then
add_defines('DI_DEBUG') add_defines('DI_DEBUG')
end end