Position independent code and global destructor order (devel/ice)

Konstantin Belousov kostikbel at gmail.com
Thu Dec 5 17:07:44 UTC 2013


On Sat, Nov 30, 2013 at 10:34:27PM +0100, Michael Gmelin wrote:
> We discussed this topic about half a year ago, but came to no
> conclusion. I CCed everyone who participated in the discussion back
> then.
> 
> Since 10-RELEASE is around the corner and I'm trying to get devel/ice
> working on it, one more attempt:
> 
> When -fPIC is used on objects that are linked into an executable (and
> not a shared library), the static destruction order guaranteed by the
> C++ standard is not followed. There is also no warning or error while
> building/linking. The end-result of this problem is subtle and will
> only surface on exit (at the end of runtime). When porting bigger
> projects (like Ice in my case), finding all those problems is really
> hard, since they're not reported at build time. It's not uncommon to
> set global CFLAGS in a build, and not every projects distinguishes
> between objects which will end up in shared libraries and those which
> will be linked to executables directly.
> 
> In case of Ice this leads to crashes (bus error etc.) on exit,
> depending on the projects the results of this could be quite severe.
> The fix is to go through every Makefile of the project, basically
> creating a big patch that touches everything to work around this
> issue, hoping not to forget a single object file. Every user of the library
> and the generated code will have to do the same in their
> Makefiles/Cmakefiles/Jamfiles etc. It makes porting and using C++ code
> on FreeBSD hard and software that runs fine on other platforms will
> break for reasons people won't understand/will take them forever to
> debug.
> 
> So is there anything that could be done to:
> 
> a) Make -fPIC just work like it did before r211706?
>    or if this is not an option.
> b) Report an error when linking the executable, so that objects built
>    with -fPIC can't be used in that case/barf at compile time.
> 
> I would appreciate any kind of constructive response at this point, the
> last I got was back in June: "I think that we could revert the
> termination calls to the functions from the dso being unloaded, but
> this is quite unfortunate, since it will restore the endless series of
> 'segfault at the process termination' reports."

If you provided link to the original discussion, it would take much less
energy and time to refresh the memory.

I looked over this, and I think that r211706 can be refined, by only
calling atexit hooks for 'wrong' dso when the call to __cxa_finalize()
is from the real dlclose(), as opposed to the process exit.  For me,
with the world where the patch is applied, your 'major tom' test passes.

Of course, when dso1 registers __cxa_atexit hook located in dso2, and
dso2 is unloaded before dso1, the hooks are called in the wrong order
still, but the only alternatives there are either crash or do not call
such hooks at all.

diff --git a/include/dlfcn.h b/include/dlfcn.h
index c508843..38957e2 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -53,7 +53,8 @@
 #define	RTLD_DI_SERINFO		4	/* Obtain search path info. */
 #define	RTLD_DI_SERINFOSIZE	5	/*  ... query for required space. */
 #define	RTLD_DI_ORIGIN		6	/* Obtain object origin */
-#define	RTLD_DI_MAX		RTLD_DI_ORIGIN
+#define	RTLD_DI_GLOBALEXIT	7
+#define	RTLD_DI_MAX		RTLD_DI_GLOBALEXIT
 
 /*
  * Special handle arguments for dlsym()/dlinfo().
diff --git a/lib/libc/stdlib/atexit.c b/lib/libc/stdlib/atexit.c
index 18c31c3..b08d1de 100644
--- a/lib/libc/stdlib/atexit.c
+++ b/lib/libc/stdlib/atexit.c
@@ -37,6 +37,7 @@ static char sccsid[] = "@(#)atexit.c	8.2 (Berkeley) 7/3/94";
 __FBSDID("$FreeBSD$");
 
 #include "namespace.h"
+#include <dlfcn.h>
 #include <link.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -162,8 +163,10 @@ __cxa_finalize(void *dso)
 	struct dl_phdr_info phdr_info;
 	struct atexit *p;
 	struct atexit_fn fn;
-	int n, has_phdr;
+	int n, has_phdr, global_exit;
 
+	global_exit = 0;
+	dlinfo(dso, RTLD_DI_GLOBALEXIT, &global_exit);
 	if (dso != NULL)
 		has_phdr = _rtld_addr_phdr(dso, &phdr_info);
 	else
@@ -177,8 +180,9 @@ __cxa_finalize(void *dso)
 			fn = p->fns[n];
 			if (dso != NULL && dso != fn.fn_dso) {
 				/* wrong DSO ? */
-				if (!has_phdr || !__elf_phdr_match_addr(
-				    &phdr_info, fn.fn_ptr.cxa_func))
+				if (!has_phdr || global_exit ||
+				    !__elf_phdr_match_addr(&phdr_info,
+				    fn.fn_ptr.cxa_func))
 					continue;
 			}
 			/*
@@ -200,6 +204,6 @@ __cxa_finalize(void *dso)
 	if (dso == NULL)
 		_MUTEX_DESTROY(&atexit_mutex);
 
-	if (has_phdr && &__pthread_cxa_finalize != NULL)
+	if (has_phdr && !global_exit && &__pthread_cxa_finalize != NULL)
 		__pthread_cxa_finalize(&phdr_info);
 }
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index b55effa..7cfc95b 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -173,6 +173,7 @@ static char *libmap_override;	/* Maps to use in addition to libmap.conf */
 static bool trust;		/* False for setuid and setgid programs */
 static bool dangerous_ld_env;	/* True if environment variables have been
 				   used to affect the libraries loaded */
+static bool global_exit;
 static char *ld_bind_now;	/* Environment variable for immediate binding */
 static char *ld_debug;		/* Environment variable for debugging */
 static char *ld_library_path;	/* Environment variable for search path */
@@ -2600,6 +2601,7 @@ rtld_exit(void)
 
     wlock_acquire(rtld_bind_lock, &lockstate);
     dbg("rtld_exit()");
+    global_exit = true;
     objlist_call_fini(&list_fini, NULL, &lockstate);
     /* No need to remove the items from the list, since we are exiting. */
     if (!libmap_disable)
@@ -3197,6 +3199,12 @@ dlinfo(void *handle, int request, void *p)
 
     rlock_acquire(rtld_bind_lock, &lockstate);
 
+    if (request == RTLD_DI_GLOBALEXIT) {
+	    *(int *)p = global_exit;
+	    error = 0;
+	    goto out;
+    }
+
     if (handle == NULL || handle == RTLD_SELF) {
 	void *retaddr;
 
@@ -3207,8 +3215,8 @@ dlinfo(void *handle, int request, void *p)
 	obj = dlcheck(handle);
 
     if (obj == NULL) {
-	lock_release(rtld_bind_lock, &lockstate);
-	return (-1);
+	error = -1;
+	goto out;
     }
 
     error = 0;
@@ -3230,8 +3238,8 @@ dlinfo(void *handle, int request, void *p)
 	error = -1;
     }
 
+out:
     lock_release(rtld_bind_lock, &lockstate);
-
     return (error);
 }
 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 834 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-ports/attachments/20131205/186f0e48/attachment.sig>


More information about the freebsd-ports mailing list