rtld: Implement fdlopen(3)
authorJohn Marino <draco@marino.st>
Fri, 6 Apr 2012 07:33:33 +0000 (09:33 +0200)
committerJohn Marino <draco@marino.st>
Fri, 6 Apr 2012 09:56:03 +0000 (11:56 +0200)
Implement rtld interface fdlopen.  It is similar to dlopen, but takes a
file description instead of a path to reference the shared object.

Taken from: FreeBSD SVN 229768 (07 JAN 2012)

include/dlfcn.h
lib/libc/gen/Makefile.inc
lib/libc/gen/dlfcn.3
lib/libc/gen/dlfcn.c
lib/libc/gen/dlopen.3
libexec/rtld-elf/rtld.c

index c2a88d6..908cc53 100644 (file)
@@ -117,6 +117,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);
index 976fd20..3313c97 100644 (file)
@@ -87,6 +87,7 @@ MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 opendir.3 \
        directory.3 readdir.3 directory.3 readdir_r.3 directory.3 rewinddir.3 \
        directory.3 seekdir.3 directory.3 telldir.3 directory.3 fdopendir.3
 MLINKS+=dlsym.3 dlfunc.3
+MLINKS+=dlopen.3 fdlopen.3
 MLINKS+=devname.3 devname_r.3 devname.3 fdevname.3 devname.3 fdevname_r.3
 MLINKS+=endutxent.3 getutxent.3 endutxent.3 getutxid.3 \
        endutxent.3 getutxline.3 endutxent.3 pututxline.3 \
index fd263ce..249f0a9 100644 (file)
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd April 28, 2011
+.Dd April 6, 2012
 .Dt DLFCN 3
 .Os
 .Sh NAME
 .Nm dlopen ,
+.Nm fdlopen ,
 .Nm dlclose ,
 .Nm dlinfo ,
 .Nm dladdr ,
@@ -48,6 +49,8 @@ It is included in every dynamically linked program automatically.
 .In dlfcn.h
 .Ft "void *"
 .Fn dlopen "const char *name" "int mode"
+.Ft "void *"
+.Fn fdlopen "int fd" "int mode"
 .Ft int
 .Fn dlclose "void *handle"
 .Ft int
@@ -82,6 +85,7 @@ under program control.
 .Xr dlopen 3 ,
 .Xr dlsym 3 ,
 .Xr dlvsym 3 ,
+.Xr fdlopen 3 ,
 .Xr link 5
 .Sh HISTORY
 Some of the
index e5f0645..326bfde 100644 (file)
@@ -174,6 +174,14 @@ dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *),
        return callback(&phdr_info, sizeof(phdr_info), data);
 }
 
+#pragma week fdlopen
+void *
+fdlopen(int fd, int mode)
+{
+        _rtld_error(sorry);
+        return NULL;
+}
+
 #pragma weak _rtld_addr_phdr
 int
 _rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info)
index 8261068..899bb66 100644 (file)
 .\" @(#) dlopen.3 1.6 90/01/31 SMI
 .\" $FreeBSD: head/lib/libc/gen/dlopen.3 211397 2010-08-16 15:18:30Z joel $
 .\"
-.Dd April 28, 2011
+.Dd April 6, 2012
 .Dt DLOPEN 3
 .Os
 .Sh NAME
-.Nm dlopen
+.Nm dlopen ,
+.Nm fdlopen
 .Nd returns handle to dynamically loaded shared object
 .Sh LIBRARY
 This function is not in a library.
@@ -45,12 +46,14 @@ It is included in every dynamically linked program automatically.
 .In dlfcn.h
 .Ft void *
 .Fn dlopen "const char *name" "int mode"
+.Ft void *
+.Fn fdlopen "int fd" "int mode"
 .Sh DESCRIPTION
 The
 .Fn dlopen
 function
 provides access to the shared object in
-.Fa path ,
+.Fa name ,
 returning a descriptor that can be used for later
 references to the object in calls to other dl functions.
 If
@@ -128,19 +131,52 @@ The same behaviour may be requested by
 option of the static linker
 .Xr ld 1 .
 .It Dv RTLD_NOLOAD
-Ony return valid handle for the object if it is already loaded in
+Only return valid handle for the object if it is already loaded in
 the process address space, otherwise
 .Dv NULL
 is returned.
 Other mode flags may be specified, which will be applied for promotion
 for the found object.
 .El
-.Sh RETURN VALUE
-The
+.Pp
+If
 .Fn dlopen
-function
-returns a null pointer in the event of an error.
+fails, it returns a null pointer, and sets an error condition which may
+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.
+.El
+.Sh RETURN VALUE
+The functions return a null pointer in the event of an error.
 Whenever an error has been detected, a message detailing it can be
 retrieved via a call to
 .Fn dlerror .
index 17e4a58..f6952eb 100644 (file)
@@ -86,7 +86,7 @@ static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *,
 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 *);
@@ -107,7 +107,7 @@ static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
 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 *);
@@ -126,6 +126,7 @@ static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now,
     int flags, 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 *);
@@ -230,6 +231,7 @@ static func_ptr_type exports[] = {
     (func_ptr_type) &dlclose,
     (func_ptr_type) &dlerror,
     (func_ptr_type) &dlopen,
+    (func_ptr_type) &fdlopen,
     (func_ptr_type) &dlfunc,
     (func_ptr_type) &dlsym,
     (func_ptr_type) &dlvsym,
@@ -1946,7 +1948,7 @@ load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags)
 {
 
     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);
     }
@@ -1970,7 +1972,7 @@ process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags)
     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);
@@ -2020,7 +2022,7 @@ load_preload_objects(void)
 
        savech = p[len];
        p[len] = '\0';
-       obj = load_object(p, NULL, 0);
+       obj = load_object(p, -1, NULL, 0);
        if (obj == NULL)
            return -1;  /* XXX - cleanup */
        p[len] = savech;
@@ -2040,43 +2042,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;
@@ -2084,7 +2111,7 @@ load_object(const char *name, const Obj_Entry *refobj, int flags)
     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);
@@ -2118,7 +2145,7 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp,
      */
     if (dangerous_ld_env) {
        if (fstatfs(fd, &fs) != 0) {
-           _rtld_error("Cannot fstatfs \"%s\"", path);
+           _rtld_error("Cannot fstatfs \"%s\"", printable_path(path));
                return NULL;
        }
        if (fs.f_flags & MNT_NOEXEC) {
@@ -2126,12 +2153,17 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp,
            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)) ==
@@ -2704,6 +2736,20 @@ dlerror(void)
 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;
 
@@ -2724,7 +2770,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)));
 }
 
@@ -2739,7 +2785,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;
@@ -2754,11 +2801,11 @@ dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
 
     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) {