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:
Ryan Prichard 2019-01-23 23:19:19 -08:00
parent ffaae70936
commit 06d2d790ad
8 changed files with 311 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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