Summary of backtrace acquisition method at the time of debugging
If you use LD_PRELOAD
, you can embed the backtrace in any place later without rebuilding the target.
Omitted because it will be the usage of gdb
Only available when SEGV occurs in the program.
how to use
$ LD_PRELOAD=/lib/libSegFault.so hoge
# or
$ catchsegv hoge
__builtin_return_address ()
You can get the address of the caller by using __builtin_return_address ()
.
By increasing the number of arguments, you can refer to the caller above.
test.c
#include <stdio.h>
#define __USE_GNU
#include <dlfcn.h>
void hoge() {
Dl_info info;
dladdr(__builtin_return_address(0), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
dladdr(__builtin_return_address(1), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(1));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
}
int main(int argc, char const* argv[])
{
hoge();
Dl_info info;
dladdr(__builtin_return_address(0), &info);
fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
return 0;
}
Build and execution results
$ gcc test.c -ldl -rdynamic && ./a.out
hoge : __builtin_return_address => 0x4007d7
hoge : Dl_info.dli_fname => ./a.out
hoge : Dl_info.dli_fbase => 0x400000
hoge : Dl_info.dli_sname => (null)
hoge : Dl_info.dli_saddr => (nil)
hoge : __builtin_return_address => 0x7f8889a6c76d
hoge : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
hoge : Dl_info.dli_fbase => 0x7f8889a4b000
hoge : Dl_info.dli_sname => __libc_start_main
hoge : Dl_info.dli_saddr => 0x7f8889a6c680
main : __builtin_return_address => 0x7f8889a6c76d
main : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
main : Dl_info.dli_fbase => 0x7f8889a4b000
main : Dl_info.dli_sname => __libc_start_main
main : Dl_info.dli_saddr => 0x7f8889a6c680
Note that with this method, if you increase the number and go back, SEGV will occur if you access an address that does not exist.
When referencing a stack frame that does not exist(SEGV occurs)
#include <stdio.h>
int main(int argc, char const* argv[])
{
printf("%p\n", __builtin_return_address(0));
printf("%p\n", __builtin_return_address(1));
return 0;
}
Build and execution results
$ gcc test.c && ./a.out
0x7f6c9450076d
Segmentation fault (core dumped)
An example of using glibc's backtrace ()
and backtrace_symbols ()
to output a backtrace.
test.cpp
// backtrace, backtrace_symbols
#include <execinfo.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
using RetVec = std::vector<std::string>;
RetVec my_backtrace() {
auto trace_size = 10;
void* trace[trace_size];
auto size = backtrace(trace, trace_size);
auto symbols = backtrace_symbols(trace, size);
RetVec result(symbols, symbols + size);
free(symbols);
return result;
}
RetVec hoge2() {
return my_backtrace();
}
RetVec hoge1() {
return hoge2();
}
int main(int argc, char const* argv[])
{
auto vec = hoge1();
int count = vec.size();
for (auto v : vec) {
count--;
fprintf(stderr, "%s : backtrace [%d] %s\n",
__func__,
count,
v.c_str()
);
}
return 0;
}
Build and execution results
$ g++ -g -std=c++11 -o test test.cpp -rdynamic
$ ./test
main : backtrace [5] ./test(_Z12my_backtracev+0x97) [0x4021ff]
main : backtrace [4] ./test(_Z5hoge2v+0x18) [0x4022ac]
main : backtrace [3] ./test(_Z5hoge1v+0x18) [0x4022ca]
main : backtrace [2] ./test(main+0x1c) [0x4022ec]
main : backtrace [1] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7fc27153076d]
main : backtrace [0] ./test() [0x402079]
You can also get a backtrace by using libunwind.
(Libunwind is a library that controls call chains, see "BINARY HACKS # 73")
There was a case where backtrace ()
did not work well on the ARM board, so I implemented it here as well.
python
// backtrace, backtrace_symbols
#include <execinfo.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <dlfcn.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
void my_backtrace() {
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
auto count = 0;
do {
unw_word_t offset, pc;
char fname[64];
unw_get_reg(&cursor, UNW_REG_IP, &pc);
fname[0] = '\0';
(void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
Dl_info info;
dladdr((void*)pc, &info);
fprintf(stderr, "%s : backtrace [%d] %s(%s+0x%lx) [%p]\n",
__func__,
count,
info.dli_fname,
fname,
offset,
(void*)pc
);
count++;
} while (unw_step(&cursor) > 0);
}
void hoge2() {
my_backtrace();
}
void hoge1() {
hoge2();
}
int main(int argc, char const* argv[])
{
hoge1();
return 0;
}
Build and execution results
$ g++ -g -std=c++11 -o test test.cpp -rdynamic -lunwind -ldl
$ ./test
my_backtrace : backtrace [0] ./test(_Z12my_backtracev+0x29) [0x400c81]
my_backtrace : backtrace [1] ./test(_Z5hoge2v+0x9) [0x400d92]
my_backtrace : backtrace [2] ./test(_Z5hoge1v+0x9) [0x400d9d]
my_backtrace : backtrace [3] ./test(main+0x14) [0x400db3]
my_backtrace : backtrace [4] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f12a138d76d]
my_backtrace : backtrace [5] ./test(_start+0x29) [0x400b69]
Binary Hacks has a lot of explanations for the above examples.
backtrace() The GNU C Library: Backtraces Return Address - Using the GNU Compiler Collection (GCC) [Tomorrow is always fresh with no mistake in it. @ Memorandum --Back trace (stack trace) in C language] (http://www35.atwiki.jp/futoyama/pages/101.html) View backtrace in C ++-Days of memos (2010-10-15) How to generate a stacktrace when my gcc C++ app crashes - Stack Overflow
libunwind libunwind documentation c - GCC return address of calling function in ARM architecture - Stack Overflow Coding Relic: Pre-mortem Backtracing Backtrace using ptrace in ARM Linux environment-blog of crimsonwoods
Recommended Posts