Loading...
libdyld/ThreadLocalVariables.h dyld-1340 /dev/null
--- dyld/dyld-1340/libdyld/ThreadLocalVariables.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (c) 2024 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef ThreadLocalVariables_h
-#define ThreadLocalVariables_h
-
-#include <stdint.h>
-
-#include "Defines.h"
-#include "Error.h"
-#include "Header.h"
-
-// cannot include LibSystemHelpers.h because that will introduce a cycle
-namespace dyld4 {
-    struct LibSystemHelpers;
-}
-
-class DyldSharedCache;
-
-namespace dyld {
-
-using mach_o::Header;
-
-/*
-              * * * How thread-local variables work on Apple platforms * * *
-
-    A thread local variable (TLV) is a per-thread variable. It is not statically allocated in
-    __DATA segment, nor is it stack allocated.  Instead, on first use of a TLV, malloc() is used
-    to allocate space for the variable and its address is stored in a thread-specific way.  This
-    allocation is lazy, so that a thread that does not access a TLV does not have space malloc()ed.
-
-    When C (__thread) or C++ (thread_local) source code define a TLV, the compiler emits a
-    thunk in the __DATA,__thread_vars section. The first pointer in the thunk is to a function.
-    When code uses a TLV, the compiler emits code to materialize the address of the thunk, then
-    calls the first pointer in the thunk, passing the thunk's address as a parameter, and the
-    function returns the address of the TLV for the current thread.  The thunk func has special
-    calling conventions where all registers are preserved (other than the result register). That
-    means the compiler does not need to spill registers to the stack when "computing" the address
-    of a TLV.
-
-    In a .o file a thunk for "myvar" looks like:
-
-            .section __DATA,__thread_vars,thread_local_variables
-            .globl _myvar
- _myvar:    .quad  __tlv_bootstrap
-            .quad  0
-            .quad  _myvar$tlv$init
-
-    A thunk is always three pointers in size.  The first points to a bootstrapping function.
-    The second is always zero.  The third is a pointer to the initial content for when the TLV
-    is instantiated at runtime.
-
-    The linker does a minor transformation of these thunks.  The linker finds all TLV defined in the
-    linkage unit and co-locates their initial content blobs (e.g. _myvar$tlv$init).  Thus making
-    one contiguous run of initial content.  This is so that the runtime can do just one malloc() for all
-    TLVs in the image on first use, and then do one copy of the initial content into the malloc()ed space.
-
-    At runtime dyld needs to do some load time processing of images with TLVs. Dyld needs to allocate
-    a pthread_key and stuff the pthread_key into the second slot of each thunk.  Once they are set up,
-    when the code calls the thunk func, it uses the key and pthread_getspecific() to get the
-    address of the malloc()ed space, then add the third field (offset) to return the address of
-    the specific TLV in the image.  If pthread_getspecific() returns NULL, that means this is the first
-    use of any TLV in this image on this thread. In that case, dyld needs to determine the overall
-    size to malloc() and the initial content bytes to set that to.
-
-    It turns out the slow path (first use of a TLV) required taking a lock and walking dyld data
-    structures to find the image containing the TLV and the initial content for it.
-
-    In Spring 2025 releases, an optimization was made to how TLVs work to optimize the slow path.
-    The code below implements this optimization which repacks the fields in the thunk after the
-    func pointer to contain all the info needed for the fast and slow paths.  That means dyld
-    does not need to maintain a side table and there is no need for a lock.  Each TLV is self
-    contained once set up.
-
-    There is also an optimization done in the dyld cache builder for dylibs in the dyld cache which
-    have TLVs.  Instead of have dyld setup the TLVs at runtime (which dirties pages), the dyld
-    cache builder does the setup.  It does this by using a range of reserved static pthread keys.
-
-
- */
-
-//
-// Class for managing thread-local variables in mach-o files at runtime
-//
-class VIS_HIDDEN ThreadLocalVariables
-{
-public:
-    // on disk format of a thread-local variable
-    struct Thunk
-    {
-        void*       func;   // really void* (*ThunkFunc)(Thunk*);
-        size_t      key;
-        size_t      offset;
-    };
-
-    typedef void  (*TermFunc)(void* objAddr);
-
-    // called during libSystem initializer
-    void                    initialize();
-
-    // called by _tlv_atexit() to register a callback to be called when a thread terminates
-    void                    addTermFunc(TermFunc func, void* objAddr);
-
-    // called by libc's exit() to run all terminators
-    void                    exit();
-
-    // called by dyld when image with thread-locals is first loaded
-    mach_o::Error           setUpImage(const DyldSharedCache* cache, const Header* hdr);
-
-    // called by pthreads when a thread goes away
-    void                    finalizeList(void* list);
-
-    // called on first use of a thread local in a thread to allocate and initialize thread locals for current thread
-#if BUILDING_UNIT_TESTS
-    void*                   instantiateVariable(const Thunk&);
-#else
-    static void*            instantiateVariable(const Thunk&);
-#endif
-
-    // internal routines to prepare the thunks in an image
-    mach_o::Error           initializeThunksFromDisk(const Header* hdr);
-    mach_o::Error           initializeThunksInDyldCache(const DyldSharedCache* cache, const Header* hdr);
-
-    // runtime structure of 64-bit arch thread-local thunk
-    struct TLV_Thunkv2
-    {
-        void*        func;
-        uint32_t     key;
-        uint32_t     offset;
-        int32_t      initialContentDelta;   // if zero, then content is all zeros
-        uint32_t     initialContentSize;
-    };
-
-    // runtime structure of 32-bit arch thread-local thunk
-    struct TLV_Thunkv2_32
-    {
-        void*        func;
-        uint16_t     key;
-        uint16_t     offset;
-        int32_t      machHeaderDelta; // if < 0, content is found by walking load commands. If > 0, then it is size and content is all zeros
-    };
-
-#if BUILDING_UNIT_TESTS
-    void                    setMock(int tlvKey, std::span<Thunk> thunks, std::span<const uint8_t> content);
-#endif
-
-private:
-#if BUILDING_UNIT_TESTS
-    void                            findInitialContent(const Header* hdr, std::span<const uint8_t>& initialContent, bool& allZeroFill);
-#else
-    static void                     findInitialContent(const Header* hdr, std::span<const uint8_t>& initialContent, bool& allZeroFill);
-#endif
-    mach_o::Error                   forEachThunkSpan(const Header* hdr, mach_o::Error (^visit)(std::span<Thunk>));
-
-    // used to record _tlv_atexit() entries to clean up on thread exit
-    struct Terminator
-    {
-        TermFunc      termFunc;
-        void*         objAddr;
-    };
-
-    struct TerminatorList {
-        TerminatorList* next  = nullptr;
-        uintptr_t       count = 0;
-        Terminator      elements[7];
-        void                reverseWalkChain(void (^work)(TerminatorList*));
-    };
-
-    static const bool verbose = false;
-
-    dyld_thread_key_t               _terminatorsKey      = 0;
-#if BUILDING_UNIT_TESTS
-    dyld_thread_key_t               _key;
-    std::span<Thunk>                _thunks;
-    std::span<const uint8_t>        _initialContent;
-    bool                            _allZeroFillContent;
-#endif
-};
-
-
-#if BUILDING_LIBDYLD
-extern ThreadLocalVariables sThreadLocalVariables;
-#endif
-
-
-} // namespace dyld
-
-#endif /* ThreadLocalVariables_h */