summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-01-07 10:33:01 +0000
committerkib <kib@FreeBSD.org>2012-01-07 10:33:01 +0000
commit2685c039fc6e472a64c44267b0145e051115a24b (patch)
treecf61a8d017fae28ad3fed9686940a74e9bafedf0
parent41b065b8d46fe265392cdb2ddde288e6685e6a17 (diff)
downloadFreeBSD-src-2685c039fc6e472a64c44267b0145e051115a24b.zip
FreeBSD-src-2685c039fc6e472a64c44267b0145e051115a24b.tar.gz
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
-rw-r--r--include/dlfcn.h1
-rw-r--r--lib/libc/gen/Makefile.inc4
-rw-r--r--lib/libc/gen/Symbol.map1
-rw-r--r--lib/libc/gen/dlfcn.c9
-rw-r--r--lib/libc/gen/dlopen.336
-rw-r--r--libexec/rtld-elf/Symbol.map4
-rw-r--r--libexec/rtld-elf/rtld.c110
7 files changed, 130 insertions, 35 deletions
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 794fde1..c508843 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -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);
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index 1e068a5..11d1a58 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -95,8 +95,8 @@ MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 opendir.3 \
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
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index adc5964..e00e746 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -382,6 +382,7 @@ FBSD_1.2 {
};
FBSD_1.3 {
+ fdlopen;
__FreeBSD_libc_enter_restricted_mode;
};
diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c
index b109cc9..7be9f87 100644
--- a/lib/libc/gen/dlfcn.c
+++ b/lib/libc/gen/dlfcn.c
@@ -147,6 +147,15 @@ dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *),
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)
diff --git a/lib/libc/gen/dlopen.3 b/lib/libc/gen/dlopen.3
index 3da9b6e..089e631 100644
--- a/lib/libc/gen/dlopen.3
+++ b/lib/libc/gen/dlopen.3
@@ -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
diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map
index 28b24a1..9ad6251 100644
--- a/libexec/rtld-elf/Symbol.map
+++ b/libexec/rtld-elf/Symbol.map
@@ -18,6 +18,10 @@ FBSD_1.0 {
__tls_get_addr;
};
+FBSD_1.3 {
+ fdlopen;
+};
+
FBSDprivate_1.0 {
_rtld_thread_init;
_rtld_allocate_tls;
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 3973c67..c6eea1a 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -83,7 +83,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 *);
@@ -103,7 +103,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 *);
@@ -120,6 +120,7 @@ static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now,
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_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);
}
@@ -1568,7 +1569,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);
@@ -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_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);
@@ -1703,20 +1729,25 @@ 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);
- 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_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) {
OpenPOWER on IntegriCloud