From f5475890894b4d542f622cee27604de15b41bb4b Mon Sep 17 00:00:00 2001 From: Jaymes Wilks Date: Mon, 4 Feb 2019 14:06:56 -0600 Subject: Support thread local storage Generally adds support to declare variables as thread_local - Add support in HBRT start assembly to skip adjusting TLS relocations - Add support in linker to generate tagged TLS entries - Update linker to process TLS relocations correctly - Update TLS code to ignore top half of module ID - Update module images to hold a "module ID" - Update custome linker to update module ID during binary link - Update TLS code to track TLS sections via module ID Change-Id: I1589550d7787beb08827ca24a728397dedf0373b RTC: 147599 Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71709 Reviewed-by: Ilya Smirnov Reviewed-by: Michael Baiocchi Tested-by: Jenkins Server Tested-by: FSP CI Jenkins Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Reviewed-by: Daniel M. Crowell --- src/lib/makefile | 3 +- src/lib/tls.C | 123 ++++++++++++++++++++++++++++++++++++------------------- src/lib/tlsrt.C | 31 ++++++++++++++ 3 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 src/lib/tlsrt.C (limited to 'src/lib') diff --git a/src/lib/makefile b/src/lib/makefile index 5e64025d1..406d5ead8 100644 --- a/src/lib/makefile +++ b/src/lib/makefile @@ -5,7 +5,7 @@ # # OpenPOWER HostBoot Project # -# Contributors Listed Below - COPYRIGHT 2010,2017 +# Contributors Listed Below - COPYRIGHT 2010,2019 # [+] International Business Machines Corp. # # @@ -50,6 +50,7 @@ OBJS += utilmisc.o OBJS += tls.o OBJS += errno.o +OBJS += tlsrt.o ifdef HOSTBOOT_MEMORY_LEAKS COMMONFLAGS += -DHOSTBOOT_MEMORY_LEAKS=1 diff --git a/src/lib/tls.C b/src/lib/tls.C index 4fcafb23b..3e2294251 100644 --- a/src/lib/tls.C +++ b/src/lib/tls.C @@ -31,9 +31,21 @@ #include #include #include +#ifndef __HOSTBOOT_RUNTIME #include +#else +#include +#endif #include +/** + * @brief Mask which suppresses ignored portions of a TLS module ID. When + * The linker creates a module ID, it prefixes it with "TLS" + 0x00 + * as the first 32 bits. This mask slices that off to leave just the + * normalized module ID + */ +const size_t TLS_MODULE_MASK = 0x00000000FFFFFFFFULL; + /** Thread Local Storage - How it works. * * For background: @@ -86,8 +98,36 @@ struct __tls_linker_tuple /** Info about the .tdata section for each module. */ struct __tls_module { - void* sect_addr; - size_t size; + void* sect_addr; //< Starting address of module + size_t size; //< Size of module + size_t module; //< Module ID of this module + //< (set by custom linker) + + /** + * @brief Default constructor + */ + __tls_module() + : sect_addr(0), + size(0), + module(0) + { + } + + /** + * @brief Specialized contstructor + * + * @param[in] i_start Module starting address. Must not be nullptr. + * @param[in] i_size Module size in bytes + * @param[in] i_module Modue ID of the module + */ + __tls_module(void* const i_start, + const size_t i_size, + const size_t i_module) + : sect_addr(i_start), + size(i_size), + module(i_module) + { + } }; /** TLS destructor data. */ @@ -103,7 +143,11 @@ struct __tls_dtor struct __tls_thread_info { size_t count; +#ifndef __HOSTBOOT_RUNTIME Util::Lockfree::Stack<__tls_dtor> dtors; +#else + Util::NoLockFree::Stack<__tls_dtor> dtors; +#endif void* blobs[0]; }; @@ -114,51 +158,47 @@ std::vector<__tls_module> __tls_pending_modules; /** Get the previously registered __tls_module data for a TLS variable */ const __tls_module* __tls_get_module(const __tls_linker_tuple* tuple) { + size_t tuple_module = tuple->module & TLS_MODULE_MASK; + // Look the module up in the __tls_modules first, in case we've seen // this module before in another thread. - if ((__tls_modules.size() > tuple->module) && - (__tls_modules[tuple->module].sect_addr != nullptr)) + if ((__tls_modules.size() > tuple_module) && + (__tls_modules[tuple_module].sect_addr != nullptr)) { - return &__tls_modules[tuple->module]; + return &__tls_modules[tuple_module]; } // We plan to insert a new module, so make sure we can contain it. - if (__tls_modules.size() <= tuple->module) + if (__tls_modules.size() <= tuple_module) { - __tls_modules.resize(tuple->module+1); + __tls_modules.resize(tuple_module+1); } // This is the first time we've seen this module. Need to look up in // pending. - // The TLS sections are at the beginning of the .rodata section and the - // linker tuples are somewhere in .data. Therefore sect_addr < tuple. - // Search __tls_pending_modules for the highest address section that is - // less than the tuple address. - auto best = __tls_pending_modules.begin(); - auto curr = best; - while(curr != __tls_pending_modules.end()) - { - if ((curr->sect_addr > best->sect_addr) && - (curr->sect_addr < tuple)) + // The module should have pre-registered its module ID in association + // with its starting address and size; look for a module ID match. + auto moduleItr = std::find_if( + __tls_pending_modules.begin(), + __tls_pending_modules.end(), + [&tuple_module](const __tls_module& i_module) { - best = curr; - } - ++curr; - } - assert(best != __tls_pending_modules.end()); + return (tuple_module == i_module.module); + }); + assert(moduleItr != __tls_pending_modules.end()); // Copy it into the __tls_modules and remove it from the pending list. - __tls_modules[tuple->module] = *best; - __tls_pending_modules.erase(best); + __tls_modules[tuple_module] = *moduleItr; + __tls_pending_modules.erase(moduleItr); - return &__tls_modules[tuple->module]; + return &__tls_modules[tuple_module]; } /* Since Hostboot runtime only has a single thread, we'll just create a * single global TLS area. */ #ifdef __HOSTBOOT_RUNTIME -task_t __tls_task_struct; +task_t tls_task_struct; #endif /** Get a TLS variable address @@ -168,6 +208,8 @@ task_t __tls_task_struct; extern "C" void* __tls_get_addr(const __tls_linker_tuple* tuple) { + size_t tuple_module = tuple->module & TLS_MODULE_MASK; + task_t* task = nullptr; #ifdef __HOSTBOOT_RUNTIME task = &tls_task_struct; @@ -184,36 +226,36 @@ void* __tls_get_addr(const __tls_linker_tuple* tuple) // - tls[module] is nullptr // Then: module blob needs to be allocated. if ((tls_info == nullptr) || - (tls_info->count <= tuple->module) || - (tls_info->blobs[tuple->module] == nullptr)) + (tls_info->count <= tuple_module) || + (tls_info->blobs[tuple_module] == nullptr)) { // If there isn't room for the module's blob, we need to allocate it. - if ((tls_info == nullptr) || (tls_info->count <= tuple->module)) + if ((tls_info == nullptr) || (tls_info->count <= tuple_module)) { - decltype(__tls_thread_info::count) old_size = 0; + decltype(__tls_thread_info::count) old_count = 0; auto new_size = sizeof(__tls_thread_info) + - sizeof(void*)*(tuple->module+1); + sizeof(void*)*(tuple_module+1); // Allocate or reallocate the tls info. if (tls_info == nullptr) { - old_size = 0; + old_count = 0; tls_info = reinterpret_cast( malloc(new_size)); memset(&tls_info->dtors, '\0', sizeof(tls_info->dtors)); } else { - old_size = tls_info->count; + old_count = tls_info->count; tls_info = reinterpret_cast( realloc(tls_info, new_size)); } // Clear the newly allocated area and update the count. - memset(&tls_info->blobs[old_size], '\0', new_size - - (sizeof(__tls_thread_info) + sizeof(void*)*old_size)); - tls_info->count = tuple->module+1; + memset(&tls_info->blobs[old_count], '\0', new_size - + (sizeof(__tls_thread_info) + sizeof(void*)*old_count)); + tls_info->count = tuple_module+1; // save into task struct. task->tls_context = tls_info; @@ -224,7 +266,7 @@ void* __tls_get_addr(const __tls_linker_tuple* tuple) mutex_lock(&__tls_mutex); { auto module = __tls_get_module(tuple); - auto blob = tls_info->blobs[tuple->module] = malloc(module->size); + auto blob = tls_info->blobs[tuple_module] = malloc(module->size); memcpy(blob, module->sect_addr, module->size); } mutex_unlock(&__tls_mutex); @@ -232,7 +274,7 @@ void* __tls_get_addr(const __tls_linker_tuple* tuple) } // Return the offset of the TLS variable from this module's blob. - return &reinterpret_cast(tls_info->blobs[tuple->module]) + return &reinterpret_cast(tls_info->blobs[tuple_module]) [tuple->offset+VFS_PPC64_DTPREL_OFFSET]; } @@ -240,13 +282,12 @@ void* __tls_get_addr(const __tls_linker_tuple* tuple) * * Called by init() in module_init. */ -void __tls_register(void* s, void* e) +void __tls_register(void* s, void* e, const size_t i_module) { if (s == e) return; - __tls_module m = { s, ((size_t)e) - ((size_t)s) }; - + __tls_module m(s, ((size_t)e) - ((size_t)s), i_module ); mutex_lock(&__tls_mutex); { __tls_pending_modules.push_back(m); diff --git a/src/lib/tlsrt.C b/src/lib/tlsrt.C new file mode 100644 index 000000000..100299d16 --- /dev/null +++ b/src/lib/tlsrt.C @@ -0,0 +1,31 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/tlsrt.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +// There are no good ways to apply the runtime condition to a non-module +// object, so this facilitates creating two different verisions of the +// same object +#define __HOSTBOOT_RUNTIME 1 +#include "tls.C" + -- cgit v1.2.1