Bug 18061

Summary: /usr/bin/ld (GNU ld 2.9.1) ignores rpath in shared libraries
Product: Base System Reporter: Bjoern Fischer <bfischer>
Component: gnuAssignee: David E. O'Brien <obrien>
Status: Closed FIXED    
Severity: Affects Only Me CC: bfischer
Priority: Normal    
Version: 4.0-STABLE   
Hardware: Any   
OS: Any   

Description Bjoern Fischer 2000-04-17 19:50:02 UTC
GNU ld ignores the rpath compiled into shared libraries
when going through the needed list at link time.

Imagine following scenario:

  library libA:

    /vol/foo/lib/libA.so
      needed: libB.so
      rpath:  /vol/bar/lib

  library libB:

    /vol/bar/lib/libB.so

Now one links a binary against libA:

  gcc -o foobar foobar.o -L/vol/foo/lib -R/vol/foo/lib -lA

The above will fail because ld won't find libB.so at link
time. The ld error message suggests that one should use
-rpath to fix this. This is even worse:
With -rpath /vol/bar/lib the path is hardcoded into the
resulting binary, which is nonsense. You want to use
-rpath-link.

But generally you should not need to specify the location
of libB.so at all, since libA.so is fully self contained
and the runtime linker will find all libraries needed for
the foobar executable w/o /vol/bar/lib in it's rpath.

Fix: 

I don't know the proper method for changes in /usr/src/contrib/.
so I changed everything in place. Maybe the changes should be
moved to /usr/src/gnu/. making source imports easier.

The changes were promoted to the GNU maintainers and it is
probable that this or a similiar fix will be incorporated
into the next binutils release.

BTW: What is the common policy, how well tested should submitted
patches be? I did a buildworld and some tests for the new feature.

--- ./contrib/binutils/bfd/elflink.h	2000/04/16 17:11:15	1.1
+++ ./contrib/binutils/bfd/elflink.h	2000/04/16 20:40:13
@@ -852,6 +852,8 @@
 	  Elf_External_Dyn *extdynend;
 	  int elfsec;
 	  unsigned long link;
+	  int pass;
+	  char *rpath = NULL;
 
 	  dynbuf = (Elf_External_Dyn *) bfd_malloc ((size_t) s->_raw_size);
 	  if (dynbuf == NULL)
@@ -866,43 +868,78 @@
 	    goto error_return;
 	  link = elf_elfsections (abfd)[elfsec]->sh_link;
 
-	  extdyn = dynbuf;
-	  extdynend = extdyn + s->_raw_size / sizeof (Elf_External_Dyn);
-	  for (; extdyn < extdynend; extdyn++)
+	  /* pass0: Look for rpath tag and save the value.
+	     pass1: Look for soname and needed tags.
+		    Reference rpath in each bfd_link_needed_list entry. */
+	  for (pass = 0; pass <= 1; pass++)
 	    {
-	      Elf_Internal_Dyn dyn;
 
-	      elf_swap_dyn_in (abfd, extdyn, &dyn);
-	      if (dyn.d_tag == DT_SONAME)
+	      extdyn = dynbuf;
+	      extdynend = extdyn + s->_raw_size / sizeof (Elf_External_Dyn);
+	      for (; extdyn < extdynend; extdyn++)
 		{
-		  name = bfd_elf_string_from_elf_section (abfd, link,
-							  dyn.d_un.d_val);
-		  if (name == NULL)
-		    goto error_return;
-		}
-	      if (dyn.d_tag == DT_NEEDED)
-		{
-		  struct bfd_link_needed_list *n, **pn;
-		  char *fnm, *anm;
+		  Elf_Internal_Dyn dyn;
+
+		  elf_swap_dyn_in (abfd, extdyn, &dyn);
 
-		  n = ((struct bfd_link_needed_list *)
-		       bfd_alloc (abfd, sizeof (struct bfd_link_needed_list)));
-		  fnm = bfd_elf_string_from_elf_section (abfd, link,
-							 dyn.d_un.d_val);
-		  if (n == NULL || fnm == NULL)
-		    goto error_return;
-		  anm = bfd_alloc (abfd, strlen (fnm) + 1);
-		  if (anm == NULL)
-		    goto error_return;
-		  strcpy (anm, fnm);
-		  n->name = anm;
-		  n->by = abfd;
-		  n->next = NULL;
-		  for (pn = &elf_hash_table (info)->needed;
-		       *pn != NULL;
-		       pn = &(*pn)->next)
-		    ;
-		  *pn = n;
+		  if (pass == 0)
+		    {
+		      if (dyn.d_tag == DT_RPATH)
+			{
+			  /* DT_RPATH is not expected to occur more than
+			     once in a dynamic section. */
+			  if (rpath == NULL)
+			    {
+			      char *string;
+
+			      string =
+				bfd_elf_string_from_elf_section (abfd, link,
+								dyn.d_un.d_val);
+			      rpath = bfd_alloc (abfd, strlen(string) + 1);
+			      if (rpath == NULL)
+				goto error_return;
+			      strcpy (rpath, string);
+			    }
+			}
+		    }
+		  else /* pass != 0 */
+		    {
+
+		      if (dyn.d_tag == DT_SONAME)
+			{
+			  name = bfd_elf_string_from_elf_section (abfd, link,
+								dyn.d_un.d_val);
+			  if (name == NULL)
+			    goto error_return;
+			}
+		      if (dyn.d_tag == DT_NEEDED)
+			{
+			  struct bfd_link_needed_list *n, **pn;
+			  char *fnm, *anm;
+
+			  n = ((struct bfd_link_needed_list *)
+			  bfd_alloc (abfd,
+				     sizeof (struct bfd_link_needed_list)));
+			  fnm = bfd_elf_string_from_elf_section (abfd, link,
+								dyn.d_un.d_val);
+			  if (n == NULL || fnm == NULL)
+			    goto error_return;
+			  anm = bfd_alloc (abfd, strlen (fnm) + 1);
+			  if (anm == NULL)
+			    goto error_return;
+			  strcpy (anm, fnm);
+
+			  n->name = anm;
+			  n->by = abfd;
+			  n->rpath = rpath;
+			  n->next = NULL;
+			  for (pn = &elf_hash_table (info)->needed;
+			       *pn != NULL;
+			       pn = &(*pn)->next)
+			    ;
+			  *pn = n;
+			}
+		    } /* pass == 0 */
 		}
 	    }
 
--- ./contrib/binutils/ld/emultempl/elf32.em	2000/04/14 23:05:41	1.1
+++ ./contrib/binutils/ld/emultempl/elf32.em	2000/04/16 20:43:46
@@ -340,7 +340,8 @@
       /* We need to find this file and include the symbol table.  We
 	 want to search for the file in the same way that the dynamic
 	 linker will search.  That means that we want to use
-	 rpath_link, rpath, then the environment variable
+	 command line rpath_link, hardcompiled rpath in shared objects,
+	 command line rpath, then the environment variable
 	 LD_LIBRARY_PATH (native only), then the linker script
 	 LIB_SEARCH_DIRS.  We do not search using the -L arguments.
 
@@ -355,6 +356,8 @@
 
 	  if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link,
 						  l->name, force))
+	    break;
+	  if (gld${EMULATION_NAME}_search_needed (l->rpath, l->name, force))
 	    break;
 	  if (gld${EMULATION_NAME}_search_needed (command_line.rpath,
 						  l->name, force))

--- ./gnu/usr.bin/binutils/libbfd/i386/bfd.h	2000/04/15 11:14:30	1.1
+++ ./gnu/usr.bin/binutils/libbfd/i386/bfd.h	2000/04/16 17:51:09
@@ -598,6 +598,7 @@
 {
   struct bfd_link_needed_list *next;
   bfd *by;
+  const char *rpath;
   const char *name;
 };
How-To-Repeat: 
See description.
Comment 1 nrahlstr freebsd_committer freebsd_triage 2000-06-13 03:03:24 UTC
Responsible Changed
From-To: freebsd-bugs->obrien

David has been working with binutils quite a bit.
Comment 2 David E. O'Brien freebsd_committer freebsd_triage 2000-11-10 18:40:05 UTC
State Changed
From-To: open->closed

Binutils has been updated to 2.10.0.