/* backtrace.c --
* Created: Sun Feb 9 10:06:01 2003 by faith@dict.org
* Copyright 2003, 2004 Rickard E. Faith (faith@dict.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* $Id: backtrace.c,v 1.20 2004/01/18 01:52:30 faith Exp $
*
* Compile with:
gcc -g -O2 -Wall backtrace.c -o backtrace -std=c99 -pedantic -Wshadow \
-Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes \
-Winline -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wundef \
-Wwrite-strings -Waggregate-return -Wchar-subscripts
*/
/*
bugs:
symbols in shared libs need to use the offset
*/
#ifdef BT_PRODUCTION
#define BT_DEMO 0
#endif
#ifndef BT_DEMO
#define BT_DEMO 1
/* 0 = production; 1 = testing */
#endif
#ifndef BT_STACK
#define BT_STACK 1
/* 1 = print out the call stack */
/* 0 = no debugging information will be read from the file system
(implies BT_SYMBOLS = BT_LINES = BT_ARGS = 0) */
#endif
#ifndef BT_SYMBOLS
#define BT_SYMBOLS 1
/* 1 = get symbol names from .symtab */
/* 0 = no ELF debugging information will be read from the file system
(implies BT_LINES = BT_ARGS = 0) */
#endif
#ifndef BT_LINES
#define BT_LINES 1
/* 1 = get file/lines from .debug_line */
/* 0 = no DWARF2 debugging information will be read from the file system
(implies BT_ARGS = 0) */
#endif
#ifndef BT_ARGS
#define BT_ARGS 1
/* 1 = get types from the DWARF2 .debug_abbrev, .debug_info, and
* .debug_str sections; and unwind stack using .debug_frame. This
* nearly doubles the code size, has not been aggressively
* debugged, and is expected to be inaccurate * in some cases. */
/* 0 = only provide symbol and file/line information, which is simpler to
* decode */
#endif
/* Make the defines consistent. */
#if !BT_STACK
#undef BT_SYMBOLS
#define BT_SYMBOLS 0
#endif
#if !BT_SYMBOLS
#undef BT_LINES
#define BT_LINES 0
#endif
#if !BT_LINES
#undef BT_ARGS
#define BT_ARGS 0
#endif
#define BT_ARGS_DEBUG_PR 0 /* 1 = debug, 2 = dump args */
#define BT_ARGS_DEBUG_RF 0
#define BT_ARGS_DEBUG_RC 0
#define BT_ARGS_DEBUG_RR 0
#define BT_ARGS_DEBUG_FA 0
#define BT_ARGS_DEBUG_RI 0
#define BT_ARGS_DEBUG_SECTION_NAMES 0
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef XAPPGROUP
#include "scrnintstr.h"
#undef bt_printf
#define bt_printf ErrorF
#endif
#ifdef BT_PRODUCTION
#include "maaP.h"
#include "backtrace.h"
#define bt_printf maaLogBacktrace
#define bt_abort maaFatalAbort(__func__, "Terminating after backtrace\n")
#endif
#ifndef bt_printf
#define bt_printf printf
#endif
#ifndef bt_abort
#define bt_abort abort()
#endif
/* Entry points */
#ifndef _BACKTRACE_H_
extern void bt_init(const char *program_name);
extern void bt_backtrace(const char *program_name);
extern void bt_signal(int sig, const char *program_name);
#endif
/* Max number of lines used from /proc//maps */
#define BT_MAX_MAPS 64
/* Max number of ELF sections used from a file. Note that we only use
* .symtab, .strtab, .shstrtab, and .debug_*. We could restrict the
* number of .debug_* sections read. */
#define BT_MAX_SECTIONS 16
/* Size of buffer used to cache section reads. */
#define BT_BUF_SIZE 128
/* Size of the string pool used for bt_strdup(). */
#define BT_NAME_SPACE (4 * 1024)
#if BT_LINES
/* Max number of file names cached from .debug_line sections. */
#define BT_MAX_FILES 512
/* Size of the string pool used for file name caching from .debug_line */
#define BT_FILE_SPACE (4 * 1024)
#endif
#define BT_MAX_REG 20
#define BT_MAX_DEPTH 3
/* Magic structures and numbers from:
*
* EXECUTABLE AND LINKABLE FORMAT (ELF): Portable Formats Specification,
* Version 1.1; Tool Interface Standards (TIS)
* A text version from: http://www.muppetlabs.com/~breadbox/software/ELF.txt
*
* DWARF Debugging Information Format. UNIX International. Programming
* Languages SIG. Revision: 2.0.0 (July 27, 1993).
* Nroff/PostScript: http://reality.sgiweb.org/davea/libdwarf2002Nov22.tar.gz
*
* Note that there are numerous GPL and LGPL ELF and DWARF libraries
* that these routines could be written to use. However, for this
* implementation:
*
* a) I read the specifications (not the code) listed above
*
* b) I asked someone else to look at the gdb sources that processed
* __restore_rt and that person told me that glibc uses signal
* trampolines called __restore and __restore_rt and that these are
* assumed to have the following byte sequences, with instruction
* boundaries marked with /, and that the top of the stack may start
* on any of the instruction boundaries (although I've only ever seen
* it start at the first one):
*
* 0x58 / 0xb8 0x77 0x00 0x00 0x00 / 0xcd 0x80
* 0xb8 0xad 0x00 0x00 0x00 / 0xcd 0x80
*
* We could just punt and assume the last frame is the fault EIP, but
* checking for these sequences makes these routines robust when not
* called from a signal handler.
*
* c) I asked someone how gdb maps the DWARF2 registers to the Linux
* registers. The mapping is as follows:
*
* DWARF2 magic number Linux symbol (from sys/ucontext.h)
* 0 REG_EAX
* 1 REG_ECX
* 2 REG_EDX
* 3 REG_EBX
* 4 REG_ESP
* 5 REG_EBP
* 6 REG_ESI
* 7 REG_EDI
* 8 REG_EIP
* 9 REG_EFL
* 10 REG_CS
* 11 REG_SS
* 12 REG_DS
* 13 REG_ES
* 14 REG_FS
* 15 REG_GS
*
*
* These routines may not be robust in the face of gcc changes, but they
* fulfill these design goals:
*
* 1) works with gcc3 under Linux 2.4.x
* 2) independent of non-X11/MIT/BSD licensed code or libraries
* 3) small
* 4) doesn't use malloc
* 5) has relatively small stack footprint
*/
/************************************************************************/
/***** Start of ELF symbol table support ********************************/
/************************************************************************/
typedef struct {
int fd;
int need; /* Need lseek() */
unsigned long offset;
unsigned long start;
unsigned long length;
unsigned long pos;
unsigned char buf[BT_BUF_SIZE];
} bt_buf_t;
typedef struct {
int idx;
int type;
const char *name;
unsigned long offset;
unsigned long size;
unsigned long entry;
bt_buf_t buf;
} bt_section_t;
typedef struct {
unsigned long lower;
unsigned long upper;
const char *name;
int seq; /* Sequence of entries with same name */
unsigned long reloc; /* Relocation offset */
int this; /* 1 = this is the executing binary */
int open;
int fd;
bt_buf_t buf;
int count;
bt_section_t sect[BT_MAX_SECTIONS];
bt_section_t *sym, *str, *sh_str;
} bt_map_t;
static const char *bt_program_name;
#if BT_SYMBOLS
typedef unsigned long elf_addr;
typedef unsigned short elf_half;
typedef unsigned long elf_off;
typedef signed long elf_sword;
typedef unsigned long elf_word;
typedef unsigned char elf_byte;
typedef struct {
elf_word magic;
elf_byte class; /* 1 = 32-bit, 2 = 64-bit */
elf_byte data; /* 1 = LSB, 2 = MSB */
elf_byte ver;
elf_byte reserved0;
elf_word reserved1;
elf_word reserved2;
elf_half type;
elf_half machine;
elf_word version;
elf_addr entry;
elf_off phOff;
elf_off shOff;
elf_word flags;
elf_half ehSize;
elf_half phEntSize;
elf_half phNum;
elf_half shEntSize;
elf_half shNum;
elf_half shStrIdx;
} elf_header_t;
typedef struct {
elf_word name;
elf_word type;
elf_word flags;
elf_addr addr;
elf_off offset;
elf_word size;
elf_word link;
elf_word info;
elf_word align;
elf_word ent_size;
} elf_section_t;
typedef struct {
elf_word name; /* Offset into shStrIdx table */
elf_addr value;
elf_word size;
elf_byte info;
elf_byte other;
elf_half shIdx;
} elf_symbol_t;
typedef struct {
elf_word type;
elf_off offset;
elf_addr vaddr;
elf_addr paddr;
elf_word fileSize;
elf_word memSize;
elf_word flags;
elf_word align;
} elf_program_t;
static int bt_map_count;
static bt_map_t bt_maps[BT_MAX_MAPS];
static char bt_name_space[BT_NAME_SPACE];
static char *bt_name_end;
static char bt_buf[256]; /* Multi-purpose buffer */
static unsigned long bt_physical; /* Physical bytes read */
static unsigned long bt_block; /* Physical bytes read as blocks */
static unsigned long bt_logical; /* Logical bytes read */
#endif
/************************************************************************/
/***** End of ELF symbol table support **********************************/
/************************************************************************/
/************************************************************************/
/***** Start of DWARF2 file/line support ********************************/
/************************************************************************/
#if BT_LINES
static int bt_file_count;
static char *bt_files[BT_MAX_FILES];
static char bt_file_space[BT_FILE_SPACE];
static char *bt_file_end;
static int bt_file_max;
typedef unsigned long uword;
typedef signed long sword;
typedef unsigned short uhalf;
typedef unsigned char ubyte;
typedef signed char sbyte;
typedef struct { /* Warning, this isn't packed. Argh. */
uword total_length;
uhalf version;
uword prologue_length;
ubyte mil; /* minimum instruction length */
ubyte default_is_stmt;
sbyte line_base;
ubyte line_range;
ubyte opcode_base;
} dwarf_header_t;
typedef struct {
uword length;
uhalf version;
uword offset;
ubyte bytes;
} dwarf_info_t;
typedef struct {
unsigned long address;
int file;
int line;
} dwarf_state_t;
typedef enum {
DW_LNE_end_sequence = 1,
DW_LNE_set_address = 2,
DW_LNE_define_file = 3
} dwarf_lne_t;
typedef enum {
DW_LNS_copy = 1,
DW_LNS_advance_pc = 2,
DW_LNS_advance_line = 3,
DW_LNS_set_file = 4,
DW_LNS_set_column = 5,
DW_LNS_negate_stmt = 6,
DW_LNS_set_basic_block = 7,
DW_LNS_const_add_pc = 8,
DW_LNS_fixed_advance_pc = 9
} dwarf_lns_t;
#endif
/************************************************************************/
/***** End of DWARF2 file/line support **********************************/
/************************************************************************/
/************************************************************************/
/***** Start of DWARF2 arg/frame support ********************************/
/************************************************************************/
#if BT_STACK
typedef enum {
bt_unknown,
bt_reg,
bt_off
} bt_flag_t;
typedef enum {
bt_enc_address = 0x00010000,
bt_enc_float = 0x00020000,
bt_enc_signed = 0x00040000,
bt_enc_unsigned = 0x00080000
} bt_enc_t;
typedef struct {
const char *name;
const char *type;
bt_enc_t enc;
bt_flag_t flag;
int offset;
unsigned char reg;
} bt_arg_t;
typedef enum {
bt_undefined = 0,
bt_offset,
bt_same_value,
bt_register
} bt_type_t;
typedef struct {
bt_type_t type;
signed long value;
} bt_reg_state_t;
typedef struct {
int invalid;
int depth;
unsigned long reg, offset;
unsigned long loc;
unsigned long size; /* Undocumented gcc 0x2e */
bt_reg_state_t r[BT_MAX_REG][BT_MAX_DEPTH];
} bt_reg_t;
typedef struct {
int valid;
int value;
} bt_regvals_t;
static const int bt_regmap[] = { REG_EAX, REG_ECX, REG_EDX, REG_EBX,
REG_ESP, REG_EBP, REG_ESI, REG_EDI,
REG_EIP, REG_EFL, REG_CS, REG_SS,
REG_DS, REG_ES, REG_FS, REG_GS };
enum {
DW_EAX = 0,
DW_ECX,
DW_EDX,
DW_EBX,
DW_ESP,
DW_EBP,
DW_ESI,
DW_EDI,
DW_EIP,
DW_EFL,
DW_CS,
DW_SS,
DW_DS,
DW_ES,
DW_FS,
DW_GS,
DW_MAX_REG
};
#endif
#if BT_ARGS
typedef struct {
uword length;
uword cie_id;
ubyte version;
ubyte augmentation[BT_BUF_SIZE];
uword caf; /* code_alignment_factor */
sword daf; /* data_alignment_factor */
ubyte rar; /* return_address_register */
} dwarf_cie_t;
typedef struct {
uword length;
uword cie_pointer;
uword initial_location;
uword address_range;
} dwarf_fde_t;
typedef enum {
DW_AT_location = 0x02,
DW_AT_name = 0x03,
DW_AT_byte_size = 0x0b,
DW_AT_bit_offset = 0x0c,
DW_AT_bit_size = 0x0d,
DW_AT_low_pc = 0x11,
DW_AT_high_pc = 0x12,
DW_AT_encoding = 0x3e,
DW_AT_frame_base = 0x40,
DW_AT_type = 0x49
} dwarf_at_t;
typedef enum {
DW_FORM_addr = 0x01,
DW_FORM_block2 = 0x03,
DW_FORM_block4 = 0x04,
DW_FORM_data2 = 0x05,
DW_FORM_data4 = 0x06,
DW_FORM_data8 = 0x07,
DW_FORM_string = 0x08,
DW_FORM_block = 0x09,
DW_FORM_block1 = 0x0a,
DW_FORM_data1 = 0x0b,
DW_FORM_flag = 0x0c,
DW_FORM_sdata = 0x0d,
DW_FORM_strp = 0x0e,
DW_FORM_udata = 0x0f,
DW_FORM_ref_addr = 0x10,
DW_FORM_ref1 = 0x11,
DW_FORM_ref2 = 0x12,
DW_FORM_ref4 = 0x13,
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16
} dwarf_form_t;
typedef enum {
DW_TAG_array_type = 0x01,
DW_TAG_class_type = 0x02,
DW_TAG_entry_point = 0x03,
DW_TAG_enumeration_type = 0x04,
DW_TAG_formal_parameter = 0x05,
DW_TAG_imported_declaration = 0x08,
DW_TAG_label = 0x0a,
DW_TAG_lexical_block = 0x0b,
DW_TAG_member = 0x0d,
DW_TAG_pointer_type = 0x0f,
DW_TAG_reference_type = 0x10,
DW_TAG_compile_unit = 0x11,
DW_TAG_string_type = 0x12,
DW_TAG_structure_type = 0x13,
DW_TAG_subroutine_type = 0x15,
DW_TAG_typedef = 0x16,
DW_TAG_union_type = 0x17,
DW_TAG_unspecified_parameters = 0x18,
DW_TAG_variant = 0x19,
DW_TAG_common_block = 0x1a,
DW_TAG_common_inclusion = 0x1b,
DW_TAG_inheritance = 0x1c,
DW_TAG_inlined_subroutine = 0x1d,
DW_TAG_module = 0x1e,
DW_TAG_ptr_to_member_type = 0x1f,
DW_TAG_set_type = 0x20,
DW_TAG_subrange_type = 0x21,
DW_TAG_with_stmt = 0x22,
DW_TAG_access_declaration = 0x23,
DW_TAG_base_type = 0x24,
DW_TAG_catch_block = 0x25,
DW_TAG_const_type = 0x26,
DW_TAG_constant = 0x27,
DW_TAG_enumerator = 0x28,
DW_TAG_file_type = 0x29,
DW_TAG_friend = 0x2a,
DW_TAG_namelist = 0x2b,
DW_TAG_namelist_item = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
DW_TAG_template_type_param = 0x2f,
DW_TAG_template_value_param = 0x30,
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
DW_TAG_variable = 0x34,
DW_TAG_volatile_type = 0x35,
DW_TAG_lo_user = 0x4080,
DW_TAG_hi_user = 0xffff
} dwarf_tag_t;
typedef enum {
DW_ATE_address = 0x1,
DW_ATE_boolean = 0x2,
DW_ATE_complex_float = 0x3,
DW_ATE_float = 0x4,
DW_ATE_signed = 0x5,
DW_ATE_signed_char = 0x6,
DW_ATE_unsigned = 0x7,
DW_ATE_unsigned_char = 0x8,
DW_ATE_lo_user = 0x80,
DW_ATE_hi_user = 0xff
} dwarf_ate_t;
typedef enum { /* High 2 bits */
DW_CFA_advance_loc = (0x01 << 6),
DW_CFA_offset = (0x02 << 6),
DW_CFA_restore = (0x03 << 6)
} dwarf_cfa_high_t;
typedef enum {
DW_CFA_set_loc = 0x01,
DW_CFA_advance_loc1 = 0x02,
DW_CFA_advance_loc2 = 0x03,
DW_CFA_advance_loc4 = 0x04,
DW_CFA_offset_extended = 0x05,
DW_CFA_restore_extended = 0x06,
DW_CFA_undefined = 0x07,
DW_CFA_same_value = 0x08,
DW_CFA_register = 0x09,
DW_CFA_remember_state = 0x0a,
DW_CFA_restore_state = 0x0b,
DW_CFA_def_cfa = 0x0c,
DW_CFA_def_cfa_register = 0x0d,
DW_CFA_def_cfa_offset = 0x0e,
DW_CFA_nop = 0x00,
DW_CFA_lo_user = 0x1c,
DW_CFA_hi_user = 0x3f
} dwarf_cfa_t;
typedef enum {
DW_OP_reg_mask = 0x50,
DW_OP_fbreg = 0x91
} dwarf_op_t;
#endif
/************************************************************************/
/***** Start of DWARF2 arg/frame support ********************************/
/************************************************************************/
/************************************************************************/
/***** Start of ELF stack backtrace functions ***************************/
/************************************************************************/
#if !BT_STACK
#define GETSTACK /* empty */
#else
#define H(X) \
do { \
if (!count) { \
if (!(frame[X] = __builtin_frame_address(X)) \
|| !(addr[X] = __builtin_return_address(X))) count = X; \
} \
} while (0)
#define GETSTACK \
do { \
memset(frame, 0, sizeof(frame)); \
memset(addr, 0, sizeof(addr)); \
H(0); H(1); H(2); H(3); H(4); H(5); H(6); H(7); \
H(8); H(9); H(10); H(11); H(12); H(13); H(14); H(15); \
H(16); H(17); H(18); H(19); H(20); H(21); H(22); H(23); \
H(24); H(25); H(26); H(27); H(28); H(29); H(30); H(31); \
} while (0)
static int bt_check(unsigned long target)
{
unsigned char t0[] = { 0x58, 0xb8, 0x77, 0x00, 0x00, 0x00, 0xcd, 0x80 };
unsigned char t1[] = { 0xb8, 0xad, 0x00, 0x00, 0x00, 0xcd, 0x80 };
size_t i;
const unsigned char *stack = (unsigned char *)target;
if (!stack) return 1;
for (i = 0; i < sizeof(t0); i++) if (stack[i] != t0[i]) goto try_t1;
return 1;
try_t1:
for (i = 0; i < sizeof(t1); i++) if (stack[i] != t1[i]) return 0;
return 1;
}
#endif
/************************************************************************/
/***** End of ELF stack backtrace functions *****************************/
/************************************************************************/
/************************************************************************/
/***** Start of ELF symbol table functions ******************************/
/************************************************************************/
#if !BT_SYMBOLS
static bt_map_t *bt_find_map(unsigned long addr)
{
return NULL;
}
static const char *bt_find_symbol(bt_map_t *map, unsigned long addr,
unsigned long *offset)
{
return NULL;
}
#else
static const char *bt_strdup(const char *s)
{
int len;
char *pt;
if (!bt_name_end) bt_name_end = bt_name_space;
if (!s) return "??";
len = strlen(s);
if ((size_t)(bt_name_end + len + 2 - bt_name_space)
>= sizeof(bt_name_space)) return "??";
strcpy(pt = bt_name_end, s);
bt_name_end[len] = '\0';
bt_name_end += len + 1;
return pt;
}
static __inline__ void bt_seek(bt_buf_t *buf, unsigned long pos)
{
int retval;
buf->pos = pos + buf->offset;
if (buf->start
&& buf->pos >= buf->start
&& buf->pos < buf->start + sizeof(buf->buf) - 16) return;
lseek(buf->fd, buf->pos, SEEK_SET);
if ((retval = read(buf->fd, buf->buf, sizeof(buf->buf))) > 0) {
buf->start = buf->pos;
buf->length = retval;
bt_block += retval;
}
}
static __inline__ unsigned long bt_tell(bt_buf_t *buf)
{
return buf->pos - buf->offset;
}
static void bt_read(bt_buf_t *buf, void *datum, int size)
{
int retval;
if (buf->start
&& buf->pos >= buf->start
&& buf->pos + size <= buf->start + buf->length) {
memcpy(datum, buf->buf + buf->pos - buf->start, size);
buf->pos += size;
bt_logical += size;
} else {
if (buf->need) lseek(buf->fd, buf->pos, SEEK_SET);
if ((retval = read(buf->fd, datum, size)) > 0) {
buf->pos += retval;
bt_physical += retval;
}
if ((retval = read(buf->fd, buf->buf, sizeof(buf->buf))) > 0) {
buf->start = buf->pos;
buf->length = retval;
bt_block += retval;
}
}
}
static __inline__ unsigned char bt_read1(bt_buf_t *buf)
{
unsigned char value = 0;
bt_read(buf, &value, 1);
return value;
}
static __inline__ unsigned short bt_read2(bt_buf_t *buf)
{
unsigned short value = 0;
bt_read(buf, &value, 2);
return value;
}
static __inline__ unsigned long bt_read4(bt_buf_t *buf)
{
unsigned long value = 0;
bt_read(buf, &value, 4);
return value;
}
static __inline__ unsigned long bt_read8(bt_buf_t *buf) /* FIXME */
{
unsigned long value = 0;
bt_read(buf, &value, 4);
bt_read(buf, &value, 4); /* Ignore high values for now */
return value;
}
static __inline__ unsigned long bt_read128(bt_buf_t *buf)
{
unsigned long result = 0;
int shift = 0;
unsigned char next;
int i;
for (i = 0; i < 5; i++) {
next = bt_read1(buf);
result |= (next & 0x7f) << shift;
shift += 7;
if (!(next & 0x80)) break;
}
return result;
}
static signed long bt_read128s(bt_buf_t *buf)
{
signed long result = 0;
int shift = 0;
int sign = 0;
unsigned char next;
int i;
for (i = 0; i < 5; i++) {
next = bt_read1(buf);
result |= (next & 0x7f) << shift;
shift += 7;
sign = next & 0x40;
if (!(next & 0x80)) break;
}
/* Sign extend */
if (shift < 32 && sign) result |= -(1 << shift);
return result;
}
static signed long bt_string_read128s(const char *s)
{
signed long result = 0;
int shift = 0;
int sign = 0;
unsigned char next;
int i;
for (i = 0; i < 5; i++) {
next = *s++;
result |= (next & 0x7f) << shift;
shift += 7;
sign = next & 0x40;
if (!(next & 0x80)) break;
}
/* Sign extend */
if (shift < 32 && sign) result |= -(1 << shift);
return result;
}
static void bt_reads(bt_buf_t *buf, char *d, int len)
{
int i;
char *pt;
char byte;
for (pt = d, i = 0; (byte = bt_read1(buf)); i++)
if (i < len-1) *pt++ = byte;
*pt = '\0';
}
static int bt_readl(bt_buf_t *buf, char *d, int len)
{
int i;
char *pt;
char byte;
for (pt = d, i = 0; (byte = bt_read1(buf)) != '\n' && byte; i++)
if (i < len-1) *pt++ = byte;
*pt = '\0';
return strlen(d);
}
static bt_section_t *bt_add_section(bt_map_t *map, elf_section_t *es, int idx)
{
bt_section_t *sect;
if (map->count >= BT_MAX_SECTIONS) return NULL;
sect = &map->sect[map->count++];
sect->buf.fd = map->fd;
sect->buf.offset = es->offset;
sect->buf.need = 1;
sect->idx = idx;
sect->type = es->type;
sect->offset = es->offset;
sect->size = es->size;
sect->entry = es->ent_size;
return sect;
}
static void bt_read_maps(void)
{
int fd;
char *pt, *start;
const char *a, *b, *p;
bt_buf_t buf;
memset(bt_maps, 0, sizeof(bt_maps));
if ((fd = open("/proc/self/maps", O_RDONLY)) < 0) return;
buf.fd = fd;
buf.offset = 0;
buf.start = 0;
buf.pos = 0;
buf.need = 0;
while (bt_readl(&buf, bt_buf, sizeof(bt_buf))) {
int state = 0;
unsigned long l = 0;
unsigned long r = 0;
for (start = pt = bt_buf; pt && *pt && *pt != '\n'; pt++) {
switch (state) {
case 0:
switch (*pt) {
case '-': l = strtoul(start, NULL, 16); start = pt + 1; break;
case ' ': r = strtoul(start, NULL, 16); state = 1; break;
}
break;
case 1:
switch(*pt) {
case '/': start = pt; state = 2; break;
}
break;
}
}
if (*pt == '\n') *pt = '\0';
if (state == 2) {
if (bt_map_count < BT_MAX_SECTIONS) {
bt_map_t *map = &bt_maps[bt_map_count];
map->fd = -1;
map->lower = l;
map->upper = r;
map->name = bt_strdup(start);
if (bt_map_count
&& !strcmp(bt_maps[bt_map_count-1].name, map->name))
map->seq = bt_maps[bt_map_count-1].seq +1;
for (a = p = map->name; p && *p; p++) if (*p == '/') a = p+1;
for (b = p = bt_program_name; p && *p; p++)
if (*p == '/') b = p+1;
if (!strcmp(a, b)) map->this = 1;
++bt_map_count;
}
}
}
close(fd);
}
static void bt_open_map(bt_map_t *map)
{
int i;
int seq;
elf_header_t eh;
elf_section_t es;
elf_program_t ep;
int str = 0;
static char buf[BT_BUF_SIZE]; /* not on stack */
union {
elf_byte c[4];
elf_word w;
} magic = { { '\x7f', 'E', 'L', 'F' } };
if (map->open) return;
++map->open;
if ((map->fd = open(map->name, O_RDONLY)) < 0) return;
map->buf.fd = map->fd;
map->buf.offset = 0;
map->buf.need = 1;
memset(&eh, 0, sizeof(eh));
bt_seek(&map->buf, 0);
bt_read(&map->buf, &eh, sizeof(eh));
if (eh.magic != magic.w /* Not ELF magic */
|| eh.class != 1 /* Not 32-bit */
|| eh.data != 1 /* Not little endian */
|| eh.ver != 1 /* Not ELF version 1 */
|| eh.version != 1) { /* Not ELF version 1 */
close(map->fd);
map->fd = -1;
return;
}
for (i = 0; i < eh.shNum; i++) {
bt_seek(&map->buf, eh.shOff + i * eh.shEntSize);
bt_read(&map->buf, &es, sizeof(es));
switch (es.type) {
case 2: /* SYMTAB */
map->sym = bt_add_section(map, &es, i);
str = es.link;
break;
case 3: /* STRTAB */
if (i && i == eh.shStrIdx)
map->sh_str = bt_add_section(map, &es, i);
break;
}
}
if (!map->sym || !map->sh_str) { /* stripped? */
close(map->fd);
map->fd = -1;
return;
}
for (i = 0; i < eh.shNum; i++) {
bt_seek(&map->buf, eh.shOff + i * eh.shEntSize);
bt_read(&map->buf, &es, sizeof(es));
bt_seek(&map->sh_str->buf, es.name);
bt_reads(&map->sh_str->buf, buf, sizeof(buf));
if (i == map->sym->idx) map->sym->name = bt_strdup(buf);
else if (i == map->sh_str->idx) map->sh_str->name = bt_strdup(buf);
else if (i == str) {
map->str = bt_add_section(map, &es, i);
map->str->name = bt_strdup(buf);
} else if (!strncmp(buf, ".debug_", 7)) {
bt_section_t *sect = bt_add_section(map, &es, i);
sect->name = bt_strdup(buf);
}
}
#if BT_ARGS_DEBUG_SECTION_NAMES
/* Print section names */
for (i = 0; i < map->count; i++)
bt_printf("%d %2d %s\n", i, map->sect[i].idx, map->sect[i].name);
#endif
/* Compute the relocation offset */
for (i = 0, seq = 0; i < eh.phNum; i++) {
bt_seek(&map->buf, eh.phOff + i * eh.phEntSize);
bt_read(&map->buf, &ep, sizeof(ep));
switch (ep.type) {
case 1: /* LOAD */
if (seq++ == map->seq)
map->reloc = map->lower - (ep.vaddr - ep.offset);
break;
}
}
}
static const char *bt_find_symbol(bt_map_t *map, unsigned long addr,
unsigned long *offset)
{
elf_symbol_t sym;
unsigned long pos;
static char buf[BT_BUF_SIZE]; /* not on stack */
*offset = 0;
if (!map) return NULL;
if (map->fd < 0) return NULL;
bt_seek(&map->sym->buf, 0);
for (pos = 0; pos < map->sym->size; pos += map->sym->entry) {
bt_read(&map->sym->buf, &sym, sizeof(sym));
if (sym.name
&& sym.shIdx
&& addr - map->reloc >= sym.value
&& addr - map->reloc < sym.value + sym.size) {
if (sym.name < map->str->size) {
memset(buf, 0, sizeof(buf));
bt_seek(&map->str->buf, sym.name);
bt_reads(&map->str->buf, buf, sizeof(buf));
*offset = addr - sym.value - map->reloc;
return buf;
}
}
}
return NULL;
}
static bt_map_t *bt_find_map(unsigned long addr)
{
int i;
bt_map_t *map;
for (i = 0; i < bt_map_count; i++) {
map = &bt_maps[i];
if (addr >= map->lower && addr <= map->upper) {
bt_open_map(map);
return map;
}
}
return NULL;
}
#endif
/************************************************************************/
/***** End of ELF symbol table functions ********************************/
/************************************************************************/
/************************************************************************/
/***** Start of DWARF2 file/line functions ******************************/
/************************************************************************/
#if !BT_LINES
#if BT_STACK
static const char *bt_find_line(void *map, unsigned long addr, int *line)
{
if (line) *line = 0;
return NULL;
}
#endif
#else
static bt_section_t *bt_find_section(bt_map_t *map, const char *name)
{
int i;
if (!map) return NULL;
for (i = 0; i < map->count; i++) {
bt_section_t *s = &map->sect[i];
if (s->name && !strcmp(s->name, name)) return s;
}
return NULL;
}
static const char *bt_find_line(bt_map_t *map, unsigned long addr, int *line)
{
dwarf_header_t dh;
dwarf_state_t ds;
int i;
int state;
unsigned long pos;
bt_section_t *s;
*line = 0;
if (!(s = bt_find_section(map, ".debug_line"))) return NULL;
bt_seek(&s->buf, 0);
for (pos =0; pos < s->size;) {
dh.total_length = bt_read4(&s->buf);
dh.version = bt_read2(&s->buf);
dh.prologue_length = bt_read4(&s->buf);
dh.mil = bt_read1(&s->buf);
dh.default_is_stmt = bt_read1(&s->buf);
dh.line_base = bt_read1(&s->buf);
dh.line_range = bt_read1(&s->buf);
dh.opcode_base = bt_read1(&s->buf);
pos += dh.total_length + 4;
for (i = 1; i < dh.opcode_base; i++) bt_read1(&s->buf);
for (state = 0; state != 3 && bt_tell(&s->buf) < pos;) {
ubyte x = bt_read1(&s->buf);
switch (state) {
case 0:
if (!x) state = 3; /* First byte is a null */
else state = 1; /* Have no 0s */
break;
case 1:
if (!x) state = 2; /* Have one 0 */
break;
case 2:
if (!x) state = 3; /* Have two 0s */
else state = 1; /* Have no 0s */
break;
}
}
if (state != 3) return NULL; /* Punt if we can't parse */
bt_file_count = 0;
bt_file_end = bt_file_space;
for (state = 0; state != 2 && bt_tell(&s->buf) < pos;) {
ubyte x = bt_read1(&s->buf);
switch (state) {
case 0:
bt_files[bt_file_count++] = bt_file_end;
*bt_file_end++ = x;
if (!x) state = 2; /* Finished */
else state = 1; /* Entry */
break;
case 1:
*bt_file_end++ = x;
if (!x) {
/* unsigned long dirIdx = */ bt_read128(&s->buf);
/* unsigned long t = */ bt_read128(&s->buf);
/* unsigned long length = */ bt_read128(&s->buf);
state = 0;
}
break;
}
}
/* Maintain a statistic */
if (bt_file_end - bt_file_space > bt_file_max)
bt_file_max = bt_file_end - bt_file_space;
#define LINE(new) do { ds.line = (new); } while (0)
#define FILE(new) do { ds.file = (new); } while (0)
#define ADDRESS(new) \
do { \
unsigned long n = (new); /* evaluate once */ \
if (ds.address && addr - map->reloc >= ds.address \
&& addr - map->reloc < n) { \
*line = ds.line; \
return bt_files[ds.file-1]; \
} \
ds.address = n; \
} while (0)
for (state = 0; state >= 0 && bt_tell(&s->buf) < pos;) {
ubyte x = bt_read1(&s->buf);
switch (state) {
case 0:
ds.address = 0;
ds.line = 1;
ds.file = 1;
state = 1;
/* Fall through */
case 1:
switch (x) {
case 0: /* Extended */
bt_read1(&s->buf); /* length */
x = bt_read1(&s->buf);
switch (x) {
case DW_LNE_end_sequence:
state = 0;
break;
case DW_LNE_set_address:
ADDRESS(bt_read4(&s->buf));
break;
case DW_LNE_define_file:
state = -1; /* Punt -- gcc doesn't seem to use
* this, and it's painful to
* decode */
bt_printf("Found DW_LNE_define_file\n");
break;
}
break;
case DW_LNS_copy: break;
case DW_LNS_advance_pc: ADDRESS(ds.address
+ (bt_read128(&s->buf)
* dh.mil));
break;
case DW_LNS_advance_line: LINE(ds.line
+ bt_read128s(&s->buf)); break;
case DW_LNS_set_file: FILE(bt_read128(&s->buf)); break;
case DW_LNS_set_column: bt_read128(&s->buf); break;
case DW_LNS_negate_stmt: break;
case DW_LNS_set_basic_block: break;
case DW_LNS_const_add_pc: ADDRESS(ds.address
+ ((255 - dh.opcode_base)
/ dh.line_range));
break;
case DW_LNS_fixed_advance_pc: ADDRESS(ds.address
+ bt_read2(&s->buf));
break;
default:
x -= dh.opcode_base;
ADDRESS(ds.address + x / dh.line_range);
LINE(ds.line + dh.line_base + (x % dh.line_range));
break;
}
break;
}
}
}
return NULL;
}
#endif
/************************************************************************/
/***** End of DWARF2 file/line functions ********************************/
/************************************************************************/
/************************************************************************/
/***** Start of DWARF2 arg/frame functions ******************************/
/************************************************************************/
#if !BT_ARGS
#if BT_STACK
static int bt_find_args(void *map, unsigned long target,
const char *symbol, int maxargs, void *argv)
{
return 0;
}
static unsigned long bt_restore_registers(unsigned long frame,
bt_reg_t *reg, bt_regvals_t *regvals)
{
return frame;
}
static int bt_read_frame(bt_map_t *map, bt_reg_t *reg, unsigned long target)
{
return -1;
}
#endif
#else
static void bt_read_instructions(bt_buf_t *buf, dwarf_cie_t *cie,
unsigned long end, bt_reg_t *reg,
unsigned long target)
{
unsigned char ins;
unsigned long value, offset, rgstr;
unsigned long delta = 0;
bt_type_t type;
int i;
while (bt_tell(buf) <= end) {
ins = bt_read1(buf);
#if BT_ARGS_DEBUG_RI
bt_printf("ins = 0x%02x (%lu <= %lu)\n", ins, bt_tell(buf), end);
#endif
if ((ins & 0xc0) == DW_CFA_advance_loc) {
delta = ins & 0x3f;
goto check_loc;
}
if ((ins & 0xc0) == DW_CFA_offset) {
rgstr = ins & 0x3f;
offset = bt_read128(buf);
goto do_offset;
}
if ((ins & 0xc0) == DW_CFA_restore) {
rgstr = ins & 0x3f;
goto do_rstr;
}
switch (ins) {
case DW_CFA_set_loc: reg->loc = bt_read128(buf);break;
case DW_CFA_advance_loc1: delta = bt_read1(buf); goto check_loc;
case DW_CFA_advance_loc2: delta = bt_read2(buf); goto check_loc;
case DW_CFA_advance_loc4: delta = bt_read4(buf); goto check_loc;
case DW_CFA_offset_extended: rgstr = bt_read128(buf);
offset = bt_read128(buf);goto do_offset;
case DW_CFA_restore_extended:rgstr = bt_read128(buf);goto do_rstr;
case DW_CFA_undefined: type = bt_undefined; goto set_type;
case DW_CFA_same_value: type = bt_same_value; goto set_type;
case DW_CFA_register:
rgstr = bt_read128(buf);
value = bt_read128(buf);
reg->r[rgstr][reg->depth].type = bt_register;
reg->r[rgstr][reg->depth].value = value;
break;
case DW_CFA_remember_state:
if (++reg->depth >= BT_MAX_DEPTH) {
reg->invalid = 1;
return;
}
for (i = 0; i < BT_MAX_REG; i++) {
reg->r[i][reg->depth].type = reg->r[i][reg->depth-1].type;
reg->r[i][reg->depth].value = reg->r[i][reg->depth-1].value;
}
break;
case DW_CFA_restore_state:
if (--reg->depth < 1) {
reg->invalid = 1;
return;
}
break;
case DW_CFA_def_cfa:
reg->reg = bt_read128(buf);
reg->offset = bt_read128(buf);
#if BT_ARGS_DEBUG_RI
bt_printf("def_cfa: %ld/%d 0x%08lx\n",
reg->reg, bt_regmap[reg->reg], reg->offset);
#endif
break;
case DW_CFA_def_cfa_register:
reg->reg = bt_read128(buf);
#if BT_ARGS_DEBUG_RI
bt_printf("def_cfa: %ld/%d 0x%08lx\n",
reg->reg, bt_regmap[reg->reg], reg->offset);
#endif
break;
case DW_CFA_def_cfa_offset:
reg->offset = bt_read128(buf);
#if BT_ARGS_DEBUG_RI
bt_printf("def_cfa: %ld/%d 0x%08lx\n",
reg->reg, bt_regmap[reg->reg], reg->offset);
#endif
break;
case 0x2e:
reg->size = bt_read128(buf);
break;
case DW_CFA_nop:
case DW_CFA_lo_user:
case DW_CFA_hi_user:
default:
#if BT_ARGS_DEBUG_RI
bt_printf("? ins = 0x%02x\n", ins);
#endif
break;
}
continue;
check_loc:
value = reg->loc + delta * cie->caf;
if (target && target >= reg->loc && target < value) return;
#if BT_ARGS_DEBUG_RI
bt_printf("advance_loc: 0x%08lx -> 0x%08lx (delta = %lu)\n",
reg->loc, value, delta);
#endif
reg->loc = value;
continue;
do_offset:
reg->r[rgstr][reg->depth].type = bt_offset;
reg->r[rgstr][reg->depth].value = offset * cie->daf;
#if BT_ARGS_DEBUG_RI
bt_printf("offset: r%lu/%d at type=%d cfa%+ld\n",
rgstr, bt_regmap[rgstr], bt_offset, offset * cie->daf);
#endif
continue;
do_rstr:
reg->r[rgstr][reg->depth].type = reg->r[rgstr][0].type;
reg->r[rgstr][reg->depth].value = reg->r[rgstr][0].value;
continue;
set_type:
rgstr = bt_read128(buf);
reg->r[rgstr][reg->depth].type = type;
continue;
}
}
static void bt_read_cie(bt_buf_t *buf, unsigned long start,
dwarf_cie_t *cie, bt_reg_t *reg,
unsigned long initial_location)
{
unsigned long previous = bt_tell(buf);
int i;
bt_seek(buf, start);
memset(reg, 0, sizeof(*reg));
reg->reg = DW_MAX_REG;
reg->loc = initial_location;
cie->length = bt_read4(buf);
cie->cie_id = bt_read4(buf);
cie->version = bt_read1(buf);
bt_reads(buf, (char *)cie->augmentation, sizeof(cie->augmentation));
cie->caf = bt_read128(buf);
cie->daf = bt_read128s(buf);
cie->rar = bt_read1(buf);
bt_read_instructions(buf, cie, start + cie->length + 4, reg, 0);
for (i = 0; i < BT_MAX_REG; i++) {
reg->depth = 1;
reg->r[i][1].type = reg->r[i][0].type;
reg->r[i][1].value = reg->r[i][0].value;
}
#if BT_ARGS_DEBUG_RC
bt_printf("RC: caf=%lu daf=%ld rar=%d\n", cie->caf, cie->daf, cie->rar);
#endif
bt_seek(buf, previous);
}
static int bt_read_frame(bt_map_t *map, bt_reg_t *reg, unsigned long target)
{
bt_section_t *frame = bt_find_section(map, ".debug_frame");
unsigned long pos = 0;
dwarf_fde_t fde;
dwarf_cie_t cie;
#if BT_ARGS_DEBUG_RF
int i;
bt_printf("RF: map=%p target=0x%08lx frame=%p\n",
(void *)map, target, (void *)frame);
#endif
if (!frame) return -1;
bt_seek(&frame->buf, 0);
while (bt_tell(&frame->buf) < frame->size && pos < frame->size) {
bt_seek(&frame->buf, pos);
fde.length = bt_read4(&frame->buf);
fde.cie_pointer = bt_read4(&frame->buf);
pos += fde.length + 4;
if (fde.cie_pointer == 0xffffffff) continue; /* CIE */
fde.initial_location = bt_read4(&frame->buf);
fde.address_range = bt_read4(&frame->buf);
if (target < fde.initial_location
|| target >= fde.initial_location + fde.address_range) continue;
#if BT_ARGS_DEBUG_RF
bt_printf("RF: start=0x%08lx t=0x%08lx init=0x%08lx range=0x%08lx\n",
fde.cie_pointer, target, fde.initial_location,
fde.address_range);
bt_printf("RF: bt_tell=%lu size=%lu pos=%lu\n",
bt_tell(&frame->buf), frame->size, pos);
#endif
bt_read_cie(&frame->buf, fde.cie_pointer, &cie, reg,
fde.initial_location);
bt_read_instructions(&frame->buf, &cie, pos, reg, target);
#if BT_ARGS_DEBUG_RF
bt_printf("RF: 0x%08lx 0x%08lx..0x%08lx %d rar=%d\n", target,
fde.initial_location,
fde.initial_location + fde.address_range,
reg->depth, cie.rar);
for (i = 0; i < BT_MAX_REG; i++) {
switch (reg->r[i][reg->depth].type) {
case bt_offset:
bt_printf("RF: r[%2d] cfa%+ld\n",
i, reg->r[i][reg->depth].value);
break;
case bt_same_value:
case bt_register:
bt_printf("RF: r[%2d]=r[%ld]\n",
i, reg->r[i][reg->depth].value);
break;
case bt_undefined:
break;
}
}
break;
#endif
}
return cie.rar;
}
static const char *bt_read_form(dwarf_form_t form, bt_buf_t *info,
bt_buf_t *str, unsigned long b,
unsigned long *value)
{
static char buf[BT_BUF_SIZE]; /* not on stack */
unsigned long offset = 0;
unsigned long length = 0;
unsigned long i;
unsigned char byte;
*value = 0;
switch (form) {
case DW_FORM_addr: *value = bt_read4(info); return NULL;
case DW_FORM_block2: length = bt_read2(info); goto do_block;
case DW_FORM_block4: length = bt_read4(info); goto do_block;
case DW_FORM_data2: *value = bt_read2(info); return NULL;
case DW_FORM_data4: *value = bt_read4(info); return NULL;
case DW_FORM_data8: *value = bt_read8(info); return NULL;
case DW_FORM_string: goto do_string;
case DW_FORM_block: length = bt_read128(info); goto do_block;
case DW_FORM_block1: length = bt_read1(info); goto do_block;
case DW_FORM_data1: *value = bt_read1(info); return NULL;
case DW_FORM_flag: *value = bt_read1(info); return NULL;
case DW_FORM_sdata: *value = bt_read128s(info); return NULL;
case DW_FORM_strp: offset = bt_read4(info); goto do_strp;
case DW_FORM_udata: *value = bt_read128(info); return NULL;
case DW_FORM_ref_addr: *value = b+bt_read128(info); return NULL;
case DW_FORM_ref1: *value = b+bt_read1(info); return NULL;
case DW_FORM_ref2: *value = b+bt_read2(info); return NULL;
case DW_FORM_ref4: *value = b+bt_read4(info); return NULL;
case DW_FORM_ref8: *value = b+bt_read8(info); return NULL;
case DW_FORM_ref_udata: *value = bt_read128(info); return NULL;
case DW_FORM_indirect:
form = bt_read128(info);
return bt_read_form(form, info, str, b, value);
}
return NULL;
do_block:
for (i = 0; i < length; i++) {
byte = bt_read1(info);
if (i < sizeof(buf)) buf[i] = byte;
}
*value = length;
return buf;
do_string:
bt_reads(info, buf, sizeof(buf));
return buf;
do_strp:
bt_seek(str, offset);
bt_reads(str, buf, sizeof(buf));
return buf;
}
static int bt_find_abbr(unsigned long code,
bt_buf_t *abbr, unsigned long offset)
{
unsigned long acode;
dwarf_at_t a;
bt_seek(abbr, offset);
while ((acode = bt_read128(abbr))) {
if (acode == code) return 0;
do {
a = bt_read128(abbr);
bt_read128(abbr); /* dwarf_form_t */
} while (a);
}
return -1;
}
static const char *bt_find_type(bt_buf_t *info, bt_buf_t *abbr,
bt_buf_t *str, unsigned long base,
unsigned long aoffset, unsigned long offset,
bt_enc_t *e)
{
unsigned long icode;
dwarf_tag_t tag;
dwarf_at_t a;
dwarf_form_t f;
const char *string = NULL;
unsigned long value;
unsigned long new = 0;
dwarf_ate_t enc = 0;
const char *name = NULL;
unsigned long apos = bt_tell(abbr);
unsigned long ipos = bt_tell(info);
bt_seek(info, offset);
icode = bt_read128(info);
if (!icode || bt_find_abbr(icode, abbr, aoffset)) {
bt_seek(abbr, apos);
bt_seek(info, ipos);
return NULL;
}
tag = bt_read128(abbr);
bt_read1(abbr); /* unsigned long children */
while ((a = bt_read128(abbr))) {
f = bt_read128(abbr);
string = bt_read_form(f, info, str, base, &value);
switch (a) {
case DW_AT_name: name = bt_strdup(string); break;
case DW_AT_type: new = value; break;
case DW_AT_encoding: enc = value; break;
case DW_AT_byte_size: *e &= ~0x00f; *e |= (value & 0x0f); break;
case DW_AT_bit_size: *e &= ~0x0f0; *e |= (value & 0x0f) << 4; break;
case DW_AT_bit_offset: *e &= ~0xf00; *e |= (value & 0x0f) << 8; break;
default: break;
}
}
if (new) string = bt_find_type(info, abbr, str, base, aoffset, new, e);
strncpy(bt_buf, string ? string : "", sizeof(bt_buf));
bt_seek(abbr, apos);
bt_seek(info, ipos);
switch (tag) {
case DW_TAG_const_type: strcat(bt_buf, " const"); break;
case DW_TAG_volatile_type: strcat(bt_buf, " volatile"); break;
case DW_TAG_class_type: strcat(bt_buf, " class"); break;
case DW_TAG_array_type:
if (!name && !string) strncpy(bt_buf, "void", sizeof(bt_buf));
strcat(bt_buf, "[]");
break;
case DW_TAG_pointer_type:
if (!name && !string) strncpy(bt_buf, "void", sizeof(bt_buf));
strcat(bt_buf, "*");
break;
case DW_TAG_reference_type:
if (!name && !string) strncpy(bt_buf, "void", sizeof(bt_buf));
strcat(bt_buf, "&");
break;
case DW_TAG_subroutine_type:
if (!name && !string) strncpy(bt_buf, "void", sizeof(bt_buf));
strcat(bt_buf, "()");
break;
case DW_TAG_enumeration_type:
strcat(bt_buf, "enum");
if (name) strcat(bt_buf, name);
break;
case DW_TAG_structure_type:
strcat(bt_buf, "struct ");
if (name) strcat(bt_buf, name);
break;
case DW_TAG_union_type:
strcat(bt_buf, "union ");
if (name) strcat(bt_buf, name);
break;
default:
switch (enc) {
case DW_ATE_address: *e |= bt_enc_address; return name;
case DW_ATE_float: *e |= bt_enc_float; return name;
case DW_ATE_signed:
case DW_ATE_signed_char: *e |= bt_enc_signed; return name;
case DW_ATE_unsigned:
case DW_ATE_unsigned_char: *e |= bt_enc_unsigned; return name;
default: return name;
}
}
return bt_strdup(bt_buf);
}
static int bt_find_args(bt_map_t *map, unsigned long target,
const char *symbol, int maxargs, bt_arg_t *argv)
{
bt_section_t *info = bt_find_section(map, ".debug_info");
bt_section_t *abbr = bt_find_section(map, ".debug_abbrev");
bt_section_t *str = bt_find_section(map, ".debug_str");
dwarf_info_t ih;
unsigned long pos;
unsigned long icode;
#if BT_ARGS_DEBUG_FA
unsigned long children;
#endif
dwarf_tag_t tag;
dwarf_at_t a;
dwarf_form_t f;
const char *string;
const char *name = NULL;
unsigned long value, type;
int argc = 0;
int state = 0;
int symmatch = 0;
int lowmatch = 0;
int highmatch = 0;
long offset;
unsigned long rgstr;
bt_flag_t flag;
if (!info || !abbr || !str) return argc;
argv[0].name = NULL;
argv[0].type = NULL;
for (pos = 0; pos < info->size; pos = pos + ih.length + 4) {
bt_seek(&info->buf, pos);
ih.length = bt_read4(&info->buf);
ih.version = bt_read2(&info->buf);
ih.offset = bt_read4(&info->buf);
ih.bytes = bt_read1(&info->buf);
#if BT_ARGS_DEBUG_FA
bt_printf("Compilation Unit @ 0x%lx 0x%08lx %d 0x%08lx %d\n", pos,
ih.length, ih.version, ih.offset, ih.bytes);
#endif
if (ih.version != 2) continue;
while (bt_tell(&info->buf) < pos + ih.length) {
while (!(icode = bt_read128(&info->buf))
&& bt_tell(&info->buf) < pos + ih.length);
if (bt_find_abbr(icode, &abbr->buf, ih.offset)) break;
tag = bt_read128(&abbr->buf);
#if BT_ARGS_DEBUG_FA
children = bt_read1(&abbr->buf);
bt_printf("Code %lu, tag = 0x%02x children = %s\n",
icode, tag, children ? "yes" : "no");
#else
bt_read1(&abbr->buf);
#endif
if (!state) symmatch = lowmatch = highmatch = 0;
type = 0;
flag = bt_unknown;
rgstr = 0;
offset = 0;
while ((a = bt_read128(&abbr->buf))) {
f = bt_read128(&abbr->buf);
#if BT_ARGS_DEBUG_FA
bt_printf(" Attribute = 0x%02x; Form = 0x%02x\n", a, f);
#endif
string = bt_read_form(f, &info->buf, &str->buf, pos, &value);
switch (a) {
case DW_AT_frame_base:
case DW_AT_location:
if ((unsigned char)string[0] == DW_OP_fbreg) {
flag = bt_off;
offset = bt_string_read128s(string + 1);
} else if (((unsigned char)string[0]
& DW_OP_reg_mask) == DW_OP_reg_mask) {
flag = bt_reg;
rgstr = string[0] & 0x0f;
}
break;
case DW_AT_name:
if (!state && !strcmp(string, symbol)) symmatch=1;
if (state) name = bt_strdup(string);
break;
case DW_AT_low_pc:
if (!state && target >= value) lowmatch=1;
break;
case DW_AT_high_pc:
if (!state && target <= value) highmatch=1;
break;
case DW_AT_type:
type = value;
break;
case DW_AT_encoding:
case DW_AT_byte_size:
case DW_AT_bit_size:
case DW_AT_bit_offset:
break;
}
}
switch (state) {
case 0:
if (tag == DW_TAG_subprogram
&& symmatch && lowmatch && highmatch) {
state = 1;
argv[0].name = NULL;
argv[0].enc = 0;
argv[0].type = bt_find_type(&info->buf, &abbr->buf,
&str->buf, pos, ih.offset,
type, &argv[0].enc);
if (!argv[0].type || !*argv[0].type) argv[0].type = "void";
/* Should be the frame base */
argv[0].flag = flag;
argv[0].reg = rgstr;
argv[0].offset = offset;
if (!argc) argc = 1;
}
break;
case 1:
if (tag != DW_TAG_formal_parameter) return argc;
if (argc >= maxargs - 1) return argc;
if (!argc) argc = 1;
argv[argc].name = name;
argv[argc].enc = 0;
argv[argc].type = bt_find_type(&info->buf, &abbr->buf,
&str->buf, pos, ih.offset,
type, &argv[argc].enc);
argv[argc].flag = flag;
argv[argc].reg = rgstr;
argv[argc].offset = offset;
++argc;
break;
}
if ((f = bt_read128(&abbr->buf)))
bt_printf(" Zero attrib found, but form = 0x%02x\n", f);
}
}
return argc;
}
static void bt_restore_registers(unsigned long fp, bt_reg_t *reg,
bt_regvals_t *regvals)
{
int i;
unsigned long cfa = fp + reg->offset;
int esp_valid = regvals[DW_ESP].valid;
int ebp_valid = regvals[DW_EBP].valid;
if (regvals[reg->reg].valid && regvals[reg->reg].value > 0x100)
cfa = regvals[reg->reg].value + reg->offset;
if (cfa < 0x100) return;
#if BT_ARGS_DEBUG_RR
bt_printf("RR: using cfa = 0x%08lx (fp=0x%08lx offset=%lu)\n",
cfa, fp, reg->offset);
#endif
/* Fill in other registers */
for (i = 0; i < BT_MAX_REG; i++) {
switch (reg->r[i][reg->depth].type) {
case bt_undefined:
regvals[i].valid = 0;
break;
case bt_offset:
regvals[i].valid = 1;
#if BT_ARGS_DEBUG_RR
bt_printf("RR: $r%d <= cfa[%+ld]\n",
i, reg->r[i][reg->depth].value);
#endif
regvals[i].value = *(unsigned long *)(cfa
+ reg->r[i][reg->depth]
.value);
break;
case bt_register:
regvals[i].valid = 1;
regvals[i].value = regvals[reg->r[i][reg->depth].value].value;
break;
case bt_same_value:
break;
}
}
if (esp_valid) regvals[DW_ESP].valid = esp_valid;
if (ebp_valid) regvals[DW_EBP].valid = ebp_valid;
}
#endif
/************************************************************************/
/***** End of DWARF2 arg/frame functions ********************************/
/************************************************************************/
/************************************************************************/
/***** Start of stack backtrace functions *******************************/
/************************************************************************/
#if !BT_STACK
static void bt_print(int count, void *frame[], void *addr[], greg_t *ctx)
{
}
#else
static unsigned long bt_readable_flag;
static unsigned long *bt_readable_addr;
static jmp_buf bt_readable_env;
static void bt_notreadable(int sig)
{
bt_readable_flag = 0;
longjmp(bt_readable_env, 1);
}
static int bt_readable(unsigned long addr)
{
struct sigaction a, old;
unsigned long value;
if (setjmp(bt_readable_env)) return 0;
a.sa_handler = bt_notreadable;
sigemptyset(&a.sa_mask);
sigaddset(&a.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &a, &old);
bt_readable_addr = (unsigned long *)addr;
bt_readable_flag = 1;
value = *bt_readable_addr;
sigaction(SIGSEGV, &old, 0);
if (bt_readable_flag) return 1;
/* This code shouldn't ever get executed */
bt_readable_flag = value & 0xff; /* Guard dereference from optimization */
return 0;
}
static void _bt_print(int idx, unsigned long target, unsigned long fp,
bt_map_t *map, bt_reg_t *reg, bt_regvals_t *regvals,
const char *quality)
{
int j;
unsigned long offset;
const char *symbol;
const char *in;
const char *filename;
int line;
int argc;
bt_arg_t argv[10];
int indent = 15;
if (!map) {
bt_printf("#%2d 0x%08lx\n", idx, target);
return;
}
if (map->this) in = NULL;
else in = " in ";
if (!(symbol = bt_find_symbol(map, target, &offset))) {
argc = 0;
symbol = "??";
} else {
argc = bt_find_args(map, target, symbol,
sizeof(argv)/sizeof(argv[0]), argv);
#if BT_ARGS_DEBUG_PR > 1
for (j = 0; j < argc; j++) {
switch (argv[j].flag) {
case bt_off:
bt_printf("argv[%d] = %+d %s\n",
j, argv[j].offset, argv[j].name);
break;
case bt_reg:
bt_printf("argv[%d] = $%d %s\n", j, argv[j].reg, argv[j].name);
break;
default:
break;
}
}
for (j = 0; j < 10; j++) {
bt_printf("$%d = 0x%08lx %s\n", j, (unsigned long)regvals[j].value,
regvals[j].valid ? "ok" : "");
}
if (fp > 0x100) for (j = -8; j <= 40; j+= 4) {
bt_printf("fp%+d = 0x%08lx\n", j, *(unsigned long *)(fp + j));
}
#endif
}
#ifdef XAPPGROUP
bt_printf("#%2d 0x%08lx ", idx, target);
if (argc && argv[0].type) bt_printf("%s ", argv[0].type);
bt_printf("%s", symbol);
if (argc > 1) {
bt_printf("(");
#else
indent = bt_printf("#%2d 0x%08lx ", idx, target);
if (argc && argv[0].type) indent += bt_printf("%s ", argv[0].type);
indent += bt_printf("%s", symbol);
if (argc > 1) {
indent += bt_printf("(");
#endif
for (j = 1; j < argc; j++) {
union {
void *addr;
unsigned long long ull;
signed long long sll;
unsigned long ul;
signed long sl;
unsigned short us;
signed short ss;
unsigned char ub;
signed char sb;
float f;
double d;
} value;
int valid = 0;
int origsize = argv[j].enc & 0x00f;
value.ull = 0;
if (j > 1) bt_printf(",\n%*.*s", indent, indent, "");
bt_printf("%s %s", argv[j].type, argv[j].name);
if (strchr(argv[j].type, '*')) {
argv[j].enc &= ~0x00f;
argv[j].enc |= sizeof(void *);
}
switch (argv[j].flag) {
case bt_off:
if (fp) {
unsigned long addr = fp + argv[j].offset;
if (bt_readable(addr)) {
valid = 1;
switch (argv[j].enc & 0x00f) {
case 1: value.ub = *(unsigned char *)addr; break;
case 2: value.us = *(unsigned short *)addr; break;
case 8: value.ull = *(unsigned long long *)addr;break;
default: value.ul = *(unsigned long *)addr; break;
}
}
}
break;
case bt_reg:
if (regvals[argv[j].reg].valid) {
int r = argv[j].reg;
valid = 1;
bt_printf("=$%d", argv[j].reg);
switch (argv[j].enc & 0x00f) {
case 1: value.ub = regvals[r].value & 0x00ff; break;
case 2: value.us = regvals[r].value & 0xffff; break;
case 8: valid = 0; break;
default: value.ul = regvals[r].value; break;
}
}
break;
case bt_unknown:
break;
}
if (!valid) continue;
if (strchr(argv[j].type, '*')) {
const char *sym = bt_find_symbol(map, value.ul, &offset);
bt_printf("=0x%08lx%s%s%s", value.ul,
sym ? " <" : "",
sym ? sym : "",
sym ? ">" : "");
if (origsize == 1
&& strchr(argv[j].type,'*') == strrchr(argv[j].type,'*')) {
const char *pt = (const char *)value.ul;
int i;
if (bt_readable(value.ul)) {
bt_printf(" \"");
for (i = 0; i < 20 && pt && *pt; i++, ++pt) {
if (*pt >= 32 && *pt < 127) bt_printf("%c", *pt);
else bt_printf(".");
}
if (pt && *pt) bt_printf("...");
bt_printf("\"");
} else {
bt_printf(" ");
}
}
} else if (argv[j].enc & bt_enc_float) {
if ((argv[j].enc & 0x00f) == 8) bt_printf("=%f", value.d);
else bt_printf("=%f", value.f);
} else if (argv[j].enc & bt_enc_signed) {
switch (argv[j].enc & 0x00f) {
case 1: bt_printf("=%d", value.sb); break;
case 2: bt_printf("=%d", value.ss); break;
case 8: bt_printf("=%lld", value.sll); break;
default: bt_printf("=%ld", value.sl); break;
}
} else {
switch (argv[j].enc & 0x00f) {
case 1: bt_printf("=%u", value.ub); break;
case 2: bt_printf("=%u", value.us); break;
case 8: bt_printf("=%llu", value.ull); break;
default: bt_printf("=%lu", value.ul); break;
}
}
if ((argv[j].enc & 0x00f) == 1
&& value.ul >= 32 && value.ul < 127) {
bt_printf(" '%c'", (char)value.ub);
}
}
bt_printf(")");
} else if (offset) bt_printf("+0x%lx", offset);
if (!(filename = bt_find_line(map, target, &line)))
bt_printf("%s%s [%s]\n", in ? in : "", in ? map->name : "", quality);
else
bt_printf(" [%s] at %s:%d\n", quality, filename, line);
}
static void bt_print(int count, void *frame[], void *addr[], greg_t *ctx)
{
int i;
bt_map_t *map;
static bt_reg_t reg; /* not on stack; zero'd in bt_read_cie */
bt_regvals_t regvals[BT_MAX_REG];
int rar = -1;
unsigned long ra = 0;
unsigned long fp = 0;
int idx = 0;
char q[3] = {0, 0, 0}; /* quality indicator */
long fpoffset;
bt_printf("Printing stack backtrace for %s (please wait)\n",
bt_program_name);
bt_printf("(t = trampoline, i = ip, r = ra; f = fp, ? = frame)\n");
/* set up first frame of registers */
memset(®vals, 0, sizeof(regvals));
if (ctx) {
int mapped = sizeof(bt_regmap)/sizeof(bt_regmap[0]);
if (mapped > BT_MAX_REG) mapped = BT_MAX_REG;
for (i = 0; i < mapped; i++) {
regvals[i].valid = 1;
regvals[i].value = ctx[bt_regmap[i]];
}
}
for (i = 0; i < count; i++) {
/* At this point, we have an RA and
* registers that are current. */
if (!ra) {
unsigned long eip = (regvals[DW_EIP].valid
? regvals[DW_EIP].value
: 0);
ra = eip;
q[0] = bt_check(ra) ? 't' : 'i';
} else {
q[0] = 'r';
}
if (!ra) return; /* Cannot continue if no RA */
map = bt_find_map(ra);
rar = bt_read_frame(map, ®, ra);
fpoffset = 0;
if (reg.reg < DW_MAX_REG && regvals[reg.reg].value
&& regvals[reg.reg].valid) {
fp = regvals[reg.reg].value;
if (reg.reg == DW_ESP) {
fpoffset = reg.size;
bt_printf("sz=%lu offset=%lu fpoffset=%ld\n",
reg.size, reg.offset, fpoffset);
}
q[1] = 'f';
} else {
fp = (unsigned long)frame[i+1];
q[1] = '?';
}
if (i && !fp) return; /* Probably the end of the stack */
#if BT_ARGS_DEBUG_PR
bt_printf("\nPR: map=0x%08lx ra=0x%08lx sz=%ld fp=0x%08lx+%ld"
" frame[%d]=%p frame[%d]=%p r%lu=0x%08lx %d +%lu rar=%d\n",
(unsigned long)map, ra, reg.size, fp, fpoffset,
i+1, frame[i+1], i, frame[i],
reg.reg,
reg.reg < DW_MAX_REG
? (unsigned long)regvals[reg.reg].value
: 0,
reg.reg < DW_MAX_REG ? regvals[reg.reg].valid : 0,
reg.offset, rar);
#endif
_bt_print(idx++, ra, fp + fpoffset, map, ®, regvals, q);
/* Unwind to next frame */
bt_restore_registers(fp, ®, regvals);
if (rar > -1 && rar <= DW_MAX_REG && regvals[rar].valid)
ra = regvals[rar].value;
else
ra = 0;
}
}
#endif
/************************************************************************/
/***** End of stack backtrace functions *********************************/
/************************************************************************/
/************************************************************************/
/***** Start of generic functions ***************************************/
/************************************************************************/
static const char *bt_signal_name(int sig)
{
switch (sig) {
case SIGHUP: return "SIGHUP";
case SIGINT: return "SIGINT";
case SIGQUIT: return "SIGQUIT";
case SIGILL: return "SIGILL";
case SIGTRAP: return "SIGTRAP";
case SIGABRT: return "SIGABRT";
case SIGBUS: return "SIGBUS";
case SIGFPE: return "SIGFPE";
case SIGKILL: return "SIGKILL";
case SIGUSR1: return "SIGUSR1";
case SIGSEGV: return "SIGSEGV";
case SIGUSR2: return "SIGUSR2";
case SIGPIPE: return "SIGPIPE";
case SIGALRM: return "SIGALRM";
case SIGTERM: return "SIGTERM";
case SIGSTKFLT: return "SIGSTKFLT";
case SIGCHLD: return "SIGCHLD";
case SIGCONT: return "SIGCONT";
case SIGSTOP: return "SIGSTOP";
case SIGTSTP: return "SIGTSTP";
case SIGTTIN: return "SIGTTIN";
case SIGTTOU: return "SIGTTOU";
case SIGURG: return "SIGURG";
case SIGXCPU: return "SIGXCPU";
case SIGXFSZ: return "SIGXFSZ";
case SIGVTALRM: return "SIGVTALRM";
case SIGPROF: return "SIGPROF";
case SIGWINCH: return "SIGWINCH";
case SIGIO: return "SIGIO";
case SIGPWR: return "SIGPWR";
case SIGSYS: return "SIGSYS";
}
return NULL;
}
static const char *bt_signal_description(siginfo_t *info)
{
static char buf[64];
if (!info) return "";
switch (info->si_code) {
case SI_KERNEL: return "kernel";
case SI_QUEUE: return "sigqueue";
case SI_TIMER: return "timer expired";
case SI_MESGQ: return "real time mesq state changed";
case SI_ASYNCIO: return "AIO completed";
case SI_SIGIO: return "queued SIGIO";
}
switch (info->si_signo) {
case SIGILL:
switch (info->si_code) {
case ILL_ILLOPC: return "illegal opcode";
case ILL_ILLOPN: return "illegal operand";
case ILL_ILLADR: return "illegal addressing mode";
case ILL_ILLTRP: return "illegal trap";
case ILL_PRVOPC: return "privileged opcode";
case ILL_PRVREG: return "privileged register";
case ILL_COPROC: return "coprocessor error";
case ILL_BADSTK: return "internal stack error";
}
break;
case SIGFPE:
switch (info->si_code) {
case FPE_INTDIV: return "integer divide by zero";
case FPE_INTOVF: return "integer overflow";
case FPE_FLTDIV: return "floating point divide by zero";
case FPE_FLTOVF: return "floating point overflow";
case FPE_FLTUND: return "floating point underflow";
case FPE_FLTRES: return "floating point inexact result";
case FPE_FLTINV: return "floating point invalid operation";
case FPE_FLTSUB: return "subscript out of range";
}
break;
case SIGSEGV:
switch (info->si_code) {
case SEGV_MAPERR: return "address not mapped to object";
case SEGV_ACCERR: return "invalid perm. for mapped object";
}
break;
case SIGBUS:
switch (info->si_code) {
case BUS_ADRALN: return "invalid address alignment";
case BUS_ADRERR: return "non-existent physical address";
case BUS_OBJERR: return "object specific hardware error";
}
break;
case SIGTRAP:
switch (info->si_code) {
case TRAP_BRKPT: return "process breakpoint";
case TRAP_TRACE: return "process trace trap";
}
break;
case SIGCHLD:
switch (info->si_code) {
case CLD_EXITED: return "child has exited";
case CLD_KILLED: return "child was killed";
case CLD_DUMPED: return "child terminated abnormally";
case CLD_TRAPPED: return "traced child has trapped";
case CLD_STOPPED: return "child has stopped";
case CLD_CONTINUED: return "stopped child has continued";
}
break;
case SIGPOLL:
switch (info->si_code) {
case POLL_IN: return "data input available";
case POLL_OUT: return "output buffers available";
case POLL_MSG: return "input message available";
case POLL_ERR: return "I/O error";
case POLL_PRI: return "high priority input available";
case POLL_HUP: return "device disconnected";
}
break;
}
if (info->si_code) {
sprintf(buf, "SI%d", info->si_code);
return buf;
}
return NULL;
}
void bt_init(const char *program_name)
{
static int initialized = 0;
if (initialized++) return;
#if !BT_SYMBOLS
bt_program_name = program_name;
#else
if (program_name) bt_program_name = bt_strdup(program_name);
bt_read_maps();
#endif
}
void bt_backtrace(const char *program_name)
{
#if BT_STACK
int count = 0;
void *addr[32];
void *frame[32];
ucontext_t uc = { 0, };
GETSTACK;
bt_init(program_name);
if (!getcontext(&uc)) {
bt_print(count, frame, addr, uc.uc_mcontext.gregs);
} else {
bt_printf("getcontext() failed\n");
}
#endif
}
static void bt_handler(int sig, siginfo_t *info, void *context)
{
int count = 0;
void *addr[32];
void *frame[32];
int fault_valid = 0;
ucontext_t *uc = context;
static int reentrant = 0;
const char *name, *desc, *msg;
unsigned long fault = 0;
if (reentrant++) {
bt_printf("%s called twice\n", __func__);
bt_abort;
}
GETSTACK;
bt_init(NULL);
switch (sig) {
case SIGILL:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
if (info) {
fault_valid = 1;
fault = (unsigned long)info->si_addr;
}
break;
}
name = bt_signal_name(sig);
desc = bt_signal_description(info);
msg = info ? strerror(info->si_errno) : "";
if (info && info->si_errno) {
bt_printf("Signal %d%s%s%s%s%s, errno = %d%s%s%s\n", sig,
name ? " (" : "", name ? name : "", name ? ")" : "",
desc ? ": " : "", desc ? desc : "",
info->si_errno,
msg ? " (" : "", msg ? msg : "", msg ? ")" : "");
} else {
bt_printf("Signal %d%s%s%s%s%s\n", sig,
name ? " (" : "", name ? name : "", name ? ")" : "",
desc ? ": " : "", desc ? desc : "");
}
if (fault_valid) {
unsigned long offset;
bt_map_t *map = bt_find_map(fault);
const char *symbol = bt_find_symbol(map, fault, &offset);
if (symbol)
bt_printf("Fault: 0x%08lx (%s+0x%lx)\n", fault, symbol, offset);
else
bt_printf("Fault: 0x%08lx\n", fault);
}
bt_print(count, frame, addr, uc ? uc->uc_mcontext.gregs : 0);
#if BT_LINES
bt_printf("Terminating (%d/%d file; %d/%d name; %luk %luk+%luk=%luk)\n",
bt_file_max, BT_FILE_SPACE,
(int)(bt_name_end - bt_name_space), BT_NAME_SPACE,
bt_block/1024, bt_physical/1024, bt_logical/1024,
(bt_physical+bt_logical)/1024);
#elif BT_SYMBOLS
bt_printf("Terminating (%d/%d name; %lu %lu+%lu=%lu)\n",
(int)(bt_name_end - bt_name_space), BT_NAME_SPACE,
bt_block/1024, bt_physical/1024, bt_logical/1024,
(bt_physical+bt_logical)/1024);
#else
bt_printf("Terminating\n");
#endif
bt_abort;
}
void bt_signal(int sig, const char *program_name)
{
int def[] = { SIGHUP, SIGQUIT, SIGILL,
SIGBUS, SIGFPE, SIGSEGV, SIGTERM };
size_t i;
struct sigaction a;
if (!sig) {
for (i = 0; i < sizeof(def)/sizeof(def[0]); i++)
bt_signal(def[i], program_name);
return;
}
bt_init(program_name);
memset(&a, 0, sizeof(a));
a.sa_sigaction = bt_handler;
a.sa_flags = SA_SIGINFO;
sigemptyset(&a.sa_mask);
sigaddset(&a.sa_mask, sig);
sigaction(sig, &a, 0);
}
/************************************************************************/
/***** End of generic functions *****************************************/
/************************************************************************/
/************************************************************************/
/***** Start of demo functions ******************************************/
/************************************************************************/
#if BT_DEMO
#define BT_TEST_ARGS 1
struct x {
int y;
};
typedef int (*xyzzy)(void);
static int ffff(void)
{
return 0;
}
static int *myf(unsigned int f, float d, char c
#if BT_TEST_ARGS
,struct x ys, xyzzy ff, const char *foo
#endif
)
{
int x = 0;
#if BT_TEST_ARGS
bt_printf("foo = %p, d = %f\n", foo, d);
#endif
int *y;
*y = 1/x;
return y;
}
int main(int argc, char **argv)
{
struct x ys;
ys.y = 42;
bt_init(argv[0]);
bt_printf("argv = 0x%08lx, ffff = 0x%08lx = %lu\n",
(unsigned long)argv, (unsigned long)ffff,
(unsigned long)ffff);
bt_backtrace(argv[0]);
bt_signal(0, argv[0]);
#if 1
myf(7, 4.0, -8
#if BT_TEST_ARGS
, ys, ffff, 0 /* "string" */
#endif
);
#endif
strcpy(0, 0);
return 0;
}
#endif
/************************************************************************/
/***** End of demo functions ********************************************/
/************************************************************************/