組み込みarm開発C言語呼び出しスタック実戦遡及
参照先:https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes
コード#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include
#include
#include
#include
#include
#include
#include
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
void * array[50];
void * caller_address;
char ** messages;
int size, i;
sig_ucontext_t * uc;
uc = (sig_ucontext_t *)ucontext;
#if defined(__i386__)
caller_address = (void *) uc->uc_mcontext.eip;
#elif defined(__x86_64__)
caller_address = (void *) uc->uc_mcontext.rip;
#else
caller_address = (void *) uc->uc_mcontext.arm_pc;
#endif
fprintf(stderr, "signal %d (%s), address is %p from %p
",
sig_num, strsignal(sig_num), info->si_addr,
(void *)caller_address);
size = backtrace(array, 50);
array[1] = caller_address;
messages = backtrace_symbols(array, size);
for (i = 1; i < size && messages != NULL; ++i)
{
fprintf(stderr, "[bt]: (%d) %s
", i, messages[i]);
}
free(messages);
exit(EXIT_FAILURE);
}
int crash()
{
char * p = NULL;
*p = 0;
return 0;
}
int foo4()
{
crash();
return 0;
}
int foo3()
{
foo4();
return 0;
}
int foo2()
{
foo3();
return 0;
}
int foo1()
{
foo2();
return 0;
}
int main(int argc, char ** argv)
{
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
{
fprintf(stderr, "error setting signal handler for %d (%s)
", SIGSEGV, strsignal(SIGSEGV));
exit(EXIT_FAILURE);
}
foo1();
exit(EXIT_SUCCESS);
}
コンパイルarm-linux-gnueabihf-gcc -o test -rdynamic -mapcs-frame -funwind-tables -ffunction-sections backtrace.c
運転signal 11 (Segmentation fault), address is (nil) from 0x10a32
[bt]: (1) ./Test(crash+0xd) [0x10a32]
[bt]: (2) ./Test(crash+0xd) [0x10a32]
[bt]: (3) ./Test(foo4+0x7) [0x10a4c]
[bt]: (4) ./Test(foo3+0x7) [0x10a5c]
[bt]: (5) ./Test(foo2+0x7) [0x10a6c]
[bt]: (6) ./Test(foo1+0x7) [0x10a7c]
[bt]: (7) ./Test(main+0x5d) [0x10ae2]
[bt]: (8) /lib/libc.so.6(__libc_start_main+0x9b) [0xb6ec9334]
遡及arm-linux-gnueabihf-addr2line -e ./test -C -f 0x10a4c
arm-linux-gnueabihf-objdump -s -d ./test --start-address=0x10a00 --stop-address=0x10a80
詳細参照自らarm関数スタックフレーム遡及【回転】を実現する用のuclibバージョンは低く、これらの関数はありませんが、自分で実現するしかありません(高いバージョンにはこれらの関数があるはずですが、バージョンを変えるのは面倒です)、そしてこの方面に対する理解を深めることができます backtraceとucontexによるsegmentエラーの位置決めの上にbacktraceを介して「segmentfault」エラー時の関数呼び出しスタックが略得られるが、backtraceだけでは異常を引き起こす命令アドレスは得られない(異常を引き起こす関数さえ得られない).Redisのソースコードでは,命令アドレスを印刷する方法が見られた.ucontext_の使用t構造で、命令レジスタの内容を印刷する.