Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 7 Jan 2012 10:33:02 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r229768 - in head: include lib/libc/gen libexec/rtld-elf
Message-ID:  <201201071033.q07AX21h035775@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Sat Jan  7 10:33:01 2012
New Revision: 229768
URL: http://svn.freebsd.org/changeset/base/229768

Log:
  Implement fdlopen(3), an rtld interface to load shared object by file
  descriptor.
  
  Requested and tested by:	des (previous version)
  Reviewed by:  des, kan (previous version)
  MFC after:    2 weeks

Modified:
  head/include/dlfcn.h
  head/lib/libc/gen/Makefile.inc
  head/lib/libc/gen/Symbol.map
  head/lib/libc/gen/dlfcn.c
  head/lib/libc/gen/dlopen.3
  head/libexec/rtld-elf/Symbol.map
  head/libexec/rtld-elf/rtld.c

Modified: head/include/dlfcn.h
==============================================================================
--- head/include/dlfcn.h	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/include/dlfcn.h	Sat Jan  7 10:33:01 2012	(r229768)
@@ -118,6 +118,7 @@ void	*dlopen(const char *, int);
 void	*dlsym(void * __restrict, const char * __restrict);
 
 #if __BSD_VISIBLE
+void	*fdlopen(int, int);
 int	 dladdr(const void * __restrict, Dl_info * __restrict);
 dlfunc_t dlfunc(void * __restrict, const char * __restrict);
 int	 dlinfo(void * __restrict, int, void * __restrict);

Modified: head/lib/libc/gen/Makefile.inc
==============================================================================
--- head/lib/libc/gen/Makefile.inc	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/lib/libc/gen/Makefile.inc	Sat Jan  7 10:33:01 2012	(r229768)
@@ -95,8 +95,8 @@ MLINKS+=directory.3 closedir.3 directory
 	directory.3 fdopendir.3 \
 	directory.3 readdir.3 directory.3 readdir_r.3 directory.3 rewinddir.3 \
 	directory.3 seekdir.3 directory.3 telldir.3
-MLINKS+=dlopen.3 dlclose.3 dlopen.3 dlerror.3 dlopen.3 dlfunc.3 \
-	dlopen.3 dlsym.3
+MLINKS+=dlopen.3 fdlopen.3 dlopen.3 dlclose.3 dlopen.3 dlerror.3 \
+	dlopen.3 dlfunc.3 dlopen.3 dlsym.3
 MLINKS+=err.3 err_set_exit.3 err.3 err_set_file.3 err.3 errc.3 err.3 errx.3 \
 	err.3 verr.3 err.3 verrc.3 err.3 verrx.3 err.3 vwarn.3 err.3 vwarnc.3 \
 	err.3 vwarnx.3 err.3 warnc.3 err.3 warn.3 err.3 warnx.3

Modified: head/lib/libc/gen/Symbol.map
==============================================================================
--- head/lib/libc/gen/Symbol.map	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/lib/libc/gen/Symbol.map	Sat Jan  7 10:33:01 2012	(r229768)
@@ -382,6 +382,7 @@ FBSD_1.2 {
 };
 
 FBSD_1.3 {
+	 fdlopen;
 	__FreeBSD_libc_enter_restricted_mode;
 };
 

Modified: head/lib/libc/gen/dlfcn.c
==============================================================================
--- head/lib/libc/gen/dlfcn.c	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/lib/libc/gen/dlfcn.c	Sat Jan  7 10:33:01 2012	(r229768)
@@ -147,6 +147,15 @@ dl_iterate_phdr(int (*callback)(struct d
 	return 0;
 }
 
+#pragma weak fdlopen
+void *
+fdlopen(int fd, int mode)
+{
+
+	_rtld_error(sorry);
+	return NULL;
+}
+
 #pragma weak _rtld_atfork_pre
 void
 _rtld_atfork_pre(int *locks)

Modified: head/lib/libc/gen/dlopen.3
==============================================================================
--- head/lib/libc/gen/dlopen.3	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/lib/libc/gen/dlopen.3	Sat Jan  7 10:33:01 2012	(r229768)
@@ -32,11 +32,12 @@
 .\" @(#) dlopen.3 1.6 90/01/31 SMI
 .\" $FreeBSD$
 .\"
-.Dd July 7, 2009
+.Dd December 21, 2011
 .Dt DLOPEN 3
 .Os
 .Sh NAME
 .Nm dlopen ,
+.Nm fdlopen ,
 .Nm dlsym ,
 .Nm dlfunc ,
 .Nm dlerror ,
@@ -49,6 +50,8 @@
 .Ft void *
 .Fn dlopen "const char *path" "int mode"
 .Ft void *
+.Fn fdlopen "int fd" "int mode"
+.Ft void *
 .Fn dlsym "void * restrict handle" "const char * restrict symbol"
 .Ft dlfunc_t
 .Fn dlfunc "void * restrict handle" "const char * restrict symbol"
@@ -164,6 +167,36 @@ be interrogated with
 .Fn dlerror .
 .Pp
 The
+.Fn fdlopen
+function is similar to
+.Fn dlopen ,
+but it takes the file descriptor argument
+.Fa fd ,
+which is used for the file operations needed to load an object
+into the address space.
+The file descriptor
+.Fa fd
+is not closed by the function regardless a result of execution,
+but a duplicate of the file descriptor is.
+This may be important if a
+.Xr lockf 3
+lock is held on the passed descriptor.
+The
+.Fa fd
+argument -1 is interpreted as a reference to the main
+executable of the process, similar to
+.Va NULL
+value for the
+.Fa name
+argument to
+.Fn dlopen .
+The
+.Fn fdlopen
+function can be used by the code that needs to perform
+additional checks on the loaded objects, to prevent races with
+symlinking or renames.
+.Pp
+The
 .Fn dlsym
 function
 returns the address binding of the symbol described in the null-terminated
@@ -354,6 +387,7 @@ option to the C language compiler.
 .Sh ERRORS
 The
 .Fn dlopen ,
+.Fn fdlopen ,
 .Fn dlsym ,
 and
 .Fn dlfunc

Modified: head/libexec/rtld-elf/Symbol.map
==============================================================================
--- head/libexec/rtld-elf/Symbol.map	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/libexec/rtld-elf/Symbol.map	Sat Jan  7 10:33:01 2012	(r229768)
@@ -18,6 +18,10 @@ FBSD_1.0 {
     __tls_get_addr;
 };
 
+FBSD_1.3 {
+    fdlopen;
+};
+
 FBSDprivate_1.0 {
     _rtld_thread_init;
     _rtld_allocate_tls;

Modified: head/libexec/rtld-elf/rtld.c
==============================================================================
--- head/libexec/rtld-elf/rtld.c	Sat Jan  7 09:41:57 2012	(r229767)
+++ head/libexec/rtld-elf/rtld.c	Sat Jan  7 10:33:01 2012	(r229768)
@@ -83,7 +83,7 @@ static void digest_dynamic2(Obj_Entry *,
 static void digest_dynamic(Obj_Entry *, int);
 static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
 static Obj_Entry *dlcheck(void *);
-static Obj_Entry *dlopen_object(const char *name, Obj_Entry *refobj,
+static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
     int lo_flags, int mode);
 static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
 static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
@@ -103,7 +103,7 @@ static void load_filtees(Obj_Entry *, in
 static void unload_filtees(Obj_Entry *);
 static int load_needed_objects(Obj_Entry *, int);
 static int load_preload_objects(void);
-static Obj_Entry *load_object(const char *, const Obj_Entry *, int);
+static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int);
 static void map_stacks_exec(RtldLockState *);
 static Obj_Entry *obj_from_addr(const void *);
 static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
@@ -120,6 +120,7 @@ static int resolve_objects_ifunc(Obj_Ent
     RtldLockState *lockstate);
 static int rtld_dirname(const char *, char *);
 static int rtld_dirname_abs(const char *, char *);
+static void *rtld_dlopen(const char *name, int fd, int mode);
 static void rtld_exit(void);
 static char *search_library_path(const char *, const char *);
 static const void **get_program_var_addr(const char *, RtldLockState *);
@@ -1544,7 +1545,7 @@ load_filtee1(Obj_Entry *obj, Needed_Entr
 {
 
     for (; needed != NULL; needed = needed->next) {
-	needed->obj = dlopen_object(obj->strtab + needed->name, obj,
+	needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj,
 	  flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) |
 	  RTLD_LOCAL);
     }
@@ -1568,7 +1569,7 @@ process_needed(Obj_Entry *obj, Needed_En
     Obj_Entry *obj1;
 
     for (; needed != NULL; needed = needed->next) {
-	obj1 = needed->obj = load_object(obj->strtab + needed->name, obj,
+	obj1 = needed->obj = load_object(obj->strtab + needed->name, -1, obj,
 	  flags & ~RTLD_LO_NOLOAD);
 	if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0)
 	    return (-1);
@@ -1615,7 +1616,7 @@ load_preload_objects(void)
 
 	savech = p[len];
 	p[len] = '\0';
-	if (load_object(p, NULL, 0) == NULL)
+	if (load_object(p, -1, NULL, 0) == NULL)
 	    return -1;	/* XXX - cleanup */
 	p[len] = savech;
 	p += len;
@@ -1625,43 +1626,68 @@ load_preload_objects(void)
     return 0;
 }
 
+static const char *
+printable_path(const char *path)
+{
+
+	return (path == NULL ? "<unknown>" : path);
+}
+
 /*
- * Load a shared object into memory, if it is not already loaded.
+ * Load a shared object into memory, if it is not already loaded.  The
+ * object may be specified by name or by user-supplied file descriptor
+ * fd_u. In the later case, the fd_u descriptor is not closed, but its
+ * duplicate is.
  *
  * Returns a pointer to the Obj_Entry for the object.  Returns NULL
  * on failure.
  */
 static Obj_Entry *
-load_object(const char *name, const Obj_Entry *refobj, int flags)
+load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags)
 {
     Obj_Entry *obj;
-    int fd = -1;
+    int fd;
     struct stat sb;
     char *path;
 
-    for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
-	if (object_match_name(obj, name))
-	    return obj;
+    if (name != NULL) {
+	for (obj = obj_list->next;  obj != NULL;  obj = obj->next) {
+	    if (object_match_name(obj, name))
+		return (obj);
+	}
 
-    path = find_library(name, refobj);
-    if (path == NULL)
-	return NULL;
+	path = find_library(name, refobj);
+	if (path == NULL)
+	    return (NULL);
+    } else
+	path = NULL;
 
     /*
-     * If we didn't find a match by pathname, open the file and check
-     * again by device and inode.  This avoids false mismatches caused
-     * by multiple links or ".." in pathnames.
+     * If we didn't find a match by pathname, or the name is not
+     * supplied, open the file and check again by device and inode.
+     * This avoids false mismatches caused by multiple links or ".."
+     * in pathnames.
      *
      * To avoid a race, we open the file and use fstat() rather than
      * using stat().
      */
-    if ((fd = open(path, O_RDONLY)) == -1) {
-	_rtld_error("Cannot open \"%s\"", path);
-	free(path);
-	return NULL;
+    fd = -1;
+    if (fd_u == -1) {
+	if ((fd = open(path, O_RDONLY)) == -1) {
+	    _rtld_error("Cannot open \"%s\"", path);
+	    free(path);
+	    return (NULL);
+	}
+    } else {
+	fd = dup(fd_u);
+	if (fd == -1) {
+	    _rtld_error("Cannot dup fd");
+	    free(path);
+	    return (NULL);
+	}
     }
     if (fstat(fd, &sb) == -1) {
-	_rtld_error("Cannot fstat \"%s\"", path);
+	_rtld_error("Cannot fstat \"%s\"", printable_path(path));
 	close(fd);
 	free(path);
 	return NULL;
@@ -1669,7 +1695,7 @@ load_object(const char *name, const Obj_
     for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
 	if (obj->ino == sb.st_ino && obj->dev == sb.st_dev)
 	    break;
-    if (obj != NULL) {
+    if (obj != NULL && name != NULL) {
 	object_add_name(obj, name);
 	free(path);
 	close(fd);
@@ -1703,20 +1729,25 @@ do_load_object(int fd, const char *name,
      */
     if (dangerous_ld_env) {
 	if (fstatfs(fd, &fs) != 0) {
-	    _rtld_error("Cannot fstatfs \"%s\"", path);
-		return NULL;
+	    _rtld_error("Cannot fstatfs \"%s\"", printable_path(path));
+	    return NULL;
 	}
 	if (fs.f_flags & MNT_NOEXEC) {
 	    _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
 	    return NULL;
 	}
     }
-    dbg("loading \"%s\"", path);
-    obj = map_object(fd, path, sbp);
+    dbg("loading \"%s\"", printable_path(path));
+    obj = map_object(fd, printable_path(path), sbp);
     if (obj == NULL)
         return NULL;
 
-    object_add_name(obj, name);
+    /*
+     * If DT_SONAME is present in the object, digest_dynamic2 already
+     * added it to the object names.
+     */
+    if (name != NULL)
+	object_add_name(obj, name);
     obj->path = path;
     digest_dynamic(obj, 0);
     if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) ==
@@ -2212,6 +2243,20 @@ dllockinit(void *context,
 void *
 dlopen(const char *name, int mode)
 {
+
+	return (rtld_dlopen(name, -1, mode));
+}
+
+void *
+fdlopen(int fd, int mode)
+{
+
+	return (rtld_dlopen(NULL, fd, mode));
+}
+
+static void *
+rtld_dlopen(const char *name, int fd, int mode)
+{
     RtldLockState lockstate;
     int lo_flags;
 
@@ -2232,7 +2277,7 @@ dlopen(const char *name, int mode)
     if (ld_tracing != NULL)
 	    lo_flags |= RTLD_LO_TRACE;
 
-    return (dlopen_object(name, obj_main, lo_flags,
+    return (dlopen_object(name, fd, obj_main, lo_flags,
       mode & (RTLD_MODEMASK | RTLD_GLOBAL)));
 }
 
@@ -2247,7 +2292,8 @@ dlopen_cleanup(Obj_Entry *obj)
 }
 
 static Obj_Entry *
-dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
+dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
+    int mode)
 {
     Obj_Entry **old_obj_tail;
     Obj_Entry *obj;
@@ -2262,11 +2308,11 @@ dlopen_object(const char *name, Obj_Entr
 
     old_obj_tail = obj_tail;
     obj = NULL;
-    if (name == NULL) {
+    if (name == NULL && fd == -1) {
 	obj = obj_main;
 	obj->refcount++;
     } else {
-	obj = load_object(name, refobj, lo_flags);
+	obj = load_object(name, fd, refobj, lo_flags);
     }
 
     if (obj) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201071033.q07AX21h035775>