diff --git a/libc/Android.bp b/libc/Android.bp index c7750ffa4..6b36f9623 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -1238,6 +1238,7 @@ cc_library_static { "bionic/pthread_setname_np.cpp", "bionic/pthread_setschedparam.cpp", "bionic/pthread_spinlock.cpp", + "bionic/sys_thread_properties.cpp", // The following implementations depend on pthread data or implementation, // so we can't include them in libc_ndk.a. diff --git a/libc/bionic/bionic_allocator.cpp b/libc/bionic/bionic_allocator.cpp index b6d6ba73f..98183d415 100644 --- a/libc/bionic/bionic_allocator.cpp +++ b/libc/bionic/bionic_allocator.cpp @@ -394,6 +394,26 @@ void BionicAllocator::free(void* ptr) { } } +size_t BionicAllocator::get_chunk_size(void* ptr) { + if (ptr == nullptr) return 0; + + page_info* info = get_page_info_unchecked(ptr); + if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) { + // Invalid pointer (mismatched signature) + return 0; + } + if (info->type == kLargeObject) { + return info->allocated_size - (static_cast(ptr) - reinterpret_cast(info)); + } + + BionicSmallObjectAllocator* allocator = get_small_object_allocator(info->type); + if (allocator != info->allocator_addr) { + // Invalid pointer. + return 0; + } + return allocator->get_block_size(); +} + BionicSmallObjectAllocator* BionicAllocator::get_small_object_allocator(uint32_t type) { if (type < kSmallObjectMinSizeLog2 || type > kSmallObjectMaxSizeLog2) { async_safe_fatal("invalid type: %u", type); diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp index 61d826c02..d5fb05aa6 100644 --- a/libc/bionic/bionic_elf_tls.cpp +++ b/libc/bionic/bionic_elf_tls.cpp @@ -28,6 +28,7 @@ #include "private/bionic_elf_tls.h" +#include #include #include #include @@ -269,6 +270,12 @@ static void update_tls_dtv(bionic_tcb* tcb) { continue; } } + if (modules.on_destruction_cb != nullptr) { + void* dtls_begin = dtv->modules[i]; + void* dtls_end = + static_cast(static_cast(dtls_begin) + allocator.get_chunk_size(dtls_begin)); + modules.on_destruction_cb(dtls_begin, dtls_end); + } allocator.free(dtv->modules[i]); dtv->modules[i] = nullptr; } @@ -297,6 +304,12 @@ __attribute__((noinline)) static void* tls_get_addr_slow_path(const TlsIndex* ti memcpy(mod_ptr, segment.init_ptr, segment.init_size); } dtv->modules[module_idx] = mod_ptr; + + // Reports the allocation to the listener, if any. + if (modules.on_creation_cb != nullptr) { + modules.on_creation_cb(mod_ptr, + static_cast(static_cast(mod_ptr) + segment.size)); + } } return static_cast(mod_ptr) + ti->offset; @@ -351,6 +364,14 @@ void __free_dynamic_tls(bionic_tcb* tcb) { // This module's TLS memory is allocated statically, so don't free it here. continue; } + + if (modules.on_destruction_cb != nullptr) { + void* dtls_begin = dtv->modules[i]; + void* dtls_end = + static_cast(static_cast(dtls_begin) + allocator.get_chunk_size(dtls_begin)); + modules.on_destruction_cb(dtls_begin, dtls_end); + } + allocator.free(dtv->modules[i]); } @@ -364,3 +385,22 @@ void __free_dynamic_tls(bionic_tcb* tcb) { // Clear the DTV slot. The DTV must not be used again with this thread. tcb->tls_slot(TLS_SLOT_DTV) = nullptr; } + +// Invokes all the registered thread_exit callbacks, if any. +void __notify_thread_exit_callbacks() { + TlsModules& modules = __libc_shared_globals()->tls_modules; + if (modules.first_thread_exit_callback == nullptr) { + // If there is no first_thread_exit_callback, there shouldn't be a tail. + CHECK(modules.thread_exit_callback_tail_node == nullptr); + return; + } + + // Callbacks are supposed to be invoked in the reverse order + // in which they were registered. + CallbackHolder* node = modules.thread_exit_callback_tail_node; + while (node != nullptr) { + node->cb(); + node = node->prev; + } + modules.first_thread_exit_callback(); +} diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp index cf5423e00..e3a447dbe 100644 --- a/libc/bionic/libc_init_static.cpp +++ b/libc/bionic/libc_init_static.cpp @@ -147,6 +147,7 @@ static void layout_static_tls(KernelArgumentBlock& args) { mod.first_generation = kTlsGenerationFirst; modules.module_count = 1; + modules.static_module_count = 1; modules.module_table = &mod; } else { layout.reserve_exe_segment_and_tcb(nullptr, progname); diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp index 3b873b314..81dab5798 100644 --- a/libc/bionic/pthread_exit.cpp +++ b/libc/bionic/pthread_exit.cpp @@ -112,7 +112,6 @@ void pthread_exit(void* return_value) { munmap(thread->shadow_call_stack_guard_region, SCS_GUARD_REGION_SIZE); #endif - // Free the ELF TLS DTV and all dynamically-allocated ELF TLS memory. __free_dynamic_tls(__get_bionic_tcb()); if (old_state == THREAD_DETACHED) { @@ -128,6 +127,7 @@ void pthread_exit(void* return_value) { if (thread->mmap_size != 0) { // We need to free mapped space for detached threads when they exit. // That's not something we can do in C. + __notify_thread_exit_callbacks(); __hwasan_thread_exit(); _exit_with_stack_teardown(thread->mmap_base, thread->mmap_size); } @@ -135,6 +135,8 @@ void pthread_exit(void* return_value) { // No need to free mapped space. Either there was no space mapped, or it is left for // the pthread_join caller to clean up. + __notify_thread_exit_callbacks(); __hwasan_thread_exit(); + __exit(0); } diff --git a/libc/bionic/sys_thread_properties.cpp b/libc/bionic/sys_thread_properties.cpp new file mode 100644 index 000000000..24d755111 --- /dev/null +++ b/libc/bionic/sys_thread_properties.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 OWNER 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 + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "private/bionic_elf_tls.h" +#include "private/bionic_globals.h" +#include "private/bionic_tls.h" +#include "pthread_internal.h" + +void __libc_get_static_tls_bounds(void** stls_begin, void** stls_end) { + const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout; + *stls_begin = reinterpret_cast(__get_bionic_tcb()) - layout.offset_bionic_tcb(); + *stls_end = reinterpret_cast(*stls_begin) + layout.size(); +} + +void __libc_register_thread_exit_callback(thread_exit_cb_t cb) { + TlsModules& modules = __libc_shared_globals()->tls_modules; + + if (modules.first_thread_exit_callback == nullptr) { + modules.first_thread_exit_callback = cb; + return; + }; + + BionicAllocator& allocator = __libc_shared_globals()->tls_allocator; + CallbackHolder* new_node = + reinterpret_cast(allocator.alloc(sizeof(CallbackHolder))); + new_node->cb = cb; + new_node->prev = modules.thread_exit_callback_tail_node; + modules.thread_exit_callback_tail_node = new_node; +} + +static inline __always_inline bionic_tcb* __get_bionic_tcb_for_thread(pid_t tid) { + // If tid is same as self, then we don't need ptrace. + if (gettid() == tid) return __get_bionic_tcb(); + + // Find the thread-pointer register for the given thread. + void** tp_reg = nullptr; + +#if defined(__x86_64__) || defined(__i386__) + struct user_regs_struct regs; + struct iovec pt_iov = { + .iov_base = ®s, + .iov_len = sizeof(regs), + }; + if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) { +#if defined(__x86_64__) + tp_reg = reinterpret_cast(regs.fs); +#elif defined(__i386__) + tp_reg = reinterpret_cast(regs.xgs); +#endif + } +#elif defined(__aarch64__) || defined(__arm__) + uint64_t reg; + struct iovec pt_iov { + .iov_base = ®, .iov_len = sizeof(reg), + }; + + if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) == 0) { + tp_reg = reinterpret_cast(reg); + } +#endif + + if (tp_reg == nullptr) { + async_safe_write_log(ANDROID_LOG_FATAL, "libc", + "__get_bionic_tcb_for_thread failed to read thread register."); + } + + return reinterpret_cast(&tp_reg[MIN_TLS_SLOT]); +} + +void __libc_iterate_dynamic_tls(pid_t tid, + void (*cb)(void* __dynamic_tls_begin, void* __dynamic_tls_end, + size_t __dso_id, void* __arg), + void* arg) { + TlsModules& modules = __libc_shared_globals()->tls_modules; + bionic_tcb* const tcb = __get_bionic_tcb_for_thread(tid); + TlsDtv* const dtv = __get_tcb_dtv(tcb); + BionicAllocator& allocator = __libc_shared_globals()->tls_allocator; + + for (size_t i = modules.static_module_count; i < dtv->count; ++i) { + void* dtls_begin = dtv->modules[i]; + if (dtls_begin == nullptr) continue; + void* dtls_end = + static_cast(static_cast(dtls_begin) + allocator.get_chunk_size(dtls_begin)); + size_t dso_id = __tls_module_idx_to_id(i); + + cb(dtls_begin, dtls_end, dso_id, arg); + } +} + +void __libc_register_dynamic_tls_listeners(dtls_listener_t on_creation, + dtls_listener_t on_destruction) { + TlsModules& tls_modules = __libc_shared_globals()->tls_modules; + tls_modules.on_creation_cb = on_creation; + tls_modules.on_destruction_cb = on_destruction; +} diff --git a/libc/include/sys/thread_properties.h b/libc/include/sys/thread_properties.h new file mode 100644 index 000000000..b5d30c77e --- /dev/null +++ b/libc/include/sys/thread_properties.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 OWNER 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 + +/** + * @file thread_properties.h + * @brief Thread properties API. + * + * https://sourceware.org/glibc/wiki/ThreadPropertiesAPI + * API for querying various properties of the current thread, used mostly by + * the sanitizers. + * + * Available since API level 31. + * + */ + +#include +#include + +__BEGIN_DECLS + +/** + * Gets the bounds of static TLS for the current thread. + * + * Available since API level 31. + */ +void __libc_get_static_tls_bounds(void** __static_tls_begin, + void** __static_tls_end) __INTRODUCED_IN(31); + + +/** + * Registers callback to be called right before the thread is dead. + * The callbacks are chained, they are called in the order opposite to the order + * they were registered. + * + * The callbacks must be registered only before any threads were created. + * No signals may arrive during the calls to these callbacks. + * The callbacks may not access the thread's dynamic TLS because they will have + * been freed by the time these callbacks are invoked. + * + * Available since API level 31. + */ +void __libc_register_thread_exit_callback(void (*__cb)(void)) __INTRODUCED_IN(31); + +/** + * Iterates over all dynamic TLS chunks for the given thread. + * The thread should have been suspended. It is undefined-behaviour if there is concurrent + * modification of the target thread's dynamic TLS. + * + * Available since API level 31. + */ +void __libc_iterate_dynamic_tls(pid_t __tid, + void (*__cb)(void* __dynamic_tls_begin, + void* __dynamic_tls_end, + size_t __dso_id, + void* __arg), + void* __arg) __INTRODUCED_IN(31); + +/** + * Register on_creation and on_destruction callbacks, which will be called after a dynamic + * TLS creation and before a dynamic TLS destruction, respectively. + * + * Available since API level 31. + */ +void __libc_register_dynamic_tls_listeners( + void (*__on_creation)(void* __dynamic_tls_begin, + void* __dynamic_tls_end), + void (*__on_destruction)(void* __dynamic_tls_begin, + void* __dynamic_tls_end)) __INTRODUCED_IN(31); + +__END_DECLS diff --git a/libc/libc.map.txt b/libc/libc.map.txt index 10732a12d..e35d1fb84 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1554,6 +1554,10 @@ LIBC_S { # introduced=S global: ffsl; ffsll; + __libc_get_static_tls_bounds; + __libc_register_thread_exit_callback; + __libc_iterate_dynamic_tls; + __libc_register_dynamic_tls_listeners; } LIBC_R; LIBC_PRIVATE { diff --git a/libc/private/bionic_allocator.h b/libc/private/bionic_allocator.h index c705ce403..342fd5149 100644 --- a/libc/private/bionic_allocator.h +++ b/libc/private/bionic_allocator.h @@ -110,6 +110,11 @@ class BionicAllocator { // Note that this implementation of realloc never shrinks allocation void* realloc(void* ptr, size_t size); void free(void* ptr); + + // Returns the size of the given allocated heap chunk, if it is valid. + // Otherwise, this may return 0 or cause a segfault if the pointer is invalid. + size_t get_chunk_size(void* ptr); + private: void* alloc_mmap(size_t align, size_t size); inline void* alloc_impl(size_t align, size_t size); diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h index fa1af768e..e0ec7b51d 100644 --- a/libc/private/bionic_elf_tls.h +++ b/libc/private/bionic_elf_tls.h @@ -111,6 +111,18 @@ struct TlsModule { void* soinfo_ptr = nullptr; }; +// Signature of the callbacks that will be called after DTLS creation and +// before DTLS destruction. +typedef void (*dtls_listener_t)(void* dynamic_tls_begin, void* dynamic_tls_end); + +// Signature of the thread-exit callbacks. +typedef void (*thread_exit_cb_t)(void); + +struct CallbackHolder { + thread_exit_cb_t cb; + CallbackHolder* prev; +}; + // Table of the ELF TLS modules. Either the dynamic linker or the static // initialization code prepares this table, and it's then used during thread // creation and for dynamic TLS lookups. @@ -128,7 +140,20 @@ struct TlsModules { // Pointer to a block of TlsModule objects. The first module has ID 1 and // is stored at index 0 in this table. size_t module_count = 0; + size_t static_module_count = 0; TlsModule* module_table = nullptr; + + // Callback to be invoked after a dynamic TLS allocation. + dtls_listener_t on_creation_cb = nullptr; + + // Callback to be invoked before a dynamic TLS deallocation. + dtls_listener_t on_destruction_cb = nullptr; + + // The first thread-exit callback; inlined to avoid allocation. + thread_exit_cb_t first_thread_exit_callback = nullptr; + + // The additional callbacks, if any. + CallbackHolder* thread_exit_callback_tail_node = nullptr; }; void __init_static_tls(void* static_tls); @@ -175,3 +200,4 @@ extern "C" void* TLS_GET_ADDR(const TlsIndex* ti) TLS_GET_ADDR_CCONV; struct bionic_tcb; void __free_dynamic_tls(bionic_tcb* tcb); +void __notify_thread_exit_callbacks(); diff --git a/linker/linker_tls.cpp b/linker/linker_tls.cpp index d2edbb335..97892f49c 100644 --- a/linker/linker_tls.cpp +++ b/linker/linker_tls.cpp @@ -128,6 +128,8 @@ void linker_setup_exe_static_tls(const char* progname) { void linker_finalize_static_tls() { g_static_tls_finished = true; __libc_shared_globals()->static_tls_layout.finish_layout(); + TlsModules& modules = __libc_shared_globals()->tls_modules; + modules.static_module_count = modules.module_count; } void register_soinfo_tls(soinfo* si) { diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp index fcf449786..ef4fddd45 100644 --- a/tests/libs/Android.bp +++ b/tests/libs/Android.bp @@ -79,6 +79,22 @@ cc_test_library { shared_libs: ["libtest_elftls_shared_var"], } +cc_test { + name: "thread_exit_cb_helper.cpp", + defaults: ["bionic_testlib_defaults"], + srcs: ["thread_exit_cb_helper.cpp"], + cflags: ["-fno-emulated-tls"], +} + +cc_test { + name: "tls_properties_helper", + defaults: ["bionic_testlib_defaults"], + srcs: ["tls_properties_helper.cpp"], + cflags: ["-fno-emulated-tls"], + shared_libs: ["libtest_elftls_shared_var"], +} + + cc_test_library { name: "libtest_elftls_dynamic_filler_1", defaults: ["bionic_testlib_defaults"], diff --git a/tests/libs/thread_exit_cb_helper.cpp b/tests/libs/thread_exit_cb_helper.cpp new file mode 100644 index 000000000..8ec1398f1 --- /dev/null +++ b/tests/libs/thread_exit_cb_helper.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 OWNER 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. + */ + +// Prevent tests from being compiled with glibc because thread_properties.h +// only exists in Bionic. +#if defined(__BIONIC__) + +#include +#include + +// Helper binary for testing thread_exit_cb registration. + +void exit_cb_1() { + printf("exit_cb_1 called "); +} + +void exit_cb_2() { + printf("exit_cb_2 called "); +} + +void exit_cb_3() { + printf("exit_cb_3 called"); +} + +void test_register_thread_exit_cb() { + // Register the exit-cb in reverse order (3,2,1) + // so that they'd be called in 1,2,3 order. + __libc_register_thread_exit_callback(&exit_cb_3); + __libc_register_thread_exit_callback(&exit_cb_2); + __libc_register_thread_exit_callback(&exit_cb_1); +} + +int main() { + test_register_thread_exit_cb(); + return 0; +} +#else +int main() { + return 0; +} +#endif // __BIONIC__ diff --git a/tests/libs/tls_properties_helper.cpp b/tests/libs/tls_properties_helper.cpp new file mode 100644 index 000000000..93f5e9f57 --- /dev/null +++ b/tests/libs/tls_properties_helper.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 OWNER 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. + */ + +// Prevent tests from being compiled with glibc because thread_properties.h +// only exists in Bionic. +#if defined(__BIONIC__) + +#include + +#include +#include +#include // for gettid + +// Helper binary to use TLS-related functions in thread_properties + +// Tests __get_static_tls_bound. +void test_static_tls_bounds() { + void* start_addr; + void* end_addr; + + __libc_get_static_tls_bounds(reinterpret_cast(&start_addr), + reinterpret_cast(&end_addr)); + printf("done_get_static_tls_bounds\n"); +} + +// Tests iterate_dynamic tls chunks. +// Export a var from the shared so. +__thread char large_tls_var[4 * 1024 * 1024]; +void test_iter_tls() { + void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW); + + int i = 0; + auto cb = [&](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) { + printf("iterate_cb i = %d\n", i++); + }; + __libc_iterate_dynamic_tls(gettid(), cb, nullptr); + printf("done_iterate_dynamic_tls\n"); +} + +int main() { + test_static_tls_bounds(); + test_iter_tls(); + return 0; +} + +#else +int main() { + return 0; +} +#endif // __BIONIC__ diff --git a/tests/sys_thread_properties_test.cpp b/tests/sys_thread_properties_test.cpp new file mode 100644 index 000000000..cf1a6ba63 --- /dev/null +++ b/tests/sys_thread_properties_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#include + +#include "gtest_globals.h" +#include "utils.h" + +TEST(thread_properties_test, iterate_dts) { +#if defined(__BIONIC__) + const char expected_out[] = + "got test_static_tls_bounds\niterate_cb i = 0\ndone_iterate_dynamic_tls\n"; + std::string helper = GetTestLibRoot() + "tls_properties_helper/tls_properties_helper"; + chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607 + + ExecTestHelper eth; + eth.SetArgs({helper.c_str(), nullptr}); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, expected_out); +#endif +} + +TEST(thread_properties_test, thread_exit_cb) { +#if defined(__BIONIC__) + // tests/libs/thread_exit_cb_helper.cpp + const char expected_out[] = "exit_cb_1 called exit_cb_2 called exit_cb_3 called"; + std::string helper = GetTestLibRoot() + "thread_exit_cb_helper/thread_exit_cb_helper"; + chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607 + + ExecTestHelper eth; + eth.SetArgs({helper.c_str(), nullptr}); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, expected_out); + +#endif +}