diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -577,6 +577,10 @@ /* pthread_set/get_name_np */ char *name; + + /* rtld thread-local dlerror message and seen control */ + char dlerror_msg[512]; + int dlerror_seen; }; #define THR_SHOULD_GC(thrd) \ diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -182,6 +182,29 @@ return (0); } +/* + * ABI bug workaround: This symbol must be present for rtld to accept + * RTLI_VERSION from RtldLockInfo + */ +char *_thr_dlerror_loc(void); +char * +_thr_dlerror_loc(void) +{ + struct pthread *curthread; + + curthread = _get_curthread(); + return (curthread->dlerror_msg); +} + +static int * +_thr_dlerror_seen(void) +{ + struct pthread *curthread; + + curthread = _get_curthread(); + return (&curthread->dlerror_seen); +} + void _thr_rtld_init(void) { @@ -205,6 +228,7 @@ mprotect(NULL, 0, 0); _rtld_get_stack_prot(); + li.rtli_version = RTLI_VERSION; li.lock_create = _thr_rtld_lock_create; li.lock_destroy = _thr_rtld_lock_destroy; li.rlock_acquire = _thr_rtld_rlock_acquire; @@ -213,6 +237,9 @@ li.thread_set_flag = _thr_rtld_set_flag; li.thread_clr_flag = _thr_rtld_clr_flag; li.at_fork = NULL; + li.dlerror_loc = _thr_dlerror_loc; + li.dlerror_loc_sz = sizeof(curthread->dlerror_msg); + li.dlerror_seen = _thr_dlerror_seen; /* * Preresolve the symbols needed for the fork interposer. We diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -389,6 +389,7 @@ Obj_Entry *globallist_next(const Obj_Entry *obj); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); +Obj_Entry *obj_from_addr(const void *); void _rtld_bind_start(void); void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def); void symlook_init(SymLook *, const char *); diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -124,7 +124,6 @@ static void map_stacks_exec(RtldLockState *); static int obj_disable_relro(Obj_Entry *); static int obj_enforce_relro(Obj_Entry *); -static Obj_Entry *obj_from_addr(const void *); static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); static void objlist_call_init(Objlist *, RtldLockState *); static void objlist_clear(Objlist *); @@ -195,7 +194,6 @@ /* * Data declarations. */ -static char *error_message; /* Message for dlerror(), or NULL */ struct r_debug r_debug __exported; /* for GDB; */ static bool libmap_disable; /* Disable libmap */ static bool ld_loadfltr; /* Immediate filters processing */ @@ -443,6 +441,8 @@ assert(aux_info[AT_BASE] != NULL); init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info); + dlerror_dflt_init(); + __progname = obj_rtld.path; argv0 = argv[0] != NULL ? argv[0] : "(null)"; environ = env; @@ -927,14 +927,14 @@ void _rtld_error(const char *fmt, ...) { - static char buf[512]; - va_list ap; + va_list ap; - va_start(ap, fmt); - rtld_vsnprintf(buf, sizeof buf, fmt, ap); - error_message = buf; - va_end(ap); - LD_UTRACE(UTRACE_RTLD_ERROR, NULL, NULL, 0, 0, error_message); + va_start(ap, fmt); + rtld_vsnprintf(lockinfo.dlerror_loc(), lockinfo.dlerror_loc_sz, + fmt, ap); + va_end(ap); + *lockinfo.dlerror_seen() = 0; + LD_UTRACE(UTRACE_RTLD_ERROR, NULL, NULL, 0, 0, lockinfo.dlerror_loc()); } /* @@ -943,7 +943,7 @@ static char * errmsg_save(void) { - return error_message == NULL ? NULL : xstrdup(error_message); + return (xstrdup(lockinfo.dlerror_loc())); } /* @@ -953,12 +953,12 @@ static void errmsg_restore(char *saved_msg) { - if (saved_msg == NULL) - error_message = NULL; - else { - _rtld_error("%s", saved_msg); - free(saved_msg); - } + if (saved_msg == NULL) + _rtld_error(""); + else { + _rtld_error("%s", saved_msg); + free(saved_msg); + } } static const char * @@ -2687,7 +2687,7 @@ return (NULL); } -static Obj_Entry * +Obj_Entry * obj_from_addr(const void *addr) { Obj_Entry *obj; @@ -3389,9 +3389,10 @@ char * dlerror(void) { - char *msg = error_message; - error_message = NULL; - return msg; + if (*(lockinfo.dlerror_seen()) != 0) + return (NULL); + *lockinfo.dlerror_seen() = 1; + return (lockinfo.dlerror_loc()); } /* diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h --- a/libexec/rtld-elf/rtld_lock.h +++ b/libexec/rtld-elf/rtld_lock.h @@ -30,7 +30,9 @@ #ifndef _RTLD_LOCK_H_ #define _RTLD_LOCK_H_ -#define RTLI_VERSION 0x01 +#define RTLI_VERSION_ONE 0x01 +#define RTLI_VERSION 0x02 + #define MAX_RTLD_LOCKS 8 struct RtldLockInfo @@ -44,6 +46,9 @@ int (*thread_set_flag)(int); int (*thread_clr_flag)(int); void (*at_fork)(void); + char *(*dlerror_loc)(void); + int *(*dlerror_seen)(void); + int dlerror_loc_sz; }; extern void _rtld_thread_init(struct RtldLockInfo *) __exported; @@ -59,6 +64,8 @@ extern rtld_lock_t rtld_libc_lock; extern rtld_lock_t rtld_phdr_lock; +extern struct RtldLockInfo lockinfo; + #define RTLD_LOCK_UNLOCKED 0 #define RTLD_LOCK_RLOCKED 1 #define RTLD_LOCK_WLOCKED 2 @@ -72,6 +79,8 @@ void lock_upgrade(rtld_lock_t, RtldLockState *); void lock_restart_for_upgrade(RtldLockState *); +void dlerror_dflt_init(void); + #endif /* IN_RTLD */ #endif diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -59,6 +59,21 @@ void _rtld_atfork_pre(int *) __exported; void _rtld_atfork_post(int *) __exported; +static char def_dlerror_msg[512]; +static int def_dlerror_seen_val; + +static char * +def_dlerror_loc(void) +{ + return (def_dlerror_msg); +} + +static int * +def_dlerror_seen(void) +{ + return (&def_dlerror_seen_val); +} + #define WAFLAG 0x1 /* A writer holds the lock */ #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ @@ -192,7 +207,7 @@ /* * Public interface exposed to the rest of the dynamic linker. */ -static struct RtldLockInfo lockinfo; +struct RtldLockInfo lockinfo; static struct RtldLockInfo deflockinfo; static __inline int @@ -299,6 +314,14 @@ } } +void +dlerror_dflt_init(void) +{ + lockinfo.dlerror_loc = def_dlerror_loc; + lockinfo.dlerror_loc_sz = sizeof(def_dlerror_msg); + lockinfo.dlerror_seen = def_dlerror_seen; +} + void lockdflt_init(void) { @@ -313,6 +336,9 @@ deflockinfo.thread_set_flag = def_thread_set_flag; deflockinfo.thread_clr_flag = def_thread_clr_flag; deflockinfo.at_fork = NULL; + deflockinfo.dlerror_loc = def_dlerror_loc; + deflockinfo.dlerror_loc_sz = sizeof(def_dlerror_msg); + deflockinfo.dlerror_seen = def_dlerror_seen; for (i = 0; i < RTLD_LOCK_CNT; i++) { rtld_locks[i].mask = (1 << i); @@ -344,8 +370,24 @@ void _rtld_thread_init(struct RtldLockInfo *pli) { - int flags, i; + const Obj_Entry *obj; + SymLook req; void *locks[RTLD_LOCK_CNT]; + int flags, i, res; + + if (pli == NULL) { + lockinfo.rtli_version = RTLI_VERSION; + } else { + lockinfo.rtli_version = RTLI_VERSION_ONE; + obj = obj_from_addr(pli->lock_create); + if (obj != NULL) { + symlook_init(&req, "_thr_dlerror_loc"); + req.flags = SYMLOOK_IN_PLT; + res = symlook_obj(&req, obj); + if (res == 0) + lockinfo.rtli_version = pli->rtli_version; + } + } /* disable all locking while this function is running */ flags = thread_mask_set(~0); @@ -389,6 +431,13 @@ lockinfo.thread_set_flag = pli->thread_set_flag; lockinfo.thread_clr_flag = pli->thread_clr_flag; lockinfo.at_fork = pli->at_fork; + if (lockinfo.rtli_version > RTLI_VERSION_ONE && pli != NULL) { + strlcpy(pli->dlerror_loc(), lockinfo.dlerror_loc(), + lockinfo.dlerror_loc_sz); + lockinfo.dlerror_loc = pli->dlerror_loc; + lockinfo.dlerror_loc_sz = pli->dlerror_loc_sz; + lockinfo.dlerror_seen = pli->dlerror_seen; + } /* restore thread locking state, this time with new locks */ thread_mask_clear(~0);