Path: blob/main/lib/libc/stdlib/cxa_thread_atexit_impl.c
39476 views
/*-1* Copyright (c) 2016 Mahdi Mokhtari <[email protected]>2* Copyright (c) 2016, 2017 The FreeBSD Foundation3* All rights reserved.4*5* Portions of this software were developed by Konstantin Belousov6* under sponsorship from the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/queue.h>31#include "namespace.h"32#include <errno.h>33#include <link.h>34#include <pthread.h>35#include <stddef.h>36#include <stdlib.h>37#include <stdio.h>38#include "un-namespace.h"39#include "libc_private.h"4041/*42* C++11 introduces the thread_local scope (like __thread with some43* additions). As a key-feature it should support non-trivial44* destructors, registered with __cxa_thread_atexit() to be executed45* at the thread termination.46*47* The implemention keeps a _Thread_local list of destructors per each48* thread, and calls __cxa_thread_call_dtors() on each thread's exit49* to do cleanup. For a thread calling exit(3), in particular, for50* the initial thread returning from main(), we call51* __cxa_thread_call_dtors() inside exit().52*53* It could be possible that a dynamically loaded library, use54* thread_local variable but is dlclose()'d before thread exit. The55* destructor of this variable will then try to access the address,56* for calling it but it's unloaded, so it'll crash. We're using57* __elf_phdr_match_addr() to detect and prevent such cases and so58* prevent the crash.59*/6061#define CXA_DTORS_ITERATIONS 46263struct cxa_thread_dtor {64void *obj;65void (*func)(void *);66void *dso;67LIST_ENTRY(cxa_thread_dtor) entry;68};69static _Thread_local LIST_HEAD(dtor_list, cxa_thread_dtor) dtors =70LIST_HEAD_INITIALIZER(dtors);7172int73__cxa_thread_atexit_impl(void (*dtor_func)(void *), void *obj,74void *dso_symbol)75{7677return (__cxa_thread_atexit_hidden(dtor_func, obj, dso_symbol));78}7980int81__cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj,82void *dso_symbol)83{84struct cxa_thread_dtor *new_dtor;8586new_dtor = malloc(sizeof(*new_dtor));87if (new_dtor == NULL) {88errno = ENOMEM; /* forcibly override malloc(3) error */89return (-1);90}9192new_dtor->obj = obj;93new_dtor->func = dtor_func;94new_dtor->dso = dso_symbol;95LIST_INSERT_HEAD(&dtors, new_dtor, entry);96return (0);97}9899static void100walk_cb_call(struct cxa_thread_dtor *dtor)101{102struct dl_phdr_info phdr_info;103104if (_rtld_addr_phdr(dtor->func, &phdr_info) &&105__elf_phdr_match_addr(&phdr_info, dtor->func))106dtor->func(dtor->obj);107else108fprintf(stderr,109"__cxa_thread_call_dtors: dtr %p from unloaded dso, skipping\n",110(void *)(dtor->func));111}112113static void114walk_cb_nocall(struct cxa_thread_dtor *dtor __unused)115{116}117118static void119cxa_thread_walk(void (*cb)(struct cxa_thread_dtor *))120{121struct cxa_thread_dtor *dtor, *tdtor;122123LIST_FOREACH_SAFE(dtor, &dtors, entry, tdtor) {124LIST_REMOVE(dtor, entry);125cb(dtor);126free(dtor);127}128}129130/*131* This is the callback function we use to call destructors, once for132* each thread. It is called in exit(3) in libc/stdlib/exit.c and133* before exit_thread() in libthr/thread/thr_exit.c.134*/135void136__cxa_thread_call_dtors(void)137{138int i;139140for (i = 0; i < CXA_DTORS_ITERATIONS && !LIST_EMPTY(&dtors); i++)141cxa_thread_walk(walk_cb_call);142143if (!LIST_EMPTY(&dtors)) {144fprintf(stderr, "Thread %p is exiting with more "145"thread-specific dtors created after %d iterations "146"of destructor calls\n",147_pthread_self(), i);148cxa_thread_walk(walk_cb_nocall);149}150}151152153