#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_totals
{
struct stab_link_includes_totals *next;
bfd_vma sum_chars;
bfd_vma num_chars;
const char* symb;
};
struct stab_link_includes_entry
{
struct bfd_hash_entry root;
struct stab_link_includes_totals *totals;
};
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];
};
static struct bfd_hash_entry *
stab_link_includes_newfunc (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 == NULL)
ret = bfd_hash_allocate (table,
sizeof (struct stab_link_includes_entry));
if (ret == NULL)
return NULL;
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 (bfd *abfd,
struct stab_info *sinfo,
asection *stabsec,
asection *stabstrsec,
void * *psecinfo,
bfd_size_type *pstring_offset)
{
bfd_boolean first;
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->size == 0
|| stabstrsec->size == 0)
return TRUE;
if (stabsec->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 (sinfo->stabstr == NULL)
{
first = TRUE;
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,
stab_link_includes_newfunc,
251))
goto error_return;
sinfo->stabstr = bfd_make_section_anyway (abfd, ".stabstr");
if (sinfo->stabstr == NULL)
goto error_return;
sinfo->stabstr->flags |= (SEC_HAS_CONTENTS | SEC_READONLY
| SEC_DEBUGGING | SEC_LINKER_CREATED);
}
count = stabsec->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;
stabsec->rawsize = stabsec->size;
secinfo->cumulative_skips = NULL;
memset (secinfo->stridxs, 0, (size_t) count * sizeof (bfd_size_type));
if (!bfd_malloc_and_get_section (abfd, stabsec, &stabbuf)
|| !bfd_malloc_and_get_section (abfd, stabstrsec, &stabstrbuf))
goto error_return;
stroff = 0;
next_stroff = pstring_offset ? *pstring_offset : 0;
skip = 0;
symend = stabbuf + stabsec->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 (pstring_offset)
*pstring_offset = next_stroff;
if (! first)
{
*pstridx = (bfd_size_type) -1;
++skip;
continue;
}
first = FALSE;
}
symstroff = stroff + bfd_get_32 (abfd, sym + STRDXOFF);
if (symstroff >= stabstrsec->size)
{
(*_bfd_error_handler)
(_("%B(%A+0x%lx): Stabs entry has invalid string index."),
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 sum_chars;
bfd_vma num_chars;
bfd_vma buf_len = 0;
char * symb;
char * symb_rover;
int nest;
bfd_byte * incl_sym;
struct stab_link_includes_entry * incl_entry;
struct stab_link_includes_totals * t;
struct stab_excl_list * ne;
symb = symb_rover = NULL;
sum_chars = num_chars = 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_EXCL)
continue;
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++)
{
if (num_chars >= buf_len)
{
buf_len += 32 * 1024;
symb = bfd_realloc (symb, buf_len);
if (symb == NULL)
goto error_return;
symb_rover = symb + num_chars;
}
* symb_rover ++ = * str;
sum_chars += *str;
num_chars ++;
if (*str == '(')
{
++str;
while (ISDIGIT (*str))
++str;
--str;
}
}
}
}
BFD_ASSERT (num_chars == (bfd_vma) (symb_rover - symb));
incl_entry = (struct stab_link_includes_entry * )
bfd_hash_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->sum_chars == sum_chars
&& t->num_chars == num_chars
&& memcmp (t->symb, symb, num_chars) == 0)
break;
amt = sizeof *ne;
ne = bfd_alloc (abfd, amt);
if (ne == NULL)
goto error_return;
ne->offset = sym - stabbuf;
ne->val = sum_chars;
ne->type = (int) N_BINCL;
ne->next = secinfo->excls;
secinfo->excls = ne;
if (t == NULL)
{
t = bfd_hash_allocate (&sinfo->includes, sizeof *t);
if (t == NULL)
goto error_return;
t->sum_chars = sum_chars;
t->num_chars = num_chars;
t->symb = bfd_realloc (symb, num_chars);
t->next = incl_entry->totals;
incl_entry->totals = t;
}
else
{
bfd_size_type *incl_pstridx;
ne->type = (int) N_EXCL;
free (symb);
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 (incl_type == (int) N_EXCL)
continue;
else if (nest == 0)
{
*incl_pstridx = (bfd_size_type) -1;
++skip;
}
}
}
}
}
free (stabbuf);
stabbuf = NULL;
free (stabstrbuf);
stabstrbuf = NULL;
stabsec->size = (count - skip) * STABSIZE;
if (stabsec->size == 0)
stabsec->flags |= SEC_EXCLUDE;
stabstrsec->flags |= SEC_EXCLUDE;
sinfo->stabstr->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_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 (bfd *abfd,
asection *stabsec,
void * psecinfo,
bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
void * 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->size == 0)
return FALSE;
if (stabsec->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->rawsize / STABSIZE;
secinfo = (struct stab_section_info *) psecinfo;
if (!bfd_malloc_and_get_section (abfd, stabsec, &stabbuf))
goto error_return;
skip = 0;
deleting = -1;
symend = stabbuf + stabsec->rawsize;
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->size -= skip * STABSIZE;
if (stabsec->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_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 (bfd *output_bfd,
struct stab_info *sinfo,
asection *stabsec,
void * *psecinfo,
bfd_byte *contents)
{
struct stab_section_info *secinfo;
struct stab_excl_list *e;
bfd_byte *sym, *tosym, *symend;
bfd_size_type *pstridx;
secinfo = (struct stab_section_info *) *psecinfo;
if (secinfo == NULL)
return bfd_set_section_contents (output_bfd, stabsec->output_section,
contents, stabsec->output_offset,
stabsec->size);
for (e = secinfo->excls; e != NULL; e = e->next)
{
bfd_byte *excl_sym;
BFD_ASSERT (e->offset < stabsec->rawsize);
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->rawsize;
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->size / STABSIZE - 1,
tosym + DESCOFF);
}
tosym += STABSIZE;
}
}
BFD_ASSERT ((bfd_size_type) (tosym - contents) == stabsec->size);
return bfd_set_section_contents (output_bfd, stabsec->output_section,
contents, (file_ptr) stabsec->output_offset,
stabsec->size);
}
bfd_boolean
_bfd_write_stab_strings (bfd *output_bfd, struct stab_info *sinfo)
{
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->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);
return TRUE;
}
bfd_vma
_bfd_stab_section_offset (asection *stabsec,
void * psecinfo,
bfd_vma offset)
{
struct stab_section_info *secinfo;
secinfo = (struct stab_section_info *) psecinfo;
if (secinfo == NULL)
return offset;
if (offset >= stabsec->rawsize)
return offset - stabsec->rawsize + stabsec->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;
}