rtld enhancement to add osversion sub-directory search

Doug Ambrisko ambrisko at ambrisko.com
Tue Mar 10 10:02:22 PDT 2009


Doug Ambrisko writes:
| Brooks Davis writes:
| | On Fri, Feb 13, 2009 at 01:08:56PM -0800, Doug Ambrisko wrote:
| | > David Schultz writes:
| | > | On Thu, Feb 12, 2009, Doug Ambrisko wrote:
| | > | > Kostik Belousov writes:
| | > | > | There is a popular feature, unfortunately, not supported by FreeBSD
| | > | > | ld.so, called Dynamic String Tokens, see
| | > | > | http://docs.sun.com/app/docs/doc/817-1984/appendixc-4?l=en&a=view
| | > | > | 
| | > | > | I have almost abandoned patch that adds support for $ORIGIN, $OSREL,
| | > | > | $OSNAME, and $PLATFORM. Quite amazingly, it merged with today CURRENT
| | > | > | without serious conflicts.
| | > | > | http://people.freebsd.org/~kib/misc/rtld_locks.4.patch
| | > | > 
| | > | > That is an interesting feature, however, it almost seems backwards for
| | > | > me if I understand it correctly.  I need old binaries to find the library 
| | > | > it was built with and not new ones based on the base OS.  The plus that 
| | > | > I see with their feature is for a library that has been optimized for a 
| | > | > specific type of CPU etc.
| | > | 
| | > | The Solaris rtld features are very useful when you want to
| | > | export a volume with a bunch of apps over NFS, and the clients are
| | > | running different releases or different architectures. It can
| | > | probably also solve your problem if you bother to place different
| | > | library versions in different directories and set your library
| | > | path appropriately.
| | > 
| | > Unless I missed something it seems to be an inverse feature to 
| | > what I want since it expands based on the current kernel's
| | > uname type results.  I need it to expand based on the original OS
| | > that it was built on.  I'll have to see if my Solaris box at work
| | > has this feature or not.  I can't change LD_LIBRARY_PATH etc.
| | > type things.  Doing ldconfig -m of various things doesn't help
| | > since that can find the wrong one.  My idea was to do something
| | > like what happens for 32bit on 64bit and Linux on FreeBSD in that
| | > it looks at osversion specific places then the standard.
| | 
| | While I commented on the Dynamic String Tokens and think it might be
| | useful, that's a distraction for your proposal.  I think you proposal
| | sounds useful as well.  If it was in 7 by the time I upgrade my cluster
| | from 6, I'd probably use it to ease the transition.
| 
| Since my scheme seems useful, then I'll proceed to clean up my patch
| some more then send it out for people to play with and comment on.

Below is the patch, got tied up with other issues.  It include the
LD32_ then LD_ search as well.  I use LD_MAP_UNKNOWN_OS_VERSION
to set the default version if the .note version is missing.
Once this gets into shape then I'll update the man page.

Thanks for looking,

Doug A.

Index: rtld.c
===================================================================
RCS file: /cvs/src/libexec/rtld-elf/rtld.c,v
retrieving revision 1.128
diff -u -p -w -r1.128 rtld.c
--- rtld.c	10 Oct 2008 00:16:32 -0000	1.128
+++ rtld.c	24 Feb 2009 00:05:10 -0000
@@ -103,7 +103,7 @@ static bool is_exported(const Elf_Sym *)
 static void linkmap_add(Obj_Entry *);
 static void linkmap_delete(Obj_Entry *);
 static int load_needed_objects(Obj_Entry *);
-static int load_preload_objects(void);
+static int load_preload_objects(char *);
 static Obj_Entry *load_object(const char *, const Obj_Entry *);
 static Obj_Entry *obj_from_addr(const void *);
 static void objlist_call_fini(Objlist *, int *lockstate);
@@ -157,6 +157,11 @@ static char *ld_debug;		/* Environment v
 static char *ld_library_path;	/* Environment variable for search path */
 static char *ld_preload;	/* Environment variable for libraries to
 				   load first */
+#ifdef COMPAT_32BIT
+static char *ld32_library_path;	/* Environment variable for search path */
+static char *ld32_preload;	/* Environment variable for libraries to
+				   load first */
+#endif
 static char *ld_tracing;	/* Called from ldd to print libs */
 static char *ld_utrace;		/* Use utrace() to log events. */
 static Obj_Entry *obj_list;	/* Head of linked list of shared objects */
@@ -284,6 +289,15 @@ ld_utrace_log(int event, void *handle, v
 }
 
 /*
+ * OS Versions to try. Will iterate through the string list looking for
+ * sub-directories with that name.  When a NULL is found it will stop
+ * iterating through the list.  To search the standard location a ""
+ * version needs to be listed before the NULL.
+ */
+static char *os_version_try[] = {NULL, NULL, NULL};
+#define OS_VERSION_LEN 10			/* max ascii size of int */
+#define OS_VERSION_MAJOR 100000			/* divisor to get os major */
+/*
  * Main entry point for dynamic linking.  The first argument is the
  * stack pointer.  The stack is expected to be laid out as described
  * in the SVR4 ABI specification, Intel 386 Processor Supplement.
@@ -363,6 +377,13 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
         unsetenv(LD_ "LIBRARY_PATH");
         unsetenv(LD_ "LIBMAP_DISABLE");
         unsetenv(LD_ "DEBUG");
+#ifdef COMPAT_32BIT
+        unsetenv(LD_32_ "PRELOAD");
+        unsetenv(LD_32_ "LIBMAP");
+        unsetenv(LD_32_ "LIBRARY_PATH");
+        unsetenv(LD_32_ "LIBMAP_DISABLE");
+        unsetenv(LD_32_ "DEBUG");
+#endif
     }
     ld_debug = getenv(LD_ "DEBUG");
     libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
@@ -373,6 +394,23 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
 	(ld_library_path != NULL) || (ld_preload != NULL);
     ld_tracing = getenv(LD_ "TRACE_LOADED_OBJECTS");
     ld_utrace = getenv(LD_ "UTRACE");
+#ifdef COMPAT_32BIT
+    ld_debug = getenv(LD_32_ "DEBUG");
+    libmap_disable = getenv(LD_32_ "LIBMAP_DISABLE") != NULL;
+    libmap_override = getenv(LD_32_ "LIBMAP");
+    ld32_library_path = getenv(LD_32_ "LIBRARY_PATH");
+    ld32_preload = getenv(LD_32_ "PRELOAD");
+    if (ld32_library_path == NULL)
+	ld32_library_path = ld_library_path;
+    if (ld32_preload == NULL)
+	ld32_preload = ld_preload;
+    dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
+	(ld32_library_path != NULL) || (ld32_preload != NULL) ||
+	(ld_library_path != NULL) || (ld_preload != NULL);
+    dangerous_ld_env = 0;
+    ld_tracing = getenv(LD_32_ "TRACE_LOADED_OBJECTS");
+    ld_utrace = getenv(LD_32_ "UTRACE");
+#endif
 
     if (ld_debug != NULL && *ld_debug != '\0')
 	debug = 1;
@@ -409,6 +447,25 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
 	if ((obj_main = digest_phdr(phdr, phnum, entry, argv0)) == NULL)
 	    die();
     }
+    if (obj_main->ndesc != NULL && strcmp(obj_main->ndesc, "FreeBSD") == 0) {
+	    os_version_try[0] = xmalloc(OS_VERSION_LEN);
+	    snprintf(os_version_try[0], OS_VERSION_LEN, "%d", obj_main->nver);
+	    os_version_try[1] = xmalloc(OS_VERSION_LEN);
+	    snprintf(os_version_try[1], OS_VERSION_LEN,	"%d", obj_main->nver
+	        / OS_VERSION_MAJOR);
+	    os_version_try[2] = "";
+    } else {
+	    /* Deal with FreeBSD 6 binaries that are missing version */
+	    os_version_try[0] = getenv(LD_ "MAP_UNKNOWN_OS_VERSION");
+	    if (os_version_try[0] == NULL) {
+		    os_version_try[0] = "";
+	    } else {
+		    os_version_try[1] = xmalloc(OS_VERSION_LEN);
+		    snprintf(os_version_try[1], OS_VERSION_LEN, "%d",
+			atoi(os_version_try[0]) / OS_VERSION_MAJOR);
+		    os_version_try[2] = "";
+	    }
+    }
 
     obj_main->path = xstrdup(argv0);
     obj_main->mainprog = true;
@@ -447,8 +504,14 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
         libmap_disable = (bool)lm_init(libmap_override);
 
     dbg("loading LD_PRELOAD libraries");
-    if (load_preload_objects() == -1)
+#ifdef COMPAT_32BIT
+    if (load_preload_objects(ld32_preload) == -1)
+        if (load_preload_objects(ld_preload) == -1)
 	die();
+#else
+    if (load_preload_objects(ld_preload) == -1)
+	die();
+#endif
     preload_tail = obj_tail;
 
     dbg("loading needed objects");
@@ -861,12 +924,24 @@ digest_phdr(const Elf_Phdr *phdr, int ph
     Obj_Entry *obj;
     const Elf_Phdr *phlimit = phdr + phnum;
     const Elf_Phdr *ph;
+    const Elf_Note *nh;
     int nsegs = 0;
 
     obj = obj_new();
     for (ph = phdr;  ph < phlimit;  ph++) {
 	switch (ph->p_type) {
 
+	case PT_NOTE:
+	    nh = ((const Elf_Note *)(const Elf_Phdr *)ph->p_vaddr);
+	    if (nh->n_type == NT_PRSTATUS) {
+		obj->ndesc = &((char *)((const Elf_Phdr *)ph->p_vaddr))
+		  [sizeof(nh->n_namesz) + sizeof(nh->n_descsz) + sizeof(nh->n_type)];
+		obj->nver = *(u_int32_t *)(&((const char *)((const Elf_Phdr *)ph->p_vaddr))
+		  [sizeof(nh->n_namesz) + sizeof(nh->n_descsz) + sizeof(nh->n_type)
+		  + ((const Elf_Note *)(const Elf_Phdr *)ph->p_vaddr)->n_namesz]);
+	    }
+	    break;
+
 	case PT_PHDR:
 	    if ((const Elf_Phdr *)ph->p_vaddr != phdr) {
 		_rtld_error("%s: invalid PT_PHDR", path);
@@ -994,6 +1069,9 @@ find_library(const char *xname, const Ob
 {
     char *pathname;
     char *name;
+    int i;
+    char *temp;
+    char new_path[PATH_MAX];
 
     if (strchr(xname, '/') != NULL) {	/* Hard coded pathname */
 	if (xname[0] != '/' && !trust) {
@@ -1001,7 +1079,24 @@ find_library(const char *xname, const Ob
 	      xname);
 	    return NULL;
 	}
-	return xstrdup(xname);
+	for (i = 0; os_version_try[i] != NULL; i++) {
+		if (os_version_try[i][0] == '\0')
+			temp = (char *)xname;
+		else {
+			bzero(new_path, sizeof(new_path));
+			bcopy(xname, new_path, strlen(xname) + 1);
+			temp = rindex(new_path, '/');
+			temp++;
+			*temp = '\0';
+			strncat(new_path, os_version_try[i], sizeof(new_path));
+			temp = rindex(xname, '/');
+			strncat(new_path, temp, sizeof(new_path));
+			temp = new_path;
+		}
+		if (access(temp, F_OK) == 0)
+			return xstrdup(temp);
+	}
+	return NULL;
     }
 
     if (libmap_disable || (refobj == NULL) ||
@@ -1010,6 +1105,14 @@ find_library(const char *xname, const Ob
 
     dbg(" Searching for \"%s\"", name);
 
+#ifdef COMPAT_32BIT
+    if ((pathname = search_library_path(name, ld32_library_path)) != NULL ||
+      (refobj != NULL &&
+      (pathname = search_library_path(name, refobj->rpath)) != NULL) ||
+      (pathname = search_library_path(name, gethints())) != NULL ||
+      (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)
+	return pathname;
+#endif
     if ((pathname = search_library_path(name, ld_library_path)) != NULL ||
       (refobj != NULL &&
       (pathname = search_library_path(name, refobj->rpath)) != NULL) ||
@@ -1311,9 +1414,9 @@ load_needed_objects(Obj_Entry *first)
 }
 
 static int
-load_preload_objects(void)
+load_preload_objects(char *preload)
 {
-    char *p = ld_preload;
+    char *p = preload;
     static const char delim[] = " \t:;";
 
     if (p == NULL)
@@ -1326,6 +1429,7 @@ load_preload_objects(void)
 
 	savech = p[len];
 	p[len] = '\0';
+	if (find_library(p, NULL) != NULL)
 	if (load_object(p, NULL) == NULL)
 	    return -1;	/* XXX - cleanup */
 	p[len] = savech;
@@ -1731,26 +1835,36 @@ static void *
 try_library_path(const char *dir, size_t dirlen, void *param)
 {
     struct try_library_args *arg;
+    char *version = NULL;
+    int temp_dirlen, i;
 
     arg = param;
     if (*dir == '/' || trust) {
 	char *pathname;
 
-	if (dirlen + 1 + arg->namelen + 1 > arg->buflen)
-		return (NULL);
-
+	for (i = 0; os_version_try[i] != NULL; i++) {
+	    if (dirlen + 1 + arg->namelen + 1 + strlen(os_version_try[i]) + 1 > arg->buflen)
+		continue;
+	    version = os_version_try[i];
+	    temp_dirlen = dirlen;
 	pathname = arg->buffer;
-	strncpy(pathname, dir, dirlen);
-	pathname[dirlen] = '/';
-	strcpy(pathname + dirlen + 1, arg->name);
-
+	    pathname[0] = '\000';
+	    strncpy(pathname, dir, temp_dirlen);
+	    pathname[temp_dirlen] = '/';
+	    if (version[0] != '\000') {
+		strcpy(pathname + temp_dirlen + 1, version);
+		temp_dirlen += strlen(os_version_try[i]) + 1;
+		pathname[temp_dirlen] = '/';
+	    }
+	    strcpy(pathname + temp_dirlen + 1, arg->name);
 	dbg("  Trying \"%s\"", pathname);
 	if (access(pathname, F_OK) == 0) {		/* We found it */
-	    pathname = xmalloc(dirlen + 1 + arg->namelen + 1);
+		pathname = xmalloc(temp_dirlen + 1 + arg->namelen + 1);
 	    strcpy(pathname, arg->buffer);
 	    return (pathname);
 	}
     }
+    }
     return (NULL);
 }
 
@@ -2729,8 +2843,9 @@ trace_loaded_objects(Obj_Entry *obj)
 	    printf("%s:\n", obj->path);
 	for (needed = obj->needed; needed; needed = needed->next) {
 	    if (needed->obj != NULL) {
-		if (needed->obj->traced && !list_containers)
+		    if (needed->obj->traced && !list_containers) {
 		    continue;
+		    }
 		needed->obj->traced = true;
 		path = needed->obj->path;
 	    } else
Index: rtld.h
===================================================================
RCS file: /cvs/src/libexec/rtld-elf/rtld.h,v
retrieving revision 1.39
diff -u -p -w -r1.39 rtld.h
--- rtld.h	4 Apr 2008 20:59:26 -0000	1.39
+++ rtld.h	24 Feb 2009 00:05:10 -0000
@@ -45,7 +45,7 @@
 #define	_PATH_ELF_HINTS		"/var/run/ld-elf32.so.hints"
 /* For running 32 bit binaries  */
 #define	STANDARD_LIBRARY_PATH	"/lib32:/usr/lib32"
-#define LD_ "LD_32_"
+#define LD_32_ "LD_32_"
 #endif
 
 #ifndef STANDARD_LIBRARY_PATH
@@ -223,6 +223,9 @@ typedef struct Struct_Obj_Entry {
     dev_t dev;			/* Object's filesystem's device */
     ino_t ino;			/* Object's inode number */
     void *priv;			/* Platform-dependant */
+
+    char *ndesc;		/* Note Description */
+    u_int32_t nver;		/* FreeBSD Version */
 } Obj_Entry;
 
 #define RTLD_MAGIC	0xd550b87a


More information about the freebsd-arch mailing list