Skip to content

Instantly share code, notes, and snippets.

@homuler
Last active March 31, 2022 01:10
Show Gist options
  • Save homuler/9fdfc483aef64dcc69d542669f1bc2cf to your computer and use it in GitHub Desktop.
Save homuler/9fdfc483aef64dcc69d542669f1bc2cf to your computer and use it in GitHub Desktop.
PoC code to show how to print stack traces in C with line numbers.
// This is a PoC code to show how to print stack traces in C with line numbers.
// This code is based on [glibc/debug/backtracesymsfd.c](https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=debug/backtracesymsfd.c;hb=244b415d386487521882debb845a040a4758cb18)
/* Copyright (C) 1998-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/*
Usage:
gcc -g -ldl -ldw backtrace.c
./a.out
Sample Output:
./a.out(baz+0x1ea4)[0x55bf9cb66ea4] (at /path/to/backtrace.c:216,3)
./a.out(bar+0x1ecd)[0x55bf9cb66ecd] (at /path/to/backtrace.c:220,3)
./a.out(foo+0x1ede)[0x55bf9cb66ede] (at /path/to/backtrace.c:224,3)
./a.out(main+0x1f03)[0x55bf9cb66f03] (at /path/to/backtrace.c:230,3)
/usr/lib/libc.so.6(__libc_start_call_main+0x2d310)[0x7f34384c9310]
/usr/lib/libc.so.6(__libc_start_main+0x81)[0x7f34384c93c1]
./a.out(_start+0x1185)[0x55bf9cb66185]
Segmentation fault (core dumped)
*/
#define _GNU_SOURCE
#include <execinfo.h>
#include <dlfcn.h>
#include <link.h>
#include <string.h>
#include <sys/uio.h>
#include <elfutils/libdwfl.h>
#include <signal.h>
#include <stdio.h>
#define WORD_WIDTH 16
#define MAX_DIGITS 10
static const char* _digits = "0123456789abcdef";
static inline char* __attribute__ ((unused, always_inline)) _itoa_word(unsigned long int value, char* bufend, unsigned int base) {
do {
*--bufend = _digits[value % base];
} while ((value /= base) != 0);
return bufend;
}
static Dwfl* init_dwfl() {
static char *debuginfo_path;
static const Dwfl_Callbacks proc_callbacks = {
.debuginfo_path = &debuginfo_path,
.find_debuginfo = dwfl_standard_find_debuginfo,
.find_elf = dwfl_linux_proc_find_elf,
};
Dwfl *dwfl = dwfl_begin(&proc_callbacks);
if (!dwfl || dwfl_linux_proc_report(dwfl, getpid()) != 0 || dwfl_report_end(dwfl, NULL, NULL) != 0) {
return NULL;
}
return dwfl;
}
static void print_frame(Dwfl* dwfl, Dwarf_Addr addr) {
struct iovec iov[20];
int cnt = 0;
char diffbuf[WORD_WIDTH];
char addrbuf[WORD_WIDTH];
char linebuf[MAX_DIGITS];
char colbuf[MAX_DIGITS];
Dl_info info;
struct link_map* map;
// TODO: probably we should read DWARF sections.
if (dladdr1((void *)addr, &info, (void *)&map, RTLD_DL_LINKMAP) && info.dli_fname != NULL && info.dli_fname[0] != '\0') {
/* Name of the file. */
iov[cnt].iov_base = (void *)info.dli_fname;
iov[cnt++].iov_len = strlen(info.dli_fname);
if (info.dli_sname != NULL || map->l_addr != 0) {
size_t diff;
iov[cnt].iov_base = (void *) "(";
iov[cnt++].iov_len = 1;
if (info.dli_sname != NULL) {
/* We have a symbol name. */
iov[cnt].iov_base = (void *)info.dli_sname;
iov[cnt++].iov_len = strlen(info.dli_sname);
} else {
if (dwfl) {
Dwfl_Module* module = dwfl_addrmodule(dwfl, addr);
const char* symbol = dwfl_module_addrname(module, addr);
if (symbol) {
iov[cnt].iov_base = (void *)symbol;
iov[cnt++].iov_len = strlen(symbol);
}
}
/* We have no symbol */
info.dli_saddr = (void *)map->l_addr;
}
if ((void *)addr >= (void *)info.dli_saddr) {
iov[cnt].iov_base = (void *)"+0x";
diff = (void *)addr - info.dli_saddr;
} else {
iov[cnt].iov_base = (void *)"-0x";
diff = info.dli_saddr - (void *)addr;
}
iov[cnt++].iov_len = 3;
iov[cnt].iov_base = _itoa_word((unsigned long int) diff, &diffbuf[WORD_WIDTH], 16);
iov[cnt].iov_len = (&diffbuf[WORD_WIDTH] - (char *)iov[cnt].iov_base);
++cnt;
iov[cnt].iov_base = (void *)")";
iov[cnt++].iov_len = 1;
}
}
iov[cnt].iov_base = (void *)"[0x";
iov[cnt++].iov_len = 3;
iov[cnt].iov_base = _itoa_word((unsigned long int)addr, &addrbuf[WORD_WIDTH], 16);
iov[cnt].iov_len = &addrbuf[WORD_WIDTH] - (char *)iov[cnt].iov_base;
++cnt;
iov[cnt].iov_base = (void *)"]";
iov[cnt++].iov_len = 1;
if (dwfl) {
Dwfl_Line* line = dwfl_getsrc(dwfl, addr - 1);
if (line) {
int linep;
int colp;
const char* filename = dwfl_lineinfo(line, NULL, &linep, &colp, NULL, NULL);
if (filename) {
iov[cnt].iov_base = (void *)" (at ";
iov[cnt++].iov_len = 5;
iov[cnt].iov_base = (void *)filename;
iov[cnt++].iov_len = strlen(filename);
iov[cnt].iov_base = (void *)":";
iov[cnt++].iov_len = 1;
iov[cnt].iov_base = _itoa_word(linep, &linebuf[MAX_DIGITS], 10);
iov[cnt].iov_len = &linebuf[MAX_DIGITS] - (char *)iov[cnt].iov_base;
++cnt;
iov[cnt].iov_base = (void *)",";
iov[cnt++].iov_len = 1;
iov[cnt].iov_base = _itoa_word(colp, &colbuf[MAX_DIGITS], 10);
iov[cnt].iov_len = &colbuf[MAX_DIGITS] - (char *)iov[cnt].iov_base;
++cnt;
iov[cnt].iov_base = (void *)")";
iov[cnt++].iov_len = 1;
}
}
}
iov[cnt].iov_base = (void *)"\n";
iov[cnt++].iov_len = 1;
writev(STDERR_FILENO, iov, cnt);
}
void invoke_default_handler(int sig) {
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL;
sigaction(sig, &sig_action, NULL);
kill(getpid(), sig);
}
void handler(int sig) {
void *array[30];
size_t size;
size = backtrace(array, 30);
Dwfl* dwfl = init_dwfl();
int cnt;
// skip signal handler's stack frames
for (cnt = 2; cnt < size; ++cnt) {
print_frame(dwfl, (Dwarf_Addr)array[cnt]);
}
invoke_default_handler(sig);
}
// The following code is for testing purposes.
void baz() {
int *foo = (int*)-1;
printf("%d\n", *foo); // SIGSEGV
}
void bar() {
baz();
}
void foo() {
bar();
}
int main() {
signal(SIGSEGV, handler);
foo();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment