Add very basic dwarfdump tool
This commit is contained in:
parent
8e7b4a953f
commit
b724d1328c
@ -350,11 +350,14 @@ if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
endif()
|
||||
endif()
|
||||
if(CPPTRACE_CONAN)
|
||||
set(dwarf_lib libdwarf::libdwarf)
|
||||
target_link_libraries(${target_name} PRIVATE libdwarf::libdwarf)
|
||||
elseif(CPPTRACE_VCPKG)
|
||||
set(dwarf_lib libdwarf::dwarf)
|
||||
target_link_libraries(${target_name} PRIVATE libdwarf::dwarf)
|
||||
elseif(CPPTRACE_USE_EXTERNAL_LIBDWARF)
|
||||
if(DEFINED LIBDWARF_LIBRARIES)
|
||||
set(dwarf_lib ${LIBDWARF_LIBRARIES})
|
||||
target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES})
|
||||
else()
|
||||
# if LIBDWARF_LIBRARIES wasn't set by find_package, try looking for libdwarf::dwarf-static,
|
||||
@ -372,6 +375,7 @@ if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find libdwarf target name to link against")
|
||||
endif()
|
||||
set(dwarf_lib ${LIBDWARF_LIBRARIES})
|
||||
target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES})
|
||||
endif()
|
||||
# There seems to be no consistency at all about where libdwarf decides to place its headers........ Figure out if
|
||||
@ -394,6 +398,7 @@ if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
message(FATAL_ERROR "Couldn't find libdwarf.h")
|
||||
endif()
|
||||
else()
|
||||
set(dwarf_lib libdwarf::dwarf-static)
|
||||
target_link_libraries(${target_name} PRIVATE libdwarf::dwarf-static)
|
||||
endif()
|
||||
if(UNIX)
|
||||
@ -509,7 +514,7 @@ if(NOT CMAKE_SKIP_INSTALL_RULES)
|
||||
include(cmake/InstallRules.cmake)
|
||||
endif()
|
||||
|
||||
# ===================================================== Demo/test ======================================================
|
||||
# ================================================== Demo/test/tools ===================================================
|
||||
|
||||
if(CPPTRACE_BUILD_TESTING)
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
@ -521,3 +526,7 @@ endif()
|
||||
if(CPPTRACE_BUILD_BENCHMARKING)
|
||||
add_subdirectory(benchmarking)
|
||||
endif()
|
||||
|
||||
if(CPPTRACE_BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
8
Makefile
8
Makefile
@ -9,12 +9,12 @@ help: # with thanks to Ben Rady
|
||||
build: debug ## build in debug mode
|
||||
|
||||
build/configured-debug:
|
||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On
|
||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
||||
rm -f build/configured-release
|
||||
touch build/configured-debug
|
||||
|
||||
build/configured-release:
|
||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On
|
||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
||||
rm -f build/configured-debug
|
||||
touch build/configured-release
|
||||
|
||||
@ -34,12 +34,12 @@ release: configure-release ## build in release mode (with debug info)
|
||||
|
||||
.PHONY: debug-msvc
|
||||
debug-msvc: ## build in debug mode
|
||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On
|
||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
||||
cmake --build build --config Debug
|
||||
|
||||
.PHONY: release-msvc
|
||||
release-msvc: ## build in release mode (with debug info)
|
||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On
|
||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
||||
cmake --build build --config RelWithDebInfo
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
@ -151,6 +151,7 @@ option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
|
||||
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
option(CPPTRACE_BUILD_TESTING "" OFF)
|
||||
option(CPPTRACE_BUILD_TOOLS "" OFF)
|
||||
option(CPPTRACE_BUILD_BENCHMARK "" OFF)
|
||||
option(CPPTRACE_BUILD_NO_SYMBOLS "" OFF)
|
||||
option(CPPTRACE_BUILD_TESTING_SPLIT_DWARF "" OFF)
|
||||
@ -158,6 +159,7 @@ if(PROJECT_IS_TOP_LEVEL)
|
||||
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
|
||||
mark_as_advanced(
|
||||
CPPTRACE_BUILD_TESTING
|
||||
CPPTRACE_BUILD_TOOLS
|
||||
CPPTRACE_BUILD_BENCHMARK
|
||||
CPPTRACE_BUILD_NO_SYMBOLS
|
||||
CPPTRACE_BUILD_TESTING_SPLIT_DWARF
|
||||
|
||||
46
tools/CMakeLists.txt
Normal file
46
tools/CMakeLists.txt
Normal file
@ -0,0 +1,46 @@
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
lyra
|
||||
GIT_SHALLOW ON
|
||||
GIT_REPOSITORY "https://github.com/bfgroup/Lyra.git"
|
||||
GIT_TAG "ee3c076fa6b9d64c9d249a21f5b9b5a8dae92cd8"
|
||||
)
|
||||
FetchContent_MakeAvailable(lyra)
|
||||
|
||||
FetchContent_Declare(
|
||||
fmt
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
||||
GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281 # v10.2.1
|
||||
)
|
||||
FetchContent_MakeAvailable(fmt)
|
||||
|
||||
set(
|
||||
COMMON_LIBS
|
||||
cpptrace::cpptrace
|
||||
bfg::lyra
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
function(binary TARGET)
|
||||
cmake_parse_arguments(BINARY "" "" "SOURCES;LIBS;FLAGS;DEFS" ${ARGN})
|
||||
add_executable(${TARGET} main.cpp)
|
||||
if(BINARY_SOURCES)
|
||||
add_library(${TARGET}_OBJ OBJECT ${BINARY_SOURCES})
|
||||
target_link_libraries(${TARGET}_OBJ PUBLIC ${COMMON_LIBS})
|
||||
endif()
|
||||
target_link_libraries(${TARGET} PUBLIC ${COMMON_LIBS})
|
||||
target_link_libraries(${TARGET} PUBLIC ${BINARY_LIBS})
|
||||
target_compile_definitions(${TARGET} PUBLIC ${BINARY_DEFS})
|
||||
target_compile_options(${TARGET} PUBLIC ${BINARY_FLAGS} ${debug} ${warning_options})
|
||||
target_include_directories(${TARGET} PUBLIC "${PROJECT_SOURCE_DIR}/src")
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_20)
|
||||
set_target_properties(
|
||||
${TARGET}
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(dwarfdump)
|
||||
3
tools/dwarfdump/CMakeLists.txt
Normal file
3
tools/dwarfdump/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
||||
binary(dwarfdump LIBS ${dwarf_lib})
|
||||
endif()
|
||||
179
tools/dwarfdump/main.cpp
Normal file
179
tools/dwarfdump/main.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include <lyra/lyra.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/std.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include <cpptrace/from_current.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "symbols/dwarf/dwarf.hpp"
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace cpptrace::detail::libdwarf;
|
||||
|
||||
template<> struct fmt::formatter<lyra::cli> : ostream_formatter {};
|
||||
|
||||
class DwarfDumper {
|
||||
std::string object_path;
|
||||
Dwarf_Debug dbg = nullptr;
|
||||
|
||||
// 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
|
||||
// TODO: Duplicate
|
||||
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 = nullptr;
|
||||
int ret = f(std::forward<Args2>(args)..., &error);
|
||||
if(ret == DW_DLV_ERROR) {
|
||||
handle_dwarf_error(dbg, error);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: Duplicate
|
||||
// walk all CU's in a dbg, callback is called on each die and should return true to
|
||||
// continue traversal
|
||||
void walk_compilation_units(const std::function<bool(const die_object&)>& fn) {
|
||||
// libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull
|
||||
Dwarf_Unsigned next_cu_header;
|
||||
Dwarf_Half header_cu_type;
|
||||
while(true) {
|
||||
int ret = wrap(
|
||||
dwarf_next_cu_header_d,
|
||||
dbg,
|
||||
true,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&next_cu_header,
|
||||
&header_cu_type
|
||||
);
|
||||
if(ret == DW_DLV_NO_ENTRY) {
|
||||
fmt::println("End walk_dbg");
|
||||
return;
|
||||
}
|
||||
if(ret != DW_DLV_OK) {
|
||||
PANIC("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();
|
||||
if(!cu_die) {
|
||||
break;
|
||||
}
|
||||
if(!walk_die_list(cu_die, fn)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fmt::println("End walk_compilation_units");
|
||||
}
|
||||
|
||||
void dump_die_tree(const die_object& die, int depth) {
|
||||
walk_die_list(
|
||||
die,
|
||||
[this, depth] (const die_object& die) {
|
||||
fmt::println("{:016x}{: <{}} {}", die.get_global_offset(), "", depth * 2, die.get_tag_name());
|
||||
fmt::println("{: <16}{: <{}} name: {}", "", "", depth * 2, die.get_name());
|
||||
fmt::println("");
|
||||
auto child = die.get_child();
|
||||
if(child) {
|
||||
dump_die_tree(child, depth + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void dump_cu(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);
|
||||
fmt::println("{:016x} Compile Unit: version = {}, unit type = {}", cu_die.get_global_offset(), dwversion, cu_die.get_tag_name());
|
||||
dump_die_tree(cu_die, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
DwarfDumper() = default;
|
||||
~DwarfDumper() {
|
||||
dwarf_finish(dbg);
|
||||
}
|
||||
|
||||
void dump(std::filesystem::path path) {
|
||||
object_path = path;
|
||||
auto ret = wrap(
|
||||
dwarf_init_path_a,
|
||||
object_path.c_str(),
|
||||
nullptr,
|
||||
0,
|
||||
DW_GROUPNUMBER_ANY,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&dbg
|
||||
);
|
||||
if(ret == DW_DLV_OK) {
|
||||
// ok
|
||||
} else if(ret == DW_DLV_NO_ENTRY) {
|
||||
// fail, no debug info
|
||||
fmt::println(stderr, "No debug info");
|
||||
std::exit(1);
|
||||
} else {
|
||||
fmt::println(stderr, "Error: Unknown return code from dwarf_init_path {}", ret);
|
||||
std::exit(1);
|
||||
}
|
||||
walk_compilation_units([this] (const die_object& cu_die) {
|
||||
dump_cu(cu_die);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) CPPTRACE_TRY {
|
||||
bool show_help = false;
|
||||
std::filesystem::path path;
|
||||
auto cli = lyra::cli()
|
||||
| lyra::help(show_help)
|
||||
| lyra::arg(path, "binary path")("binary to dwarfdump").required();
|
||||
if(auto result = cli.parse({ argc, argv }); !result) {
|
||||
fmt::println(stderr, "Error in command line: {}", result.message());
|
||||
fmt::println("{}", cli);
|
||||
return 1;
|
||||
}
|
||||
if(show_help) {
|
||||
fmt::println("{}", cli);
|
||||
return 0;
|
||||
}
|
||||
if(!std::filesystem::exists(path)) {
|
||||
fmt::println(stderr, "Error: Path doesn't exist {}", path);
|
||||
return 1;
|
||||
}
|
||||
if(!std::filesystem::is_regular_file(path)) {
|
||||
fmt::println(stderr, "Error: Path isn't a regular file {}", path);
|
||||
return 1;
|
||||
}
|
||||
DwarfDumper{}.dump(path);
|
||||
} CPPTRACE_CATCH(const std::exception& e) {
|
||||
fmt::println(stderr, "Caught exception {}: {}", cpptrace::demangle(typeid(e).name()), e.what());
|
||||
cpptrace::from_current_exception().print();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user