forked from Imagelibrary/binutils-gdb
This should sort out some very old FIXMEs in code handling stabs debug info. Necessary if we are to fuss over freeing up memory before objdump and objcopy exit. It is of course better from a user viewpoint to *not* free memory, which takes some time, and leave that to process exit. The only reason to do so is that having many memory leaks in binutils/ code tends to hide leaks in bfd/ or opcodes/, which we should care about. * budbg.h (parse_stab): Update prototype. * debug.h (debug_start_source): Update prototype. * debug.c (debug_start_source): Add name_used. Set if stashed. * rddbg.c (read_symbol_stabs_debugging_info): Always malloc stab string passed to parse_stab. Free stab string when unreferenced. (read_section_stabs_debugging_info): Likewise, and strings section contents. * stabs.c (parse_stab): Add string_used param. Set if string stashed. Pass to debug_start_source. Realloc file_types array rather that using malloc. Clarify comment about debug_make_indirect_type.
442 lines
10 KiB
C
442 lines
10 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 ();
|
||
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:
|
||
free (dhandle);
|
||
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;
|
||
|
||
*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 ()));
|
||
free (shandle);
|
||
return false;
|
||
}
|
||
|
||
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 (shandle);
|
||
free (stabs);
|
||
return false;
|
||
}
|
||
/* 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);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
*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;
|
||
bool f_used;
|
||
|
||
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;
|
||
}
|
||
if (!f)
|
||
f = xstrdup (s);
|
||
|
||
save_stab (type, desc, value, s);
|
||
|
||
if (! parse_stab (dhandle, shandle, type, desc, value,
|
||
f, &f_used))
|
||
{
|
||
stab_context ();
|
||
free_saved_stabs ();
|
||
free (f);
|
||
free (shandle);
|
||
free (stabs);
|
||
free (strings);
|
||
return false;
|
||
}
|
||
|
||
if (!f_used)
|
||
free (f);
|
||
}
|
||
}
|
||
|
||
free_saved_stabs ();
|
||
free (stabs);
|
||
free (strings);
|
||
}
|
||
}
|
||
|
||
if (shandle != NULL)
|
||
{
|
||
if (! finish_stab (dhandle, shandle))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* 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;
|
||
bool f_used;
|
||
|
||
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)
|
||
return false;
|
||
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;
|
||
}
|
||
if (!f)
|
||
f = xstrdup (s);
|
||
|
||
save_stab (i.stab_type, i.stab_desc, i.value, s);
|
||
|
||
if (! parse_stab (dhandle, shandle, i.stab_type, i.stab_desc,
|
||
i.value, f, &f_used))
|
||
{
|
||
stab_context ();
|
||
free (f);
|
||
free_saved_stabs ();
|
||
return false;
|
||
}
|
||
|
||
if (!f_used)
|
||
free (f);
|
||
}
|
||
}
|
||
|
||
free_saved_stabs ();
|
||
|
||
if (shandle != NULL)
|
||
{
|
||
if (! finish_stab (dhandle, shandle))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* 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;
|
||
}
|