musl+libunwind的堆栈获取方案

背景

musl静态链接后,通过_Unwind_Backtrace某些架构无法获取到堆栈信息了,如arm32的SIGSEGV,libbacktrace是对其进行封装,所以也是如此。

使用http://musl.cc/下载的静态编译工具+libunwind同样无法获取到堆栈信息。

需要加入libunwind库的方法解决,具体如下。

参考:
https://cloud.tencent.com/developer/article/1173442
https://github.com/boostorg/stacktrace
https://www.boost.org/doc/libs/develop/doc/html/stacktrace.html
https://www.jianshu.com/p/58d32fbd8dfa
https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/main/libunwind/musl-mips-fix.patch

交叉编译libunwind

x86的需要链接libucontext才可以正常编译,mipsel的需要自己打patch,文件如musl-mips-fix.patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ autoreconf -i # Needed only for building from git. Depends on libtool.
# HOST=x86_64-linux-gnu
# HOST=x86-linux-gnu
# HOST=arm-linux-gnueabihf
# HOST=aarch64-linux-gnu
# HOST=mipsel-linux-gnu
$ ./configure --host arm-linux CC=arm-linux-musleabi-gcc CFLAGS=-std=c11
# x86的需要链接libucontext才可以正常编译
# libcontext编译
$ make ARCH=x86 CC=/root/compile/old/i486-linux-musl-cross/bin/i486-linux-musl-gcc DESTDIR=x86-out install
$ ./configure --host x86-linux CC=i486-linux-musleabi-gcc CFLAGS="-std=c11 -m32" LDFLAGS="-L libucontext/lib/path -lucontext"
$ make
# 编译出来的库在src/.libs下
$ ls src/.libs/

具体应用

链接unwind库,该库需要使用arm-linux-musleabi-gcc工具进行编译。

1
2
# 编译方法
$ arm-linux-musleabi-gcc backtrace.c -o backtrace -lunwind -static

backtrace.c文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

#include <cxxabi.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | flags;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}

void backtrace()
{
unw_cursor_t cursor;
unw_context_t context;

unw_getcontext(&context);
unw_init_local(&cursor, &context);

int n=0;
while ( unw_step(&cursor) ) {
unw_word_t ip, sp, off;

unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);

char symbol[256] = {"<unknown>"};
char *name = symbol;

if ( !unw_get_proc_name(&cursor, symbol, sizeof(symbol), &off) ) {
int status;
if ( (name = abi::__cxa_demangle(symbol, NULL, NULL, &status)) == 0 )
name = symbol;
}

printf("#%-2d 0x%016" PRIxPTR " sp=0x%016" PRIxPTR " %s + 0x%" PRIxPTR "\n",
++n,
static_cast<uintptr_t>(ip),
static_cast<uintptr_t>(sp),
name,
static_cast<uintptr_t>(off));

if ( name != symbol )
free(name);
}
}

static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
{
long ip = 0;
ucontext_t *uc = (ucontext_t *)ctx_void;
ip = uc->uc_mcontext.arm_pc;
printf("In signal handler. Trying to unwind.\n");
dprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
sig,
/* this is void*, but using %p would print "(null)"
* even for ptrs which are not exactly 0, but, say, 0x123:
*/
(long)info->si_addr,
ip);
backtrace();
exit(0);
}

int fac(int n)
{
if ( n == 0 ) {
printf("Unwind from signal handler\n");
sethandler(SIGSEGV, sigusr1, 0);
char *p = NULL;
*p = 11;
return 1;
} else {
return n*fac(n-1);
}
}

int main()
{
fac(10);
return 0;
}

nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!