GNU-ld behaviour does not match native linker behaviour

Nick Clifton nickc@cygnus.com
Fri Dec 10 11:55:00 GMT 1999


Hi Guys,

  OK here is a revised version of my original patch to fix this
  problem.  This patch is much more conservative.  It just fixes the
  ELF linker so that if it encounters a symbol in an archive map which
  matches a symbol which is currently known to be common, the linker
  will then examine the archive element's symbol table in order to
  determine oif the element contains an actual definition of the
  symbol or just another common declaration of it.

  OK to apply ?

Cheers
	Nick

1999-12-10  Nick Clifton  <nickc@cygnus.com>

	* elflink.h (elf_link_is_defined_archive_symbol): New
	function: Decide if a symbol, in an archive map is there
	because it is defined in the archive element, or because it is
	just another common declaration of it.
	(elf_link_add_archive_symbols): Use
	elf_link_is_defined_archive_symbol to decide if an archive
	element contain a reference to a common symbol should be
	linked in or not.

Index: elflink.h
===================================================================
RCS file: /cvs/binutils/binutils/bfd/elflink.h,v
retrieving revision 1.38
diff -p -r1.38 elflink.h
*** elflink.h	1999/12/10 18:51:34	1.38
--- elflink.h	1999/12/10 19:47:57
*************** elf_bfd_link_add_symbols (abfd, info)
*** 80,86 ****
--- 80,177 ----
      }
  }
  
+ /* Search the symbol table of the archive element of the archive ABFD
+    whoes archove map contains a mention of SYMDEF, and determine if
+    the symbol is defined in this element.  */
+ static boolean
+ elf_link_is_defined_archive_symbol (abfd, symdef)
+      bfd * abfd;
+      carsym * symdef;
+ {
+   Elf_Internal_Shdr * hdr;
+   Elf_External_Sym *  esym;
+   Elf_External_Sym *  esymend;
+   Elf_External_Sym *  buf = NULL;
+   size_t symcount;
+   size_t extsymcount;
+   size_t extsymoff;
+   boolean result = false;
+   
+   abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
+   if (abfd == (bfd *) NULL)
+     return false;
+ 
+   if (! bfd_check_format (abfd, bfd_object))
+     return false;
+ 
+   /* Select the appropriate symbol table.  */
+   if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
+     hdr = &elf_tdata (abfd)->symtab_hdr;
+   else
+     hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+ 
+   symcount = hdr->sh_size / sizeof (Elf_External_Sym);
+ 
+   /* The sh_info field of the symtab header tells us where the
+      external symbols start.  We don't care about the local symbols.  */
+   if (elf_bad_symtab (abfd))
+     {
+       extsymcount = symcount;
+       extsymoff = 0;
+     }
+   else
+     {
+       extsymcount = symcount - hdr->sh_info;
+       extsymoff = hdr->sh_info;
+     }
  
+   buf = ((Elf_External_Sym *)
+ 	 bfd_malloc (extsymcount * sizeof (Elf_External_Sym)));
+   if (buf == NULL && extsymcount != 0)
+     return false;
+ 
+   /* Read in the symbol table.
+      FIXME:  This ought to be cached somewhere.  */
+   if (bfd_seek (abfd,
+ 		hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym),
+ 		SEEK_SET) != 0
+       || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym), extsymcount, abfd)
+ 	  != extsymcount * sizeof (Elf_External_Sym)))
+     {
+       free (buf);
+       return false;
+     }
+ 
+   /* Scan the symbol table looking for SYMDEF.  */
+   esymend = buf + extsymcount;
+   for (esym = buf;
+        esym < esymend;
+        esym++)
+     {
+       Elf_Internal_Sym sym;
+       const char * name;
+ 
+       elf_swap_symbol_in (abfd, esym, & sym);
+ 
+       name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name);
+       if (name == (const char *) NULL)
+ 	break;
+ 
+       if (strcmp (name, symdef->name) == 0)
+ 	{
+ 	  result =
+ 	    (ELF_ST_BIND (sym.st_info) == STB_GLOBAL)
+ 	    && (sym.st_shndx != SHN_UNDEF);
+ 	  break;
+ 	}
+     }
+ 
+   free (buf);
+   
+   return result;
+ }
+ 
+ 
  /* Add symbols from an ELF archive file to the linker hash table.  We
     don't use _bfd_generic_link_add_archive_symbols because of a
     problem which arises on UnixWare.  The UnixWare libc.so is an
*************** elf_link_add_archive_symbols (abfd, info
*** 200,206 ****
  	  if (h == NULL)
  	    continue;
  
! 	  if (h->root.type != bfd_link_hash_undefined)
  	    {
  	      if (h->root.type != bfd_link_hash_undefweak)
  		defined[i] = true;
--- 291,314 ----
  	  if (h == NULL)
  	    continue;
  
! 	  if (h->root.type == bfd_link_hash_common)
! 	    {
! 	      /* We currently have a common symbol.  The archive map contains
! 		 a reference to this symbol, so we may want to include it.  We
! 		 only want to include it however, if this archive element
! 		 contains a definition of the symbol, not just another common
! 		 declaration of it.
! 
! 		 Unfortunately some archivers (including GNU ar) will put
! 		 declarations of common symbols into their archive maps, as
! 		 well as real definitions, so we cannot just go by the archive
! 		 map alone.  Instead we must read in the element's symbol
! 		 table and check that to see what kind of symbol definition
! 		 this is.  */
! 	      if (! elf_link_is_defined_archive_symbol (abfd, symdef))
! 		continue;
! 	    }
! 	  else if (h->root.type != bfd_link_hash_undefined)
  	    {
  	      if (h->root.type != bfd_link_hash_undefweak)
  		defined[i] = true;


More information about the Binutils mailing list