FreeBSD/contrib/binutils/bfd/elflink.c
/* For DSOs loaded in via a DT_NEEDED entry, emulate ld.so in
   allowing an unsatisfied unversioned symbol in the DSO to match a
   versioned symbol that would normally require an explicit version.
   We also handle the case that a DSO references a hidden symbol
   which may be satisfied by a versioned symbol in another DSO.  */
For DSOs loaded in via a DT_NEEDED entry, emulate ld.so in
allowing an unsatisfied unversioned symbol in the DSO to match a
versioned symbol that would normally require an explicit version.
We also handle the case that a DSO references a hidden symbol
which may be satisfied by a versioned symbol in another DSO.
For DSOs loaded in via a DT_NEEDED entry emulate ld . so in allowing an unsatisfied unversioned symbol in the DSO to match a versioned symbol that would normally require an explicit version . We also handle the case that a DSO references a hidden symbol which may be satisfied by a versioned symbol in another DSO .
static bfd_boolean
elf_link_check_versioned_symbol (struct bfd_link_info *info,
				 const struct elf_backend_data *bed,
				 struct elf_link_hash_entry *h)
{
  bfd *abfd;
  struct elf_link_loaded_list *loaded;
  if (!is_elf_hash_table (info->hash))
    return FALSE;
  switch (h->root.type)
    {
    default:
      abfd = NULL;
      break;
    case bfd_link_hash_undefined:
    case bfd_link_hash_undefweak:
      abfd = h->root.u.undef.abfd;
      if ((abfd->flags & DYNAMIC) == 0
	  || (elf_dyn_lib_class (abfd) & DYN_DT_NEEDED) == 0)
	return FALSE;
      break;
    case bfd_link_hash_defined:
    case bfd_link_hash_defweak:
      abfd = h->root.u.def.section->owner;
      break;
    case bfd_link_hash_common:
      abfd = h->root.u.c.p->section->owner;
      break;
    }
  BFD_ASSERT (abfd != NULL);
  for (loaded = elf_hash_table (info)->loaded;
       loaded != NULL;
       loaded = loaded->next)
    {
      bfd *input;
      Elf_Internal_Shdr *hdr;
      bfd_size_type symcount;
      bfd_size_type extsymcount;
      bfd_size_type extsymoff;
      Elf_Internal_Shdr *versymhdr;
      Elf_Internal_Sym *isym;
      Elf_Internal_Sym *isymend;
      Elf_Internal_Sym *isymbuf;
      Elf_External_Versym *ever;
      Elf_External_Versym *extversym;
      input = loaded->abfd;
      if (input == abfd
	  || (input->flags & DYNAMIC) == 0
	  || elf_dynversym (input) == 0)
	continue;
      hdr = &elf_tdata (input)->dynsymtab_hdr;
      symcount = hdr->sh_size / bed->s->sizeof_sym;
      if (elf_bad_symtab (input))
	{
	  extsymcount = symcount;
	  extsymoff = 0;
	}
      else
	{
	  extsymcount = symcount - hdr->sh_info;
	  extsymoff = hdr->sh_info;
	}
      if (extsymcount == 0)
	continue;
      isymbuf = bfd_elf_get_elf_syms (input, hdr, extsymcount, extsymoff,
				      NULL, NULL, NULL);
      if (isymbuf == NULL)
	return FALSE;
      versymhdr = &elf_tdata (input)->dynversym_hdr;
      extversym = bfd_malloc (versymhdr->sh_size);
      if (extversym == NULL)
	goto error_ret;
      if (bfd_seek (input, versymhdr->sh_offset, SEEK_SET) != 0
	  || (bfd_bread (extversym, versymhdr->sh_size, input)
	      != versymhdr->sh_size))
	{
	  free (extversym);
	error_ret:
	  free (isymbuf);
	  return FALSE;
	}
      ever = extversym + extsymoff;
      isymend = isymbuf + extsymcount;
      for (isym = isymbuf; isym < isymend; isym++, ever++)
	{
	  const char *name;
	  Elf_Internal_Versym iver;
	  unsigned short version_index;
	  if (ELF_ST_BIND (isym->st_info) == STB_LOCAL
	      || isym->st_shndx == SHN_UNDEF)
	    continue;
	  name = bfd_elf_string_from_elf_section (input,
						  hdr->sh_link,
						  isym->st_name);
	  if (strcmp (name, h->root.root.string) != 0)
	    continue;
	  _bfd_elf_swap_versym_in (input, ever, &iver);
	  if ((iver.vs_vers & VERSYM_HIDDEN) == 0)
	    {
	      abort ();
	    }
	  version_index = iver.vs_vers & VERSYM_VERSION;
	  if (version_index == 1 || version_index == 2)
	    {
	      free (extversym);
	      free (isymbuf);
	      return TRUE;
	    }
	}
      free (extversym);
      free (isymbuf);
    }
  return FALSE;
}
static bfd_boolean elf_link_check_versioned_symbol ( struct bfd_link_info * info , const struct elf_backend_data * bed , struct elf_link_hash_entry * h ) { bfd * abfd ; struct elf_link_loaded_list * loaded ; if ( ! is_elf_hash_table ( info -> hash ) ) return FALSE ; switch ( h -> root . type ) { default : abfd = NULL ; break ; case bfd_link_hash_undefined : case bfd_link_hash_undefweak : abfd = h -> root . u . undef . abfd ; if ( ( abfd -> flags & DYNAMIC ) == 0 || ( elf_dyn_lib_class ( abfd ) & DYN_DT_NEEDED ) == 0 ) return FALSE ; break ; case bfd_link_hash_defined : case bfd_link_hash_defweak : abfd = h -> root . u . def . section -> owner ; break ; case bfd_link_hash_common : abfd = h -> root . u . c . p -> section -> owner ; break ; } BFD_ASSERT ( abfd != NULL ) ; for ( loaded = elf_hash_table ( info ) -> loaded ; loaded != NULL ; loaded = loaded -> next ) { bfd * input ; Elf_Internal_Shdr * hdr ; bfd_size_type symcount ; bfd_size_type extsymcount ; bfd_size_type extsymoff ; Elf_Internal_Shdr * versymhdr ; Elf_Internal_Sym * isym ; Elf_Internal_Sym * isymend ; Elf_Internal_Sym * isymbuf ; Elf_External_Versym * ever ; Elf_External_Versym * extversym ; input = loaded -> abfd ; if ( input == abfd || ( input -> flags & DYNAMIC ) == 0 || elf_dynversym ( input ) == 0 ) continue ; hdr = & elf_tdata ( input ) -> dynsymtab_hdr ; symcount = hdr -> sh_size / bed -> s -> sizeof_sym ; if ( elf_bad_symtab ( input ) ) { extsymcount = symcount ; extsymoff = 0 ; } else { extsymcount = symcount - hdr -> sh_info ; extsymoff = hdr -> sh_info ; } if ( extsymcount == 0 ) continue ; isymbuf = bfd_elf_get_elf_syms ( input , hdr , extsymcount , extsymoff , NULL , NULL , NULL ) ; if ( isymbuf == NULL ) return FALSE ; versymhdr = & elf_tdata ( input ) -> dynversym_hdr ; extversym = bfd_malloc ( versymhdr -> sh_size ) ; if ( extversym == NULL ) goto error_ret ; if ( bfd_seek ( input , versymhdr -> sh_offset , SEEK_SET ) != 0 || ( bfd_bread ( extversym , versymhdr -> sh_size , input ) != versymhdr -> sh_size ) ) { free ( extversym ) ; error_ret : free ( isymbuf ) ; return FALSE ; } ever = extversym + extsymoff ; isymend = isymbuf + extsymcount ; for ( isym = isymbuf ; isym < isymend ; isym ++ , ever ++ ) { const char * name ; Elf_Internal_Versym iver ; unsigned short version_index ; if ( ELF_ST_BIND ( isym -> st_info ) == STB_LOCAL || isym -> st_shndx == SHN_UNDEF ) continue ; name = bfd_elf_string_from_elf_section ( input , hdr -> sh_link , isym -> st_name ) ; if ( strcmp ( name , h -> root . root . string ) != 0 ) continue ; _bfd_elf_swap_versym_in ( input , ever , & iver ) ; if ( ( iver . vs_vers & VERSYM_HIDDEN ) == 0 ) { abort ( ) ; } version_index = iver . vs_vers & VERSYM_VERSION ; if ( version_index == 1 || version_index == 2 ) { free ( extversym ) ; free ( isymbuf ) ; return TRUE ; } } free ( extversym ) ; free ( isymbuf ) ; } return FALSE ; }
For DSOs loaded in via a DT_NEEDED entry, emulate ld.so in
allowing an unsatisfied unversioned symbol in the DSO to match a
versioned symbol that would normally require an explicit version.
For DSOs loaded in via a DT_NEEDED entry emulate ld . so in allowing an unsatisfied unversioned symbol in the DSO to match a versioned symbol that would normally require an explicit version .
/* We check each DSO for a possible hidden versioned definition.  */ /* Read in any version definitions.  */ /* If we have a non-hidden versioned sym, then it should
		 have provided a definition for the undefined sym.  */ /* This is the base or first version.  We can use it.  */