forked from Imagelibrary/binutils-gdb
Trying to free malloc'd memory used by the stabs and coff debug info
parsers is complicated, and traversing the trees generated requires a
lot of code. It's better to bfd_alloc the memory which allows it all
to be freed without fuss when the bfd is closed. In the process of
doing this I reverted most of commit a633691333.
Some of the stabs handling code grows arrays of pointers with realloc,
to deal with arbitrary numbers of fields, function args, etc. The
code still does that but copies over to bfd_alloc memory when
finished. The alternative is to parse twice, once to size, then again
to populate the arrays. I think that complication is unwarranted.
Note that there is a greater than zero chance this patch breaks
something, eg. that I missed an attempt to free obj_alloc memory.
Also it seems there are no tests in the binutils testsuite aimed at
exercising objdump --debugging.
* budbg.h (finish_stab, parse_stab): Update prototypes
* debug.c: Include bucomm.h.
(struct debug_handle): Add "abfd" field.
(debug_init): Add "abfd" param. bfd_alloc handle.
(debug_xalloc, debug_xzalloc): New functions. Use throughout
in place of xmalloc and memset.
(debug_start_source): Remove "name_used" param.
* debug.h (debug_init, debug_start_source): Update prototypes.
(debug_xalloc, debug_xzalloc): Declare.
* objcopy.c (copy_object): Don't free dhandle.
* objdump.c (dump_bfd): Likewise.
* rdcoff.c (coff_get_slot): Add dhandle arg. debug_xzalloc
memory in place of xcalloc. Update callers.
(parse_coff_struct_type): Don't leak on error return. Copy
fields over to debug_xalloc memory.
(parse_coff_enum_type): Copy names and vals over the
debug_xalloc memory.
* rddbg.c (read_debugging_info): Adjust debug_init call.
Don't free dhandle.
(read_section_stabs_debugging_info): Don't free shandle.
Adjust parse_stab call. Call finish_stab on error return.
(read_symbol_stabs_debugging_info): Similarly.
* stabs.c (savestring): Delete unnecessary forward declaration.
Add dhandle param. debug_xalloc memory. Update callers.
(start_stab): Delete unnecessary casts.
(finish_stab): Add "emit" param. Free file_types, so_string,
and stabs handle.
(parse_stab): Delete string_used param. Revert code dealing
with string_used. Copy so_string passed to debug_set_filename
and stored as main_filename to debug_xalloc memory. Similarly
for string passed to debug_start_source and push_bincl. Copy
args to debug_xalloc memory. Don't leak args.
(parse_stab_enum_type): Copy names and values to debug_xalloc
memory. Don't free name.
(parse_stab_struct_type): Don't free fields.
(parse_stab_baseclasses): Delete unnecessary cast.
(parse_stab_struct_fields): Return debug_xalloc fields.
(parse_stab_cpp_abbrev): Use debug_xalloc for _vb$ type name.
(parse_stab_one_struct_field): Don't free name.
(parse_stab_members): Copy variants and methods to
debug_xalloc memory. Don't free name or argtypes.
(parse_stab_argtypes): Use debug_xalloc memory for physname
and args.
(push_bincl): Add dhandle param. Use debug_xalloc memory.
(stab_record_variable): Use debug_xalloc memory.
(stab_emit_pending_vars): Don't free var list.
(stab_find_slot): Add dhandle param. Use debug_xzalloc
memory. Update all callers.
(stab_find_tagged_type): Don't free name. Use debug_xzalloc.
(stab_demangle_qualified): Don't free name.
(stab_demangle_template): Don't free s1.
(stab_demangle_args): Tidy pvarargs refs. Copy *pargs on
success to debug_xalloc memory, free on failure.
(stab_demangle_fund_type): Don't free name.
(stab_demangle_v3_arglist): Copy args to debug_xalloc memory.
Don't free dt.
432 lines
9.9 KiB
C
432 lines
9.9 KiB
C
/* rddbg.c -- Read debugging information into a generic form.
|
||
Copyright (C) 1995-2023 Free Software Foundation, Inc.
|
||
Written by Ian Lance Taylor <ian@cygnus.com>.
|
||
|
||
This file is part of GNU Binutils.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program 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 General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||
02110-1301, USA. */
|
||
|
||
|
||
/* This file reads debugging information into a generic form. This
|
||
file knows how to dig the debugging information out of an object
|
||
file. */
|
||
|
||
#include "sysdep.h"
|
||
#include "bfd.h"
|
||
#include "libiberty.h"
|
||
#include "bucomm.h"
|
||
#include "debug.h"
|
||
#include "budbg.h"
|
||
|
||
static bool read_section_stabs_debugging_info
|
||
(bfd *, asymbol **, long, void *, bool *);
|
||
static bool read_symbol_stabs_debugging_info
|
||
(bfd *, asymbol **, long, void *, bool *);
|
||
static void save_stab (int, int, bfd_vma, const char *);
|
||
static void stab_context (void);
|
||
static void free_saved_stabs (void);
|
||
|
||
/* Read debugging information from a BFD. Returns a generic debugging
|
||
pointer. */
|
||
|
||
void *
|
||
read_debugging_info (bfd *abfd, asymbol **syms, long symcount,
|
||
bool no_messages)
|
||
{
|
||
void *dhandle;
|
||
bool found;
|
||
|
||
dhandle = debug_init (abfd);
|
||
if (dhandle == NULL)
|
||
return NULL;
|
||
|
||
if (! read_section_stabs_debugging_info (abfd, syms, symcount, dhandle,
|
||
&found))
|
||
goto err_exit;
|
||
|
||
if (bfd_get_flavour (abfd) == bfd_target_aout_flavour)
|
||
{
|
||
if (! read_symbol_stabs_debugging_info (abfd, syms, symcount, dhandle,
|
||
&found))
|
||
goto err_exit;
|
||
}
|
||
|
||
/* Try reading the COFF symbols if we didn't find any stabs in COFF
|
||
sections. */
|
||
if (! found
|
||
&& bfd_get_flavour (abfd) == bfd_target_coff_flavour
|
||
&& symcount > 0)
|
||
{
|
||
if (! parse_coff (abfd, syms, symcount, dhandle))
|
||
goto err_exit;
|
||
found = true;
|
||
}
|
||
|
||
if (! found)
|
||
{
|
||
if (! no_messages)
|
||
non_fatal (_("%s: no recognized debugging information"),
|
||
bfd_get_filename (abfd));
|
||
err_exit:
|
||
return NULL;
|
||
}
|
||
|
||
return dhandle;
|
||
}
|
||
|
||
/* Read stabs in sections debugging information from a BFD. */
|
||
|
||
static bool
|
||
read_section_stabs_debugging_info (bfd *abfd, asymbol **syms, long symcount,
|
||
void *dhandle, bool *pfound)
|
||
{
|
||
static struct
|
||
{
|
||
const char *secname;
|
||
const char *strsecname;
|
||
}
|
||
names[] =
|
||
{
|
||
{ ".stab", ".stabstr" },
|
||
{ "LC_SYMTAB.stabs", "LC_SYMTAB.stabstr" },
|
||
{ "$GDB_SYMBOLS$", "$GDB_STRINGS$" }
|
||
};
|
||
unsigned int i;
|
||
void *shandle;
|
||
bool ret = false;
|
||
|
||
*pfound = false;
|
||
shandle = NULL;
|
||
|
||
for (i = 0; i < sizeof names / sizeof names[0]; i++)
|
||
{
|
||
asection *sec, *strsec;
|
||
|
||
sec = bfd_get_section_by_name (abfd, names[i].secname);
|
||
strsec = bfd_get_section_by_name (abfd, names[i].strsecname);
|
||
if (sec != NULL
|
||
&& (bfd_section_flags (sec) & SEC_HAS_CONTENTS) != 0
|
||
&& bfd_section_size (sec) >= 12
|
||
&& strsec != NULL
|
||
&& (bfd_section_flags (strsec) & SEC_HAS_CONTENTS) != 0)
|
||
{
|
||
bfd_size_type stabsize, strsize;
|
||
bfd_byte *stabs, *strings;
|
||
bfd_byte *stab;
|
||
bfd_size_type stroff, next_stroff;
|
||
|
||
if (!bfd_malloc_and_get_section (abfd, sec, &stabs))
|
||
{
|
||
fprintf (stderr, "%s: %s: %s\n",
|
||
bfd_get_filename (abfd), names[i].secname,
|
||
bfd_errmsg (bfd_get_error ()));
|
||
goto out;
|
||
}
|
||
|
||
if (!bfd_malloc_and_get_section (abfd, strsec, &strings))
|
||
{
|
||
fprintf (stderr, "%s: %s: %s\n",
|
||
bfd_get_filename (abfd), names[i].strsecname,
|
||
bfd_errmsg (bfd_get_error ()));
|
||
free (stabs);
|
||
goto out;
|
||
}
|
||
/* Zero terminate the strings table, just in case. */
|
||
strsize = bfd_section_size (strsec);
|
||
if (strsize != 0)
|
||
strings [strsize - 1] = 0;
|
||
if (shandle == NULL)
|
||
{
|
||
shandle = start_stab (dhandle, abfd, true, syms, symcount);
|
||
if (shandle == NULL)
|
||
{
|
||
free (strings);
|
||
free (stabs);
|
||
goto out;
|
||
}
|
||
}
|
||
|
||
*pfound = true;
|
||
|
||
stroff = 0;
|
||
next_stroff = 0;
|
||
stabsize = bfd_section_size (sec);
|
||
/* PR 17512: file: 078-60391-0.001:0.1. */
|
||
for (stab = stabs; stab <= (stabs + stabsize) - 12; stab += 12)
|
||
{
|
||
unsigned int strx;
|
||
int type;
|
||
int other ATTRIBUTE_UNUSED;
|
||
int desc;
|
||
bfd_vma value;
|
||
|
||
/* This code presumes 32 bit values. */
|
||
|
||
strx = bfd_get_32 (abfd, stab);
|
||
type = bfd_get_8 (abfd, stab + 4);
|
||
other = bfd_get_8 (abfd, stab + 5);
|
||
desc = bfd_get_16 (abfd, stab + 6);
|
||
value = bfd_get_32 (abfd, stab + 8);
|
||
|
||
if (type == 0)
|
||
{
|
||
/* Special type 0 stabs indicate the offset to the
|
||
next string table. */
|
||
stroff = next_stroff;
|
||
next_stroff += value;
|
||
}
|
||
else
|
||
{
|
||
size_t len;
|
||
char *f, *s;
|
||
|
||
if (stroff + strx >= strsize)
|
||
{
|
||
fprintf (stderr, _("%s: %s: stab entry %ld is corrupt, strx = 0x%x, type = %d\n"),
|
||
bfd_get_filename (abfd), names[i].secname,
|
||
(long) (stab - stabs) / 12, strx, type);
|
||
continue;
|
||
}
|
||
|
||
s = (char *) strings + stroff + strx;
|
||
f = NULL;
|
||
|
||
/* PR 17512: file: 002-87578-0.001:0.1.
|
||
It is possible to craft a file where, without the 'strlen (s) > 0',
|
||
an attempt to read the byte before 'strings' would occur. */
|
||
while ((len = strlen (s)) > 0
|
||
&& s[len - 1] == '\\'
|
||
&& stab + 16 <= stabs + stabsize)
|
||
{
|
||
char *p;
|
||
|
||
stab += 12;
|
||
p = s + len - 1;
|
||
*p = '\0';
|
||
strx = stroff + bfd_get_32 (abfd, stab);
|
||
if (strx >= strsize)
|
||
{
|
||
fprintf (stderr, _("%s: %s: stab entry %ld is corrupt\n"),
|
||
bfd_get_filename (abfd), names[i].secname,
|
||
(long) (stab - stabs) / 12);
|
||
break;
|
||
}
|
||
|
||
s = concat (s, (char *) strings + strx,
|
||
(const char *) NULL);
|
||
|
||
/* We have to restore the backslash, because, if
|
||
the linker is hashing stabs strings, we may
|
||
see the same string more than once. */
|
||
*p = '\\';
|
||
|
||
free (f);
|
||
f = s;
|
||
}
|
||
|
||
save_stab (type, desc, value, s);
|
||
|
||
if (!parse_stab (dhandle, shandle, type, desc, value, s))
|
||
{
|
||
stab_context ();
|
||
free_saved_stabs ();
|
||
free (f);
|
||
free (stabs);
|
||
free (strings);
|
||
goto out;
|
||
}
|
||
|
||
free (f);
|
||
}
|
||
}
|
||
|
||
free_saved_stabs ();
|
||
free (stabs);
|
||
free (strings);
|
||
}
|
||
}
|
||
ret = true;
|
||
|
||
out:
|
||
if (shandle != NULL)
|
||
{
|
||
if (! finish_stab (dhandle, shandle, ret))
|
||
return false;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* Read stabs in the symbol table. */
|
||
|
||
static bool
|
||
read_symbol_stabs_debugging_info (bfd *abfd, asymbol **syms, long symcount,
|
||
void *dhandle, bool *pfound)
|
||
{
|
||
void *shandle;
|
||
asymbol **ps, **symend;
|
||
|
||
shandle = NULL;
|
||
symend = syms + symcount;
|
||
for (ps = syms; ps < symend; ps++)
|
||
{
|
||
symbol_info i;
|
||
|
||
bfd_get_symbol_info (abfd, *ps, &i);
|
||
|
||
if (i.type == '-')
|
||
{
|
||
const char *s;
|
||
char *f;
|
||
|
||
if (shandle == NULL)
|
||
{
|
||
shandle = start_stab (dhandle, abfd, false, syms, symcount);
|
||
if (shandle == NULL)
|
||
return false;
|
||
}
|
||
|
||
*pfound = true;
|
||
|
||
s = i.name;
|
||
if (s == NULL || strlen (s) < 1)
|
||
break;
|
||
f = NULL;
|
||
|
||
while (strlen (s) > 0
|
||
&& s[strlen (s) - 1] == '\\'
|
||
&& ps + 1 < symend)
|
||
{
|
||
char *sc, *n;
|
||
|
||
++ps;
|
||
sc = xstrdup (s);
|
||
sc[strlen (sc) - 1] = '\0';
|
||
n = concat (sc, bfd_asymbol_name (*ps), (const char *) NULL);
|
||
free (sc);
|
||
free (f);
|
||
f = n;
|
||
s = n;
|
||
}
|
||
|
||
save_stab (i.stab_type, i.stab_desc, i.value, s);
|
||
|
||
if (!parse_stab (dhandle, shandle, i.stab_type, i.stab_desc,
|
||
i.value, s))
|
||
{
|
||
stab_context ();
|
||
free (f);
|
||
break;
|
||
}
|
||
|
||
free (f);
|
||
}
|
||
}
|
||
bool ret = ps >= symend;
|
||
|
||
free_saved_stabs ();
|
||
|
||
if (shandle != NULL)
|
||
{
|
||
if (! finish_stab (dhandle, shandle, ret))
|
||
return false;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* Record stabs strings, so that we can give some context for errors. */
|
||
|
||
#define SAVE_STABS_COUNT (16)
|
||
|
||
struct saved_stab
|
||
{
|
||
int type;
|
||
int desc;
|
||
bfd_vma value;
|
||
char *string;
|
||
};
|
||
|
||
static struct saved_stab saved_stabs[SAVE_STABS_COUNT];
|
||
static int saved_stabs_index;
|
||
|
||
/* Save a stabs string. */
|
||
|
||
static void
|
||
save_stab (int type, int desc, bfd_vma value, const char *string)
|
||
{
|
||
free (saved_stabs[saved_stabs_index].string);
|
||
saved_stabs[saved_stabs_index].type = type;
|
||
saved_stabs[saved_stabs_index].desc = desc;
|
||
saved_stabs[saved_stabs_index].value = value;
|
||
saved_stabs[saved_stabs_index].string = xstrdup (string);
|
||
saved_stabs_index = (saved_stabs_index + 1) % SAVE_STABS_COUNT;
|
||
}
|
||
|
||
/* Provide context for an error. */
|
||
|
||
static void
|
||
stab_context (void)
|
||
{
|
||
int i;
|
||
|
||
fprintf (stderr, _("Last stabs entries before error:\n"));
|
||
fprintf (stderr, "n_type n_desc n_value string\n");
|
||
|
||
i = saved_stabs_index;
|
||
do
|
||
{
|
||
struct saved_stab *stabp;
|
||
|
||
stabp = saved_stabs + i;
|
||
if (stabp->string != NULL)
|
||
{
|
||
const char *s;
|
||
|
||
s = bfd_get_stab_name (stabp->type);
|
||
if (s != NULL)
|
||
fprintf (stderr, "%-6s", s);
|
||
else if (stabp->type == 0)
|
||
fprintf (stderr, "HdrSym");
|
||
else
|
||
fprintf (stderr, "%-6d", stabp->type);
|
||
fprintf (stderr, " %-6d ", stabp->desc);
|
||
fprintf (stderr, "%08" PRIx64, (uint64_t) stabp->value);
|
||
if (stabp->type != 0)
|
||
fprintf (stderr, " %s", stabp->string);
|
||
fprintf (stderr, "\n");
|
||
}
|
||
i = (i + 1) % SAVE_STABS_COUNT;
|
||
}
|
||
while (i != saved_stabs_index);
|
||
}
|
||
|
||
/* Free the saved stab strings. */
|
||
|
||
static void
|
||
free_saved_stabs (void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < SAVE_STABS_COUNT; i++)
|
||
{
|
||
free (saved_stabs[i].string);
|
||
saved_stabs[i].string = NULL;
|
||
}
|
||
|
||
saved_stabs_index = 0;
|
||
}
|