diff --git a/README.md b/README.md index 5909752..67ce57c 100644 --- a/README.md +++ b/README.md @@ -178,15 +178,14 @@ namespace cpptrace { ### Object Traces -Object traces are somewhat minimal stack traces with basic information on which binary a frame corresponds to, any -symbol name libdl (in linux/macos) was able to resolve, the raw program counter and the program counter translated to -the corresponding object file's memory space. +Object traces contain the most basic information needed to construct a stack trace outside the currently running +executable. It contains the raw address, the address in the binary (ASLR and the object file's memory space and whatnot +is resolved), and the path to the object the instruction pointer is located in. ```cpp namespace cpptrace { struct object_frame { std::string obj_path; - std::string symbol; frame_ptr raw_address; frame_ptr obj_address; }; @@ -409,19 +408,19 @@ The safe API is as follows: namespace cpptrace { std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip = 0); std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth); - struct minimal_object_frame { + struct safe_object_frame { frame_ptr raw_address; frame_ptr address_relative_to_object_base_in_memory; char object_path[CPPTRACE_PATH_MAX + 1]; object_frame resolve() const; // To be called outside a signal handler. Not signal safe. }; - void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out); + void get_safe_object_frame(frame_ptr address, safe_object_frame* out); } ``` **Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported `safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe -way then `get_minimal_object_frame` will not populate fields beyond the `raw_address`. +way then `get_safe_object_frame` will not populate fields beyond the `raw_address`. **Another big note:** Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions such as `malloc()`. To avoid this, call these routines in `main()` ahead of a signal handler diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index 0919958..0d0e6cc 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -55,7 +55,6 @@ namespace cpptrace { struct CPPTRACE_EXPORT object_frame { std::string obj_path; - std::string symbol; frame_ptr raw_address; frame_ptr obj_address; }; @@ -194,7 +193,7 @@ namespace cpptrace { std::size_t skip, std::size_t max_depth ); - struct CPPTRACE_EXPORT minimal_object_frame { + struct CPPTRACE_EXPORT safe_object_frame { frame_ptr raw_address; frame_ptr address_relative_to_object_base_in_memory; char object_path[CPPTRACE_PATH_MAX + 1]; @@ -202,7 +201,7 @@ namespace cpptrace { object_frame resolve() const; }; // signal-safe - CPPTRACE_EXPORT void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out); + CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out); // utilities: CPPTRACE_EXPORT std::string demangle(const std::string& name); diff --git a/signal-safe-tracing.md b/signal-safe-tracing.md index c3f1640..24a1b31 100644 --- a/signal-safe-tracing.md +++ b/signal-safe-tracing.md @@ -44,7 +44,7 @@ namespace cpptrace { // signal-safe std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth); - struct minimal_object_frame { + struct safe_object_frame { frame_ptr raw_address; frame_ptr address_relative_to_object_base_in_memory; char object_path[CPPTRACE_PATH_MAX + 1]; @@ -52,7 +52,7 @@ namespace cpptrace { }; // signal-safe - void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out); + void get_safe_object_frame(frame_ptr address, safe_object_frame* out); } ``` @@ -61,7 +61,7 @@ information to resolve a stack trace in the currently running process after a si sufficient for resolving outside of the currently running process, unless there is no position-independent code or shared-library code. -To resolve outside the current process `minimal_object_frame` information is needed. This contains the path to the +To resolve outside the current process `safe_object_frame` information is needed. This contains the path to the object where the address is located as well as the address before address randomization. # Strategy @@ -69,10 +69,10 @@ object where the address is located as well as the address before address random Signal-safe tracing can be done three ways: - In a signal handler, call `safe_generate_raw_trace` and then outside a signal handler construct a `cpptrace:raw_trace` and resolve. -- In a signal handler, call `safe_generate_raw_trace`, then write `cpptrace::minimal_object_frame` +- In a signal handler, call `safe_generate_raw_trace`, then write `cpptrace::safe_object_frame` information to a file to be resolved later. - In a signal handler, call `safe_generate_raw_trace`, `fork()` and `exec()` a process to handle the - resolution, pass `cpptrace::minimal_object_frame` information to that child through a pipe, and + resolution, pass `cpptrace::safe_object_frame` information to that child through a pipe, and wait for the child to exit. It's not as simple as calling `cpptrace::generate_trace().print()`, I know, but these are truly the @@ -82,7 +82,7 @@ only ways to do this safely as far as I can tell. **Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported `safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe -way then `get_minimal_object_frame` will not populate fields beyond the `raw_address`. +way then `get_safe_object_frame` will not populate fields beyond the `raw_address`. Currently the only back-end that can unwind safely is libunwind. Currently, the only way I know to get `dladdr`'s information in a signal-safe manner is `_dl_find_object`, which doesn't exist on macos (or windows of course). If anyone @@ -146,10 +146,10 @@ void do_signal_safe_trace(cpptrace::frame_ptr* buffer, std::size_t size) { execl("signal_tracer", "signal_tracer", nullptr); _exit(1); } - // Resolve to minimal_object_frames and write those to the pipe + // Resolve to safe_object_frames and write those to the pipe for(std::size_t i = 0; i < count; i++) { - cpptrace::minimal_object_frame frame; - cpptrace::get_minimal_object_frame(buffer[i], &frame); + cpptrace::safe_object_frame frame; + cpptrace::get_safe_object_frame(buffer[i], &frame); write(input_pipe.write_end, &frame, sizeof(frame)); } close(input_pipe.read_end); @@ -175,8 +175,8 @@ void warmup_cpptrace() { // This is done for any dynamic-loading shenanigans cpptrace::frame_ptr buffer[10]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); - cpptrace::minimal_object_frame frame; - cpptrace::get_minimal_object_frame(buffer[0], &frame); + cpptrace::safe_object_frame frame; + cpptrace::get_safe_object_frame(buffer[0], &frame); } int main() { @@ -195,7 +195,7 @@ int main() { ## In the tracer program -The tracer program is quite simple. It just has to read `cpptrace::minimal_object_frame`s from the pipe, resolve to +The tracer program is quite simple. It just has to read `cpptrace::safe_object_frame`s from the pipe, resolve to `cpptrace::object_frame`s, and resolve an `object_trace`. ```cpp @@ -208,7 +208,7 @@ The tracer program is quite simple. It just has to read `cpptrace::minimal_objec int main() { cpptrace::object_trace trace; while(true) { - cpptrace::minimal_object_frame frame; + cpptrace::safe_object_frame frame; // fread used over read because a read() from a pipe might not read the full frame std::size_t res = fread(&frame, sizeof(frame), 1, stdin); if(res == 0) { diff --git a/src/binary/object.hpp b/src/binary/object.hpp index 7c59a17..4851b7f 100644 --- a/src/binary/object.hpp +++ b/src/binary/object.hpp @@ -78,7 +78,6 @@ namespace detail { frame.obj_address = addr - reinterpret_cast(info.dli_fbase) + get_module_image_base(info.dli_fname); - frame.symbol = info.dli_sname ?: ""; } frames.push_back(frame); } @@ -151,10 +150,9 @@ namespace detail { } #endif - inline object_frame resolve_minimal_object_frame(const minimal_object_frame& frame) { + inline object_frame resolve_safe_object_frame(const safe_object_frame& frame) { return { frame.object_path, - "", frame.raw_address, frame.address_relative_to_object_base_in_memory + get_module_image_base(frame.object_path) }; diff --git a/src/binary/safe_dl.hpp b/src/binary/safe_dl.hpp index e63d9db..3256804 100644 --- a/src/binary/safe_dl.hpp +++ b/src/binary/safe_dl.hpp @@ -21,7 +21,7 @@ namespace cpptrace { namespace detail { - inline void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out) { + inline void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { dl_find_object result; if(_dl_find_object(reinterpret_cast(address), &result) == 0) { out->raw_address = address; @@ -59,7 +59,7 @@ namespace detail { #else namespace cpptrace { namespace detail { - inline void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out) { + inline void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { out->raw_address = address; out->address_relative_to_object_base_in_memory = 0; out->object_path[0] = 0; diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 9e4f885..564b660 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -342,12 +342,12 @@ namespace cpptrace { } } - object_frame minimal_object_frame::resolve() const { - return detail::resolve_minimal_object_frame(*this); + object_frame safe_object_frame::resolve() const { + return detail::resolve_safe_object_frame(*this); } - void get_minimal_object_frame(frame_ptr address, minimal_object_frame* out) { - detail::get_minimal_object_frame(address, out); + void get_safe_object_frame(frame_ptr address, safe_object_frame* out) { + detail::get_safe_object_frame(address, out); } std::string demangle(const std::string& name) { diff --git a/src/symbols/symbols_with_addr2line.cpp b/src/symbols/symbols_with_addr2line.cpp index bae5043..ddf868a 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -272,7 +272,6 @@ namespace addr2line { trace[i].address = frames[i].raw_address; // Set what is known for now, and resolutions from addr2line should overwrite trace[i].filename = frames[i].obj_path; - trace[i].symbol = frames[i].symbol; } if(has_addr2line()) { const auto entries = collate_frames(frames, trace); diff --git a/src/symbols/symbols_with_libdwarf.cpp b/src/symbols/symbols_with_libdwarf.cpp index 3e18e7a..f79a65e 100644 --- a/src/symbols/symbols_with_libdwarf.cpp +++ b/src/symbols/symbols_with_libdwarf.cpp @@ -850,15 +850,13 @@ namespace libdwarf { frame_with_inlines resolve_frame(const object_frame& frame_info) { stacktrace_frame frame = null_frame; frame.filename = frame_info.obj_path; - frame.symbol = frame_info.symbol; frame.address = frame_info.raw_address; if(trace_dwarf) { std::fprintf( stderr, - "Starting resolution for %s %08llx %s\n", + "Starting resolution for %s %08llx\n", obj_path.c_str(), - to_ull(frame_info.obj_address), - frame_info.symbol.c_str() + to_ull(frame_info.obj_address) ); } std::vector inlines; diff --git a/test/signal_demo.cpp b/test/signal_demo.cpp index 3d757f7..c7a2db2 100644 --- a/test/signal_demo.cpp +++ b/test/signal_demo.cpp @@ -55,8 +55,8 @@ void handler(int signo, siginfo_t* info, void* context) { _exit(1); } for(std::size_t i = 0; i < count; i++) { - cpptrace::minimal_object_frame frame; - cpptrace::get_minimal_object_frame(buffer[i], &frame); + cpptrace::safe_object_frame frame; + cpptrace::get_safe_object_frame(buffer[i], &frame); write(input_pipe.write_end, &frame, sizeof(frame)); } close(input_pipe.read_end); @@ -68,8 +68,8 @@ void handler(int signo, siginfo_t* info, void* context) { void warmup_cpptrace() { cpptrace::frame_ptr buffer[10]; std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10); - cpptrace::minimal_object_frame frame; - cpptrace::get_minimal_object_frame(buffer[0], &frame); + cpptrace::safe_object_frame frame; + cpptrace::get_safe_object_frame(buffer[0], &frame); } int main() { diff --git a/test/signal_tracer.cpp b/test/signal_tracer.cpp index 048f545..b531f29 100644 --- a/test/signal_tracer.cpp +++ b/test/signal_tracer.cpp @@ -8,7 +8,7 @@ int main() { cpptrace::object_trace trace; while(true) { - cpptrace::minimal_object_frame frame; + cpptrace::safe_object_frame frame; // std::size_t res = read(STDIN_FILENO, &frame, sizeof(frame)); std::size_t res = fread(&frame, sizeof(frame), 1, stdin); if(res == 0) {