/* xcoff.c -- Get debug data from an XCOFF file for backtraces.
- Copyright (C) 2012-2017 Free Software Foundation, Inc.
+ Copyright (C) 2012-2019 Free Software Foundation, Inc.
Adapted from elf.c.
Redistribution and use in source and binary forms, with or without
#endif /* BACKTRACE_XCOFF_SIZE != 32 */
+#define STYP_DWARF 0x10 /* DWARF debugging section. */
#define STYP_TEXT 0x20 /* Executable text (code) section. */
#define STYP_OVRFLO 0x8000 /* Line-number field overflow section. */
+#define SSUBTYP_DWINFO 0x10000 /* DWARF info section. */
+#define SSUBTYP_DWLINE 0x20000 /* DWARF line-number section. */
+#define SSUBTYP_DWARNGE 0x50000 /* DWARF aranges section. */
+#define SSUBTYP_DWABREV 0x60000 /* DWARF abbreviation section. */
+#define SSUBTYP_DWSTR 0x70000 /* DWARF strings section. */
+
/* XCOFF symbol. */
#define SYMNMLEN 8
size_t count;
};
-/* Map a single PC value to a file/function/line. */
+/* A growable vector of functions information. */
-struct xcoff_line
+struct xcoff_func
{
/* PC. */
uintptr_t pc;
- /* File name. Many entries in the array are expected to point to
- the same file name. */
- const char *filename;
+ /* The size of the function. */
+ size_t size;
/* Function name. */
- const char *function;
- /* Line number. */
- int lineno;
+ const char *name;
+ /* File name. */
+ const char *filename;
+ /* Pointer to first lnno entry. */
+ uintptr_t lnnoptr;
+ /* Base address of containing section. */
+ uintptr_t sect_base;
+ /* Starting source line number. */
+ int lnno;
};
-/* A growable vector of line number information. This is used while
- reading the line numbers. */
+/* A growable vector of function information. This is used while
+ reading the function symbols. */
-struct xcoff_line_vector
+struct xcoff_func_vector
{
- /* Memory. This is an array of struct xcoff_line. */
+ /* Memory. This is an array of struct xcoff_func. */
struct backtrace_vector vec;
/* Number of valid mappings. */
size_t count;
{
/* The data for the next file we know about. */
struct xcoff_fileline_data *next;
- /* Line number information. */
- struct xcoff_line_vector vec;
+ /* Functions information. */
+ struct xcoff_func_vector func_vec;
+ /* Include files information. */
+ struct xcoff_incl_vector incl_vec;
+ /* Line numbers information. */
+ const unsigned char *linenos;
+ size_t linenos_size;
+ uint64_t lnnoptr0;
+ /* Loader address. */
+ uintptr_t base_address;
+};
+
+/* An index of DWARF sections we care about. */
+
+enum dwarf_section
+{
+ DWSECT_INFO,
+ DWSECT_LINE,
+ DWSECT_ABBREV,
+ DWSECT_RANGES,
+ DWSECT_STR,
+ DWSECT_MAX
};
+/* Information we gather for the DWARF sections we care about. */
+
+struct dwsect_info
+{
+ /* Section file offset. */
+ off_t offset;
+ /* Section size. */
+ size_t size;
+ /* Section contents, after read from file. */
+ const unsigned char *data;
+};
/* A dummy callback function used when we can't find any debug info. */
{
struct xcoff_syminfo_data *edata;
struct xcoff_symbol *sym = NULL;
+ const char *name;
if (!state->threaded)
{
if (sym == NULL)
callback (data, addr, NULL, 0, 0);
else
- callback (data, addr, sym->name, sym->address, sym->size);
+ {
+ name = sym->name;
+ /* AIX prepends a '.' to function entry points, remove it. */
+ if (name && *name == '.')
+ ++name;
+ callback (data, addr, name, sym->address, sym->size);
+ }
}
/* Return the name of an XCOFF symbol. */
return 1;
}
-/* Compare struct xcoff_line for qsort. */
+/* Compare struct xcoff_func for qsort. */
static int
-xcoff_line_compare (const void *v1, const void *v2)
+xcoff_func_compare (const void *v1, const void *v2)
{
- const struct xcoff_line *ln1 = (const struct xcoff_line *) v1;
- const struct xcoff_line *ln2 = (const struct xcoff_line *) v2;
+ const struct xcoff_func *fn1 = (const struct xcoff_func *) v1;
+ const struct xcoff_func *fn2 = (const struct xcoff_func *) v2;
- if (ln1->pc < ln2->pc)
+ if (fn1->pc < fn2->pc)
return -1;
- else if (ln1->pc > ln2->pc)
+ else if (fn1->pc > fn2->pc)
return 1;
else
return 0;
}
-/* Find a PC in a line vector. We always allocate an extra entry at
- the end of the lines vector, so that this routine can safely look
- at the next entry. */
+/* Compare a PC against an xcoff_func for bsearch. */
static int
-xcoff_line_search (const void *vkey, const void *ventry)
+xcoff_func_search (const void *vkey, const void *ventry)
{
const uintptr_t *key = (const uintptr_t *) vkey;
- const struct xcoff_line *entry = (const struct xcoff_line *) ventry;
+ const struct xcoff_func *entry = (const struct xcoff_func *) ventry;
uintptr_t pc;
pc = *key;
if (pc < entry->pc)
return -1;
- else if ((entry + 1)->pc == (uintptr_t) -1 || pc >= (entry + 1)->pc)
+ else if ((entry->size == 0 && pc > entry->pc)
+ || (entry->size > 0 && pc >= entry->pc + entry->size))
+ return 1;
+ else
+ return 0;
+}
+
+/* Compare struct xcoff_incl for qsort. */
+
+static int
+xcoff_incl_compare (const void *v1, const void *v2)
+{
+ const struct xcoff_incl *in1 = (const struct xcoff_incl *) v1;
+ const struct xcoff_incl *in2 = (const struct xcoff_incl *) v2;
+
+ if (in1->begin < in2->begin)
+ return -1;
+ else if (in1->begin > in2->begin)
return 1;
else
return 0;
}
-/* Look for a PC in the line vector for one module. On success,
+/* Find a lnnoptr in an include file. */
+
+static int
+xcoff_incl_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct xcoff_incl *entry = (const struct xcoff_incl *) ventry;
+ uintptr_t lnno;
+
+ lnno = *key;
+ if (lnno < entry->begin)
+ return -1;
+ else if (lnno > entry->end)
+ return 1;
+ else
+ return 0;
+}
+
+/* Look for a PC in the function vector for one module. On success,
call CALLBACK and return whatever it returns. On error, call
ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found,
0 if not. */
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
void *data, int *found)
{
- const struct xcoff_line *ln;
+ const struct xcoff_incl *incl, *bincl;
+ const struct xcoff_func *fn;
+ const b_xcoff_lineno *lineno;
+ const unsigned char *lineptr;
const char *function;
+ const char *filename;
+ uintptr_t lnnoptr, match;
+ uint32_t lnno = 0;
*found = 1;
- ln = (struct xcoff_line *) bsearch (&pc, fdata->vec.vec.base,
- fdata->vec.count,
- sizeof (struct xcoff_line),
- xcoff_line_search);
- if (ln == NULL)
+ if ((pc & 3) != 0)
+ ++pc;
+
+ /* Find the function first. */
+ fn = ((struct xcoff_func *)
+ bsearch (&pc, fdata->func_vec.vec.base, fdata->func_vec.count,
+ sizeof (struct xcoff_func), xcoff_func_search));
+ if (fn == NULL)
{
*found = 0;
return 0;
}
- function = ln->function;
+ filename = fn->filename;
+
+ /* Find the line number next. */
+
+ /* Skip first entry that points to symtab. */
+ lnnoptr = fn->lnnoptr + LINESZ;
+ match = lnnoptr;
+
+ lineptr = fdata->linenos + (lnnoptr - fdata->lnnoptr0);
+ while (lineptr + LINESZ <= fdata->linenos + fdata->linenos_size)
+ {
+ lineno = (const b_xcoff_lineno *) lineptr;
+ if (lineno->l_lnno == 0)
+ break;
+ if (pc <= fdata->base_address + lineno->l_addr.l_paddr - fn->sect_base)
+ break;
+ match = lnnoptr;
+ lnno = lineno->l_lnno;
+
+ lnnoptr += LINESZ;
+ lineptr += LINESZ;
+ }
+
+ /* If part of a function other than the beginning comes from an
+ include file, the line numbers are absolute, rather than
+ relative to the beginning of the function. */
+ incl = ((struct xcoff_incl *)
+ bsearch (&match, fdata->incl_vec.vec.base,
+ fdata->incl_vec.count, sizeof (struct xcoff_incl),
+ xcoff_incl_search));
+ if (incl != NULL)
+ {
+ bincl = ((struct xcoff_incl *)
+ bsearch (&fn->lnnoptr, fdata->incl_vec.vec.base,
+ fdata->incl_vec.count, sizeof (struct xcoff_incl),
+ xcoff_incl_search));
+ if (bincl != NULL && strcmp (incl->filename, bincl->filename) == 0)
+ {
+ lnno += fn->lnno - 1;
+ }
+ filename = incl->filename;
+ }
+ else
+ {
+ lnno += fn->lnno - 1;
+ }
+
+ function = fn->name;
/* AIX prepends a '.' to function entry points, remove it. */
- if (*function == '.')
+ if (function != NULL && *function == '.')
++function;
- return callback (data, pc, ln->filename, ln->lineno, function);
+ return callback (data, pc, filename, lnno, function);
}
/* Return the file/line information for a PC using the XCOFF lineno
return callback (data, pc, NULL, 0, NULL);
}
-/* Add a new mapping to the vector of line mappings that we are
- building. Returns 1 on success, 0 on failure. */
-
-static int
-xcoff_add_line (struct backtrace_state *state, uintptr_t pc,
- const char *filename, const char *function, uint32_t lnno,
- backtrace_error_callback error_callback, void *data,
- struct xcoff_line_vector *vec)
-{
- struct xcoff_line *ln;
-
- ln = ((struct xcoff_line *)
- backtrace_vector_grow (state, sizeof (struct xcoff_line),
- error_callback, data, &vec->vec));
- if (ln == NULL)
- return 0;
-
- ln->pc = pc;
- ln->filename = filename;
- ln->function = function;
- ln->lineno = lnno;
-
- ++vec->count;
-
- return 1;
-}
-
-/* Add the line number entries for a function to the line vector. */
-
-static int
-xcoff_process_linenos (struct backtrace_state *state, uintptr_t base_address,
- const b_xcoff_syment *fsym, const char *filename,
- const b_xcoff_scnhdr *sects,
- const unsigned char *strtab, size_t strtab_size,
- uint32_t fcn_lnno, struct xcoff_incl_vector *vec,
- struct xcoff_line_vector *lvec,
- const unsigned char *linenos, size_t linenos_size,
- uintptr_t lnnoptr0,
- backtrace_error_callback error_callback, void *data)
-{
- const b_xcoff_auxent *aux;
- const b_xcoff_lineno *lineno;
- const unsigned char *lineptr;
- const char *function;
- struct xcoff_incl *incl = NULL;
- uintptr_t lnnoptr;
- uintptr_t pc;
- uint32_t lnno;
- int begincl;
- size_t i;
-
- aux = (const b_xcoff_auxent *) (fsym + 1);
- lnnoptr = aux->x_fcn.x_lnnoptr;
-
- if (lnnoptr < lnnoptr0 || lnnoptr + LINESZ > lnnoptr0 + linenos_size)
- return 0;
-
- function = xcoff_symname (fsym, strtab, strtab_size);
- if (function == NULL)
- return 0;
-
- /* Skip first entry that points to symtab. */
-
- lnnoptr += LINESZ;
-
- lineptr = linenos + (lnnoptr - lnnoptr0);
-
- begincl = -1;
- while (lineptr + LINESZ <= linenos + linenos_size)
- {
- lineno = (const b_xcoff_lineno *) lineptr;
-
- lnno = lineno->l_lnno;
- if (lnno == 0)
- break;
-
- /* If part of a function other than the beginning comes from an
- include file, the line numbers are absolute, rather than
- relative to the beginning of the function. */
- for (i = 0; i < vec->count; ++i)
- {
- incl = (struct xcoff_incl *) vec->vec.base + i;
- if (incl->begin <= lnnoptr && lnnoptr <= incl->end)
- break;
- }
- if (begincl == -1)
- begincl = (i < vec->count);
- if (i < vec->count)
- {
- filename = incl->filename;
- if (begincl == 1)
- lnno += fcn_lnno - 1;
- }
- else
- lnno += fcn_lnno - 1;
-
- pc = base_address + lineno->l_addr.l_paddr
- - sects[fsym->n_scnum - 1].s_paddr;
- xcoff_add_line (state, pc, filename, function, lnno, error_callback,
- data, lvec);
-
- lnnoptr += LINESZ;
- lineptr += LINESZ;
- }
-
- return 1;
-}
-
-/* Initialize the line vector info for xcoff_fileline. */
+/* Initialize the function vector info for xcoff_fileline. */
static int
xcoff_initialize_fileline (struct backtrace_state *state,
backtrace_error_callback error_callback, void *data)
{
struct xcoff_fileline_data *fdata;
- struct xcoff_incl_vector vec;
- struct xcoff_line *ln;
+ struct xcoff_func *fn;
const b_xcoff_syment *fsym;
const b_xcoff_auxent *aux;
const char *filename;
const char *name;
struct xcoff_incl *incl;
uintptr_t begin, end;
- uintptr_t lnno;
+ uintptr_t lnno, lnnoptr;
+ uint32_t fsize;
size_t i;
fdata = ((struct xcoff_fileline_data *)
error_callback, data));
if (fdata == NULL)
return 0;
-
memset (fdata, 0, sizeof *fdata);
- memset (&vec, 0, sizeof vec);
-
- /* Process include files first. */
+ fdata->base_address = base_address;
+ fdata->linenos = linenos;
+ fdata->linenos_size = linenos_size;
+ fdata->lnnoptr0 = lnnoptr0;
begin = 0;
+ filename = NULL;
+ fsym = NULL;
+ lnnoptr = 0;
+ fsize = 0;
for (i = 0; i < nsyms; ++i)
{
const b_xcoff_syment *asym = &syms[i];
end = asym->n_value;
incl = ((struct xcoff_incl *)
backtrace_vector_grow (state, sizeof (struct xcoff_incl),
- error_callback, data, &vec.vec));
+ error_callback, data,
+ &fdata->incl_vec.vec));
if (incl != NULL)
{
incl->filename = xcoff_symname (asym, strtab, strtab_size);
incl->begin = begin;
incl->end = end;
- ++vec.count;
+ ++fdata->incl_vec.count;
}
begin = 0;
break;
- }
- i += asym->n_numaux;
- }
-
- filename = NULL;
- fsym = NULL;
- for (i = 0; i < nsyms; ++i)
- {
- const b_xcoff_syment *asym = &syms[i];
-
- switch (asym->n_sclass)
- {
case C_FILE:
filename = xcoff_symname (asym, strtab, strtab_size);
if (filename == NULL)
file auxiliary entry (by convention) contains the source
file name. */
- if (asym->n_numaux > 0 && !strcmp (filename, ".file"))
+ if (asym->n_numaux > 0 && strcmp (filename, ".file") == 0)
{
aux = (const b_xcoff_auxent *) (asym + 1);
if (aux->x_file._x.x_zeroes != 0)
case C_HIDEXT:
case C_WEAKEXT:
fsym = NULL;
- if (!ISFCN (asym->n_type) || asym->n_numaux == 0)
+ lnnoptr = 0;
+ fsize = 0;
+ if (!ISFCN (asym->n_type) || asym->n_numaux == 0
+ || asym->n_scnum <= 0)
break;
if (filename == NULL)
break;
+ aux = (const b_xcoff_auxent *) (asym + 1);
+ lnnoptr = aux->x_fcn.x_lnnoptr;
+ if (lnnoptr < lnnoptr0
+ || lnnoptr + LINESZ > lnnoptr0 + linenos_size)
+ break;
+ /* x_fsize will be 0 if there is no debug information. */
+ fsize = aux->x_fcn.x_fsize;
fsym = asym;
break;
if (fsym == NULL)
break;
name = xcoff_symname (asym, strtab, strtab_size);
- if (name == NULL)
- break;
+ if (name == NULL || strcmp (name, ".bf") != 0)
+ {
+ fsym = NULL;
+ break;
+ }
aux = (const b_xcoff_auxent *) (asym + 1);
#if BACKTRACE_XCOFF_SIZE == 32
lnno = (uint32_t) aux->x_block.x_lnnohi << 16
#else
lnno = aux->x_block.x_lnno;
#endif
- if (!strcmp (name, ".bf"))
- {
- xcoff_process_linenos (state, base_address, fsym, filename,
- sects, strtab, strtab_size, lnno, &vec,
- &fdata->vec, linenos, linenos_size,
- lnnoptr0, error_callback, data);
- }
- else if (!strcmp (name, ".ef"))
- {
- fsym = NULL;
- }
+ fn = ((struct xcoff_func *)
+ backtrace_vector_grow (state, sizeof (struct xcoff_func),
+ error_callback, data,
+ &fdata->func_vec.vec));
+ if (fn == NULL)
+ break;
+ fn->name = xcoff_symname (fsym, strtab, strtab_size);
+ fn->filename = filename;
+ fn->sect_base = sects[fsym->n_scnum - 1].s_paddr;
+ fn->pc = base_address + fsym->n_value - fn->sect_base;
+ fn->size = fsize;
+ fn->lnno = lnno;
+ fn->lnnoptr = lnnoptr;
+ ++fdata->func_vec.count;
break;
}
i += asym->n_numaux;
}
- /* Allocate one extra entry at the end. */
- ln = ((struct xcoff_line *)
- backtrace_vector_grow (state, sizeof (struct xcoff_line),
- error_callback, data, &fdata->vec.vec));
- if (ln == NULL)
+ if (!backtrace_vector_release (state, &fdata->func_vec.vec, error_callback,
+ data))
goto fail;
- ln->pc = (uintptr_t) -1;
- ln->filename = NULL;
- ln->function = NULL;
- ln->lineno = 0;
+ backtrace_qsort (fdata->func_vec.vec.base, fdata->func_vec.count,
+ sizeof (struct xcoff_func), xcoff_func_compare);
- if (!backtrace_vector_release (state, &fdata->vec.vec, error_callback, data))
+ if (!backtrace_vector_release (state, &fdata->incl_vec.vec, error_callback,
+ data))
goto fail;
-
- backtrace_qsort (fdata->vec.vec.base, fdata->vec.count,
- sizeof (struct xcoff_line), xcoff_line_compare);
+ backtrace_qsort (fdata->incl_vec.vec.base, fdata->incl_vec.count,
+ sizeof (struct xcoff_incl), xcoff_incl_compare);
if (!state->threaded)
{
struct backtrace_view linenos_view;
struct backtrace_view syms_view;
struct backtrace_view str_view;
+ struct backtrace_view dwarf_view;
b_xcoff_filhdr fhdr;
const b_xcoff_scnhdr *sects;
const b_xcoff_scnhdr *stext;
uint64_t lnnoptr;
uint32_t nlnno;
off_t str_off;
+ off_t min_offset;
+ off_t max_offset;
+ struct dwsect_info dwsect[DWSECT_MAX];
size_t sects_size;
size_t syms_size;
int32_t str_size;
int linenos_view_valid;
int syms_view_valid;
int str_view_valid;
+ int dwarf_view_valid;
int magic_ok;
int i;
linenos_view_valid = 0;
syms_view_valid = 0;
str_view_valid = 0;
+ dwarf_view_valid = 0;
+
+ str_size = 0;
/* Map the XCOFF file header. */
if (!backtrace_get_view (state, descriptor, offset, sizeof (b_xcoff_filhdr),
if (!magic_ok)
{
if (exe)
- error_callback (data, "executable file is not XCOFF", 0);
+ error_callback (data, "executable file is not XCOFF", 0);
goto fail;
}
/* FIXME: assumes only one .text section. */
for (i = 0; i < fhdr.f_nscns; ++i)
- if ((sects[i].s_flags & 0xffff) == STYP_TEXT)
- break;
+ if ((sects[i].s_flags & 0xffff) == STYP_TEXT)
+ break;
if (i == fhdr.f_nscns)
goto fail;
/* Find the matching .ovrflo section. */
for (i = 0; i < fhdr.f_nscns; ++i)
{
- if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO)
- && sects[i].s_nlnno == sntext)
- {
- nlnno = sects[i].s_vaddr;
- break;
- }
+ if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO)
+ && sects[i].s_nlnno == sntext)
+ {
+ nlnno = sects[i].s_vaddr;
+ break;
+ }
}
}
#endif
xcoff_add_syminfo_data (state, sdata);
}
- /* Read the line number entries. */
+ /* Read all the DWARF sections in a single view, since they are
+ probably adjacent in the file. We never release this view. */
- if (fhdr.f_symptr != 0 && lnnoptr != 0)
+ min_offset = 0;
+ max_offset = 0;
+ memset (dwsect, 0, sizeof dwsect);
+ for (i = 0; i < fhdr.f_nscns; ++i)
+ {
+ off_t end;
+ int idx;
+
+ if ((sects[i].s_flags & 0xffff) != STYP_DWARF
+ || sects[i].s_size == 0)
+ continue;
+ /* Map DWARF section to array index. */
+ switch (sects[i].s_flags & 0xffff0000)
+ {
+ case SSUBTYP_DWINFO:
+ idx = DWSECT_INFO;
+ break;
+ case SSUBTYP_DWLINE:
+ idx = DWSECT_LINE;
+ break;
+ case SSUBTYP_DWABREV:
+ idx = DWSECT_ABBREV;
+ break;
+ case SSUBTYP_DWARNGE:
+ idx = DWSECT_RANGES;
+ break;
+ case SSUBTYP_DWSTR:
+ idx = DWSECT_STR;
+ break;
+ default:
+ continue;
+ }
+ if (min_offset == 0 || (off_t) sects[i].s_scnptr < min_offset)
+ min_offset = sects[i].s_scnptr;
+ end = sects[i].s_scnptr + sects[i].s_size;
+ if (end > max_offset)
+ max_offset = end;
+ dwsect[idx].offset = sects[i].s_scnptr;
+ dwsect[idx].size = sects[i].s_size;
+ }
+ if (min_offset != 0 && max_offset != 0)
+ {
+ if (!backtrace_get_view (state, descriptor, offset + min_offset,
+ max_offset - min_offset,
+ error_callback, data, &dwarf_view))
+ goto fail;
+ dwarf_view_valid = 1;
+
+ for (i = 0; i < (int) DWSECT_MAX; ++i)
+ {
+ if (dwsect[i].offset == 0)
+ dwsect[i].data = NULL;
+ else
+ dwsect[i].data = ((const unsigned char *) dwarf_view.data
+ + (dwsect[i].offset - min_offset));
+ }
+
+ if (!backtrace_dwarf_add (state, 0,
+ dwsect[DWSECT_INFO].data,
+ dwsect[DWSECT_INFO].size,
+#if BACKTRACE_XCOFF_SIZE == 32
+ /* XXX workaround for broken lineoff */
+ dwsect[DWSECT_LINE].data - 4,
+#else
+ /* XXX workaround for broken lineoff */
+ dwsect[DWSECT_LINE].data - 12,
+#endif
+ dwsect[DWSECT_LINE].size,
+ dwsect[DWSECT_ABBREV].data,
+ dwsect[DWSECT_ABBREV].size,
+ dwsect[DWSECT_RANGES].data,
+ dwsect[DWSECT_RANGES].size,
+ dwsect[DWSECT_STR].data,
+ dwsect[DWSECT_STR].size,
+ 1, /* big endian */
+ NULL,
+ error_callback, data, fileline_fn,
+ NULL))
+ goto fail;
+ }
+
+ /* Read the XCOFF line number entries if DWARF sections not found. */
+
+ if (!dwarf_view_valid && fhdr.f_symptr != 0 && lnnoptr != 0)
{
size_t linenos_size = (size_t) nlnno * LINESZ;
+ /* We never release this view. */
if (!backtrace_get_view (state, descriptor, offset + lnnoptr,
linenos_size,
error_callback, data, &linenos_view))
linenos_view.data, linenos_size,
lnnoptr, error_callback, data))
*fileline_fn = xcoff_fileline;
-
- backtrace_release_view (state, &linenos_view, error_callback, data);
- linenos_view_valid = 0;
}
backtrace_release_view (state, §s_view, error_callback, data);
backtrace_release_view (state, &syms_view, error_callback, data);
if (linenos_view_valid)
backtrace_release_view (state, &linenos_view, error_callback, data);
+ if (dwarf_view_valid)
+ backtrace_release_view (state, &dwarf_view, error_callback, data);
if (descriptor != -1 && offset == 0)
backtrace_close (descriptor, error_callback, data);
return 0;
Returns 1 on success, 0 on failure. */
int
-backtrace_initialize (struct backtrace_state *state, int descriptor,
+backtrace_initialize (struct backtrace_state *state,
+ const char *filename ATTRIBUTE_UNUSED, int descriptor,
backtrace_error_callback error_callback,
void *data, fileline *fileline_fn)
{