Explorar el Código

LibC+LibPthread: Implement function forwarding for libpthread

GCC will insert various calls to pthread functions when compiling
C++ code with static initializers, even when the user doesn't link
their program against libpthread explicitly.

This is used to make static initializers thread-safe, e.g. when
building a library that does not itself use thread functionality
and thus does not link against libpthread - but is intended to
be used with other code that does use libpthread explicitly.

This makes these symbols available in libc.
Gunnar Beutner hace 4 años
padre
commit
88cebb05ad

+ 1 - 2
Userland/DynamicLoader/CMakeLists.txt

@@ -19,8 +19,7 @@ endif()
 
 file(GLOB LIBSYSTEM_SOURCES "../Libraries/LibSystem/*.cpp")
 
-list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.cpp")
-list(FILTER LIBC_SOURCES1 EXCLUDE REGEX ".+crt0.+.cpp")
+add_definitions(-D_DYNAMIC_LOADER)
 
 set(SOURCES ${LOADER_SOURCES} ${AK_SOURCES} ${ELF_SOURCES} ${LIBC_SOURCES1} ${LIBC_SOURCES2} ${LIBC_SOURCES3} ${LIBSYSTEM_SOURCES})
 

+ 2 - 0
Userland/Libraries/LibC/CMakeLists.txt

@@ -20,7 +20,9 @@ set(LIBC_SOURCES
     mntent.cpp
     netdb.cpp
     poll.cpp
+    pthread_forward.cpp
     pthread_integration.cpp
+    pthread_tls.cpp
     pwd.cpp
     qsort.cpp
     scanf.cpp

+ 49 - 0
Userland/Libraries/LibC/bits/pthread_forward.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibPthread/pthread.h>
+
+struct PthreadFunctions {
+    int (*pthread_mutex_trylock)(pthread_mutex_t* mutex);
+    int (*pthread_mutex_destroy)(pthread_mutex_t*);
+
+    int (*pthread_mutexattr_init)(pthread_mutexattr_t*);
+    int (*pthread_mutexattr_settype)(pthread_mutexattr_t*, int);
+    int (*pthread_mutexattr_destroy)(pthread_mutexattr_t*);
+
+    int (*pthread_once)(pthread_once_t*, void (*)(void));
+
+    int (*pthread_cond_broadcast)(pthread_cond_t*);
+    int (*pthread_cond_init)(pthread_cond_t*, const pthread_condattr_t*);
+    int (*pthread_cond_signal)(pthread_cond_t*);
+    int (*pthread_cond_wait)(pthread_cond_t*, pthread_mutex_t*);
+    int (*pthread_cond_destroy)(pthread_cond_t*);
+    int (*pthread_cond_timedwait)(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
+};
+
+void __init_pthread_forward(PthreadFunctions);

+ 13 - 3
Userland/Libraries/LibC/bits/pthread_integration.h

@@ -27,6 +27,7 @@
 #pragma once
 
 #include <sys/cdefs.h>
+#include <sys/types.h>
 
 __BEGIN_DECLS
 
@@ -37,12 +38,21 @@ void __pthread_fork_atfork_register_prepare(void (*)(void));
 void __pthread_fork_atfork_register_parent(void (*)(void));
 void __pthread_fork_atfork_register_child(void (*)(void));
 
-int __pthread_mutex_lock(void*);
-int __pthread_mutex_unlock(void*);
-int __pthread_mutex_init(void*, const void*);
+int __pthread_mutex_lock(pthread_mutex_t*);
+int __pthread_mutex_unlock(pthread_mutex_t*);
+int __pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*);
+
+typedef void (*KeyDestructor)(void*);
+
+int __pthread_key_create(pthread_key_t*, KeyDestructor);
+int __pthread_key_delete(pthread_key_t);
+void* __pthread_getspecific(pthread_key_t);
+int __pthread_setspecific(pthread_key_t, const void*);
 
 int __pthread_self();
 
+void __pthread_key_destroy_for_current_thread();
+
 #define __PTHREAD_MUTEX_NORMAL 0
 #define __PTHREAD_MUTEX_RECURSIVE 1
 #define __PTHREAD_MUTEX_INITIALIZER     \

+ 2 - 0
Userland/Libraries/LibC/crt0.cpp

@@ -31,6 +31,7 @@
 #include <sys/internals.h>
 #include <unistd.h>
 
+#ifndef _DYNAMIC_LOADER
 extern "C" {
 
 extern u32 __stack_chk_guard;
@@ -65,3 +66,4 @@ int _start(int argc, char** argv, char** env)
     return 20150614;
 }
 }
+#endif

+ 2 - 0
Userland/Libraries/LibC/crt0_shared.cpp

@@ -31,4 +31,6 @@
 #include <sys/internals.h>
 #include <unistd.h>
 
+#ifndef _DYNAMIC_LOADER
 void* __dso_handle __attribute__((__weak__));
+#endif

+ 107 - 0
Userland/Libraries/LibC/pthread_forward.cpp

@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibC/assert.h>
+#include <LibC/bits/pthread_forward.h>
+
+static PthreadFunctions s_pthread_functions;
+
+void __init_pthread_forward(PthreadFunctions funcs)
+{
+    s_pthread_functions = funcs;
+}
+
+int pthread_mutex_trylock(pthread_mutex_t* mutex)
+{
+    VERIFY(s_pthread_functions.pthread_mutex_trylock);
+    return s_pthread_functions.pthread_mutex_trylock(mutex);
+}
+
+int pthread_mutex_destroy(pthread_mutex_t* mutex)
+{
+    VERIFY(s_pthread_functions.pthread_mutex_destroy);
+    return s_pthread_functions.pthread_mutex_destroy(mutex);
+}
+
+int pthread_mutexattr_init(pthread_mutexattr_t* attr)
+{
+    VERIFY(s_pthread_functions.pthread_mutexattr_init);
+    return s_pthread_functions.pthread_mutexattr_init(attr);
+}
+
+int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type)
+{
+    VERIFY(s_pthread_functions.pthread_mutexattr_settype);
+    return s_pthread_functions.pthread_mutexattr_settype(attr, type);
+}
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t* attr)
+{
+    VERIFY(s_pthread_functions.pthread_mutexattr_destroy);
+    return s_pthread_functions.pthread_mutexattr_destroy(attr);
+}
+
+int pthread_once(pthread_once_t* self, void (*callback)(void))
+{
+    VERIFY(s_pthread_functions.pthread_once);
+    return s_pthread_functions.pthread_once(self, callback);
+}
+
+int pthread_cond_broadcast(pthread_cond_t* cond)
+{
+    VERIFY(s_pthread_functions.pthread_cond_broadcast);
+    return s_pthread_functions.pthread_cond_broadcast(cond);
+}
+
+int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr)
+{
+    VERIFY(s_pthread_functions.pthread_cond_init);
+    return s_pthread_functions.pthread_cond_init(cond, attr);
+}
+
+int pthread_cond_signal(pthread_cond_t* cond)
+{
+    VERIFY(s_pthread_functions.pthread_cond_signal);
+    return s_pthread_functions.pthread_cond_signal(cond);
+}
+
+int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex)
+{
+    VERIFY(s_pthread_functions.pthread_cond_wait);
+    return s_pthread_functions.pthread_cond_wait(cond, mutex);
+}
+
+int pthread_cond_destroy(pthread_cond_t* cond)
+{
+    VERIFY(s_pthread_functions.pthread_cond_destroy);
+    return s_pthread_functions.pthread_cond_destroy(cond);
+}
+
+int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime)
+{
+    VERIFY(s_pthread_functions.pthread_cond_timedwait);
+    return s_pthread_functions.pthread_cond_timedwait(cond, mutex, abstime);
+}

+ 11 - 9
Userland/Libraries/LibC/pthread_integration.cpp

@@ -26,11 +26,9 @@
 
 #include <AK/Atomic.h>
 #include <AK/NeverDestroyed.h>
-#include <AK/Types.h>
 #include <AK/Vector.h>
 #include <bits/pthread_integration.h>
 #include <sched.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 namespace {
@@ -110,9 +108,10 @@ int __pthread_self()
     return gettid();
 }
 
-int __pthread_mutex_lock(void* mutexp)
+int pthread_self() __attribute__((weak, alias("__pthread_self")));
+
+int __pthread_mutex_lock(pthread_mutex_t* mutex)
 {
-    auto* mutex = reinterpret_cast<pthread_mutex_t*>(mutexp);
     auto& atomic = reinterpret_cast<Atomic<u32>&>(mutex->lock);
     pthread_t this_thread = __pthread_self();
     for (;;) {
@@ -131,9 +130,10 @@ int __pthread_mutex_lock(void* mutexp)
     }
 }
 
-int __pthread_mutex_unlock(void* mutexp)
+int pthread_mutex_lock(pthread_mutex_t*) __attribute__((weak, alias("__pthread_mutex_lock")));
+
+int __pthread_mutex_unlock(pthread_mutex_t* mutex)
 {
-    auto* mutex = reinterpret_cast<pthread_mutex_t*>(mutexp);
     if (mutex->type == __PTHREAD_MUTEX_RECURSIVE && mutex->level > 0) {
         mutex->level--;
         return 0;
@@ -143,14 +143,16 @@ int __pthread_mutex_unlock(void* mutexp)
     return 0;
 }
 
-int __pthread_mutex_init(void* mutexp, const void* attrp)
+int pthread_mutex_unlock(pthread_mutex_t*) __attribute__((weak, alias("__pthread_mutex_unlock")));
+
+int __pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attributes)
 {
-    auto* mutex = reinterpret_cast<pthread_mutex_t*>(mutexp);
-    auto* attributes = reinterpret_cast<const pthread_mutexattr_t*>(attrp);
     mutex->lock = 0;
     mutex->owner = 0;
     mutex->level = 0;
     mutex->type = attributes ? attributes->type : __PTHREAD_MUTEX_NORMAL;
     return 0;
 }
+
+int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*) __attribute__((weak, alias("__pthread_mutex_init")));
 }

+ 130 - 0
Userland/Libraries/LibC/pthread_tls.cpp

@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/Atomic.h>
+#include <LibPthread/pthread.h>
+#include <unistd.h>
+
+#ifndef _DYNAMIC_LOADER
+extern "C" {
+
+static constexpr int max_keys = PTHREAD_KEYS_MAX;
+
+struct KeyTable {
+    KeyDestructor destructors[max_keys] { nullptr };
+    int next { 0 };
+    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+};
+
+struct SpecificTable {
+    void* values[max_keys] { nullptr };
+};
+
+static KeyTable s_keys;
+
+__thread SpecificTable t_specifics;
+
+int __pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
+{
+    int ret = 0;
+    __pthread_mutex_lock(&s_keys.mutex);
+    if (s_keys.next >= max_keys) {
+        ret = EAGAIN;
+    } else {
+        *key = s_keys.next++;
+        s_keys.destructors[*key] = destructor;
+        ret = 0;
+    }
+    __pthread_mutex_unlock(&s_keys.mutex);
+    return ret;
+}
+
+int pthread_key_create(pthread_key_t*, KeyDestructor) __attribute__((weak, alias("__pthread_key_create")));
+
+int __pthread_key_delete(pthread_key_t key)
+{
+    if (key < 0 || key >= max_keys)
+        return EINVAL;
+    __pthread_mutex_lock(&s_keys.mutex);
+    s_keys.destructors[key] = nullptr;
+    __pthread_mutex_unlock(&s_keys.mutex);
+    return 0;
+}
+
+int pthread_key_delete(pthread_key_t) __attribute__((weak, alias("__pthread_key_delete")));
+
+void* __pthread_getspecific(pthread_key_t key)
+{
+    if (key < 0)
+        return nullptr;
+    if (key >= max_keys)
+        return nullptr;
+    return t_specifics.values[key];
+}
+
+void* pthread_getspecific(pthread_key_t) __attribute__((weak, alias("__pthread_getspecific")));
+
+int __pthread_setspecific(pthread_key_t key, const void* value)
+{
+    if (key < 0)
+        return EINVAL;
+    if (key >= max_keys)
+        return EINVAL;
+
+    t_specifics.values[key] = const_cast<void*>(value);
+    return 0;
+}
+
+int pthread_setspecific(pthread_key_t, const void*) __attribute__((weak, alias("__pthread_setspecific")));
+
+void __pthread_key_destroy_for_current_thread()
+{
+    // This function will either be called during exit_thread, for a pthread, or
+    // during global program shutdown for the main thread.
+
+    __pthread_mutex_lock(&s_keys.mutex);
+    size_t num_used_keys = s_keys.next;
+
+    // Dr. POSIX accounts for weird key destructors setting their own key again.
+    // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes.
+
+    for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) {
+        bool any_nonnull_destructors = false;
+        for (size_t key_index = 0; key_index < num_used_keys; ++key_index) {
+            void* value = exchange(t_specifics.values[key_index], nullptr);
+
+            if (value && s_keys.destructors[key_index]) {
+                any_nonnull_destructors = true;
+                (*s_keys.destructors[key_index])(value);
+            }
+        }
+        if (!any_nonnull_destructors)
+            break;
+    }
+    __pthread_mutex_unlock(&s_keys.mutex);
+}
+}
+#endif

+ 3 - 4
Userland/Libraries/LibC/stdlib.cpp

@@ -49,8 +49,6 @@
 #include <syscall.h>
 #include <unistd.h>
 
-void (*__libc_pthread_key_destroy_for_current_thread)() = nullptr;
-
 static void strtons(const char* str, char** endptr)
 {
     assert(endptr);
@@ -228,8 +226,9 @@ void exit(int status)
     fflush(stdout);
     fflush(stderr);
 
-    if (__libc_pthread_key_destroy_for_current_thread)
-        __libc_pthread_key_destroy_for_current_thread();
+#ifndef _DYNAMIC_LOADER
+    __pthread_key_destroy_for_current_thread();
+#endif
 
     _exit(status);
 }

+ 1 - 0
Userland/Libraries/LibPthread/CMakeLists.txt

@@ -1,4 +1,5 @@
 set(SOURCES
+    forward.cpp
     pthread.cpp
     pthread_once.cpp
     semaphore.cpp

+ 50 - 0
Userland/Libraries/LibPthread/forward.cpp

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibC/bits/pthread_forward.h>
+
+static const PthreadFunctions s_functions = {
+    .pthread_mutex_trylock = pthread_mutex_trylock,
+    .pthread_mutex_destroy = pthread_mutex_destroy,
+
+    .pthread_mutexattr_init = pthread_mutexattr_init,
+    .pthread_mutexattr_settype = pthread_mutexattr_settype,
+    .pthread_mutexattr_destroy = pthread_mutexattr_destroy,
+
+    .pthread_once = pthread_once,
+
+    .pthread_cond_broadcast = pthread_cond_broadcast,
+    .pthread_cond_init = pthread_cond_init,
+    .pthread_cond_signal = pthread_cond_signal,
+    .pthread_cond_wait = pthread_cond_wait,
+    .pthread_cond_destroy = pthread_cond_destroy,
+    .pthread_cond_timedwait = pthread_cond_timedwait,
+};
+
+[[gnu::constructor]] static void forward_pthread_functions()
+{
+    __init_pthread_forward(s_functions);
+}

+ 10 - 80
Userland/Libraries/LibPthread/pthread.cpp

@@ -55,8 +55,6 @@ constexpr size_t highest_reasonable_stack_size = 8 * MiB; // That's the default
 #define __RETURN_PTHREAD_ERROR(rc) \
     return ((rc) < 0 ? -(rc) : 0)
 
-extern void (*__libc_pthread_key_destroy_for_current_thread)();
-
 extern "C" {
 
 static void* pthread_create_helper(void* (*routine)(void*), void* argument)
@@ -531,99 +529,31 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
     return 0;
 }
 
-static constexpr int max_keys = PTHREAD_KEYS_MAX;
-
-typedef void (*KeyDestructor)(void*);
-
-struct KeyTable {
-    KeyDestructor destructors[max_keys] { nullptr };
-    int next { 0 };
-    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-};
-
-struct SpecificTable {
-    void* values[max_keys] { nullptr };
-};
-
-static KeyTable s_keys;
-
-__thread SpecificTable t_specifics;
+// libgcc expects this function to exist in libpthread, even
+// if it is not implemented.
+int pthread_cancel(pthread_t)
+{
+    TODO();
+}
 
 int pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
 {
-    int ret = 0;
-    pthread_mutex_lock(&s_keys.mutex);
-    if (s_keys.next >= max_keys) {
-        ret = EAGAIN;
-    } else {
-        *key = s_keys.next++;
-        s_keys.destructors[*key] = destructor;
-        ret = 0;
-    }
-    pthread_mutex_unlock(&s_keys.mutex);
-    return ret;
+    return __pthread_key_create(key, destructor);
 }
 
 int pthread_key_delete(pthread_key_t key)
 {
-    if (key < 0 || key >= max_keys)
-        return EINVAL;
-    pthread_mutex_lock(&s_keys.mutex);
-    s_keys.destructors[key] = nullptr;
-    pthread_mutex_unlock(&s_keys.mutex);
-    return 0;
+    return __pthread_key_delete(key);
 }
 
 void* pthread_getspecific(pthread_key_t key)
 {
-    if (key < 0)
-        return nullptr;
-    if (key >= max_keys)
-        return nullptr;
-    return t_specifics.values[key];
+    return __pthread_getspecific(key);
 }
 
 int pthread_setspecific(pthread_key_t key, const void* value)
 {
-    if (key < 0)
-        return EINVAL;
-    if (key >= max_keys)
-        return EINVAL;
-
-    t_specifics.values[key] = const_cast<void*>(value);
-    return 0;
-}
-
-[[gnu::constructor]] static void set_libc_key_destructor()
-{
-    __libc_pthread_key_destroy_for_current_thread = __pthread_key_destroy_for_current_thread;
-}
-
-void __pthread_key_destroy_for_current_thread()
-{
-    // This function will either be called during exit_thread, for a pthread, or
-    // during global program shutdown for the main thread.
-
-    pthread_mutex_lock(&s_keys.mutex);
-    size_t num_used_keys = s_keys.next;
-
-    // Dr. POSIX accounts for weird key destructors setting their own key again.
-    // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes.
-
-    for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) {
-        bool any_nonnull_destructors = false;
-        for (size_t key_index = 0; key_index < num_used_keys; ++key_index) {
-            void* value = exchange(t_specifics.values[key_index], nullptr);
-
-            if (value && s_keys.destructors[key_index]) {
-                any_nonnull_destructors = true;
-                (*s_keys.destructors[key_index])(value);
-            }
-        }
-        if (!any_nonnull_destructors)
-            break;
-    }
-    pthread_mutex_unlock(&s_keys.mutex);
+    return __pthread_setspecific(key, value);
 }
 
 int pthread_setname_np(pthread_t thread, const char* name)

+ 0 - 2
Userland/Libraries/LibPthread/pthread.h

@@ -146,6 +146,4 @@ int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int);
 
 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
 
-void __pthread_key_destroy_for_current_thread();
-
 __END_DECLS