Compare commits

...

10 Commits

Author SHA1 Message Date
Jeremy
a3ebd42802
A couple fixes 2024-03-30 23:35:50 -05:00
Jeremy
d8b9f45a24
Merge branch 'dev' into jr/more-thorough-error-handling 2024-03-30 23:26:58 -05:00
Jeremy
35e555ae39
Error handling in dwarf stuff 2024-03-30 18:32:51 -05:00
Jeremy
4c5d4a5ee4
Remove file_error and duplicate nodiscard 2024-03-30 14:34:44 -05:00
Jeremy
530954e69f
Merge branch 'dev' into jr/more-thorough-error-handling 2024-03-30 14:32:23 -05:00
Jeremy
89a7afaded
Merge branch 'dev' into jr/more-thorough-error-handling 2024-03-30 13:25:27 -05:00
Jeremy
91a719e534
Use internal_error over std:: errors 2024-03-09 21:50:49 -06:00
Jeremy
e80a11d730
Logic for PE, and a couple tweaks 2024-03-09 21:25:57 -06:00
Jeremy
f135232933
First pass at using Result in mach-o logic 2024-03-09 21:10:14 -06:00
Jeremy
f0bedd4104
Try error handling with a Result type 2024-03-09 17:48:09 -06:00
4 changed files with 617 additions and 291 deletions

View File

@ -120,13 +120,13 @@ namespace libdwarf {
int
>::type = 0
>
int wrap(int (*f)(Args...), Args2&&... args) const {
Result<int, internal_error> wrap(int (*f)(Args...), Args2&&... args) const {
Dwarf_Error error = nullptr;
int ret = f(std::forward<Args2>(args)..., &error);
if(ret == DW_DLV_ERROR) {
handle_dwarf_error(dbg, error);
return handle_dwarf_error(dbg, error);
}
return ret;
return Ok(ret);
}
public:
@ -178,14 +178,17 @@ namespace libdwarf {
nullptr,
&dbg
);
if(ret == DW_DLV_OK) {
if(!ret) {
ret.drop_error();
ok = false;
} else if(ret.unwrap_value() == DW_DLV_OK) {
ok = true;
} else if(ret == DW_DLV_NO_ENTRY) {
} else if(ret.unwrap_value() == DW_DLV_NO_ENTRY) {
// fail, no debug info
ok = false;
} else {
ok = false;
PANIC("Unknown return code from dwarf_init_path");
ASSERT(false, "Unknown return code from dwarf_init_path");
}
if(ok) {
@ -256,7 +259,7 @@ namespace libdwarf {
Dwarf_Unsigned next_cu_header;
Dwarf_Half header_cu_type;
while(true) {
int ret = wrap(
auto ret = wrap(
dwarf_next_cu_header_d,
dbg,
true,
@ -271,20 +274,29 @@ namespace libdwarf {
&next_cu_header,
&header_cu_type
);
if(ret == DW_DLV_NO_ENTRY) {
if(!ret) {
ret.drop_error();
return;
}
if(ret.unwrap_value() == DW_DLV_NO_ENTRY) {
if(dump_dwarf) {
std::fprintf(stderr, "End walk_dbg\n");
}
return;
}
if(ret != DW_DLV_OK) {
PANIC("Unexpected return code from dwarf_next_cu_header_d");
if(ret.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "Unexpected return code from dwarf_next_cu_header_d");
return;
}
// 0 passed as the die to the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d
// to fetch the cu die
die_object cu_die(dbg, nullptr);
cu_die = cu_die.get_sibling();
auto sibling = cu_die.get_sibling();
if(sibling.is_error()) {
sibling.drop_error();
break;
}
cu_die = std::move(sibling).unwrap_value();
if(!cu_die) {
break;
}
@ -304,8 +316,17 @@ namespace libdwarf {
Dwarf_Half dwversion = 0;
dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
auto ranges_vec = cu_die.get_rangelist_entries(dwversion);
for(auto range : ranges_vec) {
cu_cache.push_back({ cu_die.clone(), dwversion, range.first, range.second });
if(!ranges_vec) {
ranges_vec.drop_error();
return true;
}
for(auto range : ranges_vec.unwrap_value()) {
auto cu = cu_die.clone();
if(!cu) {
ranges_vec.drop_error();
return true;
}
cu_cache.push_back({std::move(cu).unwrap_value(), dwversion, range.low, range.high});
}
return true;
});
@ -316,52 +337,75 @@ namespace libdwarf {
}
}
std::string subprogram_symbol(
Result<std::string, internal_error> subprogram_symbol(
const die_object& die,
Dwarf_Half dwversion
) {
ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine);
auto tag = die.get_tag();
ASSERT(tag && (tag.unwrap_value() == DW_TAG_subprogram || tag.unwrap_value() == DW_TAG_inlined_subroutine));
optional<std::string> name;
if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
name = std::move(linkage_name);
} else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) {
name = std::move(linkage_name);
} else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) {
name = std::move(linkage_name);
auto linkage_name = die.get_string_attribute(DW_AT_linkage_name);
if(!linkage_name) {
linkage_name.drop_error();
}
if(linkage_name.has_value() && linkage_name.unwrap_value()) {
name = std::move(linkage_name).unwrap_value();
} {
auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name);
if(!linkage_name) {
linkage_name.drop_error();
}
if(linkage_name.has_value() && linkage_name.unwrap_value()) {
name = std::move(linkage_name).unwrap_value();
} else {
auto linkage_name = die.get_string_attribute(DW_AT_name);
if(!linkage_name) {
linkage_name.drop_error();
}
if(linkage_name.has_value() && linkage_name.unwrap_value()) {
name = std::move(linkage_name).unwrap_value();
}
}
}
if(name.has_value()) {
return std::move(name).unwrap();
} else {
if(die.has_attr(DW_AT_specification)) {
die_object spec = die.resolve_reference_attribute(DW_AT_specification);
return subprogram_symbol(spec, dwversion);
auto spec = die.resolve_reference_attribute(DW_AT_specification);
if(!spec) {
return std::move(spec).unwrap_error();
}
return subprogram_symbol(std::move(spec).unwrap_value(), dwversion);
} else if(die.has_attr(DW_AT_abstract_origin)) {
die_object spec = die.resolve_reference_attribute(DW_AT_abstract_origin);
return subprogram_symbol(spec, dwversion);
auto spec = die.resolve_reference_attribute(DW_AT_abstract_origin);
if(!spec) {
return std::move(spec).unwrap_error();
}
return subprogram_symbol(std::move(spec).unwrap_value(), dwversion);
}
}
return "";
return Ok("");
}
// despite (some) dwarf using 1-indexing, file_i should be the 0-based index
std::string resolve_filename(const die_object& cu_die, Dwarf_Unsigned file_i) {
Result<std::string, internal_error> resolve_filename(const die_object& cu_die, Dwarf_Unsigned file_i) {
std::string filename;
if(get_cache_mode() == cache_mode::prioritize_memory) {
char** dw_srcfiles;
Dwarf_Signed dw_filecount;
VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount));
if(Dwarf_Signed(file_i) < dw_filecount) {
// dwarf is using 1-indexing
filename = dw_srcfiles[file_i];
}
dwarf_dealloc(cu_die.dbg, dw_srcfiles, DW_DLA_LIST);
} else {
auto off = cu_die.get_global_offset();
PROP_ASSIGN(off, cu_die.get_global_offset());
auto it = srcfiles_cache.find(off);
if(it == srcfiles_cache.end()) {
char** dw_srcfiles;
Dwarf_Signed dw_filecount;
VERIFY(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_srcfiles, cu_die.get(), &dw_srcfiles, &dw_filecount));
it = srcfiles_cache.insert(it, {off, {dw_srcfiles, dw_filecount}});
}
char** dw_srcfiles = it->second.first;
@ -381,7 +425,8 @@ namespace libdwarf {
Dwarf_Half dwversion,
std::vector<stacktrace_frame>& inlines
) {
ASSERT(die.get_tag() == DW_TAG_subprogram || die.get_tag() == DW_TAG_inlined_subroutine);
auto tag = die.get_tag();
ASSERT(tag && (tag.unwrap_value() == DW_TAG_subprogram || tag.unwrap_value() == DW_TAG_inlined_subroutine));
// get_inlines_info is recursive and recurses into dies with pc ranges matching the pc we're looking for,
// however, because I wouldn't want anything stack overflowing I'm breaking the recursion out into a loop
// while looping when we find the target die we need to be able to store a die somewhere that doesn't die
@ -390,47 +435,70 @@ namespace libdwarf {
optional<std::reference_wrapper<const die_object>> current_die = die;
while(current_die.has_value()) {
auto child = current_die.unwrap().get().get_child();
if(!child) {
if(!child.has_value() || !child.unwrap_value()) {
break;
}
optional<std::reference_wrapper<const die_object>> target_die;
walk_die_list(
child,
child.unwrap_value(),
[this, &cu_die, pc, dwversion, &inlines, &target_die, &current_obj_holder] (const die_object& die) {
if(die.get_tag() == DW_TAG_inlined_subroutine && die.pc_in_die(dwversion, pc)) {
auto tag = die.get_tag();
if(!tag) {
tag.drop_error();
return true;
}
if(tag.unwrap_value() == DW_TAG_inlined_subroutine && die.pc_in_die(dwversion, pc)) {
const auto name = subprogram_symbol(die, dwversion);
auto file_i = die.get_unsigned_attribute(DW_AT_call_file);
auto file_i_r = die.get_unsigned_attribute(DW_AT_call_file);
if(!file_i_r) {
file_i_r.drop_error();
return true;
}
auto file_i = std::move(file_i_r).unwrap_value();
if(file_i) {
// for dwarf 2, 3, 4, and experimental line table version 0xfe06 1-indexing is used
// for dwarf 5 0-indexing is used
auto line_table_opt = get_line_table(cu_die);
if(line_table_opt) {
auto& line_table = line_table_opt.unwrap().get();
if(line_table.version != 5) {
if(file_i.unwrap() == 0) {
file_i.reset(); // 0 means no name to be found
} else {
// decrement to 0-based index
file_i.unwrap()--;
}
if(!line_table_opt) {
line_table_opt.drop_error();
return true;
}
auto& line_table = line_table_opt.unwrap_value().get();
if(line_table.version != 5) {
if(file_i.unwrap() == 0) {
file_i.reset(); // 0 means no name to be found
} else {
// decrement to 0-based index
file_i.unwrap()--;
}
} else {
// silently continue
}
}
std::string file = file_i ? resolve_filename(cu_die, file_i.unwrap()) : "";
std::string file = file_i ? resolve_filename(cu_die, file_i.unwrap()).value_or("") : "";
const auto line = die.get_unsigned_attribute(DW_AT_call_line);
if(!line) {
line.drop_error();
return true;
}
const auto col = die.get_unsigned_attribute(DW_AT_call_column);
if(!col) {
col.drop_error();
return true;
}
inlines.push_back(stacktrace_frame{
0,
0, // TODO: Could put an object address here...
{static_cast<std::uint32_t>(line.value_or(0))},
{static_cast<std::uint32_t>(col.value_or(0))},
{static_cast<std::uint32_t>(line.unwrap_value().value_or(0))},
{static_cast<std::uint32_t>(col.unwrap_value().value_or(0))},
file,
name,
name.value_or(""),
true
});
current_obj_holder = die.clone();
auto d = die.clone();
if(!d) {
d.drop_error();
return false;
}
current_obj_holder = std::move(d).unwrap_value();
target_die = current_obj_holder;
return false;
} else {
@ -443,15 +511,16 @@ namespace libdwarf {
}
}
std::string retrieve_symbol_for_subprogram(
Result<std::string, internal_error> retrieve_symbol_for_subprogram(
const die_object& cu_die,
const die_object& die,
Dwarf_Addr pc,
Dwarf_Half dwversion,
std::vector<stacktrace_frame>& inlines
) {
ASSERT(die.get_tag() == DW_TAG_subprogram);
const auto name = subprogram_symbol(die, dwversion);
auto tag = die.get_tag();
ASSERT(tag && tag.unwrap_value() == DW_TAG_subprogram);
auto name = subprogram_symbol(die, dwversion);
if(detail::should_resolve_inlined_calls()) {
get_inlines_info(cu_die, die, pc, dwversion, inlines);
}
@ -473,42 +542,52 @@ namespace libdwarf {
die,
[this, &cu_die, pc, dwversion, &frame, &inlines, &found] (const die_object& die) {
if(dump_dwarf) {
std::fprintf(
stderr,
"-------------> %08llx %s %s\n",
to_ull(die.get_global_offset()),
microfmt::print(
std::cerr,
"-------------> {8:0h} {} {}\n",
die.get_global_offset().value_or(0),
die.get_tag_name(),
die.get_name().c_str()
die.get_name().value_or("<error>")
);
}
if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) {
auto tag = die.get_tag();
if(!tag) {
tag.drop_error();
return true;
}
if(!(tag.unwrap_value() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) {
if(dump_dwarf) {
std::fprintf(stderr, "pc not in die\n");
microfmt::print(std::cerr, "pc not in die\n");
}
} else {
if(trace_dwarf) {
std::fprintf(
stderr,
"%s %08llx %s\n",
die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die",
to_ull(die.get_global_offset()),
microfmt::print(
std::cerr,
"{} {8:0h} {}\n",
tag.unwrap_value() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die",
die.get_global_offset().value_or(0),
die.get_tag_name()
);
}
if(die.get_tag() == DW_TAG_subprogram) {
frame.symbol = retrieve_symbol_for_subprogram(cu_die, die, pc, dwversion, inlines);
if(tag.unwrap_value() == DW_TAG_subprogram) {
frame.symbol = retrieve_symbol_for_subprogram(cu_die, die, pc, dwversion, inlines)
.value_or("");
found = true;
return false;
}
auto child = die.get_child();
if(child) {
if(retrieve_symbol_walk(cu_die, child, pc, dwversion, frame, inlines)) {
if(!child) {
child.drop_error();
return true;
}
if(child.unwrap_value()) {
if(retrieve_symbol_walk(cu_die, child.unwrap_value(), pc, dwversion, frame, inlines)) {
found = true;
return false;
}
} else {
if(dump_dwarf) {
std::fprintf(stderr, "(no child)\n");
microfmt::print(std::cerr, "(no child)\n");
}
}
}
@ -516,7 +595,7 @@ namespace libdwarf {
}
);
if(dump_dwarf) {
std::fprintf(stderr, "End walk_die_list\n");
microfmt::print(std::cerr, "End walk_die_list\n");
}
return found;
}
@ -530,20 +609,38 @@ namespace libdwarf {
walk_die_list(
die,
[this, dwversion, &vec] (const die_object& die) {
switch(die.get_tag()) {
auto tag = die.get_tag();
if(!tag) {
tag.drop_error();
return true;
}
switch(tag.unwrap_value()) {
case DW_TAG_subprogram:
{
auto ranges_vec = die.get_rangelist_entries(dwversion);
if(!ranges_vec) {
ranges_vec.drop_error();
return true;
}
// TODO: Feels super inefficient and some day should maybe use an interval tree.
for(auto range : ranges_vec) {
vec.push_back({ die.clone(), range.first, range.second });
for(auto range : ranges_vec.unwrap_value()) {
auto d = die.clone();
if(!d) {
d.drop_error();
return true;
}
vec.push_back({ std::move(d).unwrap_value(), range.low, range.high });
}
// Walk children to get things like lambdas
// TODO: Somehow find a way to get better names here? For gcc it's just "operator()"
// On clang it's better
auto child = die.get_child();
if(child) {
preprocess_subprograms(child, dwversion, vec);
if(!child) {
child.drop_error();
return true;
}
if(child.unwrap_value()) {
preprocess_subprograms(child.unwrap_value(), dwversion, vec);
}
}
break;
@ -555,8 +652,12 @@ namespace libdwarf {
case DW_TAG_compile_unit:
{
auto child = die.get_child();
if(child) {
preprocess_subprograms(child, dwversion, vec);
if(!child) {
child.drop_error();
return true;
}
if(child.unwrap_value()) {
preprocess_subprograms(child.unwrap_value(), dwversion, vec);
}
}
break;
@ -583,7 +684,10 @@ namespace libdwarf {
retrieve_symbol_walk(cu_die, cu_die, pc, dwversion, frame, inlines);
} else {
auto off = cu_die.get_global_offset();
auto it = subprograms_cache.find(off);
if(!off) {
return;
}
auto it = subprograms_cache.find(off.unwrap_value());
if(it == subprograms_cache.end()) {
// TODO: Refactor. Do the sort in the preprocess function and return the vec directly.
std::vector<subprogram_entry> vec;
@ -591,8 +695,8 @@ namespace libdwarf {
std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) {
return a.low < b.low;
});
subprograms_cache.emplace(off, std::move(vec));
it = subprograms_cache.find(off);
subprograms_cache.emplace(off.unwrap_value(), std::move(vec));
it = subprograms_cache.find(off.unwrap_value());
}
auto& vec = it->second;
auto vec_it = first_less_than_or_equal(
@ -607,7 +711,8 @@ namespace libdwarf {
if(vec_it != vec.end()) {
//vec_it->die.print();
if(vec_it->die.pc_in_die(dwversion, pc)) {
frame.symbol = retrieve_symbol_for_subprogram(cu_die, vec_it->die, pc, dwversion, inlines);
frame.symbol = retrieve_symbol_for_subprogram(cu_die, vec_it->die, pc, dwversion, inlines)
.value_or("");
}
} else {
ASSERT(vec.size() == 0, "Vec should be empty?");
@ -617,29 +722,35 @@ namespace libdwarf {
// returns a reference to a CU's line table, may be invalidated if the line_tables map is modified
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
optional<std::reference_wrapper<line_table_info>> get_line_table(const die_object& cu_die) {
auto off = cu_die.get_global_offset();
Result<std::reference_wrapper<line_table_info>, internal_error> get_line_table(const die_object& cu_die) {
PROP_ASSIGN(off, cu_die.get_global_offset());
auto it = line_tables.find(off);
if(it != line_tables.end()) {
return it->second;
return std::reference_wrapper<line_table_info>{it->second};
} else {
Dwarf_Unsigned version;
Dwarf_Small table_count;
Dwarf_Line_Context line_context;
int ret = wrap(
dwarf_srclines_b,
cu_die.get(),
&version,
&table_count,
&line_context
PROP_ASSIGN(
ret,
wrap(
dwarf_srclines_b,
cu_die.get(),
&version,
&table_count,
&line_context
)
);
static_assert(std::is_unsigned<decltype(table_count)>::value, "Expected unsigned Dwarf_Small");
VERIFY(/*table_count >= 0 &&*/ table_count <= 2, "Unknown dwarf line table count");
if(ret == DW_DLV_NO_ENTRY) {
// TODO: Failing silently for now
return nullopt;
if(table_count > 2) {
return internal_error("Unknown dwarf line table count");
}
if(ret == DW_DLV_NO_ENTRY) {
return internal_error("dwarf_srclines_b DW_DLV_NO_ENTRY");
}
if(ret != DW_DLV_OK) {
return internal_error("dwarf_srclines_b not ok");
}
VERIFY(ret == DW_DLV_OK);
std::vector<line_entry> line_entries;
@ -649,7 +760,7 @@ namespace libdwarf {
Dwarf_Signed line_count = 0;
Dwarf_Line* linebuf_actuals = nullptr;
Dwarf_Signed linecount_actuals = 0;
VERIFY(
CHECK_OK(
wrap(
dwarf_srclines_two_level_from_linecontext,
line_context,
@ -657,7 +768,7 @@ namespace libdwarf {
&line_count,
&linebuf_actuals,
&linecount_actuals
) == DW_DLV_OK
)
);
// TODO: Make any attempt to note PC ranges? Handle line end sequence?
@ -665,12 +776,12 @@ namespace libdwarf {
for(int i = 0; i < line_count; i++) {
Dwarf_Line line = line_buffer[i];
Dwarf_Addr low_addr = 0;
VERIFY(wrap(dwarf_lineaddr, line, &low_addr) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_lineaddr, line, &low_addr));
// scan ahead for the last line entry matching this pc
int j;
for(j = i + 1; j < line_count; j++) {
Dwarf_Addr addr = 0;
VERIFY(wrap(dwarf_lineaddr, line_buffer[j], &addr) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_lineaddr, line_buffer[j], &addr));
if(addr != low_addr) {
break;
}
@ -707,7 +818,7 @@ namespace libdwarf {
}
it = line_tables.insert({off, {version, line_context, std::move(line_entries)}}).first;
return it->second;
return std::reference_wrapper<line_table_info>{it->second};
}
}
@ -719,9 +830,10 @@ namespace libdwarf {
) {
auto table_info_opt = get_line_table(cu_die);
if(!table_info_opt) {
return; // failing silently for now
table_info_opt.drop_error();
return;
}
auto& table_info = table_info_opt.unwrap().get();
auto& table_info = table_info_opt.unwrap_value().get();
if(get_cache_mode() == cache_mode::prioritize_speed) {
// Lookup in the table
auto& line_entries = table_info.line_entries;
@ -739,21 +851,42 @@ namespace libdwarf {
// line number
if(!table_it->line_number) {
Dwarf_Unsigned line_number = 0;
VERIFY(wrap(dwarf_lineno, line, &line_number) == DW_DLV_OK);
auto res = wrap(dwarf_lineno, line, &line_number);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
table_it->line_number = static_cast<std::uint32_t>(line_number);
}
frame.line = table_it->line_number.unwrap();
// column number
if(!table_it->column_number) {
Dwarf_Unsigned column_number = 0;
VERIFY(wrap(dwarf_lineoff_b, line, &column_number) == DW_DLV_OK);
auto res = wrap(dwarf_lineoff_b, line, &column_number);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
table_it->column_number = static_cast<std::uint32_t>(column_number);
}
frame.column = table_it->column_number.unwrap();
// filename
if(!table_it->path) {
char* filename = nullptr;
VERIFY(wrap(dwarf_linesrc, line, &filename) == DW_DLV_OK);
auto res = wrap(dwarf_linesrc, line, &filename);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
auto wrapper = raii_wrap(
filename,
[this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); }
@ -769,22 +902,34 @@ namespace libdwarf {
Dwarf_Signed line_count = 0;
Dwarf_Line* linebuf_actuals = nullptr;
Dwarf_Signed linecount_actuals = 0;
VERIFY(
wrap(
dwarf_srclines_two_level_from_linecontext,
line_context,
&line_buffer,
&line_count,
&linebuf_actuals,
&linecount_actuals
) == DW_DLV_OK
auto res = wrap(
dwarf_srclines_two_level_from_linecontext,
line_context,
&line_buffer,
&line_count,
&linebuf_actuals,
&linecount_actuals
);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
Dwarf_Addr last_lineaddr = 0;
Dwarf_Line last_line = nullptr;
for(int i = 0; i < line_count; i++) {
Dwarf_Line line = line_buffer[i];
Dwarf_Addr lineaddr = 0;
VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK);
auto res = wrap(dwarf_lineaddr, line, &lineaddr);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
Dwarf_Line found_line = nullptr;
if(pc == lineaddr) {
// Multiple PCs may correspond to a line, find the last one
@ -792,7 +937,14 @@ namespace libdwarf {
for(int j = i + 1; j < line_count; j++) {
Dwarf_Line line = line_buffer[j];
Dwarf_Addr lineaddr = 0;
VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK);
auto res = wrap(dwarf_lineaddr, line, &lineaddr);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
if(pc == lineaddr) {
found_line = line;
}
@ -803,10 +955,24 @@ namespace libdwarf {
}
if(found_line) {
Dwarf_Unsigned line_number = 0;
VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK);
auto res = wrap(dwarf_lineno, found_line, &line_number);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
frame.line = static_cast<std::uint32_t>(line_number);
char* filename = nullptr;
VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK);
auto res2 = wrap(dwarf_linesrc, found_line, &filename);
if(!res2) {
res2.drop_error();
return;
} else if(res2.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
auto wrapper = raii_wrap(
filename,
[this] (char* str) { if(str) dwarf_dealloc(dbg, str, DW_DLA_STRING); }
@ -814,7 +980,14 @@ namespace libdwarf {
frame.filename = filename;
} else {
Dwarf_Bool is_line_end;
VERIFY(wrap(dwarf_lineendsequence, line, &is_line_end) == DW_DLV_OK);
auto res = wrap(dwarf_lineendsequence, line, &is_line_end);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
if(is_line_end) {
last_lineaddr = 0;
last_line = nullptr;
@ -841,17 +1014,40 @@ namespace libdwarf {
if(aranges) {
// Try to find pc in aranges
Dwarf_Arange arange;
if(wrap(dwarf_get_arange, aranges, arange_count, pc, &arange) == DW_DLV_OK) {
auto res = wrap(dwarf_get_arange, aranges, arange_count, pc, &arange);
if(!res) {
res.drop_error();
return;
}
if(res.unwrap_value() == DW_DLV_OK) {
// Address in table, load CU die
Dwarf_Off cu_die_offset;
VERIFY(wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset) == DW_DLV_OK);
auto res = wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset);
if(!res) {
res.drop_error();
return;
} else if(res.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
Dwarf_Die raw_die;
// Setting is_info = true for now, assuming in .debug_info rather than .debug_types
VERIFY(wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die) == DW_DLV_OK);
auto res2 = wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die);
if(!res2) {
res2.drop_error();
return;
} else if(res2.unwrap_value() != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
die_object cu_die(dbg, raw_die);
Dwarf_Half offset_size = 0;
Dwarf_Half dwversion = 0;
VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK);
int res3 = dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
if(res3 != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return;
}
if(trace_dwarf) {
std::fprintf(stderr, "Found CU in aranges\n");
cu_die.print();
@ -870,19 +1066,23 @@ namespace libdwarf {
walk_compilation_units([this, pc, &frame, &inlines] (const die_object& cu_die) {
Dwarf_Half offset_size = 0;
Dwarf_Half dwversion = 0;
dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
auto res = dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
if(res != DW_DLV_OK) {
ASSERT(false, "dwarf call not ok");
return true;
}
//auto p = cu_die.get_pc_range(dwversion);
//cu_die.print();
//fprintf(stderr, " %llx, %llx\n", p.first, p.second);
if(trace_dwarf) {
std::fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str());
microfmt::print(std::cerr, "CU: {} {}\n", dwversion, cu_die.get_name().value_or("<error>"));
}
if(cu_die.pc_in_die(dwversion, pc)) {
if(trace_dwarf) {
std::fprintf(
stderr,
"pc in die %08llx %s (now searching for %08llx)\n",
to_ull(cu_die.get_global_offset()),
to_ull(cu_die.get_global_offset().value_or(0)),
cu_die.get_tag_name(),
to_ull(pc)
);

View File

@ -17,20 +17,57 @@
#include <dwarf.h>
#endif
#define CONCAT_IMPL( x, y ) x##y
#define CONCAT( x, y ) CONCAT_IMPL( x, y )
namespace cpptrace {
namespace detail {
namespace libdwarf {
static_assert(std::is_pointer<Dwarf_Die>::value, "Dwarf_Die not a pointer");
static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
[[noreturn]] void handle_dwarf_error(Dwarf_Debug dbg, Dwarf_Error error) {
static NODISCARD ErrorResult<internal_error> handle_dwarf_error(Dwarf_Debug, Dwarf_Error error) {
Dwarf_Unsigned ev = dwarf_errno(error);
char* msg = dwarf_errmsg(error);
(void)dbg;
// dwarf_dealloc_error(dbg, error);
throw internal_error("Cpptrace dwarf error {} {}", ev, msg);
return {internal_error("Cpptrace dwarf error {} {}", ev, msg)};
}
#define CHECK_OK_IMPL(res_var, expr) \
Result<int, internal_error> res_var = (expr); \
if(!res_var) { \
return std::move(res_var).unwrap_error(); \
} else if(res_var.unwrap_value() != DW_DLV_OK) { \
return Error( \
internal_error( \
"dwarf error: {} didn't evaluate to DW_DLV_OK, instead got {}", \
#expr, \
res_var.unwrap_value() \
) \
); \
}
// Check if the expression is an error or not DW_DLV_OK and error if either
#define CHECK_OK(expr) CHECK_OK_IMPL(CONCAT(res, __COUNTER__), (expr))
#define PROP_ASSIGN_IMPL(var, res_var, expr) \
auto res_var = (expr); \
if(!res_var) { \
return std::move(res_var).unwrap_error(); \
} \
auto var = res_var.unwrap_value();
// If the expression is an error, return the error. Otherwise assign the value to the variable.
#define PROP_ASSIGN(var, expr) PROP_ASSIGN_IMPL(var, CONCAT(res, __COUNTER__), (expr))
#define PROP_IMPL(res_var, expr) \
auto res_var = (expr); \
if(!res_var) { \
return std::move(res_var).unwrap_error(); \
}
// If the expression is an error, return the error. Otherwise assign the value to the variable.
#define PROP(expr) PROP_IMPL(CONCAT(res, __COUNTER__), (expr))
struct die_object {
Dwarf_Debug dbg = nullptr;
Dwarf_Die die = nullptr;
@ -51,19 +88,29 @@ namespace libdwarf {
int
>::type = 0
>
int wrap(int (*f)(Args...), Args2&&... args) const {
Result<int, internal_error> wrap(int (*f)(Args...), Args2&&... args) const {
Dwarf_Error error = nullptr;
int ret = f(std::forward<Args2>(args)..., &error);
if(ret == DW_DLV_ERROR) {
handle_dwarf_error(dbg, error);
return handle_dwarf_error(dbg, error);
}
return ret;
return Ok(ret);
}
public:
die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {
ASSERT(dbg != nullptr);
}
// public:
// Result<die_object, internal_error> create(Dwarf_Debug dbg, Dwarf_Die die) {
// if(die == nullptr) {
// return internal_error("Error creating dwarf die object: Nullptr");
// } else {
// return die_object(dbg, die);
// }
// }
~die_object() {
if(die) {
dwarf_dealloc_die(die);
@ -87,39 +134,42 @@ namespace libdwarf {
return *this;
}
die_object clone() const {
Dwarf_Off global_offset = get_global_offset();
Result<die_object, internal_error> clone() const {
auto global_offset = get_global_offset();
if(!global_offset) {
return global_offset.unwrap_error();
}
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
Dwarf_Die die_copy = nullptr;
VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &die_copy) == DW_DLV_OK);
return {dbg, die_copy};
CHECK_OK(wrap(dwarf_offdie_b, dbg, global_offset.unwrap_value(), is_info, &die_copy));
return die_object(dbg, die_copy);
}
die_object get_child() const {
Result<die_object, internal_error> get_child() const {
Dwarf_Die child = nullptr;
int ret = wrap(dwarf_child, die, &child);
PROP_ASSIGN(ret, wrap(dwarf_child, die, &child));
if(ret == DW_DLV_OK) {
return die_object(dbg, child);
} else if(ret == DW_DLV_NO_ENTRY) {
return die_object(dbg, nullptr);
} else {
PANIC();
return internal_error("dwarf error: Unexpected return from dwarf_child {}", ret);
}
}
die_object get_sibling() const {
Result<die_object, internal_error> get_sibling() const {
Dwarf_Die sibling = nullptr;
int ret = wrap(dwarf_siblingof_b, dbg, die, true, &sibling);
PROP_ASSIGN(ret, wrap(dwarf_siblingof_b, dbg, die, true, &sibling));
if(ret == DW_DLV_OK) {
return die_object(dbg, sibling);
} else if(ret == DW_DLV_NO_ENTRY) {
return die_object(dbg, nullptr);
} else {
PANIC();
return internal_error("dwarf error: Unexpected return from dwarf_siblingof_b {}", ret);
}
}
operator bool() const {
explicit operator bool() const {
return die != nullptr;
}
@ -127,79 +177,83 @@ namespace libdwarf {
return die;
}
std::string get_name() const {
Result<std::string, internal_error> get_name() const {
char empty[] = "";
char* name = empty;
int ret = wrap(dwarf_diename, die, &name);
PROP_ASSIGN(ret, wrap(dwarf_diename, die, &name));
auto wrapper = raii_wrap(name, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); });
std::string str;
if(ret != DW_DLV_NO_ENTRY) {
str = name;
}
return name;
return std::string(name);
}
optional<std::string> get_string_attribute(Dwarf_Half attr_num) const {
Result<optional<std::string>, internal_error> get_string_attribute(Dwarf_Half attr_num) const {
Dwarf_Attribute attr;
if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) {
auto ret = wrap(dwarf_attr, die, attr_num, &attr);
if(ret.is_error()) {
ret.drop_error();
} else if(ret.unwrap_value() == DW_DLV_OK) {
auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); });
char* raw_str;
VERIFY(wrap(dwarf_formstring, attr, &raw_str) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_formstring, attr, &raw_str));
auto strwrapper = raii_wrap(raw_str, [this] (char* str) { dwarf_dealloc(dbg, str, DW_DLA_STRING); });
std::string str = raw_str;
return str;
} else {
return nullopt;
return Ok(str);
}
return Ok(nullopt);
}
optional<Dwarf_Unsigned> get_unsigned_attribute(Dwarf_Half attr_num) const {
Result<optional<Dwarf_Unsigned>, internal_error> get_unsigned_attribute(Dwarf_Half attr_num) const {
Dwarf_Attribute attr;
if(wrap(dwarf_attr, die, attr_num, &attr) == DW_DLV_OK) {
auto ret = wrap(dwarf_attr, die, attr_num, &attr);
if (ret.is_error()) {
ret.drop_error();
} else if(ret.unwrap_value() == DW_DLV_OK) {
auto attwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); });
// Dwarf_Half form = 0;
// VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK);
Dwarf_Unsigned val;
VERIFY(wrap(dwarf_formudata, attr, &val) == DW_DLV_OK);
return val;
} else {
return nullopt;
CHECK_OK(wrap(dwarf_formudata, attr, &val));
return Ok(val);
}
return Ok(nullopt);
}
bool has_attr(Dwarf_Half attr_num) const {
Result<bool, internal_error> has_attr(Dwarf_Half attr_num) const {
Dwarf_Bool present = false;
VERIFY(wrap(dwarf_hasattr, die, attr_num, &present) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_hasattr, die, attr_num, &present));
return present;
}
Dwarf_Half get_tag() const {
Result<Dwarf_Half, internal_error> get_tag() const {
Dwarf_Half tag = 0;
VERIFY(wrap(dwarf_tag, die, &tag) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_tag, die, &tag));
return tag;
}
const char* get_tag_name() const {
const char* tag_name;
if(dwarf_get_TAG_name(get_tag(), &tag_name) == DW_DLV_OK) {
auto tag = get_tag();
if(!tag) {
tag.drop_error();
} else if(dwarf_get_TAG_name(tag.unwrap_value(), &tag_name) == DW_DLV_OK) {
return tag_name;
} else {
return "<unknown tag name>";
}
return "<unknown tag name>";
}
Dwarf_Off get_global_offset() const {
Result<Dwarf_Off, internal_error> get_global_offset() const {
Dwarf_Off off;
VERIFY(wrap(dwarf_dieoffset, die, &off) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_dieoffset, die, &off));
return off;
}
die_object resolve_reference_attribute(Dwarf_Half attr_num) const {
Result<die_object, internal_error> resolve_reference_attribute(Dwarf_Half attr_num) const {
Dwarf_Attribute attr;
VERIFY(dwarf_attr(die, attr_num, &attr, nullptr) == DW_DLV_OK);
CHECK_OK(dwarf_attr(die, attr_num, &attr, nullptr));
auto wrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); });
Dwarf_Half form = 0;
VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_whatform, attr, &form));
switch(form) {
case DW_FORM_ref1:
case DW_FORM_ref2:
@ -209,77 +263,86 @@ namespace libdwarf {
{
Dwarf_Off off = 0;
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
VERIFY(wrap(dwarf_formref, attr, &off, &is_info) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_formref, attr, &off, &is_info));
Dwarf_Off global_offset = 0;
VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_convert_to_global_offset, attr, off, &global_offset));
Dwarf_Die target = nullptr;
VERIFY(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_offdie_b, dbg, global_offset, is_info, &target));
return die_object(dbg, target);
}
case DW_FORM_ref_addr:
{
Dwarf_Off off;
VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_global_formref, attr, &off));
int is_info = dwarf_get_die_infotypes_flag(die);
Dwarf_Die target = nullptr;
VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &target) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_offdie_b, dbg, off, is_info, &target));
return die_object(dbg, target);
}
case DW_FORM_ref_sig8:
{
Dwarf_Sig8 signature;
VERIFY(wrap(dwarf_formsig8, attr, &signature) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_formsig8, attr, &signature));
Dwarf_Die target = nullptr;
Dwarf_Bool targ_is_info = false;
VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_find_die_given_sig8, dbg, &signature, &target, &targ_is_info));
return die_object(dbg, target);
}
default:
PANIC(microfmt::format("unknown form for attribute {} {}\n", attr_num, form));
return internal_error(microfmt::format("unknown form for attribute {} {}", attr_num, form));
}
}
Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const {
Result<Dwarf_Unsigned, internal_error> get_ranges_offset(Dwarf_Attribute attr) const {
Dwarf_Unsigned off = 0;
Dwarf_Half form = 0;
VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_whatform, attr, &form));
if (form == DW_FORM_rnglistx) {
VERIFY(wrap(dwarf_formudata, attr, &off) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_formudata, attr, &off));
} else {
VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_global_formref, attr, &off));
}
return off;
}
template<typename F>
// callback should return true to keep going
void dwarf5_ranges(F callback) const {
NODISCARD
Result<monostate, internal_error> dwarf5_ranges(F callback) const {
Dwarf_Attribute attr = nullptr;
if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) {
return;
auto ranges_res = wrap(dwarf_attr, die, DW_AT_ranges, &attr);
if(ranges_res.is_error()) {
return ranges_res.unwrap_error();
} else if(ranges_res.unwrap_value() != DW_DLV_OK) {
return monostate{}; // normal
}
auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); });
Dwarf_Unsigned offset = get_ranges_offset(attr);
PROP_ASSIGN(offset, get_ranges_offset(attr));
Dwarf_Half form = 0;
VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK);
CHECK_OK(wrap(dwarf_whatform, attr, &form));
// get .debug_rnglists info
Dwarf_Rnglists_Head head = nullptr;
Dwarf_Unsigned rnglists_entries = 0;
Dwarf_Unsigned dw_global_offset_of_rle_set = 0;
int res = wrap(
dwarf_rnglists_get_rle_head,
attr,
form,
offset,
&head,
&rnglists_entries,
&dw_global_offset_of_rle_set
PROP_ASSIGN(
res,
wrap(
dwarf_rnglists_get_rle_head,
attr,
form,
offset,
&head,
&rnglists_entries,
&dw_global_offset_of_rle_set
)
);
auto headwrapper = raii_wrap(head, [] (Dwarf_Rnglists_Head head) { dwarf_dealloc_rnglists_head(head); });
if(res == DW_DLV_NO_ENTRY) {
return;
return monostate{}; // I guess normal
}
if(res != DW_DLV_OK) {
return internal_error("dwarf_rnglists_get_rle_head not ok");
}
VERIFY(res == DW_DLV_OK);
for(std::size_t i = 0 ; i < rnglists_entries; i++) {
unsigned entrylen = 0;
unsigned rle_value_out = 0;
@ -288,22 +351,27 @@ namespace libdwarf {
Dwarf_Bool unavailable = 0;
Dwarf_Unsigned cooked1 = 0;
Dwarf_Unsigned cooked2 = 0;
res = wrap(
dwarf_get_rnglists_entry_fields_a,
head,
i,
&entrylen,
&rle_value_out,
&raw1,
&raw2,
&unavailable,
&cooked1,
&cooked2
PROP_ASSIGN(
res2,
wrap(
dwarf_get_rnglists_entry_fields_a,
head,
i,
&entrylen,
&rle_value_out,
&raw1,
&raw2,
&unavailable,
&cooked1,
&cooked2
)
);
if(res == DW_DLV_NO_ENTRY) {
if(res2 == DW_DLV_NO_ENTRY) {
continue;
}
VERIFY(res == DW_DLV_OK);
if(res2 != DW_DLV_OK) {
return internal_error("dwarf_get_rnglists_entry_fields_a not ok");
}
if(unavailable) {
continue;
}
@ -320,27 +388,31 @@ namespace libdwarf {
case DW_RLE_startx_length:
case DW_RLE_start_length:
if(!callback(cooked1, cooked2)) {
return;
return monostate{};
}
break;
default:
PANIC("Something is wrong");
return internal_error("rle_value_out: something is wrong");
break;
}
}
return monostate{};
}
template<typename F>
// callback should return true to keep going
void dwarf4_ranges(Dwarf_Addr lowpc, F callback) const {
NODISCARD
Result<monostate, internal_error> dwarf4_ranges(Dwarf_Addr lowpc, F callback) const {
Dwarf_Attribute attr = nullptr;
if(wrap(dwarf_attr, die, DW_AT_ranges, &attr) != DW_DLV_OK) {
return;
PROP_ASSIGN(res, wrap(dwarf_attr, die, DW_AT_ranges, &attr));
if(res != DW_DLV_OK) {
return monostate{}; // normal
}
auto attrwrapper = raii_wrap(attr, [] (Dwarf_Attribute attr) { dwarf_dealloc_attribute(attr); });
Dwarf_Unsigned offset;
if(wrap(dwarf_global_formref, attr, &offset) != DW_DLV_OK) {
return;
PROP_ASSIGN(res2, wrap(dwarf_global_formref, attr, &offset));
if(res2 != DW_DLV_OK) {
return monostate{}; // normal
}
Dwarf_Addr baseaddr = 0;
if(lowpc != (std::numeric_limits<Dwarf_Addr>::max)()) {
@ -348,7 +420,7 @@ namespace libdwarf {
}
Dwarf_Ranges* ranges = nullptr;
Dwarf_Signed count = 0;
VERIFY(
CHECK_OK(
wrap(
dwarf_get_ranges_b,
dbg,
@ -358,7 +430,7 @@ namespace libdwarf {
&ranges,
&count,
nullptr
) == DW_DLV_OK
)
);
auto rangeswrapper = raii_wrap(
ranges,
@ -367,7 +439,7 @@ namespace libdwarf {
for(int i = 0; i < count; i++) {
if(ranges[i].dwr_type == DW_RANGES_ENTRY) {
if(!callback(baseaddr + ranges[i].dwr_addr1, baseaddr + ranges[i].dwr_addr2)) {
return;
return monostate{};
}
} else if(ranges[i].dwr_type == DW_RANGES_ADDRESS_SELECTION) {
baseaddr = ranges[i].dwr_addr2;
@ -376,63 +448,76 @@ namespace libdwarf {
baseaddr = lowpc;
}
}
return monostate{};
}
template<typename F>
// callback should return true to keep going
void dwarf_ranges(int version, optional<Dwarf_Addr> pc, F callback) const {
NODISCARD
Result<monostate, internal_error> dwarf_ranges(int version, optional<Dwarf_Addr> pc, F callback) const {
Dwarf_Addr lowpc = (std::numeric_limits<Dwarf_Addr>::max)();
if(wrap(dwarf_lowpc, die, &lowpc) == DW_DLV_OK) {
PROP_ASSIGN(res, wrap(dwarf_lowpc, die, &lowpc));
if(res == DW_DLV_OK) {
if(pc.has_value() && pc.unwrap() == lowpc) {
callback(lowpc, lowpc + 1);
return;
return monostate{};
}
Dwarf_Addr highpc = 0;
enum Dwarf_Form_Class return_class;
if(wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class) == DW_DLV_OK) {
PROP_ASSIGN(res2, wrap(dwarf_highpc_b, die, &highpc, nullptr, &return_class));
if(res2 == DW_DLV_OK) {
if(return_class == DW_FORM_CLASS_CONSTANT) {
highpc += lowpc;
}
if(!callback(lowpc, highpc)) {
return;
return monostate{};
}
}
}
if(version >= 5) {
dwarf5_ranges(callback);
return dwarf5_ranges(callback);
} else {
dwarf4_ranges(lowpc, callback);
return dwarf4_ranges(lowpc, callback);
}
}
std::vector<std::pair<Dwarf_Addr, Dwarf_Addr>> get_rangelist_entries(int version) const {
std::vector<std::pair<Dwarf_Addr, Dwarf_Addr>> vec;
dwarf_ranges(version, nullopt, [&vec] (Dwarf_Addr low, Dwarf_Addr high) {
// Simple coalescing optimization:
// Sometimes the range list entries are really continuous: [100, 200), [200, 300)
// Other times there's just one byte of separation [300, 399), [400, 500)
// Those are the main two cases I've observed.
// This will not catch all cases, presumably, as the range lists aren't sorted. But compilers/linkers
// seem to like to emit the ranges in sorted order.
if(!vec.empty() && low - vec.back().second <= 1) {
vec.back().second = high;
} else {
vec.push_back({low, high});
}
return true;
});
struct address_range {
Dwarf_Addr low;
Dwarf_Addr high;
};
Result<std::vector<address_range>, internal_error> get_rangelist_entries(int version) const {
std::vector<address_range> vec;
PROP(
dwarf_ranges(version, nullopt, [&vec] (Dwarf_Addr low, Dwarf_Addr high) {
// Simple coalescing optimization:
// Sometimes the range list entries are really continuous: [100, 200), [200, 300)
// Other times there's just one byte of separation [300, 399), [400, 500)
// Those are the main two cases I've observed.
// This will not catch all cases, presumably, as the range lists aren't sorted.
// But compilers/linkers seem to like to emit the ranges in sorted order.
if(!vec.empty() && low - vec.back().high <= 1) {
vec.back().high = high;
} else {
vec.push_back({low, high});
}
return true;
})
);
return vec;
}
Dwarf_Bool pc_in_die(int version, Dwarf_Addr pc) const {
Result<Dwarf_Bool, internal_error> pc_in_die(int version, Dwarf_Addr pc) const {
bool found = false;
dwarf_ranges(version, pc, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) {
if(pc >= low && pc < high) {
found = true;
return false;
}
return true;
});
PROP(
dwarf_ranges(version, pc, [&found, pc] (Dwarf_Addr low, Dwarf_Addr high) {
if(pc >= low && pc < high) {
found = true;
return false;
}
return true;
})
);
return found;
}
@ -440,9 +525,9 @@ namespace libdwarf {
std::fprintf(
stderr,
"%08llx %s %s\n",
to_ull(get_global_offset()),
to_ull(get_global_offset().value_or(0)),
get_tag_name(),
get_name().c_str()
get_name().drop_error().value_or("<name error>").c_str()
);
}
};
@ -457,13 +542,22 @@ namespace libdwarf {
// TODO: Refactor so there is only one fn call
bool continue_traversal = true;
if(fn(die)) {
die_object current = die.get_sibling();
while(current) {
if(fn(current)) {
current = current.get_sibling();
} else {
continue_traversal = false;
break;
auto sibling = die.get_sibling();
if(!sibling) {
sibling.drop_error();
} else {
die_object current = std::move(sibling).unwrap_value();
while(current) {
if(fn(current)) {
auto sibling = current.get_sibling();
if(!sibling) {
sibling.drop_error();
}
current = std::move(sibling).unwrap_value();
} else {
continue_traversal = false;
break;
}
}
}
}
@ -481,8 +575,10 @@ namespace libdwarf {
die,
[&fn](const die_object& die) {
auto child = die.get_child();
if(child) {
if(!walk_die_list_recursive(child, fn)) {
if(child.is_error()) {
child.drop_error();
} else if(child.unwrap_value()) {
if(!walk_die_list_recursive(child.unwrap_value(), fn)) {
return false;
}
}

View File

@ -20,9 +20,10 @@ namespace detail {
class internal_error : public std::exception {
std::string msg;
public:
internal_error(std::string message) : msg(std::move(message)) {}
internal_error(std::string message) : msg("Cpptrace internal error: " + std::move(message)) {}
template<typename... Args>
internal_error(const char* format, Args&&... args) : msg(microfmt::format(format, args...)) {}
internal_error(const char* format, Args&&... args)
: msg("Cpptrace internal error: " + microfmt::format(format, args...)) {}
const char* what() const noexcept override {
return msg.c_str();
}
@ -75,23 +76,27 @@ namespace detail {
}
}
[[noreturn]] inline void panic(
const char* signature,
source_location location,
const std::string& message = ""
) {
if(message == "") {
throw internal_error(
"Cpptrace panic {}:{}: {}\n",
location.file, location.line, signature
);
} else {
throw internal_error(
"Cpptrace panic {}:{}: {}: {}\n",
location.file, location.line, signature, message.c_str()
);
}
}
// [[noreturn]] inline void panic(
// const char* signature,
// source_location location,
// const std::string& message = ""
// ) {
// if(message == "") {
// throw internal_error(
// stringf(
// "Cpptrace panic %s:%d: %s\n",
// location.file, location.line, signature
// )
// );
// } else {
// throw internal_error(
// stringf(
// "Cpptrace panic %s:%d: %s: %s\n",
// location.file, location.line, signature, message.c_str()
// )
// );
// }
// }
template<typename T>
void nullfn() {
@ -107,7 +112,7 @@ namespace detail {
}
// Check condition in both debug and release. std::runtime_error on failure.
#define VERIFY(c, ...) ( \
/*#define VERIFY(c, ...) ( \
(::cpptrace::detail::as_bool(c)) \
? static_cast<void>(0) \
: (::cpptrace::detail::assert_fail)( \
@ -116,7 +121,7 @@ namespace detail {
CPPTRACE_PFUNC, \
{}, \
##__VA_ARGS__) \
)
)*/
// Workaround a compiler warning
template<typename T>
@ -128,8 +133,8 @@ namespace detail {
return "";
}
// Check condition in both debug and release. std::runtime_error on failure.
#define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, ::cpptrace::detail::as_string(__VA_ARGS__)))
// // Check condition in both debug and release. std::runtime_error on failure.
// #define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, ::cpptrace::detail::as_string(__VA_ARGS__)))
#ifndef NDEBUG
// Check condition in both debug. std::runtime_error on failure.

View File

@ -325,6 +325,26 @@ namespace detail {
}
};
template<typename T>
struct OkResult {
T value;
};
template<typename T>
struct ErrorResult {
T value;
};
template<typename T>
OkResult<T> Ok(T value) {
return {value};
}
template<typename T>
ErrorResult<T> Error(T value) {
return {value};
}
// TODO: Better dump error
// TODO: Explicit constructors for value, then add Ok()/Error() helpers
template<typename T, typename E, typename std::enable_if<!std::is_same<T, E>::value, int>::type = 0>
@ -339,6 +359,10 @@ namespace detail {
public:
Result(T value) : value_(std::move(value)), active(member::value) {}
Result(E error) : error_(std::move(error)), active(member::error) {}
template<typename U>
Result(OkResult<U>&& value) : value_(std::move(value.value)), active(member::value) {}
template<typename U>
Result(ErrorResult<U>&& error) : error_(std::move(error.value)), active(member::error) {}
Result(Result&& other) : active(other.active) {
if(other.active == member::value) {
new (&value_) T(std::move(other.value_));
@ -422,10 +446,11 @@ namespace detail {
return has_value() ? std::move(value_) : static_cast<T>(std::forward<U>(default_value));
}
void drop_error() const {
const Result& drop_error() const {
if(is_error()) {
std::fprintf(stderr, "%s\n", unwrap_error().what());
}
return *this;
}
};