summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2011-09-15 16:59:21 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2011-09-15 16:59:21 +0200
commit82f8d0bce10403deab704871e638edc24e7933ee (patch)
tree1afb443b538e0dcdaed4908dac4872a73366f3bb /libc
parent200895dab88678b5b4e7d1dac9172be23e92eb31 (diff)
Defeat compiler optimization which assumes function addresses are never NULL
From email: A warning for people who can be hit by the same or similar issue: gcc 4.1.2 with -march=i486 here with -Os and even with -O2 or -O is "optimizing away" the check if (_stdio_term) in libc/stdlib/_atexit.c which results in a "call 0" and a segfault at exit if you do not happen to link in stdio. Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'libc')
-rw-r--r--libc/stdlib/_atexit.c49
-rw-r--r--libc/stdlib/abort.c13
2 files changed, 42 insertions, 20 deletions
diff --git a/libc/stdlib/_atexit.c b/libc/stdlib/_atexit.c
index 0d420d3c9..48b97ffd2 100644
--- a/libc/stdlib/_atexit.c
+++ b/libc/stdlib/_atexit.c
@@ -48,9 +48,9 @@ __UCLIBC_MUTEX_EXTERN(__atexit_lock);
-typedef void (*aefuncp) (void); /* atexit function pointer */
-typedef void (*oefuncp) (int, void *); /* on_exit function pointer */
-typedef void (*cxaefuncp) (void *); /* __cxa_atexit function pointer */
+typedef void (*aefuncp)(void); /* atexit function pointer */
+typedef void (*oefuncp)(int, void *); /* on_exit function pointer */
+typedef void (*cxaefuncp)(void *); /* __cxa_atexit function pointer */
typedef enum {
ef_free,
ef_in_use,
@@ -59,7 +59,7 @@ typedef enum {
} ef_type; /* exit function types */
/* this is in the L_exit object */
-extern void (*__exit_cleanup) (int) attribute_hidden;
+extern void (*__exit_cleanup)(int) attribute_hidden;
/* these are in the L___do_exit object */
extern int __exit_slots attribute_hidden;
@@ -88,10 +88,10 @@ extern struct exit_function *__exit_function_table attribute_hidden;
#else
extern struct exit_function __exit_function_table[__UCLIBC_MAX_ATEXIT] attribute_hidden;
#endif
-extern struct exit_function *__new_exitfn (void) attribute_hidden;
+extern struct exit_function *__new_exitfn(void) attribute_hidden;
/* this is in the L___cxa_atexit object */
-extern int __cxa_atexit (cxaefuncp, void *arg, void *dso_handle);
+extern int __cxa_atexit(cxaefuncp, void *arg, void *dso_handle);
/* remove old_atexit after 0.9.29 */
@@ -153,7 +153,7 @@ int on_exit(oefuncp func, void *arg)
#ifdef L___cxa_atexit
libc_hidden_proto(__cxa_atexit)
-int __cxa_atexit (cxaefuncp func, void *arg, void *dso_handle)
+int __cxa_atexit(cxaefuncp func, void *arg, void *dso_handle)
{
struct exit_function *efp;
@@ -183,8 +183,8 @@ libc_hidden_def(__cxa_atexit)
* with the same dso handle. Otherwise, if D is NULL, call all of the
* registered handlers.
*/
-void __cxa_finalize (void *dso_handle);
-void __cxa_finalize (void *dso_handle)
+void __cxa_finalize(void *dso_handle);
+void __cxa_finalize(void *dso_handle)
{
struct exit_function *efp;
int exit_count_snapshot = __exit_count;
@@ -214,7 +214,7 @@ void __cxa_finalize (void *dso_handle)
*/
#ifdef UNREGISTER_ATFORK
if (d != NULL) {
- UNREGISTER_ATFORK (d);
+ UNREGISTER_ATFORK(d);
}
#endif
#endif
@@ -243,7 +243,7 @@ struct exit_function attribute_hidden *__new_exitfn(void)
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
/* If we are out of function table slots, make some more */
if (__exit_slots < __exit_count+1) {
- efp=realloc(__exit_function_table,
+ efp = realloc(__exit_function_table,
(__exit_slots+20)*sizeof(struct exit_function));
if (efp == NULL) {
__set_errno(ENOMEM);
@@ -279,18 +279,18 @@ void __exit_handler(int status)
struct exit_function *efp;
/* In reverse order */
- while ( __exit_count ) {
+ while (__exit_count) {
efp = &__exit_function_table[--__exit_count];
switch (efp->type) {
case ef_on_exit:
if (efp->funcs.on_exit.func) {
- (efp->funcs.on_exit.func) (status, efp->funcs.on_exit.arg);
+ (efp->funcs.on_exit.func)(status, efp->funcs.on_exit.arg);
}
break;
case ef_cxa_atexit:
if (efp->funcs.cxa_atexit.func) {
/* glibc passes status too, but that's not in the prototype */
- (efp->funcs.cxa_atexit.func) (efp->funcs.cxa_atexit.arg);
+ (efp->funcs.cxa_atexit.func)(efp->funcs.cxa_atexit.arg);
}
break;
}
@@ -303,8 +303,19 @@ void __exit_handler(int status)
#endif
#ifdef L_exit
+/* Defeat compiler optimization which assumes function addresses are never NULL */
+static __always_inline int not_null_ptr(const void *p)
+{
+ const void *q;
+ __asm__ (""
+ : "=r" (q) /* output */
+ : "0" (p) /* input */
+ );
+ return q != 0;
+}
+
extern void weak_function _stdio_term(void) attribute_hidden;
-attribute_hidden void (*__exit_cleanup) (int) = 0;
+attribute_hidden void (*__exit_cleanup)(int) = 0;
__UCLIBC_MUTEX_INIT(__atexit_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
extern void __uClibc_fini(void);
@@ -317,19 +328,19 @@ void exit(int rv)
{
/* Perform exit-specific cleanup (atexit and on_exit) */
__UCLIBC_MUTEX_LOCK(__atexit_lock);
- if (__exit_cleanup) {
+ if (not_null_ptr(__exit_cleanup)) {
__exit_cleanup(rv);
}
__UCLIBC_MUTEX_UNLOCK(__atexit_lock);
__uClibc_fini();
- /* If we are using stdio, try to shut it down. At the very least,
+ /* If we are using stdio, try to shut it down. At the very least,
* this will attempt to commit all buffered writes. It may also
* unbuffer all writable files, or close them outright.
* Check the stdio routines for details. */
- if (_stdio_term)
- _stdio_term();
+ if (not_null_ptr(_stdio_term))
+ _stdio_term();
_exit(rv);
}
diff --git a/libc/stdlib/abort.c b/libc/stdlib/abort.c
index 3cc796370..a5bac461a 100644
--- a/libc/stdlib/abort.c
+++ b/libc/stdlib/abort.c
@@ -28,6 +28,17 @@ Cambridge, MA 02139, USA. */
+/* Defeat compiler optimization which assumes function addresses are never NULL */
+static __always_inline int not_null_ptr(const void *p)
+{
+ const void *q;
+ __asm__ (""
+ : "=r" (q) /* output */
+ : "0" (p) /* input */
+ );
+ return q != 0;
+}
+
/* Our last ditch effort to commit suicide */
#ifdef __UCLIBC_ABORT_INSTRUCTION__
# define ABORT_INSTRUCTION __asm__(__UCLIBC_ABORT_INSTRUCTION__)
@@ -68,7 +79,7 @@ void abort(void)
* this will attempt to commit all buffered writes. It may also
* unbuffer all writable files, or close them outright.
* Check the stdio routines for details. */
- if (_stdio_term) {
+ if (not_null_ptr(_stdio_term)) {
_stdio_term();
}
#endif