|  | /* Stabs in sections linking support. | 
|  | Copyright (C) 1996-2023 Free Software Foundation, Inc. | 
|  | Written by Ian Lance Taylor, Cygnus Support. | 
|  |  | 
|  | This file is part of BFD, the Binary File Descriptor library. | 
|  |  | 
|  | 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 contains support for linking stabs in sections, as used | 
|  | on COFF and ELF.  */ | 
|  |  | 
|  | #include "sysdep.h" | 
|  | #include "bfd.h" | 
|  | #include "libbfd.h" | 
|  | #include "aout/stab_gnu.h" | 
|  | #include "safe-ctype.h" | 
|  |  | 
|  | /* Stabs entries use a 12 byte format: | 
|  | 4 byte string table index | 
|  | 1 byte stab type | 
|  | 1 byte stab other field | 
|  | 2 byte stab desc field | 
|  | 4 byte stab value | 
|  | FIXME: This will have to change for a 64 bit object format. | 
|  |  | 
|  | The stabs symbols are divided into compilation units.  For the | 
|  | first entry in each unit, the type of 0, the value is the length of | 
|  | the string table for this unit, and the desc field is the number of | 
|  | stabs symbols for this unit.  */ | 
|  |  | 
|  | #define STRDXOFF  0 | 
|  | #define TYPEOFF   4 | 
|  | #define OTHEROFF  5 | 
|  | #define DESCOFF   6 | 
|  | #define VALOFF    8 | 
|  | #define STABSIZE  12 | 
|  |  | 
|  | /* A linked list of totals that we have found for a particular header | 
|  | file.  A total is a unique identifier for a particular BINCL...EINCL | 
|  | sequence of STABs that can be used to identify duplicate sequences. | 
|  | It consists of three fields, 'sum_chars' which is the sum of all the | 
|  | STABS characters; 'num_chars' which is the number of these charactes | 
|  | and 'symb' which is a buffer of all the symbols in the sequence.  This | 
|  | buffer is only checked as a last resort.  */ | 
|  |  | 
|  | struct stab_link_includes_totals | 
|  | { | 
|  | struct stab_link_includes_totals *next; | 
|  | bfd_vma sum_chars;  /* Accumulated sum of STABS characters.  */ | 
|  | bfd_vma num_chars;  /* Number of STABS characters.  */ | 
|  | const char* symb;   /* The STABS characters themselves.  */ | 
|  | }; | 
|  |  | 
|  | /* An entry in the header file hash table.  */ | 
|  |  | 
|  | struct stab_link_includes_entry | 
|  | { | 
|  | struct bfd_hash_entry root; | 
|  | /* List of totals we have found for this file.  */ | 
|  | struct stab_link_includes_totals *totals; | 
|  | }; | 
|  |  | 
|  | /* This structure is used to hold a list of N_BINCL symbols, some of | 
|  | which might be converted into N_EXCL symbols.  */ | 
|  |  | 
|  | struct stab_excl_list | 
|  | { | 
|  | /* The next symbol to convert.  */ | 
|  | struct stab_excl_list *next; | 
|  | /* The offset to this symbol in the section contents.  */ | 
|  | bfd_size_type offset; | 
|  | /* The value to use for the symbol.  */ | 
|  | bfd_vma val; | 
|  | /* The type of this symbol (N_BINCL or N_EXCL).  */ | 
|  | int type; | 
|  | }; | 
|  |  | 
|  | /* This structure is stored with each .stab section.  */ | 
|  |  | 
|  | struct stab_section_info | 
|  | { | 
|  | /* This is a linked list of N_BINCL symbols which should be | 
|  | converted into N_EXCL symbols.  */ | 
|  | struct stab_excl_list *excls; | 
|  |  | 
|  | /* This is used to map input stab offsets within their sections | 
|  | to output stab offsets, to take into account stabs that have | 
|  | been deleted.  If it is NULL, the output offsets are the same | 
|  | as the input offsets, because no stabs have been deleted from | 
|  | this section.  Otherwise the i'th entry is the number of | 
|  | bytes of stabs that have been deleted prior to the i'th | 
|  | stab.  */ | 
|  | bfd_size_type *cumulative_skips; | 
|  |  | 
|  | /* This is an array of string indices.  For each stab symbol, we | 
|  | store the string index here.  If a stab symbol should not be | 
|  | included in the final output, the string index is -1.  */ | 
|  | bfd_size_type stridxs[1]; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | EXTERNAL | 
|  | .{* This structure is used to keep track of stabs in sections | 
|  | .   information while linking.  *} | 
|  | . | 
|  | .struct stab_info | 
|  | .{ | 
|  | .  {* A hash table used to hold stabs strings.  *} | 
|  | .  struct bfd_strtab_hash *strings; | 
|  | .  {* The header file hash table.  *} | 
|  | .  struct bfd_hash_table includes; | 
|  | .  {* The first .stabstr section.  *} | 
|  | .  struct bfd_section *stabstr; | 
|  | .}; | 
|  | . | 
|  | */ | 
|  |  | 
|  | /* The function to create a new entry in the header file hash table.  */ | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /* Allocate the structure if it has not already been allocated by a | 
|  | subclass.  */ | 
|  | if (ret == NULL) | 
|  | ret = (struct stab_link_includes_entry *) | 
|  | bfd_hash_allocate (table, sizeof (struct stab_link_includes_entry)); | 
|  | if (ret == NULL) | 
|  | return NULL; | 
|  |  | 
|  | /* Call the allocation method of the superclass.  */ | 
|  | ret = ((struct stab_link_includes_entry *) | 
|  | bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); | 
|  | if (ret) | 
|  | /* Set local fields.  */ | 
|  | ret->totals = NULL; | 
|  |  | 
|  | return (struct bfd_hash_entry *) ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | INTERNAL_FUNCTION | 
|  | _bfd_link_section_stabs | 
|  |  | 
|  | SYNOPSIS | 
|  | bool _bfd_link_section_stabs | 
|  | (bfd *, struct stab_info *, asection *, asection *, void **, | 
|  | bfd_size_type *); | 
|  |  | 
|  | DESCRIPTION | 
|  | This function is called for each input file from the add_symbols | 
|  | pass of the linker. | 
|  | */ | 
|  |  | 
|  | bool | 
|  | _bfd_link_section_stabs (bfd *abfd, | 
|  | struct stab_info *sinfo, | 
|  | asection *stabsec, | 
|  | asection *stabstrsec, | 
|  | void * *psecinfo, | 
|  | bfd_size_type *pstring_offset) | 
|  | { | 
|  | bool 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 | 
|  | || (stabsec->flags & SEC_HAS_CONTENTS) == 0 | 
|  | || (stabstrsec->flags & SEC_HAS_CONTENTS) == 0) | 
|  | /* This file does not contain stabs debugging information.  */ | 
|  | return true; | 
|  |  | 
|  | if (stabsec->size % STABSIZE != 0) | 
|  | /* Something is wrong with the format of these stab symbols. | 
|  | Don't try to optimize them.  */ | 
|  | return true; | 
|  |  | 
|  | if ((stabstrsec->flags & SEC_RELOC) != 0) | 
|  | /* We shouldn't see relocations in the strings, and we aren't | 
|  | prepared to handle them.  */ | 
|  | return true; | 
|  |  | 
|  | if (bfd_is_abs_section (stabsec->output_section) | 
|  | || bfd_is_abs_section (stabstrsec->output_section)) | 
|  | /* At least one of the sections is being discarded from the | 
|  | link, so we should just ignore them.  */ | 
|  | return true; | 
|  |  | 
|  | first = false; | 
|  |  | 
|  | if (sinfo->stabstr == NULL) | 
|  | { | 
|  | flagword flags; | 
|  |  | 
|  | /* Initialize the stabs information we need to keep track of.  */ | 
|  | first = true; | 
|  | sinfo->strings = _bfd_stringtab_init (); | 
|  | if (sinfo->strings == NULL) | 
|  | goto error_return; | 
|  | /* Make sure the first byte is zero.  */ | 
|  | (void) _bfd_stringtab_add (sinfo->strings, "", true, true); | 
|  | if (! bfd_hash_table_init (&sinfo->includes, | 
|  | stab_link_includes_newfunc, | 
|  | sizeof (struct stab_link_includes_entry))) | 
|  | goto error_return; | 
|  | flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING | 
|  | | SEC_LINKER_CREATED); | 
|  | sinfo->stabstr = bfd_make_section_anyway_with_flags (abfd, ".stabstr", | 
|  | flags); | 
|  | if (sinfo->stabstr == NULL) | 
|  | goto error_return; | 
|  | } | 
|  |  | 
|  | /* Initialize the information we are going to store for this .stab | 
|  | section.  */ | 
|  | 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)); | 
|  |  | 
|  | /* Read the stabs information from abfd.  */ | 
|  | if (!bfd_malloc_and_get_section (abfd, stabsec, &stabbuf) | 
|  | || !bfd_malloc_and_get_section (abfd, stabstrsec, &stabstrbuf)) | 
|  | goto error_return; | 
|  |  | 
|  | /* Look through the stabs symbols, work out the new string indices, | 
|  | and identify N_BINCL symbols which can be eliminated.  */ | 
|  | stroff = 0; | 
|  | /* The stabs sections can be split when | 
|  | -split-by-reloc/-split-by-file is used.  We must keep track of | 
|  | each stab section's place in the single concatenated string | 
|  | table.  */ | 
|  | 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) | 
|  | /* This symbol has already been handled by an N_BINCL pass.  */ | 
|  | continue; | 
|  |  | 
|  | type = sym[TYPEOFF]; | 
|  |  | 
|  | if (type == 0) | 
|  | { | 
|  | /* Special type 0 stabs indicate the offset to the next | 
|  | string table.  We only copy the very first one.  */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Store the string in the hash table, and record the index.  */ | 
|  | symstroff = stroff + bfd_get_32 (abfd, sym + STRDXOFF); | 
|  | if (symstroff >= stabstrsec->size) | 
|  | { | 
|  | _bfd_error_handler | 
|  | /* xgettext:c-format */ | 
|  | (_("%pB(%pA+%#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); | 
|  |  | 
|  | /* An N_BINCL symbol indicates the start of the stabs entries | 
|  | for a header file.  We need to scan ahead to the next N_EINCL | 
|  | symbol, ignoring nesting, adding up all the characters in the | 
|  | symbol names, not including the file numbers in types (the | 
|  | first number after an open parenthesis).  */ | 
|  | 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 = (char *) bfd_realloc_or_free (symb, buf_len); | 
|  | if (symb == NULL) | 
|  | goto error_return; | 
|  | symb_rover = symb + num_chars; | 
|  | } | 
|  | * symb_rover ++ = * str; | 
|  | sum_chars += *str; | 
|  | num_chars ++; | 
|  | if (*str == '(') | 
|  | { | 
|  | /* Skip the file number.  */ | 
|  | ++str; | 
|  | while (ISDIGIT (*str)) | 
|  | ++str; | 
|  | --str; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | BFD_ASSERT (num_chars == (bfd_vma) (symb_rover - symb)); | 
|  |  | 
|  | /* If we have already included a header file with the same | 
|  | value, then replaced this one with an N_EXCL symbol.  */ | 
|  | 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; | 
|  |  | 
|  | /* Record this symbol, so that we can set the value | 
|  | correctly.  */ | 
|  | amt = sizeof *ne; | 
|  | ne = (struct stab_excl_list *) 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) | 
|  | { | 
|  | /* This is the first time we have seen this header file | 
|  | with this set of stabs strings.  */ | 
|  | t = (struct stab_link_includes_totals *) | 
|  | bfd_hash_allocate (&sinfo->includes, sizeof *t); | 
|  | if (t == NULL) | 
|  | goto error_return; | 
|  | t->sum_chars = sum_chars; | 
|  | t->num_chars = num_chars; | 
|  | /* Trim data down.  */ | 
|  | t->symb = symb = (char *) bfd_realloc_or_free (symb, num_chars); | 
|  | t->next = incl_entry->totals; | 
|  | incl_entry->totals = t; | 
|  | } | 
|  | else | 
|  | { | 
|  | bfd_size_type *incl_pstridx; | 
|  |  | 
|  | /* We have seen this header file before.  Tell the final | 
|  | pass to change the type to N_EXCL.  */ | 
|  | ne->type = (int) N_EXCL; | 
|  |  | 
|  | /* Free off superfluous symbols.  */ | 
|  | free (symb); | 
|  |  | 
|  | /* Mark the skipped symbols.  */ | 
|  |  | 
|  | 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) | 
|  | /* Keep existing exclusion marks.  */ | 
|  | continue; | 
|  | else if (nest == 0) | 
|  | { | 
|  | *incl_pstridx = (bfd_size_type) -1; | 
|  | ++skip; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | free (stabbuf); | 
|  | stabbuf = NULL; | 
|  | free (stabstrbuf); | 
|  | stabstrbuf = NULL; | 
|  |  | 
|  | /* We need to set the section sizes such that the linker will | 
|  | compute the output section sizes correctly.  We set the .stab | 
|  | size to not include the entries we don't want.  We set | 
|  | SEC_EXCLUDE for the .stabstr section, so that it will be dropped | 
|  | from the link.  We record the size of the strtab in the first | 
|  | .stabstr section we saw, and make sure we don't set SEC_EXCLUDE | 
|  | for that section.  */ | 
|  | stabsec->size = (count - skip) * STABSIZE; | 
|  | if (stabsec->size == 0) | 
|  | stabsec->flags |= SEC_EXCLUDE | SEC_KEEP; | 
|  | stabstrsec->flags |= SEC_EXCLUDE | SEC_KEEP; | 
|  | sinfo->stabstr->size = _bfd_stringtab_size (sinfo->strings); | 
|  |  | 
|  | /* Calculate the `cumulative_skips' array now that stabs have been | 
|  | deleted for this section.  */ | 
|  |  | 
|  | 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: | 
|  | free (stabbuf); | 
|  | free (stabstrbuf); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | INTERNAL_FUNCTION | 
|  | _bfd_discard_section_stabs | 
|  |  | 
|  | SYNOPSIS | 
|  | bool _bfd_discard_section_stabs | 
|  | (bfd *, asection *, void *, bool (*) (bfd_vma, void *), void *); | 
|  |  | 
|  | DESCRIPTION | 
|  | This function is called for each input file before the stab | 
|  | section is relocated.  It discards stab entries for discarded | 
|  | functions and variables.  The function returns TRUE iff | 
|  | any entries have been deleted. | 
|  | */ | 
|  |  | 
|  | bool | 
|  | _bfd_discard_section_stabs (bfd *abfd, | 
|  | asection *stabsec, | 
|  | void * psecinfo, | 
|  | bool (*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 || (stabsec->flags & SEC_HAS_CONTENTS) == 0) | 
|  | /* This file does not contain stabs debugging information.  */ | 
|  | return false; | 
|  |  | 
|  | if (stabsec->size % STABSIZE != 0) | 
|  | /* Something is wrong with the format of these stab symbols. | 
|  | Don't try to optimize them.  */ | 
|  | return false; | 
|  |  | 
|  | if ((stabsec->output_section != NULL | 
|  | && bfd_is_abs_section (stabsec->output_section))) | 
|  | /* At least one of the sections is being discarded from the | 
|  | link, so we should just ignore them.  */ | 
|  | return false; | 
|  |  | 
|  | /* We should have initialized our data in _bfd_link_section_stabs. | 
|  | If there was some bizarre error reading the string sections, though, | 
|  | we might not have.  Bail rather than asserting.  */ | 
|  | if (psecinfo == NULL) | 
|  | return false; | 
|  |  | 
|  | count = stabsec->rawsize / STABSIZE; | 
|  | secinfo = (struct stab_section_info *) psecinfo; | 
|  |  | 
|  | /* Read the stabs information from abfd.  */ | 
|  | if (!bfd_malloc_and_get_section (abfd, stabsec, &stabbuf)) | 
|  | goto error_return; | 
|  |  | 
|  | /* Look through the stabs symbols and discard any information for | 
|  | discarded functions.  */ | 
|  | 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) | 
|  | /* This stab was deleted in a previous pass.  */ | 
|  | 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) | 
|  | { | 
|  | /* Outside of a function.  Check for deleted variables.  */ | 
|  | if (type == (int) N_STSYM || type == (int) N_LCSYM) | 
|  | if ((*reloc_symbol_deleted_p) (sym + VALOFF - stabbuf, cookie)) | 
|  | { | 
|  | *pstridx = -1; | 
|  | skip ++; | 
|  | } | 
|  | /* We should also check for N_GSYM entries which reference a | 
|  | deleted global, but those are less harmful to debuggers | 
|  | and would require parsing the stab strings.  */ | 
|  | } | 
|  | } | 
|  |  | 
|  | free (stabbuf); | 
|  | stabbuf = NULL; | 
|  |  | 
|  | /* Shrink the stabsec as needed.  */ | 
|  | stabsec->size -= skip * STABSIZE; | 
|  | if (stabsec->size == 0) | 
|  | stabsec->flags |= SEC_EXCLUDE | SEC_KEEP; | 
|  |  | 
|  | /* Recalculate the `cumulative_skips' array now that stabs have been | 
|  | deleted for this section.  */ | 
|  |  | 
|  | 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: | 
|  | free (stabbuf); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | INTERNAL_FUNCTION | 
|  | _bfd_write_section_stabs | 
|  |  | 
|  | SYNOPSIS | 
|  | bool _bfd_write_section_stabs | 
|  | (bfd *, struct stab_info *, asection *, void **, bfd_byte *); | 
|  |  | 
|  | DESCRIPTION | 
|  | Write out the stab section.  This is called with the relocated | 
|  | contents. | 
|  | */ | 
|  |  | 
|  | bool | 
|  | _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); | 
|  |  | 
|  | /* Handle each N_BINCL entry.  */ | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Copy over all the stabs symbols, omitting the ones we don't want, | 
|  | and correcting the string indices for those we do want.  */ | 
|  | 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) | 
|  | { | 
|  | /* This is the header symbol for the stabs section.  We | 
|  | don't really need one, since we have merged all the | 
|  | input stabs sections into one, but we generate one | 
|  | for the benefit of readers which expect to see one.  */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* | 
|  | INTERNAL_FUNCTION | 
|  | _bfd_write_stab_strings | 
|  |  | 
|  | SYNOPSIS | 
|  | bool _bfd_write_stab_strings (bfd *, struct stab_info *); | 
|  |  | 
|  | DESCRIPTION | 
|  | Write out the .stabstr section. | 
|  | */ | 
|  |  | 
|  | bool | 
|  | _bfd_write_stab_strings (bfd *output_bfd, struct stab_info *sinfo) | 
|  | { | 
|  | if (bfd_is_abs_section (sinfo->stabstr->output_section)) | 
|  | /* The section was discarded from the link.  */ | 
|  | 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; | 
|  |  | 
|  | /* We no longer need the stabs information.  */ | 
|  | _bfd_stringtab_free (sinfo->strings); | 
|  | bfd_hash_table_free (&sinfo->includes); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | INTERNAL_FUNCTION | 
|  | _bfd_stab_section_offset | 
|  |  | 
|  | SYNOPSIS | 
|  | bfd_vma _bfd_stab_section_offset (asection *, void *, bfd_vma); | 
|  |  | 
|  | DESCRIPTION | 
|  | Adjust an address in the .stab section.  Given OFFSET within | 
|  | STABSEC, this returns the new offset in the adjusted stab section, | 
|  | or -1 if the address refers to a stab which has been removed. | 
|  | */ | 
|  |  | 
|  | 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; | 
|  | } |