#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "aout/stab_gnu.h"
#include "safe-ctype.h"
#define STRDXOFF (0)
#define TYPEOFF (4)
#define OTHEROFF (5)
#define DESCOFF (6)
#define VALOFF (8)
#define STABSIZE (12)
struct stab_link_includes_table
{
struct bfd_hash_table root;
};
struct stab_link_includes_totals
{
struct stab_link_includes_totals *next;
bfd_vma total;
};
struct stab_link_includes_entry
{
struct bfd_hash_entry root;
struct stab_link_includes_totals *totals;
};
#define stab_link_includes_lookup(table, string, create, copy) \
((struct stab_link_includes_entry *) \
bfd_hash_lookup (&(table)->root, (string), (create), (copy)))
struct stab_excl_list
{
struct stab_excl_list *next;
bfd_size_type offset;
bfd_vma val;
int type;
};
struct stab_section_info
{
struct stab_excl_list *excls;
bfd_size_type *cumulative_skips;
bfd_size_type stridxs[1];
};
struct stab_info
{
struct bfd_strtab_hash *strings;
struct stab_link_includes_table includes;
asection *stabstr;
};
static struct bfd_hash_entry *stab_link_includes_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_hash_entry *
stab_link_includes_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct stab_link_includes_entry *ret =
(struct stab_link_includes_entry *) entry;
if (ret == (struct stab_link_includes_entry *) NULL)
ret = ((struct stab_link_includes_entry *)
bfd_hash_allocate (table,
sizeof (struct stab_link_includes_entry)));
if (ret == (struct stab_link_includes_entry *) NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct stab_link_includes_entry *)
bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
if (ret)
{
ret->totals = NULL;
}
return (struct bfd_hash_entry *) ret;
}
bfd_boolean
_bfd_link_section_stabs (abfd, psinfo, stabsec, stabstrsec, psecinfo)
bfd *abfd;
PTR *psinfo;
asection *stabsec;
asection *stabstrsec;
PTR *psecinfo;
{
bfd_boolean first;
struct stab_info *sinfo;
bfd_size_type count, amt;
struct stab_section_info *secinfo;
bfd_byte *stabbuf = NULL;
bfd_byte *stabstrbuf = NULL;
bfd_byte *sym, *symend;
bfd_size_type stroff, next_stroff, skip;
bfd_size_type *pstridx;
if (stabsec->_raw_size == 0
|| stabstrsec->_raw_size == 0)
{
return TRUE;
}
if (stabsec->_raw_size % STABSIZE != 0)
{
return TRUE;
}
if ((stabstrsec->flags & SEC_RELOC) != 0)
{
return TRUE;
}
if ((stabsec->output_section != NULL
&& bfd_is_abs_section (stabsec->output_section))
|| (stabstrsec->output_section != NULL
&& bfd_is_abs_section (stabstrsec->output_section)))
{
return TRUE;
}
first = FALSE;
if (*psinfo == NULL)
{
first = TRUE;
amt = sizeof (struct stab_info);
*psinfo = (PTR) bfd_alloc (abfd, amt);
if (*psinfo == NULL)
goto error_return;
sinfo = (struct stab_info *) *psinfo;
sinfo->strings = _bfd_stringtab_init ();
if (sinfo->strings == NULL)
goto error_return;
(void) _bfd_stringtab_add (sinfo->strings, "", TRUE, TRUE);
if (! bfd_hash_table_init_n (&sinfo->includes.root,
stab_link_includes_newfunc,
251))
goto error_return;
sinfo->stabstr = bfd_make_section_anyway (abfd, ".stabstr");
sinfo->stabstr->flags |= SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING;
}
sinfo = (struct stab_info *) *psinfo;
count = stabsec->_raw_size / STABSIZE;
amt = sizeof (struct stab_section_info);
amt += (count - 1) * sizeof (bfd_size_type);
*psecinfo = bfd_alloc (abfd, amt);
if (*psecinfo == NULL)
goto error_return;
secinfo = (struct stab_section_info *) *psecinfo;
secinfo->excls = NULL;
secinfo->cumulative_skips = NULL;
memset (secinfo->stridxs, 0, (size_t) count * sizeof (bfd_size_type));
stabbuf = (bfd_byte *) bfd_malloc (stabsec->_raw_size);
stabstrbuf = (bfd_byte *) bfd_malloc (stabstrsec->_raw_size);
if (stabbuf == NULL || stabstrbuf == NULL)
goto error_return;
if (! bfd_get_section_contents (abfd, stabsec, stabbuf, (bfd_vma) 0,
stabsec->_raw_size)
|| ! bfd_get_section_contents (abfd, stabstrsec, stabstrbuf, (bfd_vma) 0,
stabstrsec->_raw_size))
goto error_return;
stroff = 0;
next_stroff = 0;
skip = 0;
symend = stabbuf + stabsec->_raw_size;
for (sym = stabbuf, pstridx = secinfo->stridxs;
sym < symend;
sym += STABSIZE, ++pstridx)
{
bfd_size_type symstroff;
int type;
const char *string;
if (*pstridx != 0)
{
continue;
}
type = sym[TYPEOFF];
if (type == 0)
{
stroff = next_stroff;
next_stroff += bfd_get_32 (abfd, sym + 8);
if (! first)
{
*pstridx = (bfd_size_type) -1;
++skip;
continue;
}
first = FALSE;
}
symstroff = stroff + bfd_get_32 (abfd, sym + STRDXOFF);
if (symstroff >= stabstrsec->_raw_size)
{
(*_bfd_error_handler)
(_("%s(%s+0x%lx): Stabs entry has invalid string index."),
bfd_archive_filename (abfd),
bfd_get_section_name (abfd, stabsec),
(long) (sym - stabbuf));
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
string = (char *) stabstrbuf + symstroff;
*pstridx = _bfd_stringtab_add (sinfo->strings, string, TRUE, TRUE);
if (type == (int) N_BINCL)
{
bfd_vma val;
int nest;
bfd_byte *incl_sym;
struct stab_link_includes_entry *incl_entry;
struct stab_link_includes_totals *t;
struct stab_excl_list *ne;
val = 0;
nest = 0;
for (incl_sym = sym + STABSIZE;
incl_sym < symend;
incl_sym += STABSIZE)
{
int incl_type;
incl_type = incl_sym[TYPEOFF];
if (incl_type == 0)
break;
else if (incl_type == (int) N_EINCL)
{
if (nest == 0)
break;
--nest;
}
else if (incl_type == (int) N_BINCL)
++nest;
else if (nest == 0)
{
const char *str;
str = ((char *) stabstrbuf
+ stroff
+ bfd_get_32 (abfd, incl_sym + STRDXOFF));
for (; *str != '\0'; str++)
{
val += *str;
if (*str == '(')
{
++str;
while (ISDIGIT (*str))
++str;
--str;
}
}
}
}
incl_entry = stab_link_includes_lookup (&sinfo->includes, string,
TRUE, TRUE);
if (incl_entry == NULL)
goto error_return;
for (t = incl_entry->totals; t != NULL; t = t->next)
if (t->total == val)
break;
amt = sizeof *ne;
ne = (struct stab_excl_list *) bfd_alloc (abfd, amt);
if (ne == NULL)
goto error_return;
ne->offset = sym - stabbuf;
ne->val = val;
ne->type = (int) N_BINCL;
ne->next = secinfo->excls;
secinfo->excls = ne;
if (t == NULL)
{
t = ((struct stab_link_includes_totals *)
bfd_hash_allocate (&sinfo->includes.root, sizeof *t));
if (t == NULL)
goto error_return;
t->total = val;
t->next = incl_entry->totals;
incl_entry->totals = t;
}
else
{
bfd_size_type *incl_pstridx;
ne->type = (int) N_EXCL;
nest = 0;
for (incl_sym = sym + STABSIZE, incl_pstridx = pstridx + 1;
incl_sym < symend;
incl_sym += STABSIZE, ++incl_pstridx)
{
int incl_type;
incl_type = incl_sym[TYPEOFF];
if (incl_type == (int) N_EINCL)
{
if (nest == 0)
{
*incl_pstridx = (bfd_size_type) -1;
++skip;
break;
}
--nest;
}
else if (incl_type == (int) N_BINCL)
++nest;
else if (nest == 0)
{
*incl_pstridx = (bfd_size_type) -1;
++skip;
}
}
}
}
}
free (stabbuf);
stabbuf = NULL;
free (stabstrbuf);
stabstrbuf = NULL;
stabsec->_cooked_size = (count - skip) * STABSIZE;
if (stabsec->_cooked_size == 0)
stabsec->flags |= SEC_EXCLUDE;
stabstrsec->flags |= SEC_EXCLUDE;
sinfo->stabstr->_cooked_size = _bfd_stringtab_size (sinfo->strings);
if (skip != 0)
{
bfd_size_type i, offset;
bfd_size_type *pskips;
amt = count * sizeof (bfd_size_type);
secinfo->cumulative_skips = (bfd_size_type *) bfd_alloc (abfd, amt);
if (secinfo->cumulative_skips == NULL)
goto error_return;
pskips = secinfo->cumulative_skips;
pstridx = secinfo->stridxs;
offset = 0;
for (i = 0; i < count; i++, pskips++, pstridx++)
{
*pskips = offset;
if (*pstridx == (bfd_size_type) -1)
offset += STABSIZE;
}
BFD_ASSERT (offset != 0);
}
return TRUE;
error_return:
if (stabbuf != NULL)
free (stabbuf);
if (stabstrbuf != NULL)
free (stabstrbuf);
return FALSE;
}
bfd_boolean
_bfd_discard_section_stabs (abfd, stabsec, psecinfo,
reloc_symbol_deleted_p, cookie)
bfd *abfd;
asection *stabsec;
PTR psecinfo;
bfd_boolean (*reloc_symbol_deleted_p) PARAMS ((bfd_vma, PTR));
PTR cookie;
{
bfd_size_type count, amt;
struct stab_section_info *secinfo;
bfd_byte *stabbuf = NULL;
bfd_byte *sym, *symend;
bfd_size_type skip;
bfd_size_type *pstridx;
int deleting;
if (stabsec->_raw_size == 0)
{
return FALSE;
}
if (stabsec->_raw_size % STABSIZE != 0)
{
return FALSE;
}
if ((stabsec->output_section != NULL
&& bfd_is_abs_section (stabsec->output_section)))
{
return FALSE;
}
if (psecinfo == NULL)
return FALSE;
count = stabsec->_raw_size / STABSIZE;
secinfo = (struct stab_section_info *) psecinfo;
stabbuf = (bfd_byte *) bfd_malloc (stabsec->_raw_size);
if (stabbuf == NULL)
goto error_return;
if (! bfd_get_section_contents (abfd, stabsec, stabbuf, (bfd_vma) 0,
stabsec->_raw_size))
goto error_return;
skip = 0;
deleting = -1;
symend = stabbuf + stabsec->_raw_size;
for (sym = stabbuf, pstridx = secinfo->stridxs;
sym < symend;
sym += STABSIZE, ++pstridx)
{
int type;
if (*pstridx == (bfd_size_type) -1)
{
continue;
}
type = sym[TYPEOFF];
if (type == (int) N_FUN)
{
int strx = bfd_get_32 (abfd, sym + STRDXOFF);
if (strx == 0)
{
if (deleting)
{
skip++;
*pstridx = -1;
}
deleting = -1;
continue;
}
deleting = 0;
if ((*reloc_symbol_deleted_p) (sym + VALOFF - stabbuf, cookie))
deleting = 1;
}
if (deleting == 1)
{
*pstridx = -1;
skip++;
}
else if (deleting == -1)
{
if (type == (int) N_STSYM || type == (int) N_LCSYM)
if ((*reloc_symbol_deleted_p) (sym + VALOFF - stabbuf, cookie))
{
*pstridx = -1;
skip ++;
}
}
}
free (stabbuf);
stabbuf = NULL;
stabsec->_cooked_size -= skip * STABSIZE;
if (stabsec->_cooked_size == 0)
stabsec->flags |= SEC_EXCLUDE;
if (skip != 0)
{
bfd_size_type i, offset;
bfd_size_type *pskips;
if (secinfo->cumulative_skips == NULL)
{
amt = count * sizeof (bfd_size_type);
secinfo->cumulative_skips = (bfd_size_type *) bfd_alloc (abfd, amt);
if (secinfo->cumulative_skips == NULL)
goto error_return;
}
pskips = secinfo->cumulative_skips;
pstridx = secinfo->stridxs;
offset = 0;
for (i = 0; i < count; i++, pskips++, pstridx++)
{
*pskips = offset;
if (*pstridx == (bfd_size_type) -1)
offset += STABSIZE;
}
BFD_ASSERT (offset != 0);
}
return skip > 0;
error_return:
if (stabbuf != NULL)
free (stabbuf);
return FALSE;
}
bfd_boolean
_bfd_write_section_stabs (output_bfd, psinfo, stabsec, psecinfo, contents)
bfd *output_bfd;
PTR *psinfo;
asection *stabsec;
PTR *psecinfo;
bfd_byte *contents;
{
struct stab_info *sinfo;
struct stab_section_info *secinfo;
struct stab_excl_list *e;
bfd_byte *sym, *tosym, *symend;
bfd_size_type *pstridx;
sinfo = (struct stab_info *) *psinfo;
secinfo = (struct stab_section_info *) *psecinfo;
if (secinfo == NULL)
return bfd_set_section_contents (output_bfd, stabsec->output_section,
contents,
(file_ptr) stabsec->output_offset,
stabsec->_raw_size);
for (e = secinfo->excls; e != NULL; e = e->next)
{
bfd_byte *excl_sym;
BFD_ASSERT (e->offset < stabsec->_raw_size);
excl_sym = contents + e->offset;
bfd_put_32 (output_bfd, e->val, excl_sym + VALOFF);
excl_sym[TYPEOFF] = e->type;
}
tosym = contents;
symend = contents + stabsec->_raw_size;
for (sym = contents, pstridx = secinfo->stridxs;
sym < symend;
sym += STABSIZE, ++pstridx)
{
if (*pstridx != (bfd_size_type) -1)
{
if (tosym != sym)
memcpy (tosym, sym, STABSIZE);
bfd_put_32 (output_bfd, *pstridx, tosym + STRDXOFF);
if (sym[TYPEOFF] == 0)
{
BFD_ASSERT (sym == contents);
bfd_put_32 (output_bfd, _bfd_stringtab_size (sinfo->strings),
tosym + VALOFF);
bfd_put_16 (output_bfd,
stabsec->output_section->_raw_size / STABSIZE - 1,
tosym + DESCOFF);
}
tosym += STABSIZE;
}
}
BFD_ASSERT ((bfd_size_type) (tosym - contents) == stabsec->_cooked_size);
return bfd_set_section_contents (output_bfd, stabsec->output_section,
contents, (file_ptr) stabsec->output_offset,
stabsec->_cooked_size);
}
bfd_boolean
_bfd_write_stab_strings (output_bfd, psinfo)
bfd *output_bfd;
PTR *psinfo;
{
struct stab_info *sinfo;
sinfo = (struct stab_info *) *psinfo;
if (sinfo == NULL)
return TRUE;
if (bfd_is_abs_section (sinfo->stabstr->output_section))
{
return TRUE;
}
BFD_ASSERT ((sinfo->stabstr->output_offset
+ _bfd_stringtab_size (sinfo->strings))
<= sinfo->stabstr->output_section->_raw_size);
if (bfd_seek (output_bfd,
(file_ptr) (sinfo->stabstr->output_section->filepos
+ sinfo->stabstr->output_offset),
SEEK_SET) != 0)
return FALSE;
if (! _bfd_stringtab_emit (output_bfd, sinfo->strings))
return FALSE;
_bfd_stringtab_free (sinfo->strings);
bfd_hash_table_free (&sinfo->includes.root);
return TRUE;
}
bfd_vma
_bfd_stab_section_offset (output_bfd, psinfo, stabsec, psecinfo, offset)
bfd *output_bfd ATTRIBUTE_UNUSED;
PTR *psinfo ATTRIBUTE_UNUSED;
asection *stabsec;
PTR *psecinfo;
bfd_vma offset;
{
struct stab_section_info *secinfo;
secinfo = (struct stab_section_info *) *psecinfo;
if (secinfo == NULL)
return offset;
if (offset >= stabsec->_raw_size)
return offset - (stabsec->_cooked_size - stabsec->_raw_size);
if (secinfo->cumulative_skips)
{
bfd_vma i;
i = offset / STABSIZE;
if (secinfo->stridxs [i] == (bfd_size_type) -1)
return (bfd_vma) -1;
return offset - secinfo->cumulative_skips [i];
}
return offset;
}