| /* Stabs in sections linking support. | 
 |    Copyright (C) 1996-2022 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]; | 
 | }; | 
 |  | 
 |  | 
 | /* 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; | 
 | } | 
 |  | 
 | /* 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) | 
 |     /* 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; | 
 | } | 
 |  | 
 | /* 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) | 
 |     /* 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; | 
 | } | 
 |  | 
 | /* 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); | 
 | } | 
 |  | 
 | /* 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; | 
 | } | 
 |  | 
 | /* 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; | 
 | } |