Subject: bin/14583: binutils 2.9.1 ld doesn't pay attention to DT_RPATH
To: None <gnats-bugs@gnats.netbsd.org>
From: None <skrll@netbsd.org>
List: netbsd-bugs
Date: 11/14/2001 08:47:01
>Number:         14583
>Category:       bin
>Synopsis:       binutils 2.9.1 ld doesn't pay attention to DT_RPATH
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Nov 14 00:48:00 PST 2001
>Closed-Date:
>Last-Modified:
>Originator:     Nick Hudson
>Release:        NetBSD 1.5.1
>Organization:
	None at all.
>Environment:
System: NetBSD btcl045611 1.5.1 NetBSD 1.5.1 (GENERIC_LAPTOP) #1: Sat Jun 30 20:12:19 BST 2001 nick@nthcliff:/dsk/NetBSD/NetBSD-1.5/src/sys/arch/i386/compile/GENERIC_LAPTOP i386
>Description:
	If a library contains dependencies and a binary is linked against
	this library then ld complains about not being able to find the
	dependencies if they exist outside /usr/lib.
>How-To-Repeat:
	Build (for example) qt2 from pkgsrc

	$ ldd /usr/X11R6/qt2/lib/libqt.so
	/usr/X11R6/qt2/lib/libqt.so:
		-lGLU.1 => /usr/X11R6/lib/libGLU.so.1
		-lGL.1 => /usr/X11R6/lib/libGL.so.1
		-lX11.6 => /usr/X11R6/lib/libX11.so.6
		-lICE.6 => /usr/X11R6/lib/libICE.so.6
		-lSM.6 => /usr/X11R6/lib/libSM.so.6
		-lXt.6 => /usr/X11R6/lib/libXt.so.6
		-lXext.6 => /usr/X11R6/lib/libXext.so.6
		-lXmu.6 => /usr/X11R6/lib/libXmu.so.6
		-lm.0 => /usr/lib/libm387.so.0
		-lm.0 => /usr/lib/libm.so.0
		-lXrender.1 => /usr/X11R6/lib/libXrender.so.1
		-lfreetype.6 => /usr/X11R6/lib/libfreetype.so.6
		-lXft.1 => /usr/X11R6/lib/libXft.so.1
		-lz.0 => /usr/lib/libz.so.0
		-lpng.2 => /usr/pkg/lib/libpng.so.2
		-ljpeg.62 => /usr/pkg/lib/libjpeg.so.62
		-llcms.1 => /usr/pkg/lib/liblcms.so.1
		-lmng.1 => /usr/pkg/lib/libmng.so.1
	$ cat > dummy.cpp << EOF
	> #include <qstring.h>
	> 
	> int
	> main()
	> {
	> 	QString s;
	> }
	EOF
	$ g++ -c dummy.cpp -I/usr/X11R6/qt2/include
	$ g++ dummy.o -lqt -L/usr/X11R6/qt2/lib -I/usr/X11R6/qt2/include -Wl,-rpath -Wl,/usr/X11R6/qt2/lib
	/usr/bin/ld: warning: libGLU.so.1, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libGL.so.1, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libXmu.so.6, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libXext.so.6, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libX11.so.6, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libSM.so.6, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libICE.so.6, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libXft.so.1, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libpng.so.2, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libjpeg.so.62, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/bin/ld: warning: libmng.so.1, needed by /usr/X11R6/qt2/lib/libqt.so, not found (try using --rpath)
	/usr/X11R6/qt2/lib/libqt.so: undefined reference to ...

>Fix:

Apply the following patch derived from binutils 2.11.3

Index: gnu/dist/bfd/bfd-in.h
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/bfd/bfd-in.h,v
retrieving revision 1.6
diff -c -r1.6 bfd-in.h
*** gnu/dist/bfd/bfd-in.h	2000/05/04 20:34:14	1.6
--- gnu/dist/bfd/bfd-in.h	2001/11/14 08:33:27
***************
*** 622,627 ****
--- 622,629 ----
  	   struct bfd_elf_version_tree *));
  extern void bfd_elf_set_dt_needed_name PARAMS ((bfd *, const char *));
  extern const char *bfd_elf_get_dt_soname PARAMS ((bfd *));
+ extern struct bfd_link_needed_list *bfd_elf_get_runpath_list
+   PARAMS ((bfd *, struct bfd_link_info *));
  
  /* SunOS shared library support routines for the linker.  */
  
Index: gnu/dist/bfd/bfd-in2.h
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/bfd/bfd-in2.h,v
retrieving revision 1.4
diff -c -r1.4 bfd-in2.h
*** gnu/dist/bfd/bfd-in2.h	2000/05/04 20:34:14	1.4
--- gnu/dist/bfd/bfd-in2.h	2001/11/14 08:34:11
***************
*** 622,627 ****
--- 622,629 ----
  	   struct bfd_elf_version_tree *));
  extern void bfd_elf_set_dt_needed_name PARAMS ((bfd *, const char *));
  extern const char *bfd_elf_get_dt_soname PARAMS ((bfd *));
+ extern struct bfd_link_needed_list *bfd_elf_get_runpath_list
+   PARAMS ((bfd *, struct bfd_link_info *));
  
  /* SunOS shared library support routines for the linker.  */
  
Index: gnu/dist/bfd/elf-bfd.h
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/bfd/elf-bfd.h,v
retrieving revision 1.1.1.2
diff -c -r1.1.1.2 elf-bfd.h
*** gnu/dist/bfd/elf-bfd.h	1999/02/02 19:51:52	1.1.1.2
--- gnu/dist/bfd/elf-bfd.h	2001/11/14 08:34:28
***************
*** 184,189 ****
--- 184,192 ----
    struct elf_link_hash_entry *hgot;
    /* A pointer to information used to link stabs in sections.  */
    PTR stab_info;
+   /* A linked list of DT_RPATH/DT_RUNPATH names found in dynamic
+      objects included in the link.  */
+   struct bfd_link_needed_list *runpath;
  };
  
  /* Look up an entry in an ELF linker hash table.  */
Index: gnu/dist/bfd/elf.c
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/bfd/elf.c,v
retrieving revision 1.3
diff -c -r1.3 elf.c
*** gnu/dist/bfd/elf.c	1999/07/31 20:56:12	1.3
--- gnu/dist/bfd/elf.c	2001/11/14 08:35:12
***************
*** 873,878 ****
--- 873,879 ----
    table->dynstr = NULL;
    table->bucketcount = 0;
    table->needed = NULL;
+   table->runpath = NULL;
    table->hgot = NULL;
    table->stab_info = NULL;
    return _bfd_link_hash_table_init (&table->root, abfd, newfunc);
***************
*** 926,931 ****
--- 927,945 ----
    if (info->hash->creator->flavour != bfd_target_elf_flavour)
      return NULL;
    return elf_hash_table (info)->needed;
+ }
+ 
+ /* Get the list of DT_RPATH/DT_RUNPATH entries for a link.  This is a
+    hook for the linker ELF emulation code.  */
+ 
+ struct bfd_link_needed_list *
+ bfd_elf_get_runpath_list (abfd, info)
+      bfd *abfd;
+      struct bfd_link_info *info;
+ {
+   if (info->hash->creator->flavour != bfd_target_elf_flavour)
+     return NULL;
+   return elf_hash_table (info)->runpath;
  }
  
  /* Get the name actually used for a dynamic object for a link.  This
Index: gnu/dist/bfd/elflink.h
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/bfd/elflink.h,v
retrieving revision 1.4
diff -c -r1.4 elflink.h
*** gnu/dist/bfd/elflink.h	2000/06/11 23:47:57	1.4
--- gnu/dist/bfd/elflink.h	2001/11/14 08:36:15
***************
*** 851,856 ****
--- 851,858 ----
  	  Elf_External_Dyn *extdynend;
  	  int elfsec;
  	  unsigned long link;
+ 	  int rpath;
+ 	  int runpath;
  
  	  dynbuf = (Elf_External_Dyn *) bfd_malloc ((size_t) s->_raw_size);
  	  if (dynbuf == NULL)
***************
*** 867,872 ****
--- 869,876 ----
  
  	  extdyn = dynbuf;
  	  extdynend = extdyn + s->_raw_size / sizeof (Elf_External_Dyn);
+ 	  rpath = 0;
+ 	  runpath = 0;
  	  for (; extdyn < extdynend; extdyn++)
  	    {
  	      Elf_Internal_Dyn dyn;
***************
*** 902,907 ****
--- 906,970 ----
  		       pn = &(*pn)->next)
  		    ;
  		  *pn = n;
+ 		}
+ 	      if (dyn.d_tag == DT_RUNPATH)
+ 		{
+ 		  struct bfd_link_needed_list *n, **pn;
+ 		  char *fnm, *anm;
+ 
+ 		  /* When we see DT_RPATH before DT_RUNPATH, we have
+ 		     to clear runpath.  Do _NOT_ bfd_release, as that
+ 		     frees all more recently bfd_alloc'd blocks as
+ 		     well.  */
+ 		  if (rpath && elf_hash_table (info)->runpath)
+ 		    elf_hash_table (info)->runpath = NULL;
+ 
+ 		  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)->runpath;
+ 		       *pn != NULL;
+ 		       pn = &(*pn)->next)
+ 		    ;
+ 		  *pn = n;
+ 		  runpath = 1;
+ 		  rpath = 0;
+ 		}
+ 	      /* Ignore DT_RPATH if we have seen DT_RUNPATH.  */
+ 	      if (!runpath && dyn.d_tag == DT_RPATH)
+ 	        {
+ 		  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->next = NULL;
+ 		  for (pn = &elf_hash_table (info)->runpath;
+ 		       *pn != NULL;
+ 		       pn = &(*pn)->next)
+ 		    ;
+ 		  *pn = n;
+ 		  rpath = 1;
  		}
  	    }
  
Index: gnu/dist/include/elf/common.h
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/include/elf/common.h,v
retrieving revision 1.3
diff -c -r1.3 common.h
*** gnu/dist/include/elf/common.h	1999/02/02 20:27:18	1.3
--- gnu/dist/include/elf/common.h	2001/11/14 08:36:21
***************
*** 273,278 ****
--- 273,279 ----
  #define DT_DEBUG	21
  #define DT_TEXTREL	22
  #define DT_JMPREL	23
+ #define DT_RUNPATH      29
  
  /* The next four dynamic tags are used on Solaris.  We support them
     everywhere.  */
Index: gnu/dist/ld/emultempl/elf32.em
===================================================================
RCS file: /cvsroot/gnusrc/gnu/dist/ld/emultempl/elf32.em,v
retrieving revision 1.1.1.2
diff -c -r1.1.1.2 elf32.em
*** gnu/dist/ld/emultempl/elf32.em	1999/02/02 19:53:57	1.1.1.2
--- gnu/dist/ld/emultempl/elf32.em	2001/11/14 08:36:33
***************
*** 292,297 ****
--- 292,308 ----
  	  size_t len;
  	  search_dirs_type *search;
  
+ EOF
+ if [ "x${host}" = "x${target}" ] ; then
+   if [ "x${DEFAULT_EMULATION}" = "x${EMULATION_NAME}" ] ; then
+ cat >>e${EMULATION_NAME}.c <<EOF
+ 	  struct bfd_link_needed_list *rp;
+ 	  int found;
+ EOF
+   fi
+ fi
+ cat >>e${EMULATION_NAME}.c <<EOF
+ 
  	  if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link,
  						  l->name, force))
  	    break;
***************
*** 313,318 ****
--- 324,342 ----
  	  lib_path = (const char *) getenv ("LD_LIBRARY_PATH");
  	  if (gld${EMULATION_NAME}_search_needed (lib_path, l->name, force))
  	    break;
+ 
+           found = 0;
+           rp = bfd_elf_get_runpath_list (output_bfd, &link_info);
+           for (; !found && rp != NULL; rp = rp->next)
+             {
+               found = (rp->by == l->by
+                        && gld${EMULATION_NAME}_search_needed (rp->name,
+                                                               l->name,
+                                                               force));
+             }
+           if (found)
+             break;
+ 
  EOF
    fi
  fi
>Release-Note:
>Audit-Trail:
>Unformatted: