Merge "Add a thread-properties API (Based on proposal at https://sourceware.org/glibc/wiki/ThreadPropertiesAPI)" am: d4d7efc1f8

Original change: https://android-review.googlesource.com/c/platform/bionic/+/1360925

Change-Id: I984cdfc8e5e0273ece8dba4fa82de824f40cd2d9
This commit is contained in:
Vy Nguyen 2020-08-13 20:25:45 +00:00 committed by Automerger Merge Worker
commit eb1a99cbfb
15 changed files with 536 additions and 1 deletions

View file

@ -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.

View file

@ -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<char*>(ptr) - reinterpret_cast<char*>(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);

View file

@ -28,6 +28,7 @@
#include "private/bionic_elf_tls.h"
#include <async_safe/CHECK.h>
#include <async_safe/log.h>
#include <string.h>
#include <sys/param.h>
@ -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<void*>(static_cast<char*>(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<void*>(static_cast<char*>(mod_ptr) + segment.size));
}
}
return static_cast<char*>(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<void*>(static_cast<char*>(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();
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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 <sys/thread_properties.h>
#include <async_safe/CHECK.h>
#include <async_safe/log.h>
#include <elf.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/user.h>
#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<char*>(__get_bionic_tcb()) - layout.offset_bionic_tcb();
*stls_end = reinterpret_cast<char*>(*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<CallbackHolder*>(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 = &regs,
.iov_len = sizeof(regs),
};
if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) {
#if defined(__x86_64__)
tp_reg = reinterpret_cast<void**>(regs.fs);
#elif defined(__i386__)
tp_reg = reinterpret_cast<void**>(regs.xgs);
#endif
}
#elif defined(__aarch64__) || defined(__arm__)
uint64_t reg;
struct iovec pt_iov {
.iov_base = &reg, .iov_len = sizeof(reg),
};
if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) == 0) {
tp_reg = reinterpret_cast<void**>(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<bionic_tcb*>(&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<void*>(static_cast<char*>(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;
}

View file

@ -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 <sys/cdefs.h>
#include <unistd.h>
__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

View file

@ -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 {

View file

@ -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);

View file

@ -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();

View file

@ -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) {

View file

@ -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"],

View file

@ -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 <stdio.h>
#include <sys/thread_properties.h>
// 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__

View file

@ -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 <sys/thread_properties.h>
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h> // 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<void**>(&start_addr),
reinterpret_cast<void**>(&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__

View file

@ -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 <gtest/gtest.h>
#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
}