Properly handle dwarf errors (#43)
This commit is contained in:
parent
f69448f781
commit
76fc93639e
@ -34,7 +34,7 @@ namespace detail {
|
|||||||
using Header = typename std::conditional<Bits == 32, Elf32_Ehdr, Elf64_Ehdr>::type;
|
using Header = typename std::conditional<Bits == 32, Elf32_Ehdr, Elf64_Ehdr>::type;
|
||||||
using PHeader = typename std::conditional<Bits == 32, Elf32_Phdr, Elf64_Phdr>::type;
|
using PHeader = typename std::conditional<Bits == 32, Elf32_Phdr, Elf64_Phdr>::type;
|
||||||
Header file_header = load_bytes<Header>(file, 0);
|
Header file_header = load_bytes<Header>(file, 0);
|
||||||
CPPTRACE_VERIFY(file_header.e_ehsize == sizeof(Header), "ELF file header size mismatch" + obj_path);
|
VERIFY(file_header.e_ehsize == sizeof(Header), "ELF file header size mismatch" + obj_path);
|
||||||
// PT_PHDR will occur at most once
|
// PT_PHDR will occur at most once
|
||||||
// Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
|
// Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
|
||||||
// It should occur at the beginning but may as well loop just in case
|
// It should occur at the beginning but may as well loop just in case
|
||||||
@ -56,10 +56,10 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
// Initial checks/metadata
|
// Initial checks/metadata
|
||||||
auto magic = load_bytes<std::array<char, 4>>(file, 0);
|
auto magic = load_bytes<std::array<char, 4>>(file, 0);
|
||||||
CPPTRACE_VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}), "File is not ELF " + obj_path);
|
VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}), "File is not ELF " + obj_path);
|
||||||
bool is_64 = load_bytes<uint8_t>(file, 4) == 2;
|
bool is_64 = load_bytes<uint8_t>(file, 4) == 2;
|
||||||
bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1;
|
bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1;
|
||||||
CPPTRACE_VERIFY(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF endianness " + obj_path);
|
VERIFY(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF endianness " + obj_path);
|
||||||
// get image base
|
// get image base
|
||||||
if(is_64) {
|
if(is_64) {
|
||||||
return elf_get_module_image_base_from_program_table<64>(obj_path, file, is_little_endian);
|
return elf_get_module_image_base_from_program_table<64>(obj_path, file, is_little_endian);
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
@ -37,37 +38,65 @@ namespace detail {
|
|||||||
) : file(_file), /*function(_function),*/ line(_line) {}
|
) : file(_file), /*function(_function),*/ line(_line) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void primitive_assert_impl(
|
enum class assert_type {
|
||||||
bool condition,
|
assert,
|
||||||
bool verify,
|
verify,
|
||||||
|
panic,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const char* assert_actions[] = {"assertion", "verification", "panic"};
|
||||||
|
constexpr const char* assert_names[] = {"ASSERT", "VERIFY", "PANIC"};
|
||||||
|
|
||||||
|
inline void assert_fail(
|
||||||
|
assert_type type,
|
||||||
const char* expression,
|
const char* expression,
|
||||||
const char* signature,
|
const char* signature,
|
||||||
source_location location,
|
source_location location,
|
||||||
const std::string& message = ""
|
const std::string& message = ""
|
||||||
) {
|
) {
|
||||||
if(!condition) {
|
const char* action = assert_actions[static_cast<std::underlying_type<assert_type>::type>(type)];
|
||||||
const char* action = verify ? "verification" : "assertion";
|
const char* name = assert_names[static_cast<std::underlying_type<assert_type>::type>(type)];
|
||||||
const char* name = verify ? "VERIFY" : "ASSERT";
|
|
||||||
if(message == "") {
|
if(message == "") {
|
||||||
throw std::runtime_error(
|
throw std::logic_error(
|
||||||
stringf(
|
stringf(
|
||||||
"Cpptrace %s failed at %s:%d: %s\n"
|
"Cpptrace %s failed at %s:%d: %s\n"
|
||||||
" CPPTRACE_%s(%s);\n",
|
" %s(%s);\n",
|
||||||
action, location.file, location.line, signature,
|
action, location.file, location.line, signature,
|
||||||
name, expression
|
name, expression
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(
|
throw std::logic_error(
|
||||||
stringf(
|
stringf(
|
||||||
"Cpptrace %s failed at %s:%d: %s: %s\n"
|
"Cpptrace %s failed at %s:%d: %s: %s\n"
|
||||||
" CPPTRACE_%s(%s);\n",
|
" %s(%s);\n",
|
||||||
action, location.file, location.line, signature, message.c_str(),
|
action, location.file, location.line, signature, message.c_str(),
|
||||||
name, expression
|
name, expression
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[noreturn]] inline void panic(
|
||||||
|
const char* signature,
|
||||||
|
source_location location,
|
||||||
|
const std::string& message = ""
|
||||||
|
) {
|
||||||
|
if(message == "") {
|
||||||
|
throw std::logic_error(
|
||||||
|
stringf(
|
||||||
|
"Cpptrace panic %s:%d: %s\n",
|
||||||
|
location.file, location.line, signature
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw std::logic_error(
|
||||||
|
stringf(
|
||||||
|
"Cpptrace panic %s:%d: %s: %s\n",
|
||||||
|
location.file, location.line, signature, message.c_str()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -75,25 +104,43 @@ namespace detail {
|
|||||||
|
|
||||||
#define PHONY_USE(E) (nullfn<decltype(E)>())
|
#define PHONY_USE(E) (nullfn<decltype(E)>())
|
||||||
|
|
||||||
|
// Workaround a compiler warning
|
||||||
|
template<typename T>
|
||||||
|
bool as_bool(T&& value) {
|
||||||
|
return static_cast<bool>(std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
// Check condition in both debug and release. std::runtime_error on failure.
|
// Check condition in both debug and release. std::runtime_error on failure.
|
||||||
#define CPPTRACE_VERIFY(c, ...) ( \
|
#define VERIFY(c, ...) ( \
|
||||||
::cpptrace::detail::primitive_assert_impl(c, true, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__) \
|
(::cpptrace::detail::as_bool(c)) \
|
||||||
|
? static_cast<void>(0) \
|
||||||
|
: (::cpptrace::detail::assert_fail)( \
|
||||||
|
::cpptrace::detail::assert_type::verify, \
|
||||||
|
#c, \
|
||||||
|
CPPTRACE_PFUNC, \
|
||||||
|
{}, \
|
||||||
|
##__VA_ARGS__) \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Check condition in both debug and release. std::runtime_error on failure.
|
||||||
|
#define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, {}, std::string(__VA_ARGS__)))
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Check condition in both debug. std::runtime_error on failure.
|
// Check condition in both debug. std::runtime_error on failure.
|
||||||
#define CPPTRACE_ASSERT(c, ...) ( \
|
#define ASSERT(c, ...) ( \
|
||||||
::cpptrace::detail::primitive_assert_impl(c, false, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__) \
|
(::cpptrace::detail::as_bool(c)) \
|
||||||
|
? static_cast<void>(0) \
|
||||||
|
: (::cpptrace::detail::assert_fail)( \
|
||||||
|
::cpptrace::detail::assert_type::assert, \
|
||||||
|
#c, \
|
||||||
|
CPPTRACE_PFUNC, \
|
||||||
|
{}, \
|
||||||
|
##__VA_ARGS__) \
|
||||||
)
|
)
|
||||||
#else
|
#else
|
||||||
// Check condition in both debug. std::runtime_error on failure.
|
// Check condition in both debug. std::runtime_error on failure.
|
||||||
#define CPPTRACE_ASSERT(c, ...) PHONY_USE(c)
|
#define ASSERT(c, ...) PHONY_USE(c)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Setting to silence these or make them fatal
|
|
||||||
inline void nonfatal_error(const std::string& message) {
|
|
||||||
fprintf(stderr, "Non-fatal cpptrace error: %s\n", message.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -107,7 +107,7 @@ namespace detail {
|
|||||||
if(allow_arch_mismatch) {
|
if(allow_arch_mismatch) {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
} else {
|
} else {
|
||||||
CPPTRACE_VERIFY(false, "Mach-O file cpu type and subtype do not match current machine " + obj_path);
|
PANIC("Mach-O file cpu type and subtype do not match current machine " + obj_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ncmds = header.ncmds;
|
ncmds = header.ncmds;
|
||||||
@ -130,7 +130,7 @@ namespace detail {
|
|||||||
actual_offset += cmd.cmdsize;
|
actual_offset += cmd.cmdsize;
|
||||||
}
|
}
|
||||||
// somehow no __TEXT section was found...
|
// somehow no __TEXT section was found...
|
||||||
CPPTRACE_VERIFY(false, "Couldn't find __TEXT section while parsing Mach-O object");
|
PANIC("Couldn't find __TEXT section while parsing Mach-O object");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
||||||
CPPTRACE_VERIFY(false, "Couldn't find appropriate architecture in fat Mach-O");
|
PANIC("Couldn't find appropriate architecture in fat Mach-O");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ namespace detail {
|
|||||||
throw file_error("Unable to read object file " + obj_path);
|
throw file_error("Unable to read object file " + obj_path);
|
||||||
}
|
}
|
||||||
uint32_t magic = load_bytes<uint32_t>(file, 0);
|
uint32_t magic = load_bytes<uint32_t>(file, 0);
|
||||||
CPPTRACE_VERIFY(is_mach_o(magic), "File is not Mach-O " + obj_path);
|
VERIFY(is_mach_o(magic), "File is not Mach-O " + obj_path);
|
||||||
bool is_64 = is_magic_64(magic);
|
bool is_64 = is_magic_64(magic);
|
||||||
bool should_swap = should_swap_bytes(magic);
|
bool should_swap = should_swap_bytes(magic);
|
||||||
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
||||||
@ -220,7 +220,7 @@ namespace detail {
|
|||||||
throw file_error("Unable to read object file " + obj_path);
|
throw file_error("Unable to read object file " + obj_path);
|
||||||
}
|
}
|
||||||
uint32_t magic = load_bytes<uint32_t>(file, 0);
|
uint32_t magic = load_bytes<uint32_t>(file, 0);
|
||||||
CPPTRACE_VERIFY(is_fat_magic(magic));
|
VERIFY(is_fat_magic(magic));
|
||||||
bool should_swap = should_swap_bytes(magic);
|
bool should_swap = should_swap_bytes(magic);
|
||||||
size_t header_size = sizeof(fat_header);
|
size_t header_size = sizeof(fat_header);
|
||||||
size_t arch_size = sizeof(fat_arch);
|
size_t arch_size = sizeof(fat_arch);
|
||||||
@ -244,7 +244,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
||||||
CPPTRACE_VERIFY(false, "Couldn't find appropriate architecture in fat Mach-O");
|
PANIC("Couldn't find appropriate architecture in fat Mach-O");
|
||||||
return { 0, 0 };
|
return { 0, 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,19 +37,19 @@ namespace detail {
|
|||||||
throw file_error("Unable to read object file " + obj_path);
|
throw file_error("Unable to read object file " + obj_path);
|
||||||
}
|
}
|
||||||
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
||||||
CPPTRACE_VERIFY(memcmp(magic.data(), "MZ", 2) == 0, "File is not a PE file " + obj_path);
|
VERIFY(memcmp(magic.data(), "MZ", 2) == 0, "File is not a PE file " + obj_path);
|
||||||
DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
|
DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
|
||||||
DWORD nt_header_offset = e_lfanew;
|
DWORD nt_header_offset = e_lfanew;
|
||||||
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
|
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
|
||||||
CPPTRACE_VERIFY(memcmp(signature.data(), "PE\0\0", 4) == 0, "File is not a PE file " + obj_path);
|
VERIFY(memcmp(signature.data(), "PE\0\0", 4) == 0, "File is not a PE file " + obj_path);
|
||||||
WORD size_of_optional_header = pe_byteswap_if_needed(
|
WORD size_of_optional_header = pe_byteswap_if_needed(
|
||||||
load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
|
load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
|
||||||
);
|
);
|
||||||
CPPTRACE_VERIFY(size_of_optional_header != 0);
|
VERIFY(size_of_optional_header != 0);
|
||||||
WORD optional_header_magic = pe_byteswap_if_needed(
|
WORD optional_header_magic = pe_byteswap_if_needed(
|
||||||
load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0
|
load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0
|
||||||
);
|
);
|
||||||
CPPTRACE_VERIFY(
|
VERIFY(
|
||||||
optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC,
|
optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC,
|
||||||
"PE file does not match expected bit-mode " + obj_path
|
"PE file does not match expected bit-mode " + obj_path
|
||||||
);
|
);
|
||||||
|
|||||||
@ -155,8 +155,8 @@ namespace detail {
|
|||||||
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
|
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
|
||||||
T load_bytes(FILE* obj_file, off_t offset) {
|
T load_bytes(FILE* obj_file, off_t offset) {
|
||||||
T object;
|
T object;
|
||||||
CPPTRACE_VERIFY(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
|
VERIFY(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
|
||||||
CPPTRACE_VERIFY(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
|
VERIFY(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,8 +75,8 @@ namespace addr2line {
|
|||||||
std::string resolve_addresses(const std::string& addresses, const std::string& executable) {
|
std::string resolve_addresses(const std::string& addresses, const std::string& executable) {
|
||||||
pipe_t output_pipe;
|
pipe_t output_pipe;
|
||||||
pipe_t input_pipe;
|
pipe_t input_pipe;
|
||||||
CPPTRACE_VERIFY(pipe(output_pipe.data) == 0);
|
VERIFY(pipe(output_pipe.data) == 0);
|
||||||
CPPTRACE_VERIFY(pipe(input_pipe.data) == 0);
|
VERIFY(pipe(input_pipe.data) == 0);
|
||||||
const pid_t pid = fork();
|
const pid_t pid = fork();
|
||||||
if(pid == -1) { return ""; } // error? TODO: Diagnostic
|
if(pid == -1) { return ""; } // error? TODO: Diagnostic
|
||||||
if(pid == 0) { // child
|
if(pid == 0) { // child
|
||||||
@ -120,7 +120,7 @@ namespace addr2line {
|
|||||||
#endif
|
#endif
|
||||||
_exit(1); // TODO: Diagnostic?
|
_exit(1); // TODO: Diagnostic?
|
||||||
}
|
}
|
||||||
CPPTRACE_VERIFY(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1);
|
VERIFY(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1);
|
||||||
close(input_pipe.read_end);
|
close(input_pipe.read_end);
|
||||||
close(input_pipe.write_end);
|
close(input_pipe.write_end);
|
||||||
close(output_pipe.write_end);
|
close(output_pipe.write_end);
|
||||||
@ -200,14 +200,14 @@ namespace addr2line {
|
|||||||
symbol_end = at_location;
|
symbol_end = at_location;
|
||||||
filename_start = at_location + 4;
|
filename_start = at_location + 4;
|
||||||
} else {
|
} else {
|
||||||
CPPTRACE_VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
|
VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
|
||||||
symbol_end = 2;
|
symbol_end = 2;
|
||||||
filename_start = 3;
|
filename_start = 3;
|
||||||
}
|
}
|
||||||
auto symbol = line.substr(0, symbol_end);
|
auto symbol = line.substr(0, symbol_end);
|
||||||
auto colon = line.rfind(':');
|
auto colon = line.rfind(':');
|
||||||
CPPTRACE_VERIFY(colon != std::string::npos);
|
VERIFY(colon != std::string::npos);
|
||||||
CPPTRACE_VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case
|
VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case
|
||||||
auto filename = line.substr(filename_start, colon - filename_start);
|
auto filename = line.substr(filename_start, colon - filename_start);
|
||||||
auto line_number = line.substr(colon + 1);
|
auto line_number = line.substr(colon + 1);
|
||||||
if(line_number != "?") {
|
if(line_number != "?") {
|
||||||
@ -237,7 +237,7 @@ namespace addr2line {
|
|||||||
const std::size_t symbol_end = in_location;
|
const std::size_t symbol_end = in_location;
|
||||||
entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
|
entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
|
||||||
const std::size_t obj_end = line.find(")", in_location);
|
const std::size_t obj_end = line.find(")", in_location);
|
||||||
CPPTRACE_VERIFY(
|
VERIFY(
|
||||||
obj_end != std::string::npos,
|
obj_end != std::string::npos,
|
||||||
"Unexpected edge case while processing addr2line/atos output"
|
"Unexpected edge case while processing addr2line/atos output"
|
||||||
);
|
);
|
||||||
@ -247,7 +247,7 @@ namespace addr2line {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const std::size_t filename_end = line.find(":", filename_start);
|
const std::size_t filename_end = line.find(":", filename_start);
|
||||||
CPPTRACE_VERIFY(
|
VERIFY(
|
||||||
filename_end != std::string::npos,
|
filename_end != std::string::npos,
|
||||||
"Unexpected edge case while processing addr2line/atos output"
|
"Unexpected edge case while processing addr2line/atos output"
|
||||||
);
|
);
|
||||||
@ -257,7 +257,7 @@ namespace addr2line {
|
|||||||
);
|
);
|
||||||
const std::size_t line_start = filename_end + 1;
|
const std::size_t line_start = filename_end + 1;
|
||||||
const std::size_t line_end = line.find(")", filename_end);
|
const std::size_t line_end = line.find(")", filename_end);
|
||||||
CPPTRACE_VERIFY(
|
VERIFY(
|
||||||
line_end == line.size() - 1,
|
line_end == line.size() - 1,
|
||||||
"Unexpected edge case while processing addr2line/atos output"
|
"Unexpected edge case while processing addr2line/atos output"
|
||||||
);
|
);
|
||||||
@ -296,7 +296,7 @@ namespace addr2line {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
||||||
CPPTRACE_VERIFY(output.size() == entries_vec.size());
|
VERIFY(output.size() == entries_vec.size());
|
||||||
for(size_t i = 0; i < output.size(); i++) {
|
for(size_t i = 0; i < output.size(); i++) {
|
||||||
update_trace(output[i], i, entries_vec);
|
update_trace(output[i], i, entries_vec);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stdexcept>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -42,23 +43,48 @@ namespace libdwarf {
|
|||||||
constexpr bool dump_dwarf = false;
|
constexpr bool dump_dwarf = false;
|
||||||
constexpr bool trace_dwarf = false;
|
constexpr bool trace_dwarf = false;
|
||||||
|
|
||||||
static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) {
|
|
||||||
printf("libdwarf error reading %s: %llu %s\n", "xx", to_ull(dwarf_errno(err)), dwarf_errmsg(err));
|
|
||||||
if(errarg) {
|
|
||||||
printf("Error: errarg is nonnull but it should be null\n");
|
|
||||||
}
|
|
||||||
printf("Giving up");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert(std::is_pointer<Dwarf_Die>::value, "Dwarf_Die not a pointer");
|
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");
|
static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
|
||||||
|
|
||||||
|
void handle_error(Dwarf_Debug dbg, Dwarf_Error error) {
|
||||||
|
int ev = dwarf_errno(error);
|
||||||
|
char* msg = dwarf_errmsg(error);
|
||||||
|
dwarf_dealloc_error(dbg, error);
|
||||||
|
throw std::runtime_error(stringf("Cpptrace dwarf error %d %s\n", ev, msg));
|
||||||
|
}
|
||||||
|
|
||||||
struct die_object {
|
struct die_object {
|
||||||
Dwarf_Debug dbg = nullptr;
|
Dwarf_Debug dbg = nullptr;
|
||||||
Dwarf_Die die = nullptr;
|
Dwarf_Die die = nullptr;
|
||||||
|
|
||||||
die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {}
|
// Error handling helper
|
||||||
|
// For some reason R (*f)(Args..., void*)-style deduction isn't possible, seems like a bug in all compilers
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56190
|
||||||
|
template<
|
||||||
|
typename... Args,
|
||||||
|
typename... Args2,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<
|
||||||
|
decltype(
|
||||||
|
(void)std::declval<int(Args...)>()(std::forward<Args2>(std::declval<Args2>())..., nullptr)
|
||||||
|
),
|
||||||
|
void
|
||||||
|
>::value,
|
||||||
|
int
|
||||||
|
>::type = 0
|
||||||
|
>
|
||||||
|
int wrap(int (*f)(Args...), Args2&&... args) const {
|
||||||
|
Dwarf_Error error = 0;
|
||||||
|
int ret = f(std::forward<Args2>(args)..., &error);
|
||||||
|
if(ret == DW_DLV_ERROR) {
|
||||||
|
handle_error(dbg, error);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {
|
||||||
|
ASSERT(dbg != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
~die_object() {
|
~die_object() {
|
||||||
if(die) {
|
if(die) {
|
||||||
@ -88,37 +114,31 @@ namespace libdwarf {
|
|||||||
Dwarf_Off off = get_global_offset();
|
Dwarf_Off off = get_global_offset();
|
||||||
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
|
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
|
||||||
Dwarf_Die die_copy = 0;
|
Dwarf_Die die_copy = 0;
|
||||||
CPPTRACE_VERIFY(dwarf_offdie_b(dbg, off, is_info, &die_copy, nullptr) == DW_DLV_OK);
|
VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info, &die_copy) == DW_DLV_OK);
|
||||||
return {dbg, die_copy};
|
return {dbg, die_copy};
|
||||||
}
|
}
|
||||||
|
|
||||||
die_object get_child() const {
|
die_object get_child() const {
|
||||||
Dwarf_Die child = nullptr;
|
Dwarf_Die child = nullptr;
|
||||||
int ret = dwarf_child(
|
int ret = wrap(dwarf_child, die, &child);
|
||||||
die,
|
|
||||||
&child,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
return die_object(dbg, child);
|
return die_object(dbg, child);
|
||||||
} else if(ret == DW_DLV_NO_ENTRY) {
|
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||||
return die_object(dbg, 0);
|
return die_object(dbg, 0);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error\n");
|
PANIC();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
die_object get_sibling() const {
|
die_object get_sibling() const {
|
||||||
Dwarf_Die sibling = 0;
|
Dwarf_Die sibling = 0;
|
||||||
int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr);
|
int ret = wrap(dwarf_siblingof_b, dbg, die, true, &sibling);
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
return die_object(dbg, sibling);
|
return die_object(dbg, sibling);
|
||||||
} else if(ret == DW_DLV_NO_ENTRY) {
|
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||||
return die_object(dbg, 0);
|
return die_object(dbg, 0);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error\n");
|
PANIC();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +153,7 @@ namespace libdwarf {
|
|||||||
std::string get_name() const {
|
std::string get_name() const {
|
||||||
char empty[] = "";
|
char empty[] = "";
|
||||||
char* name = empty;
|
char* name = empty;
|
||||||
int ret = dwarf_diename(die, &name, nullptr);
|
int ret = wrap(dwarf_diename, die, &name);
|
||||||
std::string str;
|
std::string str;
|
||||||
if(ret != DW_DLV_NO_ENTRY) {
|
if(ret != DW_DLV_NO_ENTRY) {
|
||||||
str = name;
|
str = name;
|
||||||
@ -144,12 +164,10 @@ namespace libdwarf {
|
|||||||
|
|
||||||
optional<std::string> get_string_attribute(Dwarf_Half dw_attrnum) const {
|
optional<std::string> get_string_attribute(Dwarf_Half dw_attrnum) const {
|
||||||
Dwarf_Attribute attr;
|
Dwarf_Attribute attr;
|
||||||
int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
|
if(wrap(dwarf_attr, die, dw_attrnum, &attr) == DW_DLV_OK) {
|
||||||
if(ret == DW_DLV_OK) {
|
|
||||||
char* raw_str;
|
char* raw_str;
|
||||||
std::string str;
|
std::string str;
|
||||||
ret = dwarf_formstring(attr, &raw_str, nullptr);
|
VERIFY(wrap(dwarf_formstring, attr, &raw_str) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
str = raw_str;
|
str = raw_str;
|
||||||
dwarf_dealloc(dbg, raw_str, DW_DLA_STRING);
|
dwarf_dealloc(dbg, raw_str, DW_DLA_STRING);
|
||||||
dwarf_dealloc_attribute(attr);
|
dwarf_dealloc_attribute(attr);
|
||||||
@ -161,34 +179,36 @@ namespace libdwarf {
|
|||||||
|
|
||||||
bool has_attr(Dwarf_Half dw_attrnum) const {
|
bool has_attr(Dwarf_Half dw_attrnum) const {
|
||||||
Dwarf_Bool present = false;
|
Dwarf_Bool present = false;
|
||||||
CPPTRACE_VERIFY(dwarf_hasattr(die, dw_attrnum, &present, nullptr) == DW_DLV_OK);
|
VERIFY(wrap(dwarf_hasattr, die, dw_attrnum, &present) == DW_DLV_OK);
|
||||||
return present;
|
return present;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dwarf_Half get_tag() const {
|
Dwarf_Half get_tag() const {
|
||||||
Dwarf_Half tag = 0;
|
Dwarf_Half tag = 0;
|
||||||
dwarf_tag(die, &tag, nullptr);
|
VERIFY(wrap(dwarf_tag, die, &tag) == DW_DLV_OK);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* get_tag_name() const {
|
const char* get_tag_name() const {
|
||||||
const char* tag_name;
|
const char* tag_name;
|
||||||
dwarf_get_TAG_name(get_tag(), &tag_name);
|
if(dwarf_get_TAG_name(get_tag(), &tag_name) == DW_DLV_OK) {
|
||||||
return tag_name;
|
return tag_name;
|
||||||
|
} else {
|
||||||
|
return "<unknown tag name>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dwarf_Off get_global_offset() const {
|
Dwarf_Off get_global_offset() const {
|
||||||
Dwarf_Off off;
|
Dwarf_Off off;
|
||||||
int ret = dwarf_dieoffset(die, &off, nullptr);
|
VERIFY(wrap(dwarf_dieoffset, die, &off) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
return off;
|
return off;
|
||||||
}
|
}
|
||||||
|
|
||||||
die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const {
|
die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const {
|
||||||
Dwarf_Attribute attr;
|
Dwarf_Attribute attr;
|
||||||
int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
|
VERIFY(dwarf_attr(die, dw_attrnum, &attr, nullptr) == DW_DLV_OK);
|
||||||
Dwarf_Half form = 0;
|
Dwarf_Half form = 0;
|
||||||
ret = dwarf_whatform(attr, &form, nullptr);
|
VERIFY(wrap(dwarf_whatform, attr, &form) == DW_DLV_OK);
|
||||||
switch(form) {
|
switch(form) {
|
||||||
case DW_FORM_ref1:
|
case DW_FORM_ref1:
|
||||||
case DW_FORM_ref2:
|
case DW_FORM_ref2:
|
||||||
@ -198,37 +218,31 @@ namespace libdwarf {
|
|||||||
{
|
{
|
||||||
Dwarf_Off off = 0;
|
Dwarf_Off off = 0;
|
||||||
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
|
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
|
||||||
ret = dwarf_formref(attr, &off, &is_info, nullptr);
|
VERIFY(wrap(dwarf_formref, attr, &off, &is_info) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
Dwarf_Off goff = 0;
|
Dwarf_Off goff = 0;
|
||||||
ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr);
|
VERIFY(wrap(dwarf_convert_to_global_offset, attr, off, &goff) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
Dwarf_Die targ_die_a = 0;
|
Dwarf_Die targ_die_a = 0;
|
||||||
ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr);
|
VERIFY(wrap(dwarf_offdie_b, dbg, goff, is_info, &targ_die_a) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
dwarf_dealloc_attribute(attr);
|
dwarf_dealloc_attribute(attr);
|
||||||
return die_object(dbg, targ_die_a);
|
return die_object(dbg, targ_die_a);
|
||||||
}
|
}
|
||||||
case DW_FORM_ref_addr:
|
case DW_FORM_ref_addr:
|
||||||
{
|
{
|
||||||
Dwarf_Off off;
|
Dwarf_Off off;
|
||||||
ret = dwarf_global_formref(attr, &off, nullptr);
|
VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK);
|
||||||
int is_info_a = dwarf_get_die_infotypes_flag(die);
|
int is_info_a = dwarf_get_die_infotypes_flag(die);
|
||||||
Dwarf_Die targ_die_a = 0;
|
Dwarf_Die targ_die_a = 0;
|
||||||
ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr);
|
VERIFY(wrap(dwarf_offdie_b, dbg, off, is_info_a, &targ_die_a) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
dwarf_dealloc_attribute(attr);
|
dwarf_dealloc_attribute(attr);
|
||||||
return die_object(dbg, targ_die_a);
|
return die_object(dbg, targ_die_a);
|
||||||
}
|
}
|
||||||
case DW_FORM_ref_sig8:
|
case DW_FORM_ref_sig8:
|
||||||
{
|
{
|
||||||
Dwarf_Sig8 signature;
|
Dwarf_Sig8 signature;
|
||||||
ret = dwarf_formsig8(attr, &signature, nullptr);
|
VERIFY(wrap(dwarf_formsig8, attr, &signature) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
Dwarf_Die targdie = 0;
|
Dwarf_Die targdie = 0;
|
||||||
Dwarf_Bool targ_is_info = false;
|
Dwarf_Bool targ_is_info = false;
|
||||||
ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr);
|
VERIFY(wrap(dwarf_find_die_given_sig8, dbg, &signature, &targdie, &targ_is_info) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(ret == DW_DLV_OK);
|
|
||||||
dwarf_dealloc_attribute(attr);
|
dwarf_dealloc_attribute(attr);
|
||||||
return die_object(dbg, targdie);
|
return die_object(dbg, targdie);
|
||||||
}
|
}
|
||||||
@ -241,13 +255,11 @@ namespace libdwarf {
|
|||||||
Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const {
|
Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) const {
|
||||||
Dwarf_Unsigned off = 0;
|
Dwarf_Unsigned off = 0;
|
||||||
Dwarf_Half attrform = 0;
|
Dwarf_Half attrform = 0;
|
||||||
dwarf_whatform(attr, &attrform, nullptr);
|
VERIFY(wrap(dwarf_whatform, attr, &attrform) == DW_DLV_OK);
|
||||||
if (attrform == DW_FORM_rnglistx) {
|
if (attrform == DW_FORM_rnglistx) {
|
||||||
int fres = dwarf_formudata(attr, &off, nullptr);
|
VERIFY(wrap(dwarf_formudata, attr, &off) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(fres == DW_DLV_OK);
|
|
||||||
} else {
|
} else {
|
||||||
int fres = dwarf_global_formref(attr, &off, nullptr);
|
VERIFY(wrap(dwarf_global_formref, attr, &off) == DW_DLV_OK);
|
||||||
CPPTRACE_VERIFY(fres == DW_DLV_OK);
|
|
||||||
}
|
}
|
||||||
return off;
|
return off;
|
||||||
}
|
}
|
||||||
@ -265,7 +277,7 @@ namespace libdwarf {
|
|||||||
Dwarf_Unsigned i = 0;
|
Dwarf_Unsigned i = 0;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
|
res = wrap(dwarf_attr, cu_die, DW_AT_ranges, &attr);
|
||||||
if(res != DW_DLV_OK) {
|
if(res != DW_DLV_OK) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -275,18 +287,18 @@ namespace libdwarf {
|
|||||||
Dwarf_Unsigned rnglists_count = 0;
|
Dwarf_Unsigned rnglists_count = 0;
|
||||||
Dwarf_Rnglists_Head head = 0;
|
Dwarf_Rnglists_Head head = 0;
|
||||||
|
|
||||||
dwarf_whatform(attr, &attrform, nullptr);
|
VERIFY(wrap(dwarf_whatform, attr, &attrform) == DW_DLV_OK);
|
||||||
/* offset is in .debug_rnglists */
|
// offset is in .debug_rnglists
|
||||||
res = dwarf_rnglists_get_rle_head(
|
res = wrap(
|
||||||
|
dwarf_rnglists_get_rle_head,
|
||||||
attr,
|
attr,
|
||||||
attrform,
|
attrform,
|
||||||
offset,
|
offset,
|
||||||
&head,
|
&head,
|
||||||
&rnglists_count,
|
&rnglists_count,
|
||||||
&rlesetoffset,
|
&rlesetoffset
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
CPPTRACE_VERIFY(res == DW_DLV_OK);
|
VERIFY(res == DW_DLV_OK);
|
||||||
if(res != DW_DLV_OK) {
|
if(res != DW_DLV_OK) {
|
||||||
/* ASSERT: is DW_DLV_NO_ENTRY */
|
/* ASSERT: is DW_DLV_NO_ENTRY */
|
||||||
dwarf_dealloc_attribute(attr);
|
dwarf_dealloc_attribute(attr);
|
||||||
@ -301,7 +313,8 @@ namespace libdwarf {
|
|||||||
Dwarf_Unsigned cooked1 = 0;
|
Dwarf_Unsigned cooked1 = 0;
|
||||||
Dwarf_Unsigned cooked2 = 0;
|
Dwarf_Unsigned cooked2 = 0;
|
||||||
|
|
||||||
res = dwarf_get_rnglists_entry_fields_a(
|
res = wrap(
|
||||||
|
dwarf_get_rnglists_entry_fields_a,
|
||||||
head,
|
head,
|
||||||
i,
|
i,
|
||||||
&entrylen,
|
&entrylen,
|
||||||
@ -310,11 +323,10 @@ namespace libdwarf {
|
|||||||
&raw2,
|
&raw2,
|
||||||
&unavail,
|
&unavail,
|
||||||
&cooked1,
|
&cooked1,
|
||||||
&cooked2,
|
&cooked2
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
if(res != DW_DLV_OK) {
|
if(res != DW_DLV_OK) {
|
||||||
/* ASSERT: is DW_DLV_NO_ENTRY */
|
ASSERT(res == DW_DLV_NO_ENTRY);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(unavail) {
|
if(unavail) {
|
||||||
@ -342,8 +354,7 @@ namespace libdwarf {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CPPTRACE_VERIFY(false);
|
PANIC("Something is wrong");
|
||||||
/* Something is wrong. */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,26 +376,28 @@ namespace libdwarf {
|
|||||||
Dwarf_Attribute attr = 0;
|
Dwarf_Attribute attr = 0;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
|
res = wrap(dwarf_attr, cu_die, DW_AT_ranges, &attr);
|
||||||
if(res != DW_DLV_OK) {
|
if(res != DW_DLV_OK) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if(dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK) {
|
if(wrap(dwarf_global_formref, attr, &offset) == DW_DLV_OK) {
|
||||||
Dwarf_Signed count = 0;
|
Dwarf_Signed count = 0;
|
||||||
Dwarf_Ranges *ranges = 0;
|
Dwarf_Ranges *ranges = 0;
|
||||||
Dwarf_Addr baseaddr = 0;
|
Dwarf_Addr baseaddr = 0;
|
||||||
if(cu_lowpc != 0xffffffffffffffff) {
|
if(cu_lowpc != 0xffffffffffffffff) {
|
||||||
baseaddr = cu_lowpc;
|
baseaddr = cu_lowpc;
|
||||||
}
|
}
|
||||||
res = dwarf_get_ranges_b(
|
VERIFY(
|
||||||
|
wrap(
|
||||||
|
dwarf_get_ranges_b,
|
||||||
dbg,
|
dbg,
|
||||||
offset,
|
offset,
|
||||||
cu_die,
|
cu_die,
|
||||||
nullptr,
|
nullptr,
|
||||||
&ranges,
|
&ranges,
|
||||||
&count,
|
&count,
|
||||||
nullptr,
|
|
||||||
nullptr
|
nullptr
|
||||||
|
) == DW_DLV_OK
|
||||||
);
|
);
|
||||||
for(int i = 0; i < count; i++) {
|
for(int i = 0; i < count; i++) {
|
||||||
Dwarf_Ranges *cur = ranges + i;
|
Dwarf_Ranges *cur = ranges + i;
|
||||||
@ -425,10 +438,9 @@ namespace libdwarf {
|
|||||||
Dwarf_Addr lowest = 0xffffffffffffffff;
|
Dwarf_Addr lowest = 0xffffffffffffffff;
|
||||||
Dwarf_Addr highest = 0;
|
Dwarf_Addr highest = 0;
|
||||||
|
|
||||||
ret = dwarf_lowpc(die, &cu_lowpc, nullptr);
|
ret = wrap(dwarf_lowpc, die, &cu_lowpc);
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
ret = dwarf_highpc_b(die, &cu_highpc,
|
ret = wrap(dwarf_highpc_b, die, &cu_highpc, nullptr, &highpc_cls);
|
||||||
nullptr, &highpc_cls, nullptr);
|
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
|
if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
|
||||||
cu_highpc += cu_lowpc;
|
cu_highpc += cu_lowpc;
|
||||||
@ -455,13 +467,12 @@ namespace libdwarf {
|
|||||||
Dwarf_Addr lowest = 0xffffffffffffffff;
|
Dwarf_Addr lowest = 0xffffffffffffffff;
|
||||||
Dwarf_Addr highest = 0;
|
Dwarf_Addr highest = 0;
|
||||||
|
|
||||||
ret = dwarf_lowpc(die, &cu_lowpc, nullptr);
|
ret = wrap(dwarf_lowpc, die, &cu_lowpc);
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
if(pc == cu_lowpc) {
|
if(pc == cu_lowpc) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ret = dwarf_highpc_b(die, &cu_highpc,
|
ret = wrap(dwarf_highpc_b, die, &cu_highpc, nullptr, &highpc_cls);
|
||||||
nullptr, &highpc_cls, nullptr);
|
|
||||||
if(ret == DW_DLV_OK) {
|
if(ret == DW_DLV_OK) {
|
||||||
if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
|
if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
|
||||||
cu_highpc += cu_lowpc;
|
cu_highpc += cu_lowpc;
|
||||||
@ -512,12 +523,38 @@ namespace libdwarf {
|
|||||||
struct dwarf_resolver {
|
struct dwarf_resolver {
|
||||||
std::string obj_path;
|
std::string obj_path;
|
||||||
Dwarf_Debug dbg;
|
Dwarf_Debug dbg;
|
||||||
|
bool ok = false;
|
||||||
std::unordered_map<Dwarf_Off, line_context> line_contexts;
|
std::unordered_map<Dwarf_Off, line_context> line_contexts;
|
||||||
std::unordered_map<Dwarf_Off, std::vector<subprogram_entry>> subprograms_cache;
|
std::unordered_map<Dwarf_Off, std::vector<subprogram_entry>> subprograms_cache;
|
||||||
|
|
||||||
// Exists only for cleaning up an awful mach-o hack
|
// Exists only for cleaning up an awful mach-o hack
|
||||||
std::string tmp_object_path;
|
std::string tmp_object_path;
|
||||||
|
|
||||||
|
// Error handling helper
|
||||||
|
// For some reason R (*f)(Args..., void*)-style deduction isn't possible, seems like a bug in all compilers
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56190
|
||||||
|
template<
|
||||||
|
typename... Args,
|
||||||
|
typename... Args2,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<
|
||||||
|
decltype(
|
||||||
|
(void)std::declval<int(Args...)>()(std::forward<Args2>(std::declval<Args2>())..., nullptr)
|
||||||
|
),
|
||||||
|
void
|
||||||
|
>::value,
|
||||||
|
int
|
||||||
|
>::type = 0
|
||||||
|
>
|
||||||
|
int wrap(int (*f)(Args...), Args2&&... args) const {
|
||||||
|
Dwarf_Error error = 0;
|
||||||
|
int ret = f(std::forward<Args2>(args)..., &error);
|
||||||
|
if(ret == DW_DLV_ERROR) {
|
||||||
|
handle_error(dbg, error);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
|
CPPTRACE_FORCE_NO_INLINE_FOR_PROFILING
|
||||||
dwarf_resolver(const std::string& object_path) {
|
dwarf_resolver(const std::string& object_path) {
|
||||||
obj_path = object_path;
|
obj_path = object_path;
|
||||||
@ -532,17 +569,17 @@ namespace libdwarf {
|
|||||||
char tmp_template[] = "/tmp/tmp.cpptrace.XXXXXX";
|
char tmp_template[] = "/tmp/tmp.cpptrace.XXXXXX";
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
CPPTRACE_VERIFY(mktemp(tmp_template) != nullptr);
|
VERIFY(mktemp(tmp_template) != nullptr);
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
std::string tmp_path = tmp_template;
|
std::string tmp_path = tmp_template;
|
||||||
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter);
|
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter);
|
||||||
auto tmp = raii_wrap(fopen(tmp_path.c_str(), "wb"), file_deleter);
|
auto tmp = raii_wrap(fopen(tmp_path.c_str(), "wb"), file_deleter);
|
||||||
CPPTRACE_VERIFY(file != nullptr);
|
VERIFY(file != nullptr);
|
||||||
CPPTRACE_VERIFY(tmp != nullptr);
|
VERIFY(tmp != nullptr);
|
||||||
std::unique_ptr<char[]> buffer(new char[sub_object.size]);
|
std::unique_ptr<char[]> buffer(new char[sub_object.size]);
|
||||||
CPPTRACE_VERIFY(fseek(file, sub_object.offset, SEEK_SET) == 0);
|
VERIFY(fseek(file, sub_object.offset, SEEK_SET) == 0);
|
||||||
CPPTRACE_VERIFY(fread(buffer.get(), 1, sub_object.size, file) == sub_object.size);
|
VERIFY(fread(buffer.get(), 1, sub_object.size, file) == sub_object.size);
|
||||||
CPPTRACE_VERIFY(fwrite(buffer.get(), 1, sub_object.size, tmp) == sub_object.size);
|
VERIFY(fwrite(buffer.get(), 1, sub_object.size, tmp) == sub_object.size);
|
||||||
obj_path = tmp_path;
|
obj_path = tmp_path;
|
||||||
tmp_object_path = std::move(tmp_path);
|
tmp_object_path = std::move(tmp_path);
|
||||||
}
|
}
|
||||||
@ -551,21 +588,24 @@ namespace libdwarf {
|
|||||||
// Giving libdwarf a buffer for a true output path is needed for its automatic resolution of debuglink and
|
// Giving libdwarf a buffer for a true output path is needed for its automatic resolution of debuglink and
|
||||||
// dSYM files. We don't utilize the dSYM logic here, we just care about debuglink.
|
// dSYM files. We don't utilize the dSYM logic here, we just care about debuglink.
|
||||||
std::unique_ptr<char[]> buffer(new char[CPPTRACE_MAX_PATH]);
|
std::unique_ptr<char[]> buffer(new char[CPPTRACE_MAX_PATH]);
|
||||||
Dwarf_Ptr errarg = 0;
|
auto ret = wrap(
|
||||||
auto ret = dwarf_init_path(
|
dwarf_init_path,
|
||||||
obj_path.c_str(),
|
obj_path.c_str(),
|
||||||
buffer.get(),
|
buffer.get(),
|
||||||
CPPTRACE_MAX_PATH,
|
CPPTRACE_MAX_PATH,
|
||||||
DW_GROUPNUMBER_ANY,
|
DW_GROUPNUMBER_ANY,
|
||||||
err_handler,
|
nullptr,
|
||||||
errarg,
|
nullptr,
|
||||||
&dbg,
|
&dbg
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
if(ret == DW_DLV_NO_ENTRY) {
|
if(ret == DW_DLV_OK) {
|
||||||
|
ok = true;
|
||||||
|
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||||
// fail, no debug info
|
// fail, no debug info
|
||||||
} else if(ret != DW_DLV_OK) {
|
ok = false;
|
||||||
fprintf(stderr, "Error\n");
|
} else {
|
||||||
|
ok = false;
|
||||||
|
PANIC("Unknown return code from dwarf_init_path");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,7 +677,7 @@ namespace libdwarf {
|
|||||||
Dwarf_Half dwversion,
|
Dwarf_Half dwversion,
|
||||||
stacktrace_frame& frame
|
stacktrace_frame& frame
|
||||||
) {
|
) {
|
||||||
CPPTRACE_ASSERT(die.get_tag() == DW_TAG_subprogram);
|
ASSERT(die.get_tag() == DW_TAG_subprogram);
|
||||||
optional<std::string> name;
|
optional<std::string> name;
|
||||||
if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
|
if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
|
||||||
name = std::move(linkage_name);
|
name = std::move(linkage_name);
|
||||||
@ -827,7 +867,7 @@ namespace libdwarf {
|
|||||||
retrieve_symbol_for_subprogram(vec_it->die, pc, dwversion, frame);
|
retrieve_symbol_for_subprogram(vec_it->die, pc, dwversion, frame);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CPPTRACE_ASSERT(vec.size() == 0, "Vec should be empty?");
|
ASSERT(vec.size() == 0, "Vec should be empty?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,9 +877,8 @@ namespace libdwarf {
|
|||||||
Dwarf_Unsigned lineno = 0;
|
Dwarf_Unsigned lineno = 0;
|
||||||
|
|
||||||
if(line) {
|
if(line) {
|
||||||
/* These never return DW_DLV_NO_ENTRY */
|
VERIFY(wrap(dwarf_linesrc, line, &linesrc) == DW_DLV_OK);
|
||||||
dwarf_linesrc(line, &linesrc, nullptr);
|
VERIFY(wrap(dwarf_lineno, line, &lineno) == DW_DLV_OK);
|
||||||
dwarf_lineno(line, &lineno, nullptr);
|
|
||||||
}
|
}
|
||||||
if(dump_dwarf) {
|
if(dump_dwarf) {
|
||||||
printf("%s:%u\n", linesrc, to<unsigned>(lineno));
|
printf("%s:%u\n", linesrc, to<unsigned>(lineno));
|
||||||
@ -871,41 +910,42 @@ namespace libdwarf {
|
|||||||
table_count = entry.table_count;
|
table_count = entry.table_count;
|
||||||
ctxt = entry.ctx;
|
ctxt = entry.ctx;
|
||||||
} else {
|
} else {
|
||||||
int ret = dwarf_srclines_b(
|
int ret = wrap(
|
||||||
|
dwarf_srclines_b,
|
||||||
die.get(),
|
die.get(),
|
||||||
&version,
|
&version,
|
||||||
&table_count,
|
&table_count,
|
||||||
&ctxt,
|
&ctxt
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
line_contexts.insert({off, {version, table_count, ctxt}});
|
|
||||||
if(ret == DW_DLV_NO_ENTRY) {
|
if(ret == DW_DLV_NO_ENTRY) {
|
||||||
fprintf(stderr, "dwarf_srclines_b error\n");
|
// TODO: Failing silently for now
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
line_contexts.insert({off, {version, table_count, ctxt}});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(table_count == 1) {
|
if(table_count == 1) {
|
||||||
Dwarf_Line* linebuf = 0;
|
Dwarf_Line* linebuf = 0;
|
||||||
Dwarf_Signed linecount = 0;
|
Dwarf_Signed linecount = 0;
|
||||||
Dwarf_Addr prev_lineaddr = 0;
|
Dwarf_Addr prev_lineaddr = 0;
|
||||||
|
VERIFY(
|
||||||
dwarf_srclines_from_linecontext(ctxt, &linebuf,
|
wrap(
|
||||||
&linecount, nullptr);
|
dwarf_srclines_from_linecontext,
|
||||||
|
ctxt,
|
||||||
|
&linebuf,
|
||||||
|
&linecount
|
||||||
|
) == DW_DLV_OK
|
||||||
|
);
|
||||||
Dwarf_Line prev_line = 0;
|
Dwarf_Line prev_line = 0;
|
||||||
for(int i = 0; i < linecount; i++) {
|
for(int i = 0; i < linecount; i++) {
|
||||||
Dwarf_Line line = linebuf[i];
|
Dwarf_Line line = linebuf[i];
|
||||||
Dwarf_Addr lineaddr = 0;
|
Dwarf_Addr lineaddr = 0;
|
||||||
|
VERIFY(wrap(dwarf_lineaddr, line, &lineaddr) == DW_DLV_OK);
|
||||||
dwarf_lineaddr(line, &lineaddr, nullptr);
|
|
||||||
if(pc == lineaddr) {
|
if(pc == lineaddr) {
|
||||||
/* Print the last line entry containing current pc. */
|
// Find the last line entry containing current pc
|
||||||
Dwarf_Line last_pc_line = line;
|
Dwarf_Line last_pc_line = line;
|
||||||
|
|
||||||
for(int j = i + 1; j < linecount; j++) {
|
for(int j = i + 1; j < linecount; j++) {
|
||||||
Dwarf_Line j_line = linebuf[j];
|
Dwarf_Line j_line = linebuf[j];
|
||||||
dwarf_lineaddr(j_line, &lineaddr, nullptr);
|
VERIFY(wrap(dwarf_lineaddr, j_line, &lineaddr) == DW_DLV_OK);
|
||||||
|
|
||||||
if(pc == lineaddr) {
|
if(pc == lineaddr) {
|
||||||
last_pc_line = j_line;
|
last_pc_line = j_line;
|
||||||
}
|
}
|
||||||
@ -917,7 +957,7 @@ namespace libdwarf {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Dwarf_Bool is_lne;
|
Dwarf_Bool is_lne;
|
||||||
dwarf_lineendsequence(line, &is_lne, nullptr);
|
VERIFY(wrap(dwarf_lineendsequence, line, &is_lne) == DW_DLV_OK);
|
||||||
if(is_lne) {
|
if(is_lne) {
|
||||||
prev_line = 0;
|
prev_line = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -978,7 +1018,8 @@ namespace libdwarf {
|
|||||||
Dwarf_Half header_cu_type;
|
Dwarf_Half header_cu_type;
|
||||||
//fprintf(stderr, "-----------------\n");
|
//fprintf(stderr, "-----------------\n");
|
||||||
while(true) {
|
while(true) {
|
||||||
int ret = dwarf_next_cu_header_d(
|
int ret = wrap(
|
||||||
|
dwarf_next_cu_header_d,
|
||||||
dbg,
|
dbg,
|
||||||
true,
|
true,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -990,8 +1031,7 @@ namespace libdwarf {
|
|||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
&next_cu_header,
|
&next_cu_header,
|
||||||
&header_cu_type,
|
&header_cu_type
|
||||||
nullptr
|
|
||||||
);
|
);
|
||||||
if(ret == DW_DLV_NO_ENTRY) {
|
if(ret == DW_DLV_NO_ENTRY) {
|
||||||
if(dump_dwarf) {
|
if(dump_dwarf) {
|
||||||
@ -1000,7 +1040,7 @@ namespace libdwarf {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(ret != DW_DLV_OK) {
|
if(ret != DW_DLV_OK) {
|
||||||
fprintf(stderr, "Error\n");
|
PANIC("Unexpected return code from dwarf_next_cu_header_d");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
walk_compilation_units(pc, frame);
|
walk_compilation_units(pc, frame);
|
||||||
@ -1019,20 +1059,20 @@ namespace libdwarf {
|
|||||||
// Check for .debug_aranges for fast lookup
|
// Check for .debug_aranges for fast lookup
|
||||||
Dwarf_Arange *aranges;
|
Dwarf_Arange *aranges;
|
||||||
Dwarf_Signed arange_count;
|
Dwarf_Signed arange_count;
|
||||||
if(dwarf_get_aranges(dbg, &aranges, &arange_count, nullptr) == DW_DLV_OK) {
|
if(wrap(dwarf_get_aranges, dbg, &aranges, &arange_count) == DW_DLV_OK) {
|
||||||
// Try to find pc in aranges
|
// Try to find pc in aranges
|
||||||
Dwarf_Arange arange;
|
Dwarf_Arange arange;
|
||||||
if(dwarf_get_arange(aranges, arange_count, pc, &arange, nullptr) == DW_DLV_OK) {
|
if(wrap(dwarf_get_arange, aranges, arange_count, pc, &arange) == DW_DLV_OK) {
|
||||||
// Address in table, load CU die
|
// Address in table, load CU die
|
||||||
Dwarf_Off cu_die_offset;
|
Dwarf_Off cu_die_offset;
|
||||||
CPPTRACE_VERIFY(dwarf_get_cu_die_offset(arange, &cu_die_offset, nullptr) == DW_DLV_OK);
|
VERIFY(wrap(dwarf_get_cu_die_offset, arange, &cu_die_offset) == DW_DLV_OK);
|
||||||
Dwarf_Die raw_die;
|
Dwarf_Die raw_die;
|
||||||
// Setting is_info = true for now, assuming in .debug_info rather than .debug_types
|
// Setting is_info = true for now, assuming in .debug_info rather than .debug_types
|
||||||
CPPTRACE_VERIFY(dwarf_offdie_b(dbg, cu_die_offset, true, &raw_die, nullptr) == DW_DLV_OK);
|
VERIFY(wrap(dwarf_offdie_b, dbg, cu_die_offset, true, &raw_die) == DW_DLV_OK);
|
||||||
die_object cu_die(dbg, raw_die);
|
die_object cu_die(dbg, raw_die);
|
||||||
Dwarf_Half offset_size = 0;
|
Dwarf_Half offset_size = 0;
|
||||||
Dwarf_Half dwversion = 0;
|
Dwarf_Half dwversion = 0;
|
||||||
dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
|
VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK);
|
||||||
if(trace_dwarf) {
|
if(trace_dwarf) {
|
||||||
fprintf(stderr, "Found CU in aranges\n");
|
fprintf(stderr, "Found CU in aranges\n");
|
||||||
cu_die.print();
|
cu_die.print();
|
||||||
@ -1079,12 +1119,15 @@ namespace libdwarf {
|
|||||||
for(const auto& obj_entry : collate_frames(frames, trace)) {
|
for(const auto& obj_entry : collate_frames(frames, trace)) {
|
||||||
const auto& obj_name = obj_entry.first;
|
const auto& obj_name = obj_entry.first;
|
||||||
dwarf_resolver resolver(obj_name);
|
dwarf_resolver resolver(obj_name);
|
||||||
|
// If there's no debug information it'll mark itself as not ok
|
||||||
|
if(resolver.ok) {
|
||||||
for(const auto& entry : obj_entry.second) {
|
for(const auto& entry : obj_entry.second) {
|
||||||
const auto& dlframe = entry.first.get();
|
const auto& dlframe = entry.first.get();
|
||||||
auto& frame = entry.second.get();
|
auto& frame = entry.second.get();
|
||||||
frame = resolver.resolve_frame(dlframe);
|
frame = resolver.resolve_frame(dlframe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,7 +1225,7 @@ namespace libdwarf {
|
|||||||
std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = "");
|
std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = "");
|
||||||
|
|
||||||
std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) {
|
std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) {
|
||||||
CPPTRACE_VERIFY(die.get_tag() == DW_TAG_array_type);
|
VERIFY(die.get_tag() == DW_TAG_array_type);
|
||||||
std::string extents = "";
|
std::string extents = "";
|
||||||
walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
|
walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
|
||||||
if(subrange.get_tag() == DW_TAG_subrange_type) {
|
if(subrange.get_tag() == DW_TAG_subrange_type) {
|
||||||
@ -1216,7 +1259,7 @@ namespace libdwarf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
|
std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
|
||||||
CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subroutine_type);
|
VERIFY(die.get_tag() == DW_TAG_subroutine_type);
|
||||||
std::vector<std::string> params;
|
std::vector<std::string> params;
|
||||||
walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) {
|
walk_die_list(dbg, die.get_child(), [¶ms](Dwarf_Debug dbg, const die_object& die) {
|
||||||
if(die.get_tag() == DW_TAG_formal_parameter) {
|
if(die.get_tag() == DW_TAG_formal_parameter) {
|
||||||
|
|||||||
@ -31,7 +31,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CPPTRACE_VERIFY(
|
VERIFY(
|
||||||
state.count < state.vec.size(),
|
state.count < state.vec.size(),
|
||||||
"Somehow cpptrace::detail::unwind_callback is overflowing a vector"
|
"Somehow cpptrace::detail::unwind_callback is overflowing a vector"
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user