tech-toolchain archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

support dlinfo() in the dynlinker



Hi,

The attached patch adds support for dlinfo() to the dynlinker.
Specifically, it implements the RTLD_DI_LINKMAP request, which fetches
an object's linkmap pointer.  This linkmap can be used to iterate over
all the objects linked into program.

The interface is the same as the one present in Solaris.  I realize
we historically prefer the name "dlctl()" for implementing similar
functionality, but as it is completely unimplemented in the elf dynamic
linker, I think we can attempt to be compatible with Solarisa interfaces
here.  Requests other than LINKMAP supported by solaris are currently
unimplemented.
http://docs.sun.com/app/docs/doc/816-5168/dlinfo-3c?a=view

Out of other operating systems FreeBSD implements the solaris interface
and Linux, from what I could tell, does not implement anything similar.

I need this interface for iterating over all rump components which were
linked in on the command line.  This solves the last link set problem in
rump (incidentally, the only one that ever existed).  Previously, only
the first rump module component linked on the command line would work,
e.g. cc -o prog -lrumpfs_ffs -lrumpfs_tmpfs would support only ffs.
tmpfs could be dlopen()d e.g. via ukfs_modload(), but the linker idiom
is very useful, since it avoids having to spread component information
over the Makefile, code and/or configuration file.

proof-of-concept code (tested):
=== snip ===
static void
process(struct link_map *map)
{
        void *handle;
        struct modinfo **mi;

        if (strstr(map->l_name, "librump") == NULL)
                return;

        handle = dlopen(map->l_name, RTLD_LAZY);
        assert(handle != NULL);

        mi = dlsym(handle, "__start_link_set_modules");
        if (!mi)
                return;

        rump_module_init(*mi, NULL);

        dlclose(handle);
}

/*
 * Get the linkmap from the dynlinker.  Try to load kernel modules
 * from all objects in the linkmap.
 */
void
rumpuser_dl_module_bootstrap(void)
{
        struct link_map *map, *map_iter;

        if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map) == -1) {
                fprintf(stderr, "warning: rumpuser module bootstrap "
                    "failed: %s\n", dlerror());
                return;
        }

        for (map_iter = map; map_iter; map_iter = map_iter->l_next)
                process(map_iter);
        for (map_iter = map->l_prev; map_iter; map_iter = map_iter->l_prev)
                process(map_iter);
}
=== snip ===

Comments?
(patch is against netbsd-5)
Index: lib/libc/dlfcn/dlfcn_elf.c
===================================================================
RCS file: /cvsroot/src/lib/libc/dlfcn/dlfcn_elf.c,v
retrieving revision 1.5
diff -p -u -r1.5 dlfcn_elf.c
--- lib/libc/dlfcn/dlfcn_elf.c  18 Jul 2004 17:26:19 -0000      1.5
+++ lib/libc/dlfcn/dlfcn_elf.c  18 Sep 2009 15:40:19 -0000
@@ -37,12 +37,14 @@ __RCSID("$NetBSD: dlfcn_elf.c,v 1.5 2004
 #undef dlsym
 #undef dlerror
 #undef dladdr
+#undef dfinfo
 
 #define        dlopen          ___dlopen
 #define        dlclose         ___dlclose
 #define        dlsym           ___dlsym
 #define        dlerror         ___dlerror
 #define        dladdr          ___dladdr
+#define        dlinfo          ___dlinfo
 
 #define ELFSIZE ARCH_ELFSIZE
 #include "rtld.h"
@@ -53,12 +55,14 @@ __weak_alias(dlclose,___dlclose)
 __weak_alias(dlsym,___dlsym)
 __weak_alias(dlerror,___dlerror)
 __weak_alias(dladdr,___dladdr)
+__weak_alias(dlinfo,___dlinfo)
 
 __weak_alias(__dlopen,___dlopen)
 __weak_alias(__dlclose,___dlclose)
 __weak_alias(__dlsym,___dlsym)
 __weak_alias(__dlerror,___dlerror)
 __weak_alias(__dladdr,___dladdr)
+__weak_alias(__dlinfo,___dlinfo)
 #endif
 
 /*
@@ -112,3 +116,11 @@ dladdr(const void *addr, Dl_info *dli)
 
        return 0;
 }
+
+/*ARGSUSED*/
+int
+dlinfo(void *handle, int req, void *v)
+{
+
+       return -1;
+}
Index: libexec/ld.elf_so/reloc.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/reloc.c,v
retrieving revision 1.96
diff -p -u -r1.96 reloc.c
--- libexec/ld.elf_so/reloc.c   29 Jul 2008 16:27:01 -0000      1.96
+++ libexec/ld.elf_so/reloc.c   18 Sep 2009 15:40:23 -0000
@@ -210,6 +210,7 @@ _rtld_relocate_objects(Obj_Entry *first,
                obj->dlerror = dlerror;
                obj->dlclose = dlclose;
                obj->dladdr = dladdr;
+               obj->dlinfo = dlinfo;
 
                dbg(("fixing up PLTGOT"));
                /* Set the special PLTGOT entries. */
Index: libexec/ld.elf_so/rtld.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/rtld.c,v
retrieving revision 1.123
diff -p -u -r1.123 rtld.c
--- libexec/ld.elf_so/rtld.c    26 Oct 2008 07:11:54 -0000      1.123
+++ libexec/ld.elf_so/rtld.c    18 Sep 2009 15:40:23 -0000
@@ -985,6 +985,47 @@ dladdr(const void *addr, Dl_info *info)
        return 1;
 }
 
+__strong_alias(__dlinfo,dlinfo)
+int
+dlinfo(void *handle, int req, void *v)
+{
+       const Obj_Entry *obj;
+       void *retaddr;
+
+       if (handle == RTLD_SELF) {
+#ifdef __powerpc__
+               retaddr = hackish_return_address();
+#else
+               retaddr = __builtin_return_address(0);
+#endif
+               if ((obj = _rtld_obj_from_addr(retaddr)) == NULL) {
+                       _rtld_error("Cannot determine caller's shared object");
+                       return -1;
+               }
+       } else {
+               if ((obj = _rtld_dlcheck(handle)) == NULL) {
+                       _rtld_error("Invalid handle");
+                       return -1;
+               }
+       }
+
+       switch (req) {
+       case RTLD_DI_LINKMAP:
+               {
+               const struct link_map **map = v;
+
+               *map = &obj->linkmap;
+               break;
+               }
+
+       default:
+               _rtld_error("Invalid request");
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  * Error reporting function.  Use it like printf.  If formats the message
  * into a buffer, and sets things up so that the next call to dlerror()
Index: libexec/ld.elf_so/rtld.h
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/rtld.h,v
retrieving revision 1.79
diff -p -u -r1.79 rtld.h
--- libexec/ld.elf_so/rtld.h    4 Oct 2008 09:37:12 -0000       1.79
+++ libexec/ld.elf_so/rtld.h    18 Sep 2009 15:40:24 -0000
@@ -168,6 +168,7 @@ typedef struct Struct_Obj_Entry {
        char           *(*dlerror)(void);
        int             (*dlclose)(void *);
        int             (*dladdr)(const void *, Dl_info *);
+       int             (*dlinfo)(void *, int, void *);
 
        u_int32_t       mainprog:1,     /* True if this is the main program */
                        rtld:1,         /* True if this is the dynamic linker */
@@ -222,6 +223,7 @@ void *dlopen(const char *, int);
 void *dlsym(void *, const char *);
 int dlclose(void *);
 int dladdr(const void *, Dl_info *);
+int dlinfo(void *, int, void *);
 
 void _rtld_error(const char *, ...)
      __attribute__((__format__(__printf__,1,2)));
Index: libexec/ld.elf_so/symbol.c
===================================================================
RCS file: /cvsroot/src/libexec/ld.elf_so/symbol.c,v
retrieving revision 1.47
diff -p -u -r1.47 symbol.c
--- libexec/ld.elf_so/symbol.c  4 Oct 2008 09:37:12 -0000       1.47
+++ libexec/ld.elf_so/symbol.c  18 Sep 2009 15:40:24 -0000
@@ -69,6 +69,7 @@ _rtld_is_exported(const Elf_Sym *def)
                (fptr_t)dlsym,
                (fptr_t)dlerror,
                (fptr_t)dladdr,
+               (fptr_t)dlinfo,
                NULL
        };
        int i;
Index: include/dlfcn.h
===================================================================
RCS file: /cvsroot/src/include/dlfcn.h,v
retrieving revision 1.19
diff -p -u -r1.19 dlfcn.h
--- include/dlfcn.h     28 Apr 2008 20:22:54 -0000      1.19
+++ include/dlfcn.h     18 Sep 2009 15:40:25 -0000
@@ -54,6 +54,7 @@ void  *dlsym(void * __restrict, const cha
 #if defined(_NETBSD_SOURCE)
 int    dladdr(const void * __restrict, Dl_info * __restrict);
 int    dlctl(void *, int, void *);
+int    dlinfo(void *, int, void *);
 #endif
 __aconst char *dlerror(void);
 __END_DECLS
@@ -88,4 +89,23 @@ __END_DECLS
 #endif /* 0 */
 #endif /* defined(_NETBSD_SOURCE) */
 
+/*
+ * dlinfo() commands
+ *
+ * From Solarisa: http://docs.sun.com/app/docs/doc/816-5168/dlinfo-3c?a=view
+ */
+#if defined(_NETBSD_SOURCE)
+#define RTLD_DI_LINKMAP                3
+#if 0
+#define RTLD_DI_ARGSINFO       1
+#define RTLD_DI_CONFIGADDR     2
+#define RTLD_DI_LMID           4
+#define RTLD_DI_SERINFO                5
+#define RTLD_DI_SERINFOSIZE    6
+#define RTLD_DI_ORIGIN         7
+#define RTLD_DI_GETSIGNAL      8
+#define RTLD_DI_SETSIGNAL      9
+#endif
+#endif /* _NETBSD_SOURCE */
+
 #endif /* !defined(_DLFCN_H_) */
Index: tests/libexec/ld.elf_so/Atffile
===================================================================
RCS file: tests/libexec/ld.elf_so/Atffile
diff -N tests/libexec/ld.elf_so/Atffile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/libexec/ld.elf_so/Atffile     18 Sep 2009 16:21:11 -0000
@@ -0,0 +1,6 @@
+Content-Type: application/X-atf-atffile; version="1"
+X-NetBSD-Id: "$NetBSD$"
+
+prop: test-suite = "NetBSD"
+
+tp-glob: *
Index: tests/libexec/ld.elf_so/Makefile
===================================================================
RCS file: tests/libexec/ld.elf_so/Makefile
diff -N tests/libexec/ld.elf_so/Makefile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/libexec/ld.elf_so/Makefile    18 Sep 2009 16:21:11 -0000
@@ -0,0 +1,9 @@
+# $NetBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR=      ${TESTSBASE}/libexec/rtld
+
+TESTS_C+=      t_dlinfo
+
+.include <bsd.test.mk>
Index: tests/libexec/ld.elf_so/t_dlinfo.c
===================================================================
RCS file: tests/libexec/ld.elf_so/t_dlinfo.c
diff -N tests/libexec/ld.elf_so/t_dlinfo.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/libexec/ld.elf_so/t_dlinfo.c  18 Sep 2009 16:21:11 -0000
@@ -0,0 +1,92 @@
+/*     $NetBSD$        */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <link_elf.h>
+
+#include "../../h_macros.h"
+
+ATF_TC(rtld_dlinfo_linkmap_self);
+ATF_TC_HEAD(rtld_dlinfo_linkmap_self, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "dlinfo with RTLD_SELF handle works");
+}
+ATF_TC_BODY(rtld_dlinfo_linkmap_self, tc)
+{
+       struct link_map *map;
+       int rv;
+
+       rv = dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
+       ATF_CHECK_EQ(rv, 0);
+       ATF_CHECK((strstr(map->l_name, "t_dlinfo") != NULL));
+}
+
+ATF_TC(rtld_dlinfo_linkmap_inval);
+ATF_TC_HEAD(rtld_dlinfo_linkmap_inval, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "dlinfo with invalid handle fails");
+}
+ATF_TC_BODY(rtld_dlinfo_linkmap_inval, tc)
+{
+       void *v;
+       int rv;
+
+       rv = dlinfo((void *)0xcafebabe, RTLD_DI_LINKMAP, &v);
+       ATF_CHECK_EQ(rv, -1);
+}
+
+ATF_TC(rtld_dlinfo_linkmap_dlopen);
+ATF_TC_HEAD(rtld_dlinfo_linkmap_dlopen, tc)
+{
+       atf_tc_set_md_var(tc, "descr", "dlinfo dlopen'd handle works");
+}
+ATF_TC_BODY(rtld_dlinfo_linkmap_dlopen, tc)
+{
+       struct link_map *map;
+       void *handle;
+       int rv;
+
+       handle = dlopen("libutil.so", RTLD_LAZY);
+       ATF_CHECK(handle);
+
+       rv = dlinfo(handle, RTLD_DI_LINKMAP, &map);
+       ATF_CHECK_EQ(rv, 0);
+       ATF_CHECK((strstr(map->l_name, "libutil.so") != NULL));
+       dlclose(handle);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+       ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_self);
+       ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_inval);
+       ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_dlopen);
+}


Home | Main Index | Thread Index | Old Index