Add tests for dynamic ELF TLS
Bug: http://b/78026329 Test: bionic unit tests Merged-In: I508fa38b331eeec7dae53039b4b1ec6cedea3034 Change-Id: I508fa38b331eeec7dae53039b4b1ec6cedea3034
This commit is contained in:
parent
ffaae70936
commit
06d2d790ad
|
@ -533,7 +533,6 @@ cc_test {
|
|||
"libdl_preempt_test_1",
|
||||
"libdl_preempt_test_2",
|
||||
"libdl_test_df_1_global",
|
||||
"libelf-tls-library",
|
||||
"libgnu-hash-table-library",
|
||||
"libsysv-hash-table-library",
|
||||
"libtestshared",
|
||||
|
@ -571,6 +570,10 @@ cc_test {
|
|||
"libtest_dlsym_from_this",
|
||||
"libtest_dlsym_weak_func",
|
||||
"libtest_dt_runpath_d",
|
||||
"libtest_elftls_dynamic",
|
||||
"libtest_elftls_dynamic_filler_1",
|
||||
"libtest_elftls_dynamic_filler_2",
|
||||
"libtest_elftls_dynamic_filler_3",
|
||||
"libtest_elftls_shared_var",
|
||||
"libtest_elftls_shared_var_ie",
|
||||
"libtest_elftls_tprel",
|
||||
|
|
|
@ -1082,19 +1082,6 @@ TEST(dlfcn, dlopen_library_with_only_sysv_hash) {
|
|||
ASSERT_SUBSTR("libsysv-hash-table-library.so", dlinfo.dli_fname);
|
||||
}
|
||||
|
||||
TEST(dlfcn, dlopen_library_with_ELF_TLS) {
|
||||
// TODO: Remove this test. Once ELF TLS is implemented, this test will be
|
||||
// replaced with a larger set of tests. Removing the test requires matching CLs
|
||||
// in CTS and in internal test suites.
|
||||
#if 0
|
||||
dlerror(); // Clear any pending errors.
|
||||
void* handle = dlopen("libelf-tls-library.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
ASSERT_SUBSTR("unknown reloc type ", dlerror());
|
||||
#endif
|
||||
GTEST_LOG_(INFO) << "This test is disabled pending replacement with dynamic ELF TLS tests.\n";
|
||||
}
|
||||
|
||||
TEST(dlfcn, dlopen_bad_flags) {
|
||||
dlerror(); // Clear any pending errors.
|
||||
void* handle;
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
#include "gtest_globals.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#include "bionic/pthread_internal.h"
|
||||
#endif
|
||||
|
||||
// Access libtest_elftls_shared_var.so's TLS variable using an IE access.
|
||||
__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
|
||||
|
||||
|
@ -78,3 +82,175 @@ TEST(elftls_dl, dlopen_ie_error) {
|
|||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, error.c_str());
|
||||
}
|
||||
|
||||
// Use a GD access (__tls_get_addr or TLSDESC) to modify a variable in static
|
||||
// TLS memory.
|
||||
TEST(elftls_dl, access_static_tls) {
|
||||
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_NE(nullptr, lib);
|
||||
|
||||
auto bump_shared_var = reinterpret_cast<int(*)()>(dlsym(lib, "bump_shared_var"));
|
||||
ASSERT_NE(nullptr, bump_shared_var);
|
||||
|
||||
ASSERT_EQ(21, ++elftls_shared_var);
|
||||
ASSERT_EQ(22, bump_shared_var());
|
||||
|
||||
std::thread([bump_shared_var] {
|
||||
ASSERT_EQ(21, ++elftls_shared_var);
|
||||
ASSERT_EQ(22, bump_shared_var());
|
||||
}).join();
|
||||
}
|
||||
|
||||
TEST(elftls_dl, bump_local_vars) {
|
||||
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_NE(nullptr, lib);
|
||||
|
||||
auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
|
||||
ASSERT_NE(nullptr, bump_local_vars);
|
||||
|
||||
ASSERT_EQ(42, bump_local_vars());
|
||||
std::thread([bump_local_vars] {
|
||||
ASSERT_EQ(42, bump_local_vars());
|
||||
}).join();
|
||||
}
|
||||
|
||||
// The behavior of accessing an unresolved weak TLS symbol using a dynamic TLS
|
||||
// relocation depends on which kind of implementation the target uses. With
|
||||
// TLSDESC, the result is NULL. With __tls_get_addr, the result is the
|
||||
// generation count (or maybe undefined behavior)? This test only tests TLSDESC.
|
||||
TEST(elftls_dl, missing_weak) {
|
||||
#if defined(__aarch64__)
|
||||
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_NE(nullptr, lib);
|
||||
|
||||
auto missing_weak_dyn_tls_addr = reinterpret_cast<int*(*)()>(dlsym(lib, "missing_weak_dyn_tls_addr"));
|
||||
ASSERT_NE(nullptr, missing_weak_dyn_tls_addr);
|
||||
|
||||
ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
|
||||
std::thread([missing_weak_dyn_tls_addr] {
|
||||
ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
|
||||
}).join();
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test is only run on TLSDESC-based targets.\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(elftls_dl, dtv_resize) {
|
||||
#if defined(__BIONIC__)
|
||||
#define LOAD_LIB(soname) ({ \
|
||||
auto lib = dlopen(soname, RTLD_LOCAL | RTLD_NOW); \
|
||||
ASSERT_NE(nullptr, lib); \
|
||||
reinterpret_cast<int(*)()>(dlsym(lib, "bump")); \
|
||||
})
|
||||
|
||||
auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
|
||||
|
||||
static_assert(sizeof(TlsDtv) == 3 * sizeof(void*),
|
||||
"This test assumes that the Dtv has a 3-word header");
|
||||
|
||||
// Initially there are 3 modules:
|
||||
// - the main test executable
|
||||
// - libtest_elftls_shared_var
|
||||
// - libtest_elftls_tprel
|
||||
|
||||
// The initial DTV is an empty DTV with no generation and a size of 0.
|
||||
TlsDtv* zero_dtv = dtv();
|
||||
ASSERT_EQ(0u, zero_dtv->count);
|
||||
ASSERT_EQ(nullptr, zero_dtv->next);
|
||||
ASSERT_EQ(kTlsGenerationNone, zero_dtv->generation);
|
||||
|
||||
// Load the fourth module.
|
||||
auto func1 = LOAD_LIB("libtest_elftls_dynamic_filler_1.so");
|
||||
ASSERT_EQ(101, func1());
|
||||
|
||||
// After loading one module, the DTV should be initialized to the next
|
||||
// power-of-2 size (including the header).
|
||||
TlsDtv* initial_dtv = dtv();
|
||||
ASSERT_EQ(5u, initial_dtv->count);
|
||||
ASSERT_EQ(zero_dtv, initial_dtv->next);
|
||||
ASSERT_LT(0u, initial_dtv->generation);
|
||||
|
||||
// Load module 5.
|
||||
auto func2 = LOAD_LIB("libtest_elftls_dynamic_filler_2.so");
|
||||
ASSERT_EQ(102, func1());
|
||||
ASSERT_EQ(201, func2());
|
||||
ASSERT_EQ(initial_dtv, dtv());
|
||||
ASSERT_EQ(5u, initial_dtv->count);
|
||||
|
||||
// Load module 6.
|
||||
auto func3 = LOAD_LIB("libtest_elftls_dynamic_filler_3.so");
|
||||
ASSERT_EQ(103, func1());
|
||||
ASSERT_EQ(202, func2());
|
||||
|
||||
#if defined(__aarch64__)
|
||||
// The arm64 TLSDESC resolver doesn't update the DTV if it is new enough for
|
||||
// the given access.
|
||||
ASSERT_EQ(5u, dtv()->count);
|
||||
#else
|
||||
// __tls_get_addr updates the DTV anytime the generation counter changes.
|
||||
ASSERT_EQ(13u, dtv()->count);
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(301, func3());
|
||||
|
||||
TlsDtv* new_dtv = dtv();
|
||||
ASSERT_EQ(13u, new_dtv->count);
|
||||
ASSERT_NE(initial_dtv, new_dtv);
|
||||
ASSERT_EQ(initial_dtv, new_dtv->next);
|
||||
|
||||
#undef LOAD_LIB
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Verify that variables are reset to their initial values after the library
|
||||
// containing them is closed.
|
||||
TEST(elftls_dl, dlclose_resets_values) {
|
||||
for (int round = 0; round < 2; ++round) {
|
||||
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_NE(nullptr, lib);
|
||||
|
||||
auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
|
||||
ASSERT_NE(nullptr, bump_local_vars);
|
||||
|
||||
ASSERT_EQ(42, bump_local_vars());
|
||||
ASSERT_EQ(44, bump_local_vars());
|
||||
|
||||
ASSERT_EQ(0, dlclose(lib));
|
||||
}
|
||||
}
|
||||
|
||||
// Calling dlclose should remove the entry for the solib from the global list of
|
||||
// ELF TLS modules. Test that repeatedly loading and unloading a library doesn't
|
||||
// increase the DTV size.
|
||||
TEST(elftls_dl, dlclose_removes_entry) {
|
||||
#if defined(__BIONIC__)
|
||||
auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
|
||||
|
||||
bool first = true;
|
||||
size_t count = 0;
|
||||
|
||||
// Use a large number of rounds in case the DTV is initially larger than
|
||||
// expected.
|
||||
for (int round = 0; round < 32; ++round) {
|
||||
void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_NE(nullptr, lib);
|
||||
|
||||
auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
|
||||
ASSERT_NE(nullptr, bump_local_vars);
|
||||
|
||||
ASSERT_EQ(42, bump_local_vars());
|
||||
if (first) {
|
||||
first = false;
|
||||
count = dtv()->count;
|
||||
} else {
|
||||
ASSERT_EQ(count, dtv()->count);
|
||||
}
|
||||
|
||||
dlclose(lib);
|
||||
}
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -83,3 +83,17 @@ TEST(elftls, tprel_addend) {
|
|||
ASSERT_EQ(8, bump_static_tls_var_2());
|
||||
}).join();
|
||||
}
|
||||
|
||||
// Because this C++ source file is built with -fpic, the compiler will access
|
||||
// this variable using a GD model. Typically, the static linker will relax the
|
||||
// GD to LE, but the arm32 linker doesn't do TLS relaxations, so we can test
|
||||
// calling __tls_get_addr in a static executable. The static linker knows that
|
||||
// the main executable's TlsIndex::module_id is 1 and writes that into the GOT.
|
||||
__thread int tlsvar_general = 30;
|
||||
|
||||
TEST(elftls, general) {
|
||||
ASSERT_EQ(31, ++tlsvar_general);
|
||||
std::thread([] {
|
||||
ASSERT_EQ(31, ++tlsvar_general);
|
||||
}).join();
|
||||
}
|
||||
|
|
|
@ -42,14 +42,6 @@ cc_defaults {
|
|||
// -----------------------------------------------------------------------------
|
||||
// Libraries and helper binaries for ELF TLS
|
||||
// -----------------------------------------------------------------------------
|
||||
cc_test_library {
|
||||
name: "libelf-tls-library",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["elf_tls_test_library.cpp"],
|
||||
cflags: ["-fno-emulated-tls"],
|
||||
allow_undefined_symbols: true, // __tls_get_addr is undefined.
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libtest_elftls_shared_var",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
|
@ -79,6 +71,35 @@ cc_test {
|
|||
ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libtest_elftls_dynamic",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["elftls_dynamic.cpp"],
|
||||
cflags: ["-fno-emulated-tls"],
|
||||
shared_libs: ["libtest_elftls_shared_var"],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libtest_elftls_dynamic_filler_1",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["elftls_dynamic_filler.cpp"],
|
||||
cflags: ["-fno-emulated-tls", "-DTLS_FILLER=100"],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libtest_elftls_dynamic_filler_2",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["elftls_dynamic_filler.cpp"],
|
||||
cflags: ["-fno-emulated-tls", "-DTLS_FILLER=200"],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "libtest_elftls_dynamic_filler_3",
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["elftls_dynamic_filler.cpp"],
|
||||
cflags: ["-fno-emulated-tls", "-DTLS_FILLER=300"],
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Library to test gnu-styled hash
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
thread_local int elf_tls_variable;
|
||||
|
||||
extern "C" int* get() { return &elf_tls_variable; }
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This shared object test library is dlopen'ed by the main test executable.
|
||||
// This variable comes from libtest_elftls_shared_var.so, which is part of
|
||||
// static TLS. Verify that a GD-model access can access the variable.
|
||||
//
|
||||
// Accessing the static TLS variable from an solib prevents the static linker
|
||||
// from relaxing the GD access to IE and lets us test that __tls_get_addr and
|
||||
// the tlsdesc resolver handle a static TLS variable.
|
||||
extern "C" __thread int elftls_shared_var;
|
||||
|
||||
extern "C" int bump_shared_var() {
|
||||
return ++elftls_shared_var;
|
||||
}
|
||||
|
||||
// The static linker denotes the current module by omitting the symbol from
|
||||
// the DTPMOD/TLSDESC relocations.
|
||||
static __thread int local_var_1 = 15;
|
||||
static __thread int local_var_2 = 25;
|
||||
|
||||
extern "C" int bump_local_vars() {
|
||||
return ++local_var_1 + ++local_var_2;
|
||||
}
|
||||
|
||||
__attribute__((weak)) extern "C" __thread int missing_weak_dyn_tls;
|
||||
|
||||
extern "C" int* missing_weak_dyn_tls_addr() {
|
||||
return &missing_weak_dyn_tls;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
__thread int var = TLS_FILLER;
|
||||
|
||||
extern "C" int bump() {
|
||||
return ++var;
|
||||
}
|
Loading…
Reference in New Issue