[PATCH v3 3/7] bfd: Improve nm and objdump without section header

Alan Modra amodra@gmail.com
Tue Jun 6 06:44:49 GMT 2023


On Mon, Jun 05, 2023 at 08:32:18AM -0700, H.J. Lu wrote:
> --- a/bfd/elf.c
> +++ b/bfd/elf.c
> @@ -397,6 +397,17 @@ bfd_elf_get_elf_syms (bfd *ibfd,
>    if (symcount == 0)
>      return intsym_buf;
>  
> +  if (elf_use_dt_symtab_p (ibfd))
> +    {
> +      /* Use dynamic symbol table.  */
> +      if (elf_tdata (ibfd)->dt_symtab_count != (symcount + symoffset))

Nitpick: excess parentheses.

> +	{
> +	  bfd_set_error (bfd_error_invalid_operation);
> +	  return NULL;
> +	}
> +      return elf_tdata (ibfd)->dt_symtab + symoffset;
> +    }
> +
>    /* Normal syms might have section extension entries.  */
>    shndx_hdr = NULL;
>    if (elf_symtab_shndx_list (ibfd) != NULL)
> @@ -1873,6 +1884,564 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
>    return false;
>  }
>  
> +/* Find the file offset corresponding to VMA by using the program
> +   headers.  */
> +
> +static file_ptr
> +offset_from_vma (Elf_Internal_Phdr *phdrs, size_t phnum, bfd_vma vma,
> +		 size_t size, size_t *max_size_p)
> +{
> +  Elf_Internal_Phdr *seg;
> +  size_t i;
> +
> +  for (seg = phdrs, i = 0; i < phnum; ++seg, ++i)
> +    if (seg->p_type == PT_LOAD
> +	&& vma >= (seg->p_vaddr & -seg->p_align)
> +	&& vma + size <= seg->p_vaddr + seg->p_filesz)
> +      {
> +	if (max_size_p)
> +	  *max_size_p = seg->p_vaddr + seg->p_filesz - vma;
> +	return (file_ptr) (vma - seg->p_vaddr + seg->p_offset);

No need for the cast here (and then no need for parens).

> +      }
> +
> +  bfd_set_error (bfd_error_invalid_operation);
> +  return (file_ptr) -1;
> +}
> +
> +/* Convert hash table to internal form.  */
> +
> +static bfd_vma *
> +get_hash_table_data (bfd *abfd, bfd_size_type number,
> +		     unsigned int ent_size, bfd_size_type filesize)
> +{
> +  unsigned char *e_data = NULL;
> +  bfd_vma *i_data = NULL;
> +  bfd_size_type size;
> +
> +  if (ent_size != 4 && ent_size != 8)
> +    return NULL;
> +
> +  /* If the size_t type is smaller than the bfd_size_type, eg because
> +     you are building a 32-bit tool on a 64-bit host, then make sure
> +     that when (number) is cast to (size_t) no information is lost.  */
> +  if (sizeof (size_t) < sizeof (bfd_size_type)
> +      && (bfd_size_type) ((size_t) number) != number)
> +    {

Just "if ((size_t) number != number)" will do.

> +      bfd_set_error (bfd_error_file_too_big);
> +      return NULL;
> +    }
> +
> +  size = ent_size * number;
> +  /* Be kind to memory checkers (eg valgrind, address sanitizer) by not
> +     attempting to allocate memory when the read is bound to fail.  */
> +  if (size > filesize
> +      || number >= ~(size_t) 0 / ent_size
> +      || number >= ~(size_t) 0 / sizeof (*i_data))
> +    {
> +      bfd_set_error (bfd_error_file_too_big);
> +      return NULL;
> +    }
> +
> +  e_data = _bfd_malloc_and_read (abfd, size, size);
> +  if (e_data == NULL)
> +    return NULL;
> +
> +  i_data = (bfd_vma *) bfd_malloc (number * sizeof (*i_data));
> +  if (i_data == NULL)
> +    {
> +      free (e_data);
> +      return NULL;
> +    }
> +
> +  if (ent_size == 4)
> +    while (number--)
> +      i_data[number] = bfd_get_32 (abfd, e_data + number * ent_size);
> +  else
> +    while (number--)
> +      i_data[number] = bfd_get_64 (abfd, e_data + number * ent_size);
> +
> +  free (e_data);
> +  return i_data;
> +}
> +
> +/* Address of .MIPS.xhash section.  FIXME: What is the best way to
> +   support DT_MIPS_XHASH?  */
> +#define DT_MIPS_XHASH	       0x70000036
> +
> +/* Reconstruct dynamic symbol table from PT_DYNAMIC segment.  */
> +
> +bool
> +_bfd_elf_get_dynamic_symbols (bfd *abfd, Elf_Internal_Phdr *phdr,
> +			      Elf_Internal_Phdr *phdrs, size_t phnum,
> +			      bfd_size_type filesize)
> +{
> +  bfd_byte *extdyn, *extdynend;
> +  size_t extdynsize;
> +  void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
> +  bool (*swap_symbol_in) (bfd *, const void *, const void *,
> +			  Elf_Internal_Sym *);
> +  Elf_Internal_Dyn dyn;
> +  bfd_vma dt_hash = 0;
> +  bfd_vma dt_gnu_hash = 0;
> +  bfd_vma dt_mips_xhash = 0;
> +  bfd_vma dt_strtab = 0;
> +  bfd_vma dt_symtab = 0;
> +  size_t dt_strsz = 0;
> +  bfd_vma dt_versym = 0;
> +  bfd_vma dt_verdef = 0;
> +  bfd_vma dt_verneed = 0;
> +  bfd_byte *dynbuf = NULL;
> +  char *strbuf = NULL;
> +  bfd_vma *gnubuckets = NULL;
> +  bfd_vma *gnuchains = NULL;
> +  bfd_vma *mipsxlat = NULL;
> +  file_ptr saved_filepos, filepos;
> +  bool res = false;
> +  size_t amt;
> +  bfd_byte *esymbuf = NULL, *esym;
> +  bfd_size_type symcount;
> +  Elf_Internal_Sym *isymbuf = NULL;
> +  Elf_Internal_Sym *isym, *isymend;
> +  bfd_byte *versym = NULL;
> +  bfd_byte *verdef = NULL;
> +  bfd_byte *verneed = NULL;
> +  size_t verdef_size;
> +  size_t verneed_size;
> +  size_t extsym_size;
> +  const struct elf_backend_data *bed;
> +
> +  /* Return TRUE if symbol table is bad.  */
> +  if (elf_bad_symtab (abfd))
> +    return true;
> +
> +  /* Return TRUE if DT_HASH/DT_GNU_HASH have bee processed before.  */
> +  if (elf_tdata (abfd)->dt_strtab != NULL)
> +    return true;
> +
> +  bed = get_elf_backend_data (abfd);
> +
> +  /* Save file position for elf_object_p.  */
> +  saved_filepos = bfd_tell (abfd);
> +
> +  if (bfd_seek (abfd, phdr->p_offset, SEEK_SET) != 0)
> +    goto error_return;
> +
> +  dynbuf = _bfd_malloc_and_read (abfd, phdr->p_filesz, phdr->p_filesz);
> +  if (dynbuf == NULL)
> +    goto error_return;
> +
> +  extsym_size = bed->s->sizeof_sym;
> +  extdynsize = bed->s->sizeof_dyn;
> +  swap_dyn_in = bed->s->swap_dyn_in;
> +
> +  extdyn = dynbuf;
> +  if (phdr->p_filesz < extdynsize)
> +    goto error_return;
> +  extdynend = extdyn + phdr->p_filesz;
> +  for (; extdyn <= (extdynend - extdynsize); extdyn += extdynsize)
> +    {
> +      swap_dyn_in (abfd, extdyn, &dyn);
> +
> +      if (dyn.d_tag == DT_NULL)
> +	break;
> +
> +      switch (dyn.d_tag)
> +	{
> +	case DT_HASH:
> +	  dt_hash = dyn.d_un.d_val;
> +	  break;
> +	case DT_GNU_HASH:
> +	  if (bed->elf_machine_code != EM_MIPS
> +	      && bed->elf_machine_code != EM_MIPS_RS3_LE)
> +	    dt_gnu_hash = dyn.d_un.d_val;
> +	  break;
> +	case DT_STRTAB:
> +	  dt_strtab = dyn.d_un.d_val;
> +	  break;
> +	case DT_SYMTAB:
> +	  dt_symtab = dyn.d_un.d_val;
> +	  break;
> +	case DT_STRSZ:
> +	  dt_strsz = dyn.d_un.d_val;
> +	  break;
> +	case DT_SYMENT:
> +	  if (dyn.d_un.d_val != extsym_size)
> +	    goto error_return;
> +	  break;
> +	case DT_VERSYM:
> +	  dt_versym = dyn.d_un.d_val;
> +	  break;
> +	case DT_VERDEF:
> +	  dt_verdef = dyn.d_un.d_val;
> +	  break;
> +	case DT_VERNEED:
> +	  dt_verneed = dyn.d_un.d_val;
> +	  break;
> +	default:
> +	  if (dyn.d_tag == DT_MIPS_XHASH
> +	      && (bed->elf_machine_code == EM_MIPS
> +		  || bed->elf_machine_code == EM_MIPS_RS3_LE))
> +	    {
> +	      dt_gnu_hash = dyn.d_un.d_val;
> +	      dt_mips_xhash = dyn.d_un.d_val;
> +	    }
> +	  break;
> +	}
> +    }
> +
> +  /* Check if we can reconstruct dynamic symbol table from PT_DYNAMIC
> +     segment.  */
> +  if ((!dt_hash && !dt_gnu_hash)
> +      || !dt_strtab
> +      || !dt_symtab
> +      || !dt_strsz)
> +    goto error_return;
> +
> +  /* Get dynamic string table.  */
> +  filepos = offset_from_vma (phdrs, phnum, dt_strtab, dt_strsz, NULL);
> +  if (filepos == (file_ptr) -1
> +      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +    goto error_return;
> +
> +  /* Dynamic string table must be valid until ABFD is closed.  */
> +  strbuf = (char *) _bfd_alloc_and_read (abfd, dt_strsz, dt_strsz);
> +  if (strbuf == NULL)
> +    goto error_return;
> +
> +  /* Get the real symbol count from DT_HASH or DT_GNU_HASH.  Prefer
> +     DT_HASH since it is simpler than DT_GNU_HASH.  */
> +  if (dt_hash)
> +    {
> +      unsigned char nb[16];
> +      unsigned int hash_ent_size;
> +
> +      switch (bed->elf_machine_code)
> +	{
> +	case EM_ALPHA:
> +	case EM_S390:
> +	case EM_S390_OLD:
> +	  if (bed->s->elfclass == ELFCLASS64)
> +	    {
> +	      hash_ent_size = 8;
> +	      break;
> +	    }
> +	  /* FALLTHROUGH */
> +	default:
> +	  hash_ent_size = 4;
> +	  break;
> +	}
> +
> +      filepos = offset_from_vma (phdrs, phnum, dt_hash, sizeof (nb),
> +				 NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0
> +	  || (bfd_bread (nb, 2 * hash_ent_size, abfd)
> +	      != (2 * hash_ent_size)))
> +	goto error_return;
> +
> +      /* The number of dynamic symbol table entries equals the number
> +	 of chains.  */
> +      if (hash_ent_size == 8)
> +	symcount = bfd_get_64 (abfd, nb + hash_ent_size);
> +      else
> +	symcount = bfd_get_32 (abfd, nb + hash_ent_size);
> +    }
> +  else
> +    {
> +      /* For DT_GNU_HASH, only defined symbols with non-STB_LOCAL
> +	 bindings are in hash table.  Since in dynamic symbol table,
> +	 all symbols with STB_LOCAL binding are placed before symbols
> +	 with other bindings and all undefined symbols are placed
> +	 before defined ones, the highest symbol index in DT_GNU_HASH
> +	 is the highest dynamic symbol table index.  */
> +      unsigned char nb[16];
> +      bfd_vma ngnubuckets;
> +      bfd_vma gnusymidx;
> +      size_t i, ngnuchains;
> +      bfd_vma maxchain = 0xffffffff, bitmaskwords;
> +      bfd_vma buckets_vma;
> +
> +      filepos = offset_from_vma (phdrs, phnum, dt_gnu_hash,
> +				 sizeof (nb), NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0
> +	  || bfd_bread (nb, sizeof (nb), abfd) != sizeof (nb))
> +	goto error_return;
> +
> +      ngnubuckets = bfd_get_32 (abfd, nb);
> +      gnusymidx = bfd_get_32 (abfd, nb + 4);
> +      bitmaskwords = bfd_get_32 (abfd, nb + 8);
> +      buckets_vma = dt_gnu_hash + 16;
> +      if (bed->s->elfclass == ELFCLASS32)
> +	buckets_vma += bitmaskwords * 4;
> +      else
> +	buckets_vma += bitmaskwords * 8;
> +      filepos = offset_from_vma (phdrs, phnum, buckets_vma, 4, NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	goto error_return;
> +
> +      gnubuckets = get_hash_table_data (abfd, ngnubuckets, 4, filesize);
> +      if (gnubuckets == NULL)
> +	goto error_return;
> +
> +      for (i = 0; i < ngnubuckets; i++)
> +	if (gnubuckets[i] != 0)
> +	  {
> +	    if (gnubuckets[i] < gnusymidx)
> +	      goto error_return;
> +
> +	    if (maxchain == 0xffffffff || gnubuckets[i] > maxchain)
> +	      maxchain = gnubuckets[i];
> +	  }
> +
> +      if (maxchain == 0xffffffff)
> +	{
> +	  symcount = 0;
> +	  goto empty_gnu_hash;
> +	}
> +
> +      maxchain -= gnusymidx;
> +      filepos = offset_from_vma (phdrs, phnum,
> +				 (buckets_vma +
> +				  4 * (ngnubuckets + maxchain)),
> +				 4, NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	goto error_return;
> +
> +      do
> +	{
> +	  if (bfd_bread (nb, 4, abfd) != 4)
> +	    goto error_return;
> +	  ++maxchain;
> +	  if (maxchain == 0)
> +	    goto error_return;
> +	}
> +      while ((bfd_get_32 (abfd, nb) & 1) == 0);
> +
> +      filepos = offset_from_vma (phdrs, phnum,
> +				 (buckets_vma + 4 * ngnubuckets),
> +				 4, NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	goto error_return;
> +
> +      gnuchains = get_hash_table_data (abfd, maxchain, 4, filesize);
> +      if (gnubuckets == NULL)
> +	goto error_return;
> +      ngnuchains = maxchain;
> +
> +      if (dt_mips_xhash)
> +	{
> +	  filepos = offset_from_vma (phdrs, phnum,
> +				     (buckets_vma
> +				      + 4 * (ngnubuckets + maxchain)),
> +				     4, NULL);
> +	  if (filepos == (file_ptr) -1
> +	      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	    goto error_return;
> +
> +	  mipsxlat = get_hash_table_data (abfd, maxchain, 4, filesize);
> +	  if (mipsxlat == NULL)
> +	    goto error_return;
> +	}
> +
> +      symcount = 0;
> +      for (i = 0; i < ngnubuckets; ++i)
> +	if (gnubuckets[i] != 0)
> +	  {
> +	    bfd_vma si = gnubuckets[i];
> +	    bfd_vma off = si - gnusymidx;
> +	    do
> +	      {
> +		if (mipsxlat)
> +		  {
> +		    if (mipsxlat[off] >= symcount)
> +		      symcount = mipsxlat[off] + 1;
> +		  }
> +		else
> +		  {
> +		    if (si >= symcount)
> +		      symcount = si + 1;
> +		  }
> +		si++;
> +	      }
> +	    while (off < ngnuchains && (gnuchains[off++] & 1) == 0);
> +	  }
> +    }
> +
> +  /* Swap in dynamic symbol table.  */
> +  if (_bfd_mul_overflow (symcount, extsym_size, &amt))
> +    {
> +      bfd_set_error (bfd_error_file_too_big);
> +      goto error_return;
> +    }
> +
> +  filepos = offset_from_vma (phdrs, phnum, dt_symtab, amt, NULL);
> +  if (filepos == (file_ptr) -1
> +      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +    goto error_return;
> +  esymbuf = _bfd_malloc_and_read (abfd, amt, amt);
> +  if (esymbuf == NULL)
> +    goto error_return;
> +
> +  if (_bfd_mul_overflow (symcount, sizeof (Elf_Internal_Sym), &amt))
> +    {
> +      bfd_set_error (bfd_error_file_too_big);
> +      goto error_return;
> +    }
> +
> +  /* Dynamic symbol table must be valid until ABFD is closed.  */
> +  isymbuf = (Elf_Internal_Sym *) bfd_alloc (abfd, amt);
> +  if (isymbuf == NULL)
> +    goto error_return;
> +
> +  swap_symbol_in = bed->s->swap_symbol_in;
> +
> +  /* Convert the symbols to internal form.  */
> +  isymend = isymbuf + symcount;
> +  for (esym = esymbuf, isym = isymbuf;
> +       isym < isymend;
> +       esym += extsym_size, isym++)
> +    if (!swap_symbol_in (abfd, esym, NULL, isym)
> +	|| isym->st_name >= dt_strsz)
> +      {
> +	bfd_set_error (bfd_error_invalid_operation);
> +	goto error_return;
> +      }
> +
> +  if (dt_versym)
> +    {
> +      /* Swap in DT_VERSYM.  */
> +      if (_bfd_mul_overflow (symcount, 2, &amt))
> +	{
> +	  bfd_set_error (bfd_error_file_too_big);
> +	  goto error_return;
> +	}
> +
> +      filepos = offset_from_vma (phdrs, phnum, dt_versym, amt, NULL);
> +      if (filepos == (file_ptr) -1
> +	  || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	goto error_return;
> +
> +      /* DT_VERSYM info must be valid until ABFD is closed.  */
> +      versym = _bfd_alloc_and_read (abfd, amt, amt);
> +
> +      if (dt_verdef)
> +	{
> +	  /* Read in DT_VERDEF.  */
> +	  filepos = offset_from_vma (phdrs, phnum, dt_verdef,
> +				     0, &verdef_size);
> +	  if (filepos == (file_ptr) -1
> +	      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	    goto error_return;
> +
> +	  /* DT_VERDEF info must be valid until ABFD is closed.  */
> +	  verdef = _bfd_alloc_and_read (abfd, verdef_size,
> +					verdef_size);
> +	}
> +
> +      if (dt_verneed)
> +	{
> +	  /* Read in DT_VERNEED.  */
> +	  filepos = offset_from_vma (phdrs, phnum, dt_verneed,
> +				     0, &verneed_size);
> +	  if (filepos == (file_ptr) -1
> +	      || bfd_seek (abfd, filepos, SEEK_SET) != 0)
> +	    goto error_return;
> +
> +	  /* DT_VERNEED info must be valid until ABFD is closed.  */
> +	  verneed = _bfd_alloc_and_read (abfd, verneed_size,
> +					 verneed_size);
> +	}
> +    }
> +
> + empty_gnu_hash:
> +  elf_tdata (abfd)->dt_strtab = strbuf;
> +  elf_tdata (abfd)->dt_symtab = isymbuf;
> +  elf_tdata (abfd)->dt_symtab_count = symcount;
> +  elf_tdata (abfd)->dt_versym = versym;
> +  elf_tdata (abfd)->dt_verdef = verdef;
> +  elf_tdata (abfd)->dt_verneed = verneed;
> +  elf_tdata (abfd)->dt_verdef_count
> +    = verdef_size / sizeof (Elf_External_Verdef);
> +  elf_tdata (abfd)->dt_verneed_count
> +    = verneed_size / sizeof (Elf_External_Verneed);

These are all bfd_alloc by the look of it, and we'll have file size
sanity checks done before any alloc.  Good!

> +
> +  res = true;
> +
> + error_return:
> +  /* Restore file position for elf_object_p.  */
> +  if (bfd_seek (abfd, saved_filepos, SEEK_SET) != 0)
> +    res = false;
> +  if (dynbuf != NULL)
> +    free (dynbuf);
> +  if (esymbuf != NULL)
> +    free (esymbuf);
> +  if (gnubuckets != NULL)
> +    free (gnubuckets);
> +  if (gnuchains != NULL)
> +    free (gnuchains);
> +  if (mipsxlat != NULL)
> +    free (mipsxlat);

These days we just write free (x) without if (x != NULL).

> +  return res;
> +}
> +
> +/* Reconstruct section from dynamic symbol.  */
> +
> +asection *
> +_bfd_elf_get_section_from_dynamic_symbol (bfd *abfd,
> +					  Elf_Internal_Sym *isym)
> +{
> +  asection *sec;
> +  flagword flags;
> +
> +  if (!elf_use_dt_symtab_p (abfd))
> +    return NULL;
> +
> +  flags = SEC_ALLOC | SEC_LOAD;
> +  switch (ELF_ST_TYPE (isym->st_info))
> +    {
> +    case STT_FUNC:
> +    case STT_GNU_IFUNC:
> +      sec = bfd_get_section_by_name (abfd, ".text");
> +      if (sec == NULL)
> +	sec = bfd_make_section_with_flags (abfd,
> +					   ".text",
> +					   flags | SEC_CODE);
> +      break;
> +    case STT_COMMON:
> +      sec = bfd_com_section_ptr;
> +      break;
> +    case STT_OBJECT:
> +      sec = bfd_get_section_by_name (abfd, ".data");
> +      if (sec == NULL)
> +	sec = bfd_make_section_with_flags (abfd,
> +					   ".data",
> +					   flags | SEC_DATA);
> +      break;
> +    case STT_TLS:
> +      sec = bfd_get_section_by_name (abfd, ".tdata");
> +      if (sec == NULL)
> +	sec = bfd_make_section_with_flags (abfd,
> +					   ".tdata",
> +					   (flags
> +					    | SEC_DATA
> +					    | SEC_THREAD_LOCAL));
> +      break;
> +    default:
> +      sec = bfd_abs_section_ptr;
> +      break;
> +    }
> +
> +  /* NB: Don't place this section in output.  */
> +  if (sec && 0)
> +    sec->output_section = bfd_abs_section_ptr;

Please delete this rather than leaving it disabled, and delete the comment.

> +
> +  return sec;
> +}
> +
>  /* Get version name.  If BASE_P is TRUE, return "Base" for VER_FLG_BASE
>     and return symbol version for symbol version itself.   */
>  
> @@ -1882,8 +2451,11 @@ _bfd_elf_get_symbol_version_string (bfd *abfd, asymbol *symbol,
>  				    bool *hidden)
>  {
>    const char *version_string = NULL;
> -  if (elf_dynversym (abfd) != 0
> -      && (elf_dynverdef (abfd) != 0 || elf_dynverref (abfd) != 0))
> +  if ((elf_dynversym (abfd) != 0
> +       && (elf_dynverdef (abfd) != 0 || elf_dynverref (abfd) != 0))
> +      || (elf_tdata (abfd)->dt_versym != NULL
> +	  && (elf_tdata (abfd)->dt_verdef != NULL
> +	      || elf_tdata (abfd)->dt_verneed != NULL)))
>      {
>        unsigned int vernum = ((elf_symbol_type *) symbol)->version;
>  
> @@ -8609,6 +9181,11 @@ _bfd_elf_get_dynamic_symtab_upper_bound (bfd *abfd)
>  
>    if (elf_dynsymtab (abfd) == 0)
>      {
> +      /* Check if there is dynamic symbol table.  */
> +      symcount = elf_tdata (abfd)->dt_symtab_count;
> +      if (symcount)
> +	goto compute_symtab_size;
> +
>        bfd_set_error (bfd_error_invalid_operation);
>        return -1;
>      }
> @@ -8619,6 +9196,8 @@ _bfd_elf_get_dynamic_symtab_upper_bound (bfd *abfd)
>        bfd_set_error (bfd_error_file_too_big);
>        return -1;
>      }
> +
> + compute_symtab_size:
>    symtab_size = symcount * (sizeof (asymbol *));
>    if (symcount == 0)
>      symtab_size = sizeof (asymbol *);
> @@ -8826,35 +9405,51 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>    unsigned int freeidx = 0;
>    size_t amt;
>  
> -  if (elf_dynverref (abfd) != 0)
> +  if (elf_dynverref (abfd) != 0 || elf_tdata (abfd)->dt_verneed != NULL)
>      {
>        Elf_Internal_Shdr *hdr;
>        Elf_External_Verneed *everneed;
>        Elf_Internal_Verneed *iverneed;
>        unsigned int i;
>        bfd_byte *contents_end;
> +      size_t verneed_count;
> +      size_t verneed_size;
>  
> -      hdr = &elf_tdata (abfd)->dynverref_hdr;
> -
> -      if (hdr->sh_info > hdr->sh_size / sizeof (Elf_External_Verneed))
> +      if (elf_tdata (abfd)->dt_verneed != NULL)
>  	{
> -	error_return_bad_verref:
> -	  _bfd_error_handler
> -	    (_("%pB: .gnu.version_r invalid entry"), abfd);
> -	  bfd_set_error (bfd_error_bad_value);
> -	error_return_verref:
> -	  elf_tdata (abfd)->verref = NULL;
> -	  elf_tdata (abfd)->cverrefs = 0;
> -	  goto error_return;
> +	  hdr = NULL;
> +	  contents = elf_tdata (abfd)->dt_verneed;
> +	  verneed_count = elf_tdata (abfd)->dt_verneed_count;
> +	  verneed_size = verneed_count * sizeof (Elf_External_Verneed);
>  	}
> +      else
> +	{
> +	  hdr = &elf_tdata (abfd)->dynverref_hdr;
>  
> -      if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
> -	goto error_return_verref;
> -      contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
> -      if (contents == NULL)
> -	goto error_return_verref;
> +	  if (hdr->sh_info > hdr->sh_size / sizeof (Elf_External_Verneed))
> +	    {
> +	    error_return_bad_verref:
> +	      _bfd_error_handler
> +		(_("%pB: .gnu.version_r invalid entry"), abfd);
> +	      bfd_set_error (bfd_error_bad_value);
> +	    error_return_verref:
> +	      elf_tdata (abfd)->verref = NULL;
> +	      elf_tdata (abfd)->cverrefs = 0;
> +	      goto error_return;
> +	    }
> +
> +	  if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
> +	    goto error_return_verref;
> +	  contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
> +	  if (contents == NULL)
> +	    goto error_return_verref;
> +
> +	  verneed_size = hdr->sh_size;
> +	  verneed_count = hdr->sh_info;
> +	}
>  
> -      if (_bfd_mul_overflow (hdr->sh_info, sizeof (Elf_Internal_Verneed), &amt))
> +      if (_bfd_mul_overflow (verneed_count,
> +			     sizeof (Elf_Internal_Verneed), &amt))
>  	{
>  	  bfd_set_error (bfd_error_file_too_big);
>  	  goto error_return_verref;
> @@ -8867,10 +9462,11 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  
>        BFD_ASSERT (sizeof (Elf_External_Verneed)
>  		  == sizeof (Elf_External_Vernaux));
> -      contents_end = contents + hdr->sh_size - sizeof (Elf_External_Verneed);
> +      contents_end = (contents + verneed_size
> +		      - sizeof (Elf_External_Verneed));
>        everneed = (Elf_External_Verneed *) contents;
>        iverneed = elf_tdata (abfd)->verref;
> -      for (i = 0; i < hdr->sh_info; i++, iverneed++)
> +      for (i = 0; i < verneed_count; i++, iverneed++)
>  	{
>  	  Elf_External_Vernaux *evernaux;
>  	  Elf_Internal_Vernaux *ivernaux;
> @@ -8880,9 +9476,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  
>  	  iverneed->vn_bfd = abfd;
>  
> -	  iverneed->vn_filename =
> -	    bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> -					     iverneed->vn_file);
> +	  if (elf_use_dt_symtab_p (abfd))
> +	    iverneed->vn_filename
> +	      = elf_tdata (abfd)->dt_strtab + iverneed->vn_file;
> +	  else
> +	    iverneed->vn_filename
> +	      = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> +						 iverneed->vn_file);
>  	  if (iverneed->vn_filename == NULL)
>  	    goto error_return_bad_verref;
>  
> @@ -8913,9 +9513,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  	    {
>  	      _bfd_elf_swap_vernaux_in (abfd, evernaux, ivernaux);
>  
> -	      ivernaux->vna_nodename =
> -		bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> -						 ivernaux->vna_name);
> +	      if (elf_use_dt_symtab_p (abfd))
> +		ivernaux->vna_nodename
> +		  = elf_tdata (abfd)->dt_strtab + ivernaux->vna_name;
> +	      else
> +		ivernaux->vna_nodename
> +		  = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> +						     ivernaux->vna_name);
>  	      if (ivernaux->vna_nodename == NULL)
>  		goto error_return_bad_verref;
>  
> @@ -8954,11 +9558,12 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  	}
>        elf_tdata (abfd)->cverrefs = i;
>  
> -      free (contents);
> +      if (elf_tdata (abfd)->dt_verneed == NULL)
> +	free (contents);
>        contents = NULL;
>      }
>  
> -  if (elf_dynverdef (abfd) != 0)
> +  if (elf_dynverdef (abfd) != 0 || elf_tdata (abfd)->dt_verdef != NULL)
>      {
>        Elf_Internal_Shdr *hdr;
>        Elf_External_Verdef *everdef;
> @@ -8968,40 +9573,56 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>        unsigned int i;
>        unsigned int maxidx;
>        bfd_byte *contents_end_def, *contents_end_aux;
> +      size_t verdef_count;
> +      size_t verdef_size;
>  
> -      hdr = &elf_tdata (abfd)->dynverdef_hdr;
> -
> -      if (hdr->sh_size < sizeof (Elf_External_Verdef))
> +      if (elf_tdata (abfd)->dt_verdef != NULL)
>  	{
> -	error_return_bad_verdef:
> -	  _bfd_error_handler
> -	    (_("%pB: .gnu.version_d invalid entry"), abfd);
> -	  bfd_set_error (bfd_error_bad_value);
> -	error_return_verdef:
> -	  elf_tdata (abfd)->verdef = NULL;
> -	  elf_tdata (abfd)->cverdefs = 0;
> -	  goto error_return;
> +	  hdr = NULL;
> +	  contents = elf_tdata (abfd)->dt_verdef;
> +	  verdef_count = elf_tdata (abfd)->dt_verdef_count;
> +	  verdef_size = verdef_count * sizeof (Elf_External_Verdef);
>  	}
> +      else
> +	{
> +	  hdr = &elf_tdata (abfd)->dynverdef_hdr;
>  
> -      if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
> -	goto error_return_verdef;
> -      contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
> -      if (contents == NULL)
> -	goto error_return_verdef;
> +	  if (hdr->sh_size < sizeof (Elf_External_Verdef))
> +	    {
> +	    error_return_bad_verdef:
> +	      _bfd_error_handler
> +		(_("%pB: .gnu.version_d invalid entry"), abfd);
> +	      bfd_set_error (bfd_error_bad_value);
> +	    error_return_verdef:
> +	      elf_tdata (abfd)->verdef = NULL;
> +	      elf_tdata (abfd)->cverdefs = 0;
> +	      goto error_return;
> +	    }
> +
> +	  if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0)
> +	    goto error_return_verdef;
> +	  contents = _bfd_malloc_and_read (abfd, hdr->sh_size, hdr->sh_size);
> +	  if (contents == NULL)
> +	    goto error_return_verdef;
>  
> -      BFD_ASSERT (sizeof (Elf_External_Verdef)
> -		  >= sizeof (Elf_External_Verdaux));
> -      contents_end_def = contents + hdr->sh_size
> -			 - sizeof (Elf_External_Verdef);
> -      contents_end_aux = contents + hdr->sh_size
> -			 - sizeof (Elf_External_Verdaux);
> +	  BFD_ASSERT (sizeof (Elf_External_Verdef)
> +		      >= sizeof (Elf_External_Verdaux));
> +
> +	  verdef_count = hdr->sh_info;
> +	  verdef_size = hdr->sh_size;
> +	}
> +
> +      contents_end_def = (contents + verdef_size
> +			  - sizeof (Elf_External_Verdef));
> +      contents_end_aux = (contents + verdef_size
> +			  - sizeof (Elf_External_Verdaux));
>  
>        /* We know the number of entries in the section but not the maximum
>  	 index.  Therefore we have to run through all entries and find
>  	 the maximum.  */
>        everdef = (Elf_External_Verdef *) contents;
>        maxidx = 0;
> -      for (i = 0; i < hdr->sh_info; ++i)
> +      for (i = 0; i < verdef_count; ++i)
>  	{
>  	  _bfd_elf_swap_verdef_in (abfd, everdef, &iverdefmem);
>  
> @@ -9044,7 +9665,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  
>        everdef = (Elf_External_Verdef *) contents;
>        iverdefarr = elf_tdata (abfd)->verdef;
> -      for (i = 0; i < hdr->sh_info; i++)
> +      for (i = 0; i < verdef_count; ++i)
>  	{
>  	  Elf_External_Verdaux *everdaux;
>  	  Elf_Internal_Verdaux *iverdaux;
> @@ -9087,9 +9708,13 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  	    {
>  	      _bfd_elf_swap_verdaux_in (abfd, everdaux, iverdaux);
>  
> -	      iverdaux->vda_nodename =
> -		bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> -						 iverdaux->vda_name);
> +	      if (elf_use_dt_symtab_p (abfd))
> +		iverdaux->vda_nodename
> +		  = elf_tdata (abfd)->dt_strtab + iverdaux->vda_name;
> +	      else
> +		iverdaux->vda_nodename
> +		  = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
> +						     iverdaux->vda_name);
>  	      if (iverdaux->vda_nodename == NULL)
>  		goto error_return_bad_verdef;
>  
> @@ -9124,7 +9749,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bool default_imported_symver)
>  		     ((bfd_byte *) everdef + iverdef->vd_next));
>  	}
>  
> -      free (contents);
> +      if (elf_tdata (abfd)->dt_verdef == NULL)
> +	free (contents);
>        contents = NULL;
>      }
>    else if (default_imported_symver)
> diff --git a/bfd/elfcode.h b/bfd/elfcode.h
> index 495e498838d..aae66bcebf8 100644
> --- a/bfd/elfcode.h
> +++ b/bfd/elfcode.h
> @@ -839,6 +839,21 @@ elf_object_p (bfd *abfd)
>  		  abfd->read_only = 1;
>  		}
>  	    }
> +	  if (i_phdr->p_filesz != 0)
> +	    {
> +	      if ((i_phdr->p_offset + i_phdr->p_filesz) > filesize)
> +		goto got_no_match;
> +	      /* Try to reconstruct dynamic symbol table from PT_DYNAMIC
> +		 segment if there is no section header.  */
> +	      if (i_phdr->p_type == PT_DYNAMIC
> +		  && i_ehdrp->e_shstrndx == 0
> +		  && i_ehdrp->e_shoff == 0
> +		  && !_bfd_elf_get_dynamic_symbols (abfd, i_phdr,
> +						    elf_tdata (abfd)->phdr,
> +						    i_ehdrp->e_phnum,
> +						    filesize))
> +		goto got_no_match;
> +	    }
>  	}
>      }
>  
> @@ -1245,7 +1260,9 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>        if ((elf_dynverdef (abfd) != 0
>  	   && elf_tdata (abfd)->verdef == NULL)
>  	  || (elf_dynverref (abfd) != 0
> -	      && elf_tdata (abfd)->verref == NULL))
> +	      && elf_tdata (abfd)->verref == NULL)
> +	  || elf_tdata (abfd)->dt_verdef != NULL
> +	  || elf_tdata (abfd)->dt_verneed != NULL)
>  	{
>  	  if (!_bfd_elf_slurp_version_tables (abfd, false))
>  	    return -1;
> @@ -1253,11 +1270,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>      }
>  
>    ebd = get_elf_backend_data (abfd);
> -  symcount = hdr->sh_size / sizeof (Elf_External_Sym);
> +  symcount = elf_tdata (abfd)->dt_symtab_count;
> +  if (symcount == 0)
> +    symcount = hdr->sh_size / sizeof (Elf_External_Sym);
>    if (symcount == 0)
>      sym = symbase = NULL;
>    else
>      {
> +      size_t i;
> +
>        isymbuf = bfd_elf_get_elf_syms (abfd, hdr, symcount, 0,
>  				      NULL, NULL, NULL);
>        if (isymbuf == NULL)
> @@ -1304,12 +1325,18 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>        if (xver != NULL)
>  	++xver;
>        isymend = isymbuf + symcount;
> -      for (isym = isymbuf + 1, sym = symbase; isym < isymend; isym++, sym++)
> +      for (isym = isymbuf + 1, sym = symbase, i = 1;
> +	   isym < isymend;
> +	   isym++, sym++, i++)
>  	{
>  	  memcpy (&sym->internal_elf_sym, isym, sizeof (Elf_Internal_Sym));
>  
>  	  sym->symbol.the_bfd = abfd;
> -	  sym->symbol.name = bfd_elf_sym_name (abfd, hdr, isym, NULL);
> +	  if (elf_use_dt_symtab_p (abfd))
> +	    sym->symbol.name = (elf_tdata (abfd)->dt_strtab
> +				+ isym->st_name);
> +	  else
> +	    sym->symbol.name = bfd_elf_sym_name (abfd, hdr, isym, NULL);
>  	  sym->symbol.value = isym->st_value;
>  
>  	  if (isym->st_shndx == SHN_UNDEF)
> @@ -1343,6 +1370,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>  		 moment) about the alignment.  */
>  	      sym->symbol.value = isym->st_size;
>  	    }
> +	  else if (elf_use_dt_symtab_p (abfd))
> +	    {
> +	      asection *sec;
> +	      sec = _bfd_elf_get_section_from_dynamic_symbol (abfd,
> +							      isym);
> +	      if (sec == NULL)
> +		goto error_return;
> +	      sym->symbol.section = sec;
> +	    }
>  	  else
>  	    {
>  	      sym->symbol.section
> @@ -1423,7 +1459,10 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>  	  if (dynamic)
>  	    sym->symbol.flags |= BSF_DYNAMIC;
>  
> -	  if (xver != NULL)
> +	  if (elf_tdata (abfd)->dt_versym)
> +	    sym->version = bfd_get_16 (abfd,
> +				       elf_tdata (abfd)->dt_versym + 2 * i);
> +	  else if (xver != NULL)
>  	    {
>  	      Elf_Internal_Versym iversym;
>  
> @@ -1461,13 +1500,15 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bool dynamic)
>      }
>  
>    free (xverbuf);
> -  if (hdr->contents != (unsigned char *) isymbuf)
> +  if (hdr->contents != (unsigned char *) isymbuf
> +      && !elf_use_dt_symtab_p (abfd))
>      free (isymbuf);
>    return symcount;
>  
>   error_return:
>    free (xverbuf);
> -  if (hdr->contents != (unsigned char *) isymbuf)
> +  if (hdr->contents != (unsigned char *) isymbuf
> +      && !elf_use_dt_symtab_p (abfd))
>      free (isymbuf);
>    return -1;
>  }
> diff --git a/bfd/elflink.c b/bfd/elflink.c
> index f10faa5f8bd..422c7b25c81 100644
> --- a/bfd/elflink.c
> +++ b/bfd/elflink.c
> @@ -3571,6 +3571,12 @@ elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
>    if (! bfd_check_format (abfd, bfd_object))
>      return false;
>  
> +  if (elf_use_dt_symtab_p (abfd))
> +    {
> +      bfd_set_error (bfd_error_wrong_format);
> +      return false;
> +    }
> +
>    /* Select the appropriate symbol table.  If we don't know if the
>       object file is an IR object, give linker LTO plugin a chance to
>       get the correct symbol table.  */
> @@ -4233,6 +4239,12 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
>    htab = elf_hash_table (info);
>    bed = get_elf_backend_data (abfd);
>  
> +  if (elf_use_dt_symtab_p (abfd))
> +    {
> +      bfd_set_error (bfd_error_wrong_format);
> +      return false;
> +    }
> +
>    if ((abfd->flags & DYNAMIC) == 0)
>      dynamic = false;
>    else
> -- 
> 2.40.1

-- 
Alan Modra
Australia Development Lab, IBM


More information about the Binutils mailing list