parlib: Fix dlopen() with -rdynamic
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 30 Aug 2017 17:20:48 +0000 (13:20 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 30 Aug 2017 17:51:41 +0000 (13:51 -0400)
Part 3 of trying to fix shared objects, following up on commit c77d5ade84b7
("parlib: Fix fake parlib detection for dlopen() (XCC)").

If we use -rdynamic, all symbols from the binary (e.g. hello.c) will
override functions in the .so.  For instance, __in_fake_parlib() and
vcore_lib_init() will be from the binary, not the .so, even when we're
running the library's ctors.

The fix is relatively simple: __in_fake_parlib() is a static inline, which
won't give the opportunity for the .so to link against a global version,
since there is no global version.

For the ctors, we actually could leave vcore_lib_init() as a global ctor,
since it can safely be called multiple times.  But that's a little sloppy
and confusing - I wondered for a little while why vcore_lib_init() ran from
hello, while uthread_lib_init() was run from within the .so.  static vs
global.  We still need the guts vcore_lib_init() to be in a header for
uthread.c, so the sanest thing was to just split it up.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/parlib/alarm.c
user/parlib/include/parlib/vcore.h
user/parlib/panic.c
user/parlib/uthread.c
user/parlib/vcore.c
user/parlib/vcore_tick.c

index 54eaad4..b11accd 100644 (file)
@@ -147,7 +147,7 @@ static void devalarm_forked(void)
        global_tchain.timerfd = -42;
 }
 
-static void __attribute__((constructor)) init_alarm_service(void)
+static void __attribute__((constructor)) alarm_service_ctor(void)
 {
        int ctlfd, timerfd, alarmid;
        struct event_queue *ev_q;
index 6e8f2ef..242902e 100644 (file)
@@ -40,8 +40,9 @@ static inline void set_vcpd_tls_desc(uint32_t vcoreid, void *tls_desc);
 static inline uint64_t vcore_account_resume_nsec(uint32_t vcoreid);
 static inline uint64_t vcore_account_total_nsec(uint32_t vcoreid);
 static inline void cpu_relax_any(void);
+static inline bool __in_fake_parlib(void);
+
 void vcore_lib_init(void);
-bool __in_fake_parlib(void);
 void vcore_change_to_m(void);
 void vcore_request_more(long nr_new_vcores);
 void vcore_request_total(long nr_vcores_wanted);
@@ -193,6 +194,23 @@ static inline void cpu_relax_any(void)
        return cpu_relax_vc(vcore_id());
 }
 
+/* Shared libraries also contain parlib.  That'll be true until we start making
+ * parlib a .so, which has some TLS implications (and maybe others).  The real
+ * parlib is the one in the program binary, not the shared libraries.  This
+ * detection works because all shared libs, both the -l and the dlopens, are
+ * mapped above the BRK.
+ *
+ * Previously, we tried using weak symbols, specifically _start or _end, but be
+ * careful.  If you pass e.g. _start or _end to a function or inline asm, the
+ * program binary will do something slightly different, which may make the
+ * shared library load different values. */
+static inline bool __in_fake_parlib(void)
+{
+       static char dummy;
+
+       return (uintptr_t)&dummy > BRK_START;
+}
+
 #ifndef __PIC__
 
 #define begin_safe_access_tls_vars()
index dc796f5..44e992e 100644 (file)
@@ -5,7 +5,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 
-static void __attribute__((constructor)) parlib_stdio_init(void)
+static void __attribute__((constructor)) parlib_stdio_ctor(void)
 {
        if (__in_fake_parlib())
                return;
index 5f5ec9e..85f7ea0 100644 (file)
@@ -159,13 +159,13 @@ static char *__ros_errstr_loc(void)
                return current_uthread->err_str;
 }
 
-static void __attribute__((constructor)) uthread_lib_init(void)
+static void __attribute__((constructor)) uthread_lib_ctor(void)
 {
        /* Surprise!  Parlib's ctors also run in shared objects.  We can't have
         * multiple versions of parlib (with multiple data structures). */
        if (__in_fake_parlib())
                return;
-       /* Need to make sure the vcore_lib_init() ctor runs first */
+       /* Need to make sure vcore_lib_init() runs first */
        vcore_lib_init();
        /* Instead of relying on ctors for the specific 2LS, we make sure they are
         * called next.  They will call uthread_2ls_init().
index 3a29c65..32c89e3 100644 (file)
@@ -37,28 +37,6 @@ void __attribute__((noreturn)) __vcore_entry(void)
 }
 void vcore_entry(void) __attribute__((weak, alias ("__vcore_entry")));
 
-static void __fake_start(void)
-{
-}
-void _start(void) __attribute__((weak, alias ("__fake_start")));
-
-/* Shared libraries also contain parlib.  That'll be true until we start making
- * parlib a .so, which has some TLS implications (and maybe others).  The real
- * parlib is the one in the program binary, not the shared libraries.  This
- * detection works because all shared libs, both the -l and the dlopens, are
- * mapped above the BRK.
- *
- * Previously, we tried using weak symbols, specifically _start or _end, but be
- * careful.  If you pass e.g. _start or _end to a function or inline asm, the
- * program binary will do something slightly different, which may make the
- * shared library load different values. */
-bool __in_fake_parlib(void)
-{
-       static char dummy;
-
-       return (uintptr_t)&dummy > BRK_START;
-}
-
 /* TODO: probably don't want to dealloc.  Considering caching */
 static void free_transition_tls(int id)
 {
@@ -192,10 +170,14 @@ static void vcore_libc_init(void)
         * program. */
 }
 
-void __attribute__((constructor)) vcore_lib_init(void)
+/* We need to separate the guts of vcore_lib_ctor() into a separate function,
+ * since the uthread ctor depends on this ctor running first.
+ *
+ * Also note that if you make a global ctor (not static, like this used to be),
+ * any shared objects that you load when the binary is built with -rdynamic will
+ * run the global ctor from the binary, not the one from the .so. */
+void vcore_lib_init(void)
 {
-       if (__in_fake_parlib())
-               return;
        /* Note this is racy, but okay.  The first time through, we are _S.
         * Also, this is the "lowest" level constructor for now, so we don't need
         * to call any other init functions after our run_once() call. This may
@@ -209,6 +191,13 @@ void __attribute__((constructor)) vcore_lib_init(void)
        vcore_libc_init();
 }
 
+static void __attribute__((constructor)) vcore_lib_ctor(void)
+{
+       if (__in_fake_parlib())
+               return;
+       vcore_lib_init();
+}
+
 /* Helper functions used to reenter at the top of a vcore's stack for an
  * arbitrary function */
 static void __attribute__((noinline, noreturn)) 
index fcd9e1d..7df2e5f 100644 (file)
@@ -33,7 +33,7 @@ struct vcore_tick {
 
 static struct vcore_tick *__vc_ticks;
 
-static void __attribute__((constructor)) vcore_tick_lib_init(void)
+static void __attribute__((constructor)) vcore_tick_lib_ctor(void)
 {
        if (__in_fake_parlib())
                return;