Merge "bionic: update for glibc 2.17"
diff --git a/libc/Android.bp b/libc/Android.bp
index e0d0fee..226a81f 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1014,6 +1014,7 @@
"bionic/arpa_inet.cpp",
"bionic/assert.cpp",
"bionic/atof.cpp",
+ "bionic/bionic_allocator.cpp",
"bionic/bionic_arc4random.cpp",
"bionic/bionic_futex.cpp",
"bionic/bionic_netlink.cpp",
@@ -1612,6 +1613,8 @@
symbol_file: "libc.map.txt",
versions: ["10000"],
},
+
+ symbol_ordering_file: "symbol_ordering",
}
genrule {
@@ -1646,6 +1649,59 @@
cmd: "$(location :bionic-generate-version-script) x86_64 $(in) $(out)",
}
+// libc_headers for libasync_safe and libpropertyinfoparser
+cc_library_headers {
+ name: "libc_headers",
+
+ host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
+
+ no_libcrt: true,
+ no_libgcc: true,
+ stl: "none",
+ system_shared_libs: [],
+
+ export_include_dirs: [
+ "include",
+ "kernel/uapi",
+ "kernel/android/uapi",
+ ],
+
+ arch: {
+ arm: {
+ export_include_dirs: [
+ "kernel/uapi/asm-arm",
+ ],
+ },
+ arm64: {
+ export_include_dirs: [
+ "kernel/uapi/asm-arm64",
+ ],
+ },
+ mips: {
+ export_include_dirs: [
+ "kernel/uapi/asm-mips",
+ ],
+ },
+ mips64: {
+ export_include_dirs: [
+ "kernel/uapi/asm-mips",
+ ],
+ },
+ x86: {
+ export_include_dirs: [
+ "kernel/uapi/asm-x86",
+ ],
+ },
+ x86_64: {
+ export_include_dirs: [
+ "kernel/uapi/asm-x86",
+ ],
+ },
+ },
+}
+
// ========================================================
// libstdc++.so and libstdc++.a.
// ========================================================
@@ -1999,47 +2055,39 @@
symbol_file: "libc.map.txt",
export_headers_as_system: true,
export_preprocessed_headers: ["include"],
+ export_include_dirs: [
+ "kernel/android/uapi",
+ "kernel/uapi",
+ ],
arch: {
arm: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-arm",
- "kernel/android/uapi",
],
},
arm64: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-arm64",
- "kernel/android/uapi",
],
},
mips: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-mips",
- "kernel/android/uapi",
],
},
mips64: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-mips",
- "kernel/android/uapi",
],
},
x86: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-x86",
- "kernel/android/uapi",
],
},
x86_64: {
export_include_dirs: [
- "kernel/uapi",
"kernel/uapi/asm-x86",
- "kernel/android/uapi",
],
},
},
@@ -2165,6 +2213,91 @@
},
}
+python_binary_host {
+ name: "genfunctosyscallnrs",
+ main: "tools/genfunctosyscallnrs.py",
+
+ srcs: [
+ "tools/genseccomp.py",
+ "tools/genfunctosyscallnrs.py",
+ "tools/gensyscalls.py",
+ ],
+
+ data: [
+ "kernel/uapi/**/*.h",
+ ],
+
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
+
+cc_genrule {
+ name: "func_to_syscall_nrs",
+ recovery_available: true,
+ cmd: "$(location genfunctosyscallnrs) --out-dir=$(genDir) $(in)",
+
+ tools: [ "genfunctosyscallnrs" ],
+
+ srcs: [
+ "SYSCALLS.TXT",
+ ":libseccomp_gen_syscall_nrs_arm",
+ ":libseccomp_gen_syscall_nrs_arm64",
+ ":libseccomp_gen_syscall_nrs_mips",
+ ":libseccomp_gen_syscall_nrs_mips64",
+ ":libseccomp_gen_syscall_nrs_x86",
+ ":libseccomp_gen_syscall_nrs_x86_64",
+ ],
+
+ out: [
+ "func_to_syscall_nrs.h",
+ ],
+}
+
+// SECCOMP_BLACKLIST_APP_ZYGOTE.TXT = SECCOMP_BLACKLIST_APP.txt - setresgid*
+genrule {
+ name: "generate_app_zygote_blacklist",
+ out: ["SECCOMP_BLACKLIST_APP_ZYGOTE.TXT"],
+ srcs: ["SECCOMP_BLACKLIST_APP.TXT"],
+ cmd: "grep -v '^int[ \t]*setresgid' $(in) > $(out)",
+}
+
+cc_genrule {
+ name: "libseccomp_policy_app_zygote_sources",
+ recovery_available: true,
+ cmd: "$(location genseccomp) --out-dir=$(genDir) --name-modifier=app_zygote $(in)",
+
+ tools: [ "genseccomp" ],
+
+ srcs: [
+ "SYSCALLS.TXT",
+ "SECCOMP_WHITELIST_COMMON.TXT",
+ "SECCOMP_WHITELIST_APP.TXT",
+ "SECCOMP_BLACKLIST_COMMON.TXT",
+ ":generate_app_zygote_blacklist",
+ ":libseccomp_gen_syscall_nrs_arm",
+ ":libseccomp_gen_syscall_nrs_arm64",
+ ":libseccomp_gen_syscall_nrs_mips",
+ ":libseccomp_gen_syscall_nrs_mips64",
+ ":libseccomp_gen_syscall_nrs_x86",
+ ":libseccomp_gen_syscall_nrs_x86_64",
+ ],
+
+ out: [
+ "arm64_app_zygote_policy.cpp",
+ "arm_app_zygote_policy.cpp",
+ "mips64_app_zygote_policy.cpp",
+ "mips_app_zygote_policy.cpp",
+ "x86_64_app_zygote_policy.cpp",
+ "x86_app_zygote_policy.cpp",
+ ],
+}
+
cc_genrule {
name: "libseccomp_policy_app_sources",
recovery_available: true,
@@ -2261,8 +2394,10 @@
cc_library {
name: "libseccomp_policy",
recovery_available: true,
+ generated_headers: ["func_to_syscall_nrs"],
generated_sources: [
"libseccomp_policy_app_sources",
+ "libseccomp_policy_app_zygote_sources",
"libseccomp_policy_global_sources",
"libseccomp_policy_system_sources",
],
diff --git a/libc/SECCOMP_BLACKLIST_APP.TXT b/libc/SECCOMP_BLACKLIST_APP.TXT
index 66e24cb..b7a05c4 100644
--- a/libc/SECCOMP_BLACKLIST_APP.TXT
+++ b/libc/SECCOMP_BLACKLIST_APP.TXT
@@ -29,17 +29,21 @@
# This file is processed by a python script named genseccomp.py.
# Note: Some privileged syscalls are still needed in app process after fork before uid change,
-# including capset and setresuid.
+# including capset and setresuid. This is because the seccomp filter must be installed while
+# the process still has CAP_SYS_ADMIN; changing the uid would remove that capability.
# syscalls to modify IDs
int setgid:setgid32(gid_t) arm,x86
int setgid:setgid(gid_t) arm64,mips,mips64,x86_64
int setuid:setuid32(uid_t) arm,x86
int setuid:setuid(uid_t) arm64,mips,mips64,x86_64
+int setregid:setregid32(gid_t, gid_t) arm,x86
+int setregid:setregid(gid_t, gid_t) arm64,mips,mips64,x86_64
int setreuid:setreuid32(uid_t, uid_t) arm,x86
int setreuid:setreuid(uid_t, uid_t) arm64,mips,mips64,x86_64
int setresgid:setresgid32(gid_t, gid_t, gid_t) arm,x86
int setresgid:setresgid(gid_t, gid_t, gid_t) arm64,mips,mips64,x86_64
+# setresuid is explicitly allowed, see above.
int setfsgid(gid_t) all
int setfsuid(uid_t) all
int setgroups:setgroups32(int, const gid_t*) arm,x86
diff --git a/libc/arch-arm/bionic/vfork.S b/libc/arch-arm/bionic/vfork.S
index 8329111..0b17d64 100644
--- a/libc/arch-arm/bionic/vfork.S
+++ b/libc/arch-arm/bionic/vfork.S
@@ -27,12 +27,13 @@
*/
#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
ENTRY(vfork)
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
mrc p15, 0, r3, c13, c0, 3
- ldr r3, [r3, #4]
+ ldr r3, [r3, #(TLS_SLOT_THREAD_ID * 4)]
mov r0, #0
str r0, [r3, #12]
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 0a83cc7..6acd64b 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -27,6 +27,7 @@
*/
#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
#include <asm/signal.h>
#include <linux/sched.h>
@@ -34,7 +35,7 @@
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
mrs x0, tpidr_el0
- ldr x0, [x0, #8]
+ ldr x0, [x0, #(TLS_SLOT_THREAD_ID * 8)]
str wzr, [x0, #20]
mov x0, #(CLONE_VM | CLONE_VFORK | SIGCHLD)
diff --git a/libc/arch-x86/bionic/vfork.S b/libc/arch-x86/bionic/vfork.S
index 79d7899..24ede3d 100644
--- a/libc/arch-x86/bionic/vfork.S
+++ b/libc/arch-x86/bionic/vfork.S
@@ -27,6 +27,7 @@
*/
#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
// This custom code preserves the return address across the system call.
@@ -38,7 +39,7 @@
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
movl %gs:0, %eax
- movl 4(%eax), %eax
+ movl (TLS_SLOT_THREAD_ID * 4)(%eax), %eax
movl $0, 12(%eax)
movl $__NR_vfork, %eax
diff --git a/libc/arch-x86_64/bionic/vfork.S b/libc/arch-x86_64/bionic/vfork.S
index ce96a8c..e32b517 100644
--- a/libc/arch-x86_64/bionic/vfork.S
+++ b/libc/arch-x86_64/bionic/vfork.S
@@ -27,6 +27,7 @@
*/
#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
// This custom code preserves the return address across the system call.
@@ -36,7 +37,7 @@
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
mov %fs:0, %rax
- mov 8(%rax), %rax
+ mov (TLS_SLOT_THREAD_ID * 8)(%rax), %rax
movl $0, 20(%rax)
movl $__NR_vfork, %eax
diff --git a/libc/async_safe/Android.bp b/libc/async_safe/Android.bp
index 29f90d1..a54d3b0 100644
--- a/libc/async_safe/Android.bp
+++ b/libc/async_safe/Android.bp
@@ -12,6 +12,7 @@
recovery_available: true,
include_dirs: ["bionic/libc"],
+ header_libs: ["libc_headers"],
export_include_dirs: ["include"],
}
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 2b90c90..4984e38 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -74,6 +74,7 @@
__libc_init_sysinfo(); // uses AT_SYSINFO auxv entry
#endif
__init_tcb(temp_tcb, &main_thread);
+ __init_tcb_dtv(temp_tcb);
__set_tls(&temp_tcb->tls_slot(0));
main_thread.tid = __getpid();
main_thread.set_cached_pid(main_thread.tid);
@@ -126,6 +127,7 @@
auto new_tcb = reinterpret_cast<bionic_tcb*>(mapping.static_tls + layout.offset_bionic_tcb());
auto new_tls = reinterpret_cast<bionic_tls*>(mapping.static_tls + layout.offset_bionic_tls());
+ __init_static_tls(mapping.static_tls);
new_tcb->copy_from_bootstrap(temp_tcb);
new_tls->copy_from_bootstrap(temp_tls);
__init_tcb(new_tcb, &main_thread);
diff --git a/linker/linker_allocator.cpp b/libc/bionic/bionic_allocator.cpp
similarity index 77%
rename from linker/linker_allocator.cpp
rename to libc/bionic/bionic_allocator.cpp
index 015768a..d9302ad 100644
--- a/linker/linker_allocator.cpp
+++ b/libc/bionic/bionic_allocator.cpp
@@ -26,21 +26,25 @@
* SUCH DAMAGE.
*/
-#include "linker_allocator.h"
-#include "linker_debug.h"
-#include "linker.h"
+#include "private/bionic_allocator.h"
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
+#include <sys/param.h>
#include <sys/prctl.h>
#include <unistd.h>
+#include <new>
+
#include <async_safe/log.h>
+#include "private/bionic_macros.h"
+#include "private/bionic_page.h"
+
//
-// LinkerMemeoryAllocator is general purpose allocator
-// designed to provide the same functionality as the malloc/free/realloc
-// libc functions.
+// BionicAllocator is a general purpose allocator designed to provide the same
+// functionality as the malloc/free/realloc libc functions.
//
// On alloc:
// If size is >= 1k allocator proxies malloc call directly to mmap
@@ -90,7 +94,7 @@
return result;
}
-LinkerSmallObjectAllocator::LinkerSmallObjectAllocator(uint32_t type,
+BionicSmallObjectAllocator::BionicSmallObjectAllocator(uint32_t type,
size_t block_size)
: type_(type),
block_size_(block_size),
@@ -99,7 +103,7 @@
free_pages_cnt_(0),
page_list_(nullptr) {}
-void* LinkerSmallObjectAllocator::alloc() {
+void* BionicSmallObjectAllocator::alloc() {
CHECK(block_size_ != 0);
if (page_list_ == nullptr) {
@@ -141,7 +145,7 @@
return block_record;
}
-void LinkerSmallObjectAllocator::free_page(small_object_page_info* page) {
+void BionicSmallObjectAllocator::free_page(small_object_page_info* page) {
CHECK(page->free_blocks_cnt == blocks_per_page_);
if (page->prev_page) {
page->prev_page->next_page = page->next_page;
@@ -156,7 +160,7 @@
free_pages_cnt_--;
}
-void LinkerSmallObjectAllocator::free(void* ptr) {
+void BionicSmallObjectAllocator::free(void* ptr) {
small_object_page_info* const page =
reinterpret_cast<small_object_page_info*>(
PAGE_START(reinterpret_cast<uintptr_t>(ptr)));
@@ -186,7 +190,7 @@
}
}
-void LinkerSmallObjectAllocator::alloc_page() {
+void BionicSmallObjectAllocator::alloc_page() {
void* const map_ptr = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_ptr == MAP_FAILED) {
@@ -194,7 +198,7 @@
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, PAGE_SIZE,
- "linker_alloc_small_objects");
+ "bionic_alloc_small_objects");
small_object_page_info* const page =
reinterpret_cast<small_object_page_info*>(map_ptr);
@@ -220,7 +224,7 @@
free_pages_cnt_++;
}
-void LinkerSmallObjectAllocator::add_to_page_list(small_object_page_info* page) {
+void BionicSmallObjectAllocator::add_to_page_list(small_object_page_info* page) {
page->next_page = page_list_;
page->prev_page = nullptr;
if (page_list_) {
@@ -229,7 +233,7 @@
page_list_ = page;
}
-void LinkerSmallObjectAllocator::remove_from_page_list(
+void BionicSmallObjectAllocator::remove_from_page_list(
small_object_page_info* page) {
if (page->prev_page) {
page->prev_page->next_page = page->next_page;
@@ -244,24 +248,30 @@
page->next_page = nullptr;
}
-void LinkerMemoryAllocator::initialize_allocators() {
+void BionicAllocator::initialize_allocators() {
if (allocators_ != nullptr) {
return;
}
- LinkerSmallObjectAllocator* allocators =
- reinterpret_cast<LinkerSmallObjectAllocator*>(allocators_buf_);
+ BionicSmallObjectAllocator* allocators =
+ reinterpret_cast<BionicSmallObjectAllocator*>(allocators_buf_);
for (size_t i = 0; i < kSmallObjectAllocatorsCount; ++i) {
uint32_t type = i + kSmallObjectMinSizeLog2;
- new (allocators + i) LinkerSmallObjectAllocator(type, 1 << type);
+ new (allocators + i) BionicSmallObjectAllocator(type, 1 << type);
}
allocators_ = allocators;
}
-void* LinkerMemoryAllocator::alloc_mmap(size_t size) {
- size_t allocated_size = PAGE_END(size + kPageInfoSize);
+void* BionicAllocator::alloc_mmap(size_t align, size_t size) {
+ size_t header_size = __BIONIC_ALIGN(kPageInfoSize, align);
+ size_t allocated_size;
+ if (__builtin_add_overflow(header_size, size, &allocated_size) ||
+ PAGE_END(allocated_size) < allocated_size) {
+ async_safe_fatal("overflow trying to alloc %zu bytes", size);
+ }
+ allocated_size = PAGE_END(allocated_size);
void* map_ptr = mmap(nullptr, allocated_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
@@ -269,25 +279,21 @@
async_safe_fatal("mmap failed: %s", strerror(errno));
}
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "linker_alloc_lob");
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "bionic_alloc_lob");
- page_info* info = reinterpret_cast<page_info*>(map_ptr);
+ void* result = static_cast<char*>(map_ptr) + header_size;
+ page_info* info = get_page_info_unchecked(result);
memcpy(info->signature, kSignature, sizeof(kSignature));
info->type = kLargeObject;
info->allocated_size = allocated_size;
- return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(info) +
- kPageInfoSize);
+ return result;
}
-void* LinkerMemoryAllocator::alloc(size_t size) {
- // treat alloc(0) as alloc(1)
- if (size == 0) {
- size = 1;
- }
+inline void* BionicAllocator::alloc_impl(size_t align, size_t size) {
if (size > kSmallObjectMaxSize) {
- return alloc_mmap(size);
+ return alloc_mmap(align, size);
}
uint16_t log2_size = log2(size);
@@ -299,8 +305,33 @@
return get_small_object_allocator(log2_size)->alloc();
}
-page_info* LinkerMemoryAllocator::get_page_info(void* ptr) {
- page_info* info = reinterpret_cast<page_info*>(PAGE_START(reinterpret_cast<size_t>(ptr)));
+void* BionicAllocator::alloc(size_t size) {
+ // treat alloc(0) as alloc(1)
+ if (size == 0) {
+ size = 1;
+ }
+ return alloc_impl(16, size);
+}
+
+void* BionicAllocator::memalign(size_t align, size_t size) {
+ // The Bionic allocator only supports alignment up to one page, which is good
+ // enough for ELF TLS.
+ align = MIN(align, PAGE_SIZE);
+ align = MAX(align, 16);
+ if (!powerof2(align)) {
+ align = BIONIC_ROUND_UP_POWER_OF_2(align);
+ }
+ size = MAX(size, align);
+ return alloc_impl(align, size);
+}
+
+inline page_info* BionicAllocator::get_page_info_unchecked(void* ptr) {
+ uintptr_t header_page = PAGE_START(reinterpret_cast<size_t>(ptr) - kPageInfoSize);
+ return reinterpret_cast<page_info*>(header_page);
+}
+
+inline page_info* BionicAllocator::get_page_info(void* ptr) {
+ page_info* info = get_page_info_unchecked(ptr);
if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) {
async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
}
@@ -308,7 +339,7 @@
return info;
}
-void* LinkerMemoryAllocator::realloc(void* ptr, size_t size) {
+void* BionicAllocator::realloc(void* ptr, size_t size) {
if (ptr == nullptr) {
return alloc(size);
}
@@ -323,9 +354,9 @@
size_t old_size = 0;
if (info->type == kLargeObject) {
- old_size = info->allocated_size - kPageInfoSize;
+ old_size = info->allocated_size - (static_cast<char*>(ptr) - reinterpret_cast<char*>(info));
} else {
- LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
+ BionicSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
if (allocator != info->allocator_addr) {
async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
}
@@ -343,7 +374,7 @@
return ptr;
}
-void LinkerMemoryAllocator::free(void* ptr) {
+void BionicAllocator::free(void* ptr) {
if (ptr == nullptr) {
return;
}
@@ -353,7 +384,7 @@
if (info->type == kLargeObject) {
munmap(info, info->allocated_size);
} else {
- LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
+ BionicSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
if (allocator != info->allocator_addr) {
async_safe_fatal("invalid pointer %p (invalid allocator address for the page)", ptr);
}
@@ -362,7 +393,7 @@
}
}
-LinkerSmallObjectAllocator* LinkerMemoryAllocator::get_small_object_allocator(uint32_t type) {
+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 55c2c31..3fa5182 100644
--- a/libc/bionic/bionic_elf_tls.cpp
+++ b/libc/bionic/bionic_elf_tls.cpp
@@ -28,13 +28,117 @@
#include "private/bionic_elf_tls.h"
+#include <async_safe/log.h>
+#include <string.h>
#include <sys/param.h>
+#include <unistd.h>
+#include "private/ScopedRWLock.h"
+#include "private/ScopedSignalBlocker.h"
+#include "private/bionic_globals.h"
#include "private/bionic_macros.h"
#include "private/bionic_tls.h"
+#include "pthread_internal.h"
-void StaticTlsLayout::reserve_tcb() {
- offset_bionic_tcb_ = reserve_type<bionic_tcb>();
+// Every call to __tls_get_addr needs to check the generation counter, so
+// accesses to the counter need to be as fast as possible. Keep a copy of it in
+// a hidden variable, which can be accessed without using the GOT. The linker
+// will update this variable when it updates its counter.
+//
+// To allow the linker to update this variable, libc.so's constructor passes its
+// address to the linker. To accommodate a possible __tls_get_addr call before
+// libc.so's constructor, this local copy is initialized to SIZE_MAX, forcing
+// __tls_get_addr to initially use the slow path.
+__LIBC_HIDDEN__ _Atomic(size_t) __libc_tls_generation_copy = SIZE_MAX;
+
+// Search for a TLS segment in the given phdr table. Returns true if it has a
+// TLS segment and false otherwise.
+bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+ ElfW(Addr) load_bias, TlsSegment* out) {
+ for (size_t i = 0; i < phdr_count; ++i) {
+ const ElfW(Phdr)& phdr = phdr_table[i];
+ if (phdr.p_type == PT_TLS) {
+ *out = TlsSegment {
+ phdr.p_memsz,
+ phdr.p_align,
+ reinterpret_cast<void*>(load_bias + phdr.p_vaddr),
+ phdr.p_filesz,
+ };
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if the alignment of a TLS segment is a valid power-of-two. Also
+// cap the alignment if it's too high.
+bool __bionic_check_tls_alignment(size_t* alignment) {
+ // N.B. The size does not need to be a multiple of the alignment. With
+ // ld.bfd (or after using binutils' strip), the TLS segment's size isn't
+ // rounded up.
+ if (*alignment == 0 || !powerof2(*alignment)) {
+ return false;
+ }
+ // Bionic only respects TLS alignment up to one page.
+ *alignment = MIN(*alignment, PAGE_SIZE);
+ return true;
+}
+
+size_t StaticTlsLayout::offset_thread_pointer() const {
+ return offset_bionic_tcb_ + (-MIN_TLS_SLOT * sizeof(void*));
+}
+
+// Reserves space for the Bionic TCB and the executable's TLS segment. Returns
+// the offset of the executable's TLS segment.
+size_t StaticTlsLayout::reserve_exe_segment_and_tcb(const TlsSegment* exe_segment,
+ const char* progname __attribute__((unused))) {
+ // Special case: if the executable has no TLS segment, then just allocate a
+ // TCB and skip the minimum alignment check on ARM.
+ if (exe_segment == nullptr) {
+ offset_bionic_tcb_ = reserve_type<bionic_tcb>();
+ return 0;
+ }
+
+#if defined(__arm__) || defined(__aarch64__)
+
+ // First reserve enough space for the TCB before the executable segment.
+ reserve(sizeof(bionic_tcb), 1);
+
+ // Then reserve the segment itself.
+ const size_t result = reserve(exe_segment->size, exe_segment->alignment);
+
+ // The variant 1 ABI that ARM linkers follow specifies a 2-word TCB between
+ // the thread pointer and the start of the executable's TLS segment, but both
+ // the thread pointer and the TLS segment are aligned appropriately for the
+ // TLS segment. Calculate the distance between the thread pointer and the
+ // EXE's segment.
+ const size_t exe_tpoff = __BIONIC_ALIGN(sizeof(void*) * 2, exe_segment->alignment);
+
+ const size_t min_bionic_alignment = BIONIC_ROUND_UP_POWER_OF_2(MAX_TLS_SLOT) * sizeof(void*);
+ if (exe_tpoff < min_bionic_alignment) {
+ async_safe_fatal("error: \"%s\": executable's TLS segment is underaligned: "
+ "alignment is %zu, needs to be at least %zu for %s Bionic",
+ progname, exe_segment->alignment, min_bionic_alignment,
+ (sizeof(void*) == 4 ? "ARM" : "ARM64"));
+ }
+
+ offset_bionic_tcb_ = result - exe_tpoff - (-MIN_TLS_SLOT * sizeof(void*));
+ return result;
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+ // x86 uses variant 2 TLS layout. The executable's segment is located just
+ // before the TCB.
+ static_assert(MIN_TLS_SLOT == 0, "First slot of bionic_tcb must be slot #0 on x86");
+ const size_t exe_size = round_up_with_overflow_check(exe_segment->size, exe_segment->alignment);
+ reserve(exe_size, 1);
+ const size_t max_align = MAX(alignof(bionic_tcb), exe_segment->alignment);
+ offset_bionic_tcb_ = reserve(sizeof(bionic_tcb), max_align);
+ return offset_bionic_tcb_ - exe_size;
+
+#else
+#error "Unrecognized architecture"
+#endif
}
void StaticTlsLayout::reserve_bionic_tls() {
@@ -44,6 +148,10 @@
void StaticTlsLayout::finish_layout() {
// Round the offset up to the alignment.
offset_ = round_up_with_overflow_check(offset_, alignment_);
+
+ if (overflowed_) {
+ async_safe_fatal("error: TLS segments in static TLS overflowed");
+ }
}
// The size is not required to be a multiple of the alignment. The alignment
@@ -62,3 +170,197 @@
if (value < old_value) overflowed_ = true;
return value;
}
+
+// Copy each TLS module's initialization image into a newly-allocated block of
+// static TLS memory. To reduce dirty pages, this function only writes to pages
+// within the static TLS that need initialization. The memory should already be
+// zero-initialized on entry.
+void __init_static_tls(void* static_tls) {
+ // The part of the table we care about (i.e. static TLS modules) never changes
+ // after startup, but we still need the mutex because the table could grow,
+ // moving the initial part. If this locking is too slow, we can duplicate the
+ // static part of the table.
+ TlsModules& modules = __libc_shared_globals()->tls_modules;
+ ScopedSignalBlocker ssb;
+ ScopedReadLock locker(&modules.rwlock);
+
+ for (size_t i = 0; i < modules.module_count; ++i) {
+ TlsModule& module = modules.module_table[i];
+ if (module.static_offset == SIZE_MAX) {
+ // All of the static modules come before all of the dynamic modules, so
+ // once we see the first dynamic module, we're done.
+ break;
+ }
+ if (module.segment.init_size == 0) {
+ // Skip the memcpy call for TLS segments with no initializer, which is
+ // common.
+ continue;
+ }
+ memcpy(static_cast<char*>(static_tls) + module.static_offset,
+ module.segment.init_ptr,
+ module.segment.init_size);
+ }
+}
+
+static inline size_t dtv_size_in_bytes(size_t module_count) {
+ return sizeof(TlsDtv) + module_count * sizeof(void*);
+}
+
+// Calculates the number of module slots to allocate in a new DTV. For small
+// objects (up to 1KiB), the TLS allocator allocates memory in power-of-2 sizes,
+// so for better space usage, ensure that the DTV size (header + slots) is a
+// power of 2.
+//
+// The lock on TlsModules must be held.
+static size_t calculate_new_dtv_count() {
+ size_t loaded_cnt = __libc_shared_globals()->tls_modules.module_count;
+ size_t bytes = dtv_size_in_bytes(MAX(1, loaded_cnt));
+ if (!powerof2(bytes)) {
+ bytes = BIONIC_ROUND_UP_POWER_OF_2(bytes);
+ }
+ return (bytes - sizeof(TlsDtv)) / sizeof(void*);
+}
+
+// This function must be called with signals blocked and a write lock on
+// TlsModules held.
+static void update_tls_dtv(bionic_tcb* tcb) {
+ const TlsModules& modules = __libc_shared_globals()->tls_modules;
+ BionicAllocator& allocator = __libc_shared_globals()->tls_allocator;
+
+ // Use the generation counter from the shared globals instead of the local
+ // copy, which won't be initialized yet if __tls_get_addr is called before
+ // libc.so's constructor.
+ if (__get_tcb_dtv(tcb)->generation == atomic_load(&modules.generation)) {
+ return;
+ }
+
+ const size_t old_cnt = __get_tcb_dtv(tcb)->count;
+
+ // If the DTV isn't large enough, allocate a larger one. Because a signal
+ // handler could interrupt the fast path of __tls_get_addr, we don't free the
+ // old DTV. Instead, we add the old DTV to a list, then free all of a thread's
+ // DTVs at thread-exit. Each time the DTV is reallocated, its size at least
+ // doubles.
+ if (modules.module_count > old_cnt) {
+ size_t new_cnt = calculate_new_dtv_count();
+ TlsDtv* const old_dtv = __get_tcb_dtv(tcb);
+ TlsDtv* const new_dtv = static_cast<TlsDtv*>(allocator.alloc(dtv_size_in_bytes(new_cnt)));
+ memcpy(new_dtv, old_dtv, dtv_size_in_bytes(old_cnt));
+ new_dtv->count = new_cnt;
+ new_dtv->next = old_dtv;
+ __set_tcb_dtv(tcb, new_dtv);
+ }
+
+ TlsDtv* const dtv = __get_tcb_dtv(tcb);
+
+ const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+ char* static_tls = reinterpret_cast<char*>(tcb) - layout.offset_bionic_tcb();
+
+ // Initialize static TLS modules and free unloaded modules.
+ for (size_t i = 0; i < dtv->count; ++i) {
+ if (i < modules.module_count) {
+ const TlsModule& mod = modules.module_table[i];
+ if (mod.static_offset != SIZE_MAX) {
+ dtv->modules[i] = static_tls + mod.static_offset;
+ continue;
+ }
+ if (mod.first_generation != kTlsGenerationNone &&
+ mod.first_generation <= dtv->generation) {
+ continue;
+ }
+ }
+ allocator.free(dtv->modules[i]);
+ dtv->modules[i] = nullptr;
+ }
+
+ dtv->generation = atomic_load(&modules.generation);
+}
+
+__attribute__((noinline)) static void* tls_get_addr_slow_path(const TlsIndex* ti) {
+ TlsModules& modules = __libc_shared_globals()->tls_modules;
+ bionic_tcb* tcb = __get_bionic_tcb();
+
+ // Block signals and lock TlsModules. We may need the allocator, so take
+ // a write lock.
+ ScopedSignalBlocker ssb;
+ ScopedWriteLock locker(&modules.rwlock);
+
+ update_tls_dtv(tcb);
+
+ TlsDtv* dtv = __get_tcb_dtv(tcb);
+ const size_t module_idx = __tls_module_id_to_idx(ti->module_id);
+ void* mod_ptr = dtv->modules[module_idx];
+ if (mod_ptr == nullptr) {
+ const TlsSegment& segment = modules.module_table[module_idx].segment;
+ mod_ptr = __libc_shared_globals()->tls_allocator.memalign(segment.alignment, segment.size);
+ if (segment.init_size > 0) {
+ memcpy(mod_ptr, segment.init_ptr, segment.init_size);
+ }
+ dtv->modules[module_idx] = mod_ptr;
+ }
+
+ return static_cast<char*>(mod_ptr) + ti->offset;
+}
+
+// Returns the address of a thread's TLS memory given a module ID and an offset
+// into that module's TLS segment. This function is called on every access to a
+// dynamic TLS variable on targets that don't use TLSDESC. arm64 uses TLSDESC,
+// so it only calls this function on a thread's first access to a module's TLS
+// segment.
+//
+// On most targets, this accessor function is __tls_get_addr and
+// TLS_GET_ADDR_CCONV is unset. 32-bit x86 uses ___tls_get_addr instead and a
+// regparm() calling convention.
+extern "C" void* TLS_GET_ADDR(const TlsIndex* ti) TLS_GET_ADDR_CCONV {
+ TlsDtv* dtv = __get_tcb_dtv(__get_bionic_tcb());
+
+ // TODO: See if we can use a relaxed memory ordering here instead.
+ size_t generation = atomic_load(&__libc_tls_generation_copy);
+ if (__predict_true(generation == dtv->generation)) {
+ void* mod_ptr = dtv->modules[__tls_module_id_to_idx(ti->module_id)];
+ if (__predict_true(mod_ptr != nullptr)) {
+ return static_cast<char*>(mod_ptr) + ti->offset;
+ }
+ }
+
+ return tls_get_addr_slow_path(ti);
+}
+
+// This function frees:
+// - TLS modules referenced by the current DTV.
+// - The list of DTV objects associated with the current thread.
+//
+// The caller must have already blocked signals.
+void __free_dynamic_tls(bionic_tcb* tcb) {
+ TlsModules& modules = __libc_shared_globals()->tls_modules;
+ BionicAllocator& allocator = __libc_shared_globals()->tls_allocator;
+
+ // If we didn't allocate any dynamic memory, skip out early without taking
+ // the lock.
+ TlsDtv* dtv = __get_tcb_dtv(tcb);
+ if (dtv->generation == kTlsGenerationNone) {
+ return;
+ }
+
+ // We need the write lock to use the allocator.
+ ScopedWriteLock locker(&modules.rwlock);
+
+ // First free everything in the current DTV.
+ for (size_t i = 0; i < dtv->count; ++i) {
+ if (i < modules.module_count && modules.module_table[i].static_offset != SIZE_MAX) {
+ // This module's TLS memory is allocated statically, so don't free it here.
+ continue;
+ }
+ allocator.free(dtv->modules[i]);
+ }
+
+ // Now free the thread's list of DTVs.
+ while (dtv->generation != kTlsGenerationNone) {
+ TlsDtv* next = dtv->next;
+ allocator.free(dtv);
+ dtv = next;
+ }
+
+ // Clear the DTV slot. The DTV must not be used again with this thread.
+ tcb->tls_slot(TLS_SLOT_DTV) = nullptr;
+}
diff --git a/libc/bionic/bionic_systrace.cpp b/libc/bionic/bionic_systrace.cpp
index bac3d88..6182ed8 100644
--- a/libc/bionic/bionic_systrace.cpp
+++ b/libc/bionic/bionic_systrace.cpp
@@ -82,7 +82,7 @@
return;
}
- TEMP_FAILURE_RETRY(write(trace_marker_fd, "E", 1));
+ TEMP_FAILURE_RETRY(write(trace_marker_fd, "E|", 2));
}
ScopedTrace::ScopedTrace(const char* message) : called_end_(false) {
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index af1b847..7140776 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -51,6 +51,7 @@
#include <elf.h>
#include "libc_init_common.h"
+#include "private/bionic_elf_tls.h"
#include "private/bionic_globals.h"
#include "private/bionic_macros.h"
#include "private/bionic_ssp.h"
@@ -82,6 +83,12 @@
__libc_init_sysinfo();
#endif
+ // Register libc.so's copy of the TLS generation variable so the linker can
+ // update it when it loads or unloads a shared object.
+ TlsModules& tls_modules = __libc_shared_globals()->tls_modules;
+ tls_modules.generation_libc_so = &__libc_tls_generation_copy;
+ __libc_tls_generation_copy = tls_modules.generation;
+
__libc_init_globals();
__libc_init_common();
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 68650ed..514423d 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -83,10 +83,32 @@
}
}
-static void layout_static_tls() {
+static void layout_static_tls(KernelArgumentBlock& args) {
StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
layout.reserve_bionic_tls();
- layout.reserve_tcb();
+
+ const char* progname = args.argv[0];
+ ElfW(Phdr)* phdr_start = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR));
+ size_t phdr_ct = getauxval(AT_PHNUM);
+
+ static TlsModule mod;
+ TlsModules& modules = __libc_shared_globals()->tls_modules;
+ if (__bionic_get_tls_segment(phdr_start, phdr_ct, 0, &mod.segment)) {
+ if (!__bionic_check_tls_alignment(&mod.segment.alignment)) {
+ async_safe_fatal("error: TLS segment alignment in \"%s\" is not a power of 2: %zu\n",
+ progname, mod.segment.alignment);
+ }
+ mod.static_offset = layout.reserve_exe_segment_and_tcb(&mod.segment, progname);
+ mod.first_generation = kTlsGenerationFirst;
+
+ modules.module_count = 1;
+ modules.module_table = &mod;
+ } else {
+ layout.reserve_exe_segment_and_tcb(nullptr, progname);
+ }
+ // Enable the fast path in __tls_get_addr.
+ __libc_tls_generation_copy = modules.generation;
+
layout.finish_layout();
}
@@ -111,7 +133,7 @@
__libc_init_globals();
__libc_shared_globals()->init_progname = args.argv[0];
__libc_init_AT_SECURE(args.envp);
- layout_static_tls();
+ layout_static_tls(args);
__libc_init_main_thread_final();
__libc_init_common();
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index d530fa4..fc65e15 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -47,6 +47,7 @@
#include <private/bionic_defs.h>
#include <private/bionic_config.h>
#include <private/bionic_globals.h>
+#include <private/bionic_malloc.h>
#include <private/bionic_malloc_dispatch.h>
#if __has_feature(hwaddress_sanitizer)
@@ -304,6 +305,13 @@
async_safe_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
// =============================================================================
+// In a Zygote child process, this is set to true if profiling of this process
+// is allowed. Note that this set at a later time than the above
+// gMallocLeakZygoteChild. The latter is set during the fork (while still in
+// zygote's SELinux domain). While this bit is set after the child is
+// specialized (and has transferred SELinux domains if applicable).
+static _Atomic bool gMallocZygoteChildProfileable = false;
+
// =============================================================================
// Exported for use by ddms.
// =============================================================================
@@ -554,13 +562,7 @@
}
}
-static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
- void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
- if (impl_handle == nullptr) {
- error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
- return nullptr;
- }
-
+static bool InitSharedLibrary(void* impl_handle, const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
static constexpr const char* names[] = {
"initialize",
"finalize",
@@ -575,48 +577,61 @@
g_functions[i] = dlsym(impl_handle, symbol);
if (g_functions[i] == nullptr) {
error_log("%s: %s routine not found in %s", getprogname(), symbol, shared_lib);
- dlclose(impl_handle);
ClearGlobalFunctions();
- return nullptr;
+ return false;
}
}
if (!InitMallocFunctions(impl_handle, dispatch_table, prefix)) {
- dlclose(impl_handle);
ClearGlobalFunctions();
+ return false;
+ }
+ return true;
+}
+
+static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
+ void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
+ if (impl_handle == nullptr) {
+ error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
return nullptr;
}
+ if (!InitSharedLibrary(impl_handle, shared_lib, prefix, dispatch_table)) {
+ dlclose(impl_handle);
+ impl_handle = nullptr;
+ }
+
return impl_handle;
}
// A function pointer to heapprofds init function. Used to re-initialize
// heapprofd. This will start a new profiling session and tear down the old
// one in case it is still active.
-static _Atomic init_func_t g_heapprofd_init_func = nullptr;
+static _Atomic (void*) g_heapprofd_handle = nullptr;
static void install_hooks(libc_globals* globals, const char* options,
const char* prefix, const char* shared_lib) {
- init_func_t init_func = atomic_load(&g_heapprofd_init_func);
- if (init_func != nullptr) {
- init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options);
- info_log("%s: malloc %s re-enabled", getprogname(), prefix);
- return;
- }
-
MallocDispatch dispatch_table;
- void* impl_handle = LoadSharedLibrary(shared_lib, prefix, &dispatch_table);
- if (impl_handle == nullptr) {
- return;
+
+ void* impl_handle = atomic_load(&g_heapprofd_handle);
+ if (impl_handle != nullptr) {
+ if (!InitSharedLibrary(impl_handle, shared_lib, prefix, &dispatch_table)) {
+ return;
+ }
+ } else {
+ impl_handle = LoadSharedLibrary(shared_lib, prefix, &dispatch_table);
+ if (impl_handle == nullptr) {
+ return;
+ }
}
- init_func = reinterpret_cast<init_func_t>(g_functions[FUNC_INITIALIZE]);
+ init_func_t init_func = reinterpret_cast<init_func_t>(g_functions[FUNC_INITIALIZE]);
if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) {
+ error_log("%s: failed to enable malloc %s", getprogname(), prefix);
dlclose(impl_handle);
ClearGlobalFunctions();
return;
}
- atomic_store(&g_heapprofd_init_func, init_func);
// We assign free first explicitly to prevent the case where we observe a
// alloc, but miss the corresponding free because of initialization order.
//
@@ -628,6 +643,7 @@
// _Atomic. Assigning to an _Atomic is an atomic_store operation.
// The assignment is done in declaration order.
globals->malloc_dispatch = dispatch_table;
+ atomic_store(&g_heapprofd_handle, impl_handle);
info_log("%s: malloc %s enabled", getprogname(), prefix);
@@ -641,12 +657,36 @@
}
}
-extern "C" void InstallInitHeapprofdHook(int);
+// The logic for triggering heapprofd (at runtime) is as follows:
+// 1. HEAPPROFD_SIGNAL is received by the process, entering the
+// MaybeInstallInitHeapprofdHook signal handler.
+// 2. If the initialization is not already in flight
+// (g_heapprofd_init_in_progress is false), the malloc hook is set to
+// point at InitHeapprofdHook, and g_heapprofd_init_in_progress is set to
+// true.
+// 3. The next malloc call enters InitHeapprofdHook, which removes the malloc
+// hook, and spawns a detached pthread to run the InitHeapprofd task.
+// (g_heapprofd_init_hook_installed atomic is used to perform this once.)
+// 4. InitHeapprofd, on a dedicated pthread, loads the heapprofd client library,
+// installs the full set of heapprofd hooks, and invokes the client's
+// initializer. The dedicated pthread then terminates.
+// 5. g_heapprofd_init_in_progress and g_heapprofd_init_hook_installed are
+// reset to false such that heapprofd can be reinitialized. Reinitialization
+// means that a new profiling session is started, and any still active is
+// torn down.
+//
+// The incremental hooking and a dedicated task thread are used since we cannot
+// do heavy work within a signal handler, or when blocking a malloc invocation.
+
+static _Atomic bool g_heapprofd_init_in_progress = false;
+static _Atomic bool g_heapprofd_init_hook_installed = false;
+
+extern "C" void MaybeInstallInitHeapprofdHook(int);
// Initializes memory allocation framework once per process.
static void malloc_init_impl(libc_globals* globals) {
struct sigaction action = {};
- action.sa_handler = InstallInitHeapprofdHook;
+ action.sa_handler = MaybeInstallInitHeapprofdHook;
sigaction(HEAPPROFD_SIGNAL, &action, nullptr);
const char* prefix;
@@ -667,7 +707,10 @@
} else {
return;
}
- install_hooks(globals, options, prefix, shared_lib);
+ if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
+ install_hooks(globals, options, prefix, shared_lib);
+ atomic_store(&g_heapprofd_init_in_progress, false);
+ }
}
// Initializes memory allocation framework.
@@ -677,33 +720,6 @@
malloc_init_impl(globals);
}
-// The logic for triggering heapprofd below is as following.
-// 1. HEAPPROFD_SIGNAL is received by the process.
-// 2. If neither InitHeapprofd nor InitHeapprofdHook are currently installed
-// (g_heapprofd_init_hook_installed is false), InitHeapprofdHook is
-// installed and g_heapprofd_init_in_progress is set to true.
-//
-// On the next subsequent malloc, InitHeapprofdHook is called and
-// 3a. If the signal is currently being handled (g_heapprofd_init_in_progress
-// is true), no action is taken.
-// 3b. Otherwise, The signal handler (InstallInitHeapprofdHook) installs a
-// temporary malloc hook (InitHeapprofdHook).
-// 4. When this hook gets run the first time, it uninstalls itself and spawns
-// a thread running InitHeapprofd that loads heapprofd.so and installs the
-// hooks within.
-// 5. g_heapprofd_init_in_progress and g_heapprofd_init_hook_installed are
-// reset to false so heapprofd can be reinitialized. Reinitialization
-// means that a new profiling session is started and any still active is
-// torn down.
-//
-// This roundabout way is needed because we are running non AS-safe code, so
-// we cannot run it directly in the signal handler. The other approach of
-// running a standby thread and signalling through write(2) and read(2) would
-// significantly increase the number of active threads in the system.
-
-static _Atomic bool g_heapprofd_init_in_progress = false;
-static _Atomic bool g_heapprofd_init_hook_installed = false;
-
static void* InitHeapprofd(void*) {
__libc_globals.mutate([](libc_globals* globals) {
install_hooks(globals, nullptr, HEAPPROFD_PREFIX, HEAPPROFD_SHARED_LIB);
@@ -732,7 +748,13 @@
return Malloc(malloc)(bytes);
}
-extern "C" void InstallInitHeapprofdHook(int) {
+extern "C" void MaybeInstallInitHeapprofdHook(int) {
+ // Zygote child processes must be marked profileable.
+ if (gMallocLeakZygoteChild &&
+ !atomic_load_explicit_const(&gMallocZygoteChildProfileable, memory_order_acquire)) {
+ return;
+ }
+
if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
__libc_globals.mutate([](libc_globals* globals) {
atomic_store(&globals->malloc_dispatch.malloc, InitHeapprofdHook);
@@ -743,6 +765,69 @@
#endif // !LIBC_STATIC
// =============================================================================
+// Platform-internal mallopt variant.
+// =============================================================================
+
+#if !defined(LIBC_STATIC)
+bool MallocDispatchReset() {
+ if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
+ __libc_globals.mutate([](libc_globals* globals) {
+ globals->malloc_dispatch = __libc_malloc_default_dispatch;
+ });
+ atomic_store(&g_heapprofd_init_in_progress, false);
+ return true;
+ }
+ errno = EAGAIN;
+ return false;
+}
+
+// Marks this process as a profileable zygote child.
+bool HandleInitZygoteChildProfiling() {
+ atomic_store_explicit(&gMallocZygoteChildProfileable, true,
+ memory_order_release);
+
+ // Conditionally start "from startup" profiling.
+ if (CheckLoadHeapprofd()) {
+ // Directly call the signal handler (will correctly guard against
+ // concurrent signal delivery).
+ MaybeInstallInitHeapprofdHook(HEAPPROFD_SIGNAL);
+ }
+ return true;
+}
+
+#else
+
+bool MallocDispatchReset() {
+ return true;
+}
+
+bool HandleInitZygoteChildProfiling() {
+ return true;
+}
+
+#endif // !defined(LIBC_STATIC)
+
+bool android_mallopt(int opcode, void* arg, size_t arg_size) {
+ if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
+ if (arg != nullptr || arg_size != 0) {
+ errno = EINVAL;
+ return false;
+ }
+ return HandleInitZygoteChildProfiling();
+ }
+ if (opcode == M_RESET_HOOKS) {
+ if (arg != nullptr || arg_size != 0) {
+ errno = EINVAL;
+ return false;
+ }
+ return MallocDispatchReset();
+ }
+
+ errno = ENOTSUP;
+ return false;
+}
+
+// =============================================================================
// Exported for use by libmemunreachable.
// =============================================================================
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index dbacf18..2c3299f 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -355,9 +355,14 @@
return malloc(size);
}
+} // extern "C"
+
#define __get_thread __real_get_thread
#include "pthread_internal.h"
#undef __get_thread
+
+extern "C" {
+
// Various third-party apps contain a backport of our pthread_rwlock implementation that uses this.
pthread_internal_t* __get_thread() {
return __real_get_thread();
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 8676a45..b8784b8 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -44,6 +44,7 @@
#include "private/bionic_globals.h"
#include "private/bionic_macros.h"
#include "private/bionic_ssp.h"
+#include "private/bionic_systrace.h"
#include "private/bionic_tls.h"
#include "private/ErrnoRestorer.h"
@@ -70,6 +71,14 @@
tcb->tls_slot(TLS_SLOT_STACK_GUARD) = reinterpret_cast<void*>(__stack_chk_guard);
}
+__attribute__((no_stack_protector))
+void __init_tcb_dtv(bionic_tcb* tcb) {
+ // Initialize the DTV slot to a statically-allocated empty DTV. The first
+ // access to a dynamic TLS variable allocates a new DTV.
+ static const TlsDtv zero_dtv = {};
+ __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
+}
+
void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls) {
tcb->thread()->bionic_tls = tls;
tcb->tls_slot(TLS_SLOT_BIONIC_TLS) = tls;
@@ -288,8 +297,10 @@
auto tcb = reinterpret_cast<bionic_tcb*>(mapping.static_tls + layout.offset_bionic_tcb());
auto tls = reinterpret_cast<bionic_tls*>(mapping.static_tls + layout.offset_bionic_tls());
- // (Re)initialize TLS pointers.
+ // Initialize TLS memory.
+ __init_static_tls(mapping.static_tls);
__init_tcb(tcb, thread);
+ __init_tcb_dtv(tcb);
__init_tcb_stack_guard(tcb);
__init_bionic_tls_ptrs(tcb, tls);
@@ -337,6 +348,7 @@
ErrnoRestorer errno_restorer;
pthread_attr_t thread_attr;
+ ScopedTrace trace("pthread_create");
if (attr == nullptr) {
pthread_attr_init(&thread_attr);
} else {
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index 84ea2e6..3b873b3 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -98,15 +98,22 @@
thread->alternate_signal_stack = nullptr;
}
+ ThreadJoinState old_state = THREAD_NOT_JOINED;
+ while (old_state == THREAD_NOT_JOINED &&
+ !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
+ }
+
+ // We don't want to take a signal after unmapping the stack, the shadow call
+ // stack, or dynamic TLS memory.
+ ScopedSignalBlocker ssb;
+
#ifdef __aarch64__
// Free the shadow call stack and guard pages.
munmap(thread->shadow_call_stack_guard_region, SCS_GUARD_REGION_SIZE);
#endif
- ThreadJoinState old_state = THREAD_NOT_JOINED;
- while (old_state == THREAD_NOT_JOINED &&
- !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
- }
+ // Free the ELF TLS DTV and all dynamically-allocated ELF TLS memory.
+ __free_dynamic_tls(__get_bionic_tcb());
if (old_state == THREAD_DETACHED) {
// The thread is detached, no one will use pthread_internal_t after pthread_exit.
@@ -121,10 +128,6 @@
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.
-
- // We don't want to take a signal after we've unmapped the stack.
- // That's one last thing we can do before dropping to assembler.
- ScopedSignalBlocker ssb;
__hwasan_thread_exit();
_exit_with_stack_teardown(thread->mmap_base, thread->mmap_size);
}
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 870a526..46fa630 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -35,30 +35,13 @@
#include <async_safe/log.h>
+#include "private/ScopedRWLock.h"
#include "private/bionic_futex.h"
#include "private/bionic_tls.h"
static pthread_internal_t* g_thread_list = nullptr;
static pthread_rwlock_t g_thread_list_lock = PTHREAD_RWLOCK_INITIALIZER;
-template <bool write> class ScopedRWLock {
- public:
- explicit ScopedRWLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
- (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(rwlock_);
- }
-
- ~ScopedRWLock() {
- pthread_rwlock_unlock(rwlock_);
- }
-
- private:
- pthread_rwlock_t* rwlock_;
- BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
-};
-
-typedef ScopedRWLock<true> ScopedWriteLock;
-typedef ScopedRWLock<false> ScopedReadLock;
-
pthread_t __pthread_internal_add(pthread_internal_t* thread) {
ScopedWriteLock locker(&g_thread_list_lock);
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 27ab3df..cbcdadf 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -38,6 +38,7 @@
#define __hwasan_thread_exit()
#endif
+#include "private/bionic_elf_tls.h"
#include "private/bionic_lock.h"
#include "private/bionic_tls.h"
@@ -154,6 +155,7 @@
__LIBC_HIDDEN__ void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread);
__LIBC_HIDDEN__ void __init_tcb_stack_guard(bionic_tcb* tcb);
+__LIBC_HIDDEN__ void __init_tcb_dtv(bionic_tcb* tcb);
__LIBC_HIDDEN__ void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls);
__LIBC_HIDDEN__ bionic_tls* __allocate_temp_bionic_tls();
__LIBC_HIDDEN__ void __free_temp_bionic_tls(bionic_tls* tls);
@@ -179,6 +181,15 @@
return *static_cast<bionic_tls*>(__get_tls()[TLS_SLOT_BIONIC_TLS]);
}
+static inline __always_inline TlsDtv* __get_tcb_dtv(bionic_tcb* tcb) {
+ uintptr_t dtv_slot = reinterpret_cast<uintptr_t>(tcb->tls_slot(TLS_SLOT_DTV));
+ return reinterpret_cast<TlsDtv*>(dtv_slot - offsetof(TlsDtv, generation));
+}
+
+static inline void __set_tcb_dtv(bionic_tcb* tcb, TlsDtv* val) {
+ tcb->tls_slot(TLS_SLOT_DTV) = &val->generation;
+}
+
extern "C" __LIBC_HIDDEN__ int __set_tls(void* ptr);
__LIBC_HIDDEN__ void pthread_key_clean_all(void);
diff --git a/libc/bionic/pthread_join.cpp b/libc/bionic/pthread_join.cpp
index 9aad458..8e4ca59 100644
--- a/libc/bionic/pthread_join.cpp
+++ b/libc/bionic/pthread_join.cpp
@@ -30,10 +30,12 @@
#include "private/bionic_defs.h"
#include "private/bionic_futex.h"
+#include "private/bionic_systrace.h"
#include "pthread_internal.h"
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int pthread_join(pthread_t t, void** return_value) {
+ ScopedTrace trace("pthread_join");
if (t == pthread_self()) {
return EDEADLK;
}
diff --git a/libc/bionic/sigaction.cpp b/libc/bionic/sigaction.cpp
index 42dcccd..96e6f3c 100644
--- a/libc/bionic/sigaction.cpp
+++ b/libc/bionic/sigaction.cpp
@@ -43,7 +43,8 @@
if (bionic_new_action != nullptr) {
kernel_new_action.sa_flags = bionic_new_action->sa_flags;
kernel_new_action.sa_handler = bionic_new_action->sa_handler;
- kernel_new_action.sa_mask = filter_reserved_signals(bionic_new_action->sa_mask, SIG_SETMASK);
+ // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
+ kernel_new_action.sa_mask = bionic_new_action->sa_mask;
#if defined(SA_RESTORER)
kernel_new_action.sa_restorer = bionic_new_action->sa_restorer;
#if defined(__aarch64__)
@@ -95,6 +96,7 @@
#if defined(SA_RESTORER)
kernel_new.sa_restorer = bionic_new->sa_restorer;
#endif
+ // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
memcpy(&kernel_new.sa_mask, &bionic_new->sa_mask, sizeof(bionic_new->sa_mask));
}
@@ -122,7 +124,8 @@
kernel_new.sa_restorer = (kernel_new.sa_flags & SA_SIGINFO) ? &__restore_rt : &__restore;
}
#endif
- kernel_new.sa_mask = filter_reserved_signals(kernel_new.sa_mask, SIG_SETMASK);
+ // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
+ kernel_new.sa_mask = kernel_new.sa_mask;
}
return __rt_sigaction(signal,
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index e9d0e4b..a919a79 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -280,7 +280,7 @@
*/
# define __BIONIC_FORTIFY_VARIADIC static __inline__
/* Error functions don't have bodies, so they can just be static. */
-# define __BIONIC_ERROR_FUNCTION_VISIBILITY static
+# define __BIONIC_ERROR_FUNCTION_VISIBILITY static __attribute__((unused))
#else
/* Further increase sharing for some inline functions */
# define __pass_object_size_n(n)
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 9dfdbc0..6a6ea7d 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1446,8 +1446,10 @@
LIBC_Q { # introduced=Q
global:
+ ___tls_get_addr; # x86
__aeabi_read_tp; # arm
__res_randomid;
+ __tls_get_addr; # arm x86_64
android_fdsan_close_with_tag;
android_fdsan_create_owner_tag;
android_fdsan_exchange_owner_tag;
@@ -1480,15 +1482,8 @@
android_getaddrinfofornet; # apex
# Used by libandroid_runtime
+ android_mallopt; # apex
gMallocLeakZygoteChild; # apex
-
- # TODO(b/120266448) hide these symbols again
- # Used by libndk_translation
- __getdents64; # arm x86 mips apex
- tkill; # arm x86 mips apex
- # Used by PtsBionicDeviceTestCases
- __bionic_brk; # arm x86 mips apex
- __system_property_add; # apex
} LIBC_P;
LIBC_PRIVATE {
@@ -1565,6 +1560,7 @@
__arm_fadvise64_64; # arm
__ashldi3; # arm
__ashrdi3; # arm
+ __bionic_brk; # arm x86 mips
__bionic_libgcc_compat_symbols; # arm x86
__cmpdf2; # arm
__divdf3; # arm
@@ -1590,6 +1586,7 @@
__gedf2; # arm
__get_thread; # arm x86 mips
__get_tls; # arm x86 mips
+ __getdents64; # arm x86 mips
__gnu_ldivmod_helper; # arm
__gnu_uldivmod_helper; # arm
__gnu_Unwind_Backtrace; # arm
@@ -1697,6 +1694,7 @@
strntoumax; # arm x86 mips
strtotimeval; # arm x86 mips
sysv_signal; # arm x86 mips
+ tkill; # arm x86 mips
wait3; # arm x86 mips
wcswcs; # arm x86 mips
} LIBC_Q;
@@ -1708,6 +1706,7 @@
LIBC_PLATFORM {
global:
+ __system_property_add;
__system_property_area__; # var
__system_property_area_init;
__system_property_set_filename;
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 926b265..dd20b5c 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -132,6 +132,9 @@
{
"verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty},
},
+ {
+ "abort_on_error", {ABORT_ON_ERROR, &Config::VerifyValueEmpty},
+ },
};
bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 86d1ee4..011dc77 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -44,6 +44,7 @@
constexpr uint64_t LEAK_TRACK = 0x100;
constexpr uint64_t RECORD_ALLOCS = 0x200;
constexpr uint64_t BACKTRACE_FULL = 0x400;
+constexpr uint64_t ABORT_ON_ERROR = 0x800;
// In order to guarantee posix compliance, set the minimum alignment
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index debc14e..c307dc9 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -64,6 +64,9 @@
error_log("Backtrace at time of failure:");
BacktraceAndLog();
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset)
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index b0e2fc8..638061b 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -206,7 +206,7 @@
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
auto entry = pointers_.find(pointer);
if (entry == pointers_.end()) {
- // Error.
+ // Attempt to remove unknown pointer.
error_log("No tracked pointer found for 0x%" PRIxPTR, pointer);
return;
}
@@ -283,6 +283,9 @@
}
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) {
@@ -295,6 +298,9 @@
error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE",
info.pointer, header->tag);
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
// Stop processing here, it is impossible to tell how the header
// may have been damaged.
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index a8289b3..93b9b1e 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -394,6 +394,13 @@
**NOTE**: This option is not available until the P release of Android.
+### abort\_on\_error
+When malloc debug detects an error, abort after sending the error
+log message.
+
+**NOTE**: If leak\_track is enabled, no abort occurs if leaks have been
+detected when the process is exiting.
+
Additional Errors
-----------------
There are a few other error messages that might appear in the log.
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 9075a9c..2e6afff 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -154,6 +154,9 @@
error_log("Backtrace at time of failure:");
BacktraceAndLog();
error_log(LOG_DIVIDER);
+ if (g_debug->config().options() & ABORT_ON_ERROR) {
+ abort();
+ }
}
static bool VerifyPointer(const void* pointer, const char* function_name) {
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index a083b4f..fb54ee5 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -725,3 +725,21 @@
"value must be <= 50000000: 100000000\n");
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
}
+
+TEST_F(MallocDebugConfigTest, abort_on_error) {
+ ASSERT_TRUE(InitConfig("abort_on_error")) << getFakeLogPrint();
+ ASSERT_EQ(ABORT_ON_ERROR, config->options());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, trigger_abort_fail) {
+ ASSERT_FALSE(InitConfig("abort_on_error=200")) << getFakeLogPrint();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'abort_on_error' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 2d6346f..44f9795 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -2380,3 +2380,59 @@
expected_log += DIVIDER;
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
+
+TEST_F(MallocDebugTest, abort_on_error_log_error) {
+ Init("abort_on_error verify_pointers");
+
+ void* pointer = debug_malloc(10);
+ memset(pointer, 0, 10);
+ debug_free(pointer);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+ EXPECT_DEATH(debug_free(pointer), "");
+}
+
+TEST_F(MallocDebugTest, abort_on_error_guard_corrupted) {
+ Init("abort_on_error front_guard=32");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ pointer[-16] = 0x00;
+ EXPECT_DEATH(debug_free(pointer), "");
+ pointer[-16] = 0xaa;
+ debug_free(pointer);
+}
+
+TEST_F(MallocDebugTest, abort_on_error_use_after_free) {
+ Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ debug_free(pointer);
+
+ pointer[56] = 0x91;
+
+ EXPECT_DEATH(debug_finalize(), "");
+
+ pointer[56] = 0xef;
+}
+
+TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) {
+ Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0 rear_guard");
+
+ uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ debug_free(pointer);
+
+ uint8_t tag_value = pointer[-get_tag_offset()];
+ pointer[-get_tag_offset()] = 0x00;
+
+ EXPECT_DEATH(debug_finalize(), "");
+
+ pointer[-get_tag_offset()] = tag_value;
+}
+
diff --git a/libc/private/ScopedRWLock.h b/libc/private/ScopedRWLock.h
new file mode 100644
index 0000000..f034505
--- /dev/null
+++ b/libc/private/ScopedRWLock.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 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
+
+#include <pthread.h>
+
+#include "private/bionic_macros.h"
+
+template <bool write> class ScopedRWLock {
+ public:
+ explicit ScopedRWLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
+ (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(rwlock_);
+ }
+
+ ~ScopedRWLock() {
+ pthread_rwlock_unlock(rwlock_);
+ }
+
+ private:
+ pthread_rwlock_t* rwlock_;
+ BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
+};
+
+typedef ScopedRWLock<true> ScopedWriteLock;
+typedef ScopedRWLock<false> ScopedReadLock;
diff --git a/linker/linker_allocator.h b/libc/private/bionic_allocator.h
similarity index 80%
rename from linker/linker_allocator.h
rename to libc/private/bionic_allocator.h
index 44a8b0d..c705ce4 100644
--- a/linker/linker_allocator.h
+++ b/libc/private/bionic_allocator.h
@@ -36,13 +36,11 @@
#include <stddef.h>
#include <unistd.h>
-#include <async_safe/log.h>
-
const uint32_t kSmallObjectMaxSizeLog2 = 10;
const uint32_t kSmallObjectMinSizeLog2 = 4;
const uint32_t kSmallObjectAllocatorsCount = kSmallObjectMaxSizeLog2 - kSmallObjectMinSizeLog2 + 1;
-class LinkerSmallObjectAllocator;
+class BionicSmallObjectAllocator;
// This structure is placed at the beginning of each addressable page
// and has all information we need to find the corresponding memory allocator.
@@ -53,7 +51,7 @@
// we use allocated_size for large objects allocator
size_t allocated_size;
// and allocator_addr for small ones.
- LinkerSmallObjectAllocator* allocator_addr;
+ BionicSmallObjectAllocator* allocator_addr;
};
};
@@ -63,14 +61,14 @@
};
// This structure is placed at the beginning of each page managed by
-// LinkerSmallObjectAllocator. Note that a page_info struct is expected at the
+// BionicSmallObjectAllocator. Note that a page_info struct is expected at the
// beginning of each page as well, and therefore this structure contains a
// page_info as its *first* field.
struct small_object_page_info {
page_info info; // Must be the first field.
// Doubly linked list for traversing all pages allocated by a
- // LinkerSmallObjectAllocator.
+ // BionicSmallObjectAllocator.
small_object_page_info* next_page;
small_object_page_info* prev_page;
@@ -81,9 +79,9 @@
size_t free_blocks_cnt;
};
-class LinkerSmallObjectAllocator {
+class BionicSmallObjectAllocator {
public:
- LinkerSmallObjectAllocator(uint32_t type, size_t block_size);
+ BionicSmallObjectAllocator(uint32_t type, size_t block_size);
void* alloc();
void free(void* ptr);
@@ -103,20 +101,23 @@
small_object_page_info* page_list_;
};
-class LinkerMemoryAllocator {
+class BionicAllocator {
public:
- constexpr LinkerMemoryAllocator() : allocators_(nullptr), allocators_buf_() {}
+ constexpr BionicAllocator() : allocators_(nullptr), allocators_buf_() {}
void* alloc(size_t size);
+ void* memalign(size_t align, size_t size);
// Note that this implementation of realloc never shrinks allocation
void* realloc(void* ptr, size_t size);
void free(void* ptr);
private:
- void* alloc_mmap(size_t size);
- page_info* get_page_info(void* ptr);
- LinkerSmallObjectAllocator* get_small_object_allocator(uint32_t type);
+ void* alloc_mmap(size_t align, size_t size);
+ inline void* alloc_impl(size_t align, size_t size);
+ inline page_info* get_page_info_unchecked(void* ptr);
+ inline page_info* get_page_info(void* ptr);
+ BionicSmallObjectAllocator* get_small_object_allocator(uint32_t type);
void initialize_allocators();
- LinkerSmallObjectAllocator* allocators_;
- uint8_t allocators_buf_[sizeof(LinkerSmallObjectAllocator)*kSmallObjectAllocatorsCount];
+ BionicSmallObjectAllocator* allocators_;
+ uint8_t allocators_buf_[sizeof(BionicSmallObjectAllocator)*kSmallObjectAllocatorsCount];
};
diff --git a/libc/private/bionic_asm_tls.h b/libc/private/bionic_asm_tls.h
index 06e3dce..92f707a 100644
--- a/libc/private/bionic_asm_tls.h
+++ b/libc/private/bionic_asm_tls.h
@@ -65,6 +65,9 @@
//
// - TLS_SLOT_BIONIC_TLS: Optimizes accesses to bionic_tls by one load versus
// finding it using __get_thread().
+//
+// - TLS_SLOT_APP: Available for use by apps in Android Q and later. (This slot
+// was used for errno in P and earlier.)
#if defined(__arm__) || defined(__aarch64__)
@@ -81,17 +84,15 @@
#define TLS_SLOT_BIONIC_TLS -1
#define TLS_SLOT_DTV 0
#define TLS_SLOT_THREAD_ID 1
-// Slot 2 is free (was historically used for TLS_SLOT_ERRNO)
+#define TLS_SLOT_APP 2 // was historically used for errno
#define TLS_SLOT_OPENGL 3
#define TLS_SLOT_OPENGL_API 4
#define TLS_SLOT_STACK_GUARD 5
#define TLS_SLOT_SANITIZER 6 // was historically used for dlerror
#define TLS_SLOT_ART_THREAD_SELF 7
-#define TLS_SLOT_TSAN 8 // should be replaced with TLS_SLOT_SANITIZER
// The maximum slot is fixed by the minimum TLS alignment in Bionic executables.
-// It should be changed to 7 once TLS_SLOT_TSAN is removed.
-#define MAX_TLS_SLOT 8
+#define MAX_TLS_SLOT 7
#elif defined(__i386__) || defined(__x86_64__)
@@ -103,16 +104,15 @@
#define TLS_SLOT_SELF 0
#define TLS_SLOT_THREAD_ID 1
-// Slot 2 is free (was historically used for TLS_SLOT_ERRNO)
+#define TLS_SLOT_APP 2 // was historically used for errno
#define TLS_SLOT_OPENGL 3
#define TLS_SLOT_OPENGL_API 4
#define TLS_SLOT_STACK_GUARD 5
#define TLS_SLOT_SANITIZER 6 // was historically used for dlerror
#define TLS_SLOT_ART_THREAD_SELF 7
-#define TLS_SLOT_TSAN 8 // should be replaced with TLS_SLOT_SANITIZER
-#define TLS_SLOT_DTV 9
-#define TLS_SLOT_BIONIC_TLS 10
-#define MAX_TLS_SLOT 10 // update this value when reserving a slot
+#define TLS_SLOT_DTV 8
+#define TLS_SLOT_BIONIC_TLS 9
+#define MAX_TLS_SLOT 9 // update this value when reserving a slot
#endif
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index e847669..fa1af76 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -28,7 +28,25 @@
#pragma once
+#include <link.h>
+#include <pthread.h>
+#include <stdatomic.h>
#include <stdint.h>
+#include <sys/cdefs.h>
+
+__LIBC_HIDDEN__ extern _Atomic(size_t) __libc_tls_generation_copy;
+
+struct TlsSegment {
+ size_t size = 0;
+ size_t alignment = 1;
+ const void* init_ptr = ""; // Field is non-null even when init_size is 0.
+ size_t init_size = 0;
+};
+
+__LIBC_HIDDEN__ bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+ ElfW(Addr) load_bias, TlsSegment* out);
+
+__LIBC_HIDDEN__ bool __bionic_check_tls_alignment(size_t* alignment);
struct StaticTlsLayout {
constexpr StaticTlsLayout() {}
@@ -45,13 +63,17 @@
public:
size_t offset_bionic_tcb() const { return offset_bionic_tcb_; }
size_t offset_bionic_tls() const { return offset_bionic_tls_; }
+ size_t offset_thread_pointer() const;
size_t size() const { return offset_; }
size_t alignment() const { return alignment_; }
bool overflowed() const { return overflowed_; }
- void reserve_tcb();
+ size_t reserve_exe_segment_and_tcb(const TlsSegment* exe_segment, const char* progname);
void reserve_bionic_tls();
+ size_t reserve_solib_segment(const TlsSegment& segment) {
+ return reserve(segment.size, segment.alignment);
+ }
void finish_layout();
private:
@@ -63,3 +85,93 @@
size_t round_up_with_overflow_check(size_t value, size_t alignment);
};
+
+static constexpr size_t kTlsGenerationNone = 0;
+static constexpr size_t kTlsGenerationFirst = 1;
+
+// The first ELF TLS module has ID 1. Zero is reserved for the first word of
+// the DTV, a generation count. Unresolved weak symbols also use module ID 0.
+static constexpr size_t kTlsUninitializedModuleId = 0;
+
+static inline size_t __tls_module_id_to_idx(size_t id) { return id - 1; }
+static inline size_t __tls_module_idx_to_id(size_t idx) { return idx + 1; }
+
+// A descriptor for a single ELF TLS module.
+struct TlsModule {
+ TlsSegment segment;
+
+ // Offset into the static TLS block or SIZE_MAX for a dynamic module.
+ size_t static_offset = SIZE_MAX;
+
+ // The generation in which this module was loaded. Dynamic TLS lookups use
+ // this field to detect when a module has been unloaded.
+ size_t first_generation = kTlsGenerationNone;
+
+ // Used by the dynamic linker to track the associated soinfo* object.
+ void* soinfo_ptr = nullptr;
+};
+
+// 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.
+struct TlsModules {
+ constexpr TlsModules() {}
+
+ // A pointer to the TLS generation counter in libc.so. The counter is
+ // incremented each time an solib is loaded or unloaded.
+ _Atomic(size_t) generation = kTlsGenerationFirst;
+ _Atomic(size_t) *generation_libc_so = nullptr;
+
+ // Access to the TlsModule[] table requires taking this lock.
+ pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+ // 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;
+ TlsModule* module_table = nullptr;
+};
+
+void __init_static_tls(void* static_tls);
+
+// Dynamic Thread Vector. Each thread has a different DTV. For each module
+// (executable or solib), the DTV has a pointer to that module's TLS memory. The
+// DTV is initially empty and is allocated on-demand. It grows as more modules
+// are dlopen'ed. See https://www.akkadia.org/drepper/tls.pdf.
+//
+// The layout of the DTV is specified in various documents, but it is not part
+// of Bionic's public ABI. A compiler can't generate code to access it directly,
+// because it can't access libc's global generation counter.
+struct TlsDtv {
+ // Number of elements in this object's modules field.
+ size_t count;
+
+ // A pointer to an older TlsDtv object that should be freed when the thread
+ // exits. The objects aren't immediately freed because a DTV could be
+ // reallocated by a signal handler that interrupted __tls_get_addr's fast
+ // path.
+ TlsDtv* next;
+
+ // The DTV slot points at this field, which allows omitting an add instruction
+ // on the fast path for a TLS lookup. The arm64 tlsdesc_resolver.S depends on
+ // the layout of fields past this point.
+ size_t generation;
+ void* modules[];
+};
+
+struct TlsIndex {
+ size_t module_id;
+ size_t offset;
+};
+
+#if defined(__i386__)
+#define TLS_GET_ADDR_CCONV __attribute__((regparm(1)))
+#define TLS_GET_ADDR ___tls_get_addr
+#else
+#define TLS_GET_ADDR_CCONV
+#define TLS_GET_ADDR __tls_get_addr
+#endif
+
+extern "C" void* TLS_GET_ADDR(const TlsIndex* ti) TLS_GET_ADDR_CCONV;
+
+struct bionic_tcb;
+void __free_dynamic_tls(bionic_tcb* tcb);
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index b5e677e..21a2a24 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -33,6 +33,7 @@
#include <link.h>
#include <pthread.h>
+#include "private/bionic_allocator.h"
#include "private/bionic_elf_tls.h"
#include "private/bionic_fdsan.h"
#include "private/bionic_malloc_dispatch.h"
@@ -69,6 +70,8 @@
abort_msg_t* abort_msg = nullptr;
StaticTlsLayout static_tls_layout;
+ TlsModules tls_modules;
+ BionicAllocator tls_allocator;
// Values passed from the linker to libc.so.
const char* init_progname = nullptr;
diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h
new file mode 100644
index 0000000..5f4a75d
--- /dev/null
+++ b/libc/private/bionic_malloc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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
+
+#include <stdbool.h>
+
+// Opcodes for android_mallopt.
+
+enum {
+ // Marks the calling process as a profileable zygote child, possibly
+ // initializing profiling infrastructure.
+ M_INIT_ZYGOTE_CHILD_PROFILING = 1,
+#define M_INIT_ZYGOTE_CHILD_PROFILING M_INIT_ZYGOTE_CHILD_PROFILING
+ M_RESET_HOOKS = 2,
+#define M_RESET_HOOKS M_RESET_HOOKS
+};
+
+// Manipulates bionic-specific handling of memory allocation APIs such as
+// malloc. Only for use by the Android platform itself.
+//
+// On success, returns true. On failure, returns false and sets errno.
+extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size);
diff --git a/libc/seccomp/include/seccomp_policy.h b/libc/seccomp/include/seccomp_policy.h
index 49280f4..fd0fb60 100644
--- a/libc/seccomp/include/seccomp_policy.h
+++ b/libc/seccomp/include/seccomp_policy.h
@@ -17,8 +17,14 @@
#pragma once
#include <stddef.h>
+#include <stdint.h>
#include <linux/filter.h>
bool set_app_seccomp_filter();
+bool set_app_zygote_seccomp_filter();
bool set_system_seccomp_filter();
bool set_global_seccomp_filter();
+
+// Installs a filter that limits setresuid/setresgid to a range of
+// [uid_gid_min..uid_gid_max] (for the real-, effective- and super-ids).
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max);
diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h
index 797dfc5..d9e8047 100644
--- a/libc/seccomp/seccomp_bpfs.h
+++ b/libc/seccomp/seccomp_bpfs.h
@@ -21,6 +21,8 @@
extern const struct sock_filter arm_app_filter[];
extern const size_t arm_app_filter_size;
+extern const struct sock_filter arm_app_zygote_filter[];
+extern const size_t arm_app_zygote_filter_size;
extern const struct sock_filter arm_system_filter[];
extern const size_t arm_system_filter_size;
extern const struct sock_filter arm_global_filter[];
@@ -28,6 +30,8 @@
extern const struct sock_filter arm64_app_filter[];
extern const size_t arm64_app_filter_size;
+extern const struct sock_filter arm64_app_zygote_filter[];
+extern const size_t arm64_app_zygote_filter_size;
extern const struct sock_filter arm64_system_filter[];
extern const size_t arm64_system_filter_size;
extern const struct sock_filter arm64_global_filter[];
@@ -35,6 +39,8 @@
extern const struct sock_filter x86_app_filter[];
extern const size_t x86_app_filter_size;
+extern const struct sock_filter x86_app_zygote_filter[];
+extern const size_t x86_app_zygote_filter_size;
extern const struct sock_filter x86_system_filter[];
extern const size_t x86_system_filter_size;
extern const struct sock_filter x86_global_filter[];
@@ -42,6 +48,8 @@
extern const struct sock_filter x86_64_app_filter[];
extern const size_t x86_64_app_filter_size;
+extern const struct sock_filter x86_64_app_zygote_filter[];
+extern const size_t x86_64_app_zygote_filter_size;
extern const struct sock_filter x86_64_system_filter[];
extern const size_t x86_64_system_filter_size;
extern const struct sock_filter x86_64_global_filter[];
@@ -49,6 +57,8 @@
extern const struct sock_filter mips_app_filter[];
extern const size_t mips_app_filter_size;
+extern const struct sock_filter mips_app_zygote_filter[];
+extern const size_t mips_app_zygote_filter_size;
extern const struct sock_filter mips_system_filter[];
extern const size_t mips_system_filter_size;
extern const struct sock_filter mips_global_filter[];
@@ -56,6 +66,8 @@
extern const struct sock_filter mips64_app_filter[];
extern const size_t mips64_app_filter_size;
+extern const struct sock_filter mips64_app_zygote_filter[];
+extern const size_t mips64_app_zygote_filter_size;
extern const struct sock_filter mips64_system_filter[];
extern const size_t mips64_system_filter_size;
extern const struct sock_filter mips64_global_filter[];
diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp
index 3d617be..222a2c8 100644
--- a/libc/seccomp/seccomp_policy.cpp
+++ b/libc/seccomp/seccomp_policy.cpp
@@ -20,78 +20,111 @@
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
+#include <sys/syscall.h>
#include <vector>
#include <android-base/logging.h>
+#include "func_to_syscall_nrs.h"
#include "seccomp_bpfs.h"
-
#if defined __arm__ || defined __aarch64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_AARCH64
static const struct sock_filter* primary_app_filter = arm64_app_filter;
static const size_t primary_app_filter_size = arm64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = arm64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = arm64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = arm64_system_filter;
static const size_t primary_system_filter_size = arm64_system_filter_size;
static const struct sock_filter* primary_global_filter = arm64_global_filter;
static const size_t primary_global_filter_size = arm64_global_filter_size;
+
+static const long primary_setresgid = __arm64_setresgid;
+static const long primary_setresuid = __arm64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_ARM
static const struct sock_filter* secondary_app_filter = arm_app_filter;
static const size_t secondary_app_filter_size = arm_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = arm_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = arm_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = arm_system_filter;
static const size_t secondary_system_filter_size = arm_system_filter_size;
static const struct sock_filter* secondary_global_filter = arm_global_filter;
static const size_t secondary_global_filter_size = arm_global_filter_size;
+static const long secondary_setresgid = __arm_setresgid;
+static const long secondary_setresuid = __arm_setresuid;
#elif defined __i386__ || defined __x86_64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_X86_64
static const struct sock_filter* primary_app_filter = x86_64_app_filter;
static const size_t primary_app_filter_size = x86_64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = x86_64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = x86_64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = x86_64_system_filter;
static const size_t primary_system_filter_size = x86_64_system_filter_size;
static const struct sock_filter* primary_global_filter = x86_64_global_filter;
static const size_t primary_global_filter_size = x86_64_global_filter_size;
+
+static const long primary_setresgid = __x86_64_setresgid;
+static const long primary_setresuid = __x86_64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_I386
static const struct sock_filter* secondary_app_filter = x86_app_filter;
static const size_t secondary_app_filter_size = x86_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = x86_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = x86_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = x86_system_filter;
static const size_t secondary_system_filter_size = x86_system_filter_size;
static const struct sock_filter* secondary_global_filter = x86_global_filter;
static const size_t secondary_global_filter_size = x86_global_filter_size;
+static const long secondary_setresgid = __x86_setresgid;
+static const long secondary_setresuid = __x86_setresuid;
#elif defined __mips__ || defined __mips64__
#define DUAL_ARCH
#define PRIMARY_ARCH AUDIT_ARCH_MIPSEL64
static const struct sock_filter* primary_app_filter = mips64_app_filter;
static const size_t primary_app_filter_size = mips64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = mips64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = mips64_app_zygote_filter_size;
static const struct sock_filter* primary_system_filter = mips64_system_filter;
static const size_t primary_system_filter_size = mips64_system_filter_size;
static const struct sock_filter* primary_global_filter = mips64_global_filter;
static const size_t primary_global_filter_size = mips64_global_filter_size;
+
+static const long primary_setresgid = __mips64_setresgid;
+static const long primary_setresuid = __mips64_setresuid;
#define SECONDARY_ARCH AUDIT_ARCH_MIPSEL
static const struct sock_filter* secondary_app_filter = mips_app_filter;
static const size_t secondary_app_filter_size = mips_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = mips_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = mips_app_zygote_filter_size;
static const struct sock_filter* secondary_system_filter = mips_system_filter;
static const size_t secondary_system_filter_size = mips_system_filter_size;
static const struct sock_filter* secondary_global_filter = mips_global_filter;
static const size_t secondary_global_filter_size = mips_global_filter_size;
+static const long secondary_setresgid = __mips_setresgid;
+static const long secondary_setresuid = __mips_setresuid;
#else
#error No architecture was defined!
#endif
#define syscall_nr (offsetof(struct seccomp_data, nr))
+#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
#define arch_nr (offsetof(struct seccomp_data, arch))
typedef std::vector<sock_filter> filter;
+inline void Allow(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
inline void Disallow(filter& f) {
f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
}
@@ -128,6 +161,49 @@
}
#endif
+static void ValidateSyscallArgInRange(filter& f, __u32 arg_num, __u32 range_min, __u32 range_max) {
+ const __u32 syscall_arg = syscall_arg(arg_num);
+
+ if (range_max == UINT32_MAX) {
+ LOG(FATAL) << "range_max exceeds maximum argument range.";
+ return;
+ }
+
+ f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg));
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_min, 0, 1));
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_max + 1, 0, 1));
+ Disallow(f);
+}
+
+// This filter is meant to be installed in addition to a regular whitelist filter.
+// Therefore, it's default action has to be Allow, except when the evaluated
+// system call matches setresuid/setresgid and the arguments don't fall within the
+// passed in range.
+//
+// The regular whitelist only allows setresuid/setresgid for UID/GID changes, so
+// that's the only system call we need to check here. A CTS test ensures the other
+// calls will remain blocked.
+static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_max, bool primary) {
+ // Check setresuid(ruid, euid, sguid) fall within range
+ ExamineSyscall(f);
+ __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid;
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresuid_nr, 0, 12));
+ for (int arg = 0; arg < 3; arg++) {
+ ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+ }
+
+ // Check setresgid(rgid, egid, sgid) fall within range
+ ExamineSyscall(f);
+ __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid;
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresgid_nr, 0, 12));
+ for (int arg = 0; arg < 3; arg++) {
+ ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+ }
+
+ // Default is to allow; other filters may still reject this call.
+ Allow(f);
+}
+
static bool install_filter(filter const& f) {
struct sock_fprog prog = {
static_cast<unsigned short>(f.size()),
@@ -141,8 +217,33 @@
return true;
}
+bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+ filter f;
+#ifdef DUAL_ARCH
+ // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
+ // jump that must be changed to point to the start of the 32-bit policy
+ // 32 bit syscalls will not hit the policy between here and the call to SetJump
+ auto offset_to_secondary_filter = ValidateArchitectureAndJumpIfNeeded(f);
+#else
+ ValidateArchitecture(f);
+#endif
+
+ ValidateSetUidGid(f, uid_gid_min, uid_gid_max, true /* primary */);
+
+#ifdef DUAL_ARCH
+ if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
+ return false;
+ }
+
+ ValidateSetUidGid(f, uid_gid_min, uid_gid_max, false /* primary */);
+#endif
+
+ return install_filter(f);
+}
+
enum FilterType {
APP,
+ APP_ZYGOTE,
SYSTEM,
GLOBAL
};
@@ -159,6 +260,12 @@
s = secondary_app_filter;
s_size = secondary_app_filter_size;
break;
+ case APP_ZYGOTE:
+ p = primary_app_zygote_filter;
+ p_size = primary_app_zygote_filter_size;
+ s = secondary_app_zygote_filter;
+ s_size = secondary_app_zygote_filter_size;
+ break;
case SYSTEM:
p = primary_system_filter;
p_size = primary_system_filter_size;
@@ -210,6 +317,10 @@
return _set_seccomp_filter(FilterType::APP);
}
+bool set_app_zygote_seccomp_filter() {
+ return _set_seccomp_filter(FilterType::APP_ZYGOTE);
+}
+
bool set_system_seccomp_filter() {
return _set_seccomp_filter(FilterType::SYSTEM);
}
@@ -217,3 +328,7 @@
bool set_global_seccomp_filter() {
return _set_seccomp_filter(FilterType::GLOBAL);
}
+
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+ return _install_setuidgid_filter(uid_gid_min, uid_gid_max);
+}
diff --git a/libc/symbol_ordering b/libc/symbol_ordering
new file mode 100644
index 0000000..b672b35
--- /dev/null
+++ b/libc/symbol_ordering
@@ -0,0 +1,209 @@
+# This file is generated by sorting symbols in the .bss section in libc.so by
+# their sizes and taking out symbols that are unique to a target. By sorting
+# symbols by size, we usually have less dirty pages at runtime, because small
+# symbols are grouped together.
+
+je_background_thread_enabled_state
+je_can_enable_background_thread
+_ZZ17__find_icu_symbolPKcE9found_icu
+_ZL28g_heapprofd_init_in_progress
+_ZL31g_heapprofd_init_hook_installed
+je_opt_abort
+je_opt_abort_conf
+je_opt_junk_alloc
+je_opt_junk_free
+je_opt_utrace
+je_opt_xmalloc
+je_opt_zero
+malloc_disabled_tcache
+had_conf_error
+malloc_slow_flags
+je_opt_background_thread
+background_thread_enabled_at_fork
+ctl_initialized
+je_log_init_done
+mmap_flags
+os_overcommits
+je_opt_stats_print
+je_tsd_booted
+global_hashtable_initialized
+gmtcheck.gmt_is_set
+restartloop
+_ZZ12bindresvportE4port
+ru_counter
+ru_a
+ru_x
+ru_b
+ru_seed
+ru_g
+ru_seed2
+ru_msb
+je_narenas_auto
+je_ncpus
+je_init_system_thp_mode
+je_nhbins
+je_tsd_tsd
+optreset
+_rs_forked
+daylight
+_ZL17g_icudata_version
+gMallocLeakZygoteChild
+_ZL18netdClientInitOnce
+je_opt_narenas
+narenas_total
+je_malloc_disable.once_control
+je_opt_metadata_thp
+je_opt_thp
+stack_nelms
+tcaches_past
+ncleanups
+error_message_count
+error_one_per_line
+_ZZ13error_at_lineE9last_line
+_ZL13g_locale_once
+_ZL30g_propservice_protocol_version
+_res_cache_once
+_res_key
+_rs_forkdetect._rs_pid
+ru_pid
+lcl_is_set
+__cxa_finalize.call_depth
+seed48.sseed
+ether_aton.addr
+je_background_thread_info
+je_max_background_threads
+je_n_background_threads
+je_malloc_message
+je_tcache_bin_info
+je_tcache_maxclass
+je_tcaches
+optarg
+suboptarg
+timezone
+_ZGVZ17__find_icu_symbolPKcE9found_icu
+_ZL17g_libicuuc_handle
+__malloc_hook
+__realloc_hook
+__free_hook
+__memalign_hook
+je_malloc_conf
+malloc_initializer
+a0
+je_opt_dirty_decay_ms
+je_opt_muzzy_decay_ms
+dirty_decay_ms_default.0
+muzzy_decay_ms_default.0
+pthread_create_fptr
+b0
+ctl_arenas
+ctl_stats
+je_hooks_arena_new_hook
+os_page
+tcaches_avail
+_ZN9prop_area8pa_size_E
+_ZN9prop_area13pa_data_size_E
+_ZL6g_lock
+_ZL6g_tags
+_ZZ8c16rtombE15__private_state
+_ZZ8c32rtombE15__private_state
+environ
+error_print_progname
+_ZZ13error_at_lineE9last_file
+_ZZ14__icu_charTypejE10u_charType
+_ZGVZ14__icu_charTypejE10u_charType
+_ZZ25__icu_getIntPropertyValuej9UPropertyE21u_getIntPropertyValue
+_ZGVZ25__icu_getIntPropertyValuej9UPropertyE21u_getIntPropertyValue
+_ZZ23__icu_hasBinaryPropertyj9UPropertyPFiiEE19u_hasBinaryProperty
+_ZGVZ23__icu_hasBinaryPropertyj9UPropertyPFiiEE19u_hasBinaryProperty
+__progname
+_ZZ8mbrtoc16E15__private_state
+_ZZ8mbrtoc32E15__private_state
+_ZL14syslog_log_tag
+__system_property_area__
+_ZZ7mbrtowcE15__private_state
+_ZZ10mbsnrtowcsE15__private_state
+_ZZ7wcrtombE15__private_state
+_ZZ10wcsnrtombsE15__private_state
+_ZZ8iswcntrlE10u_charType
+_ZGVZ8iswcntrlE10u_charType
+_ZZ8iswdigitE9u_isdigit
+_ZGVZ8iswdigitE9u_isdigit
+_ZZ8iswpunctE9u_ispunct
+_ZGVZ8iswpunctE9u_ispunct
+_ZZ8towlowerE9u_tolower
+_ZGVZ8towlowerE9u_tolower
+_ZZ8towupperE9u_toupper
+_ZGVZ8towupperE9u_toupper
+global_hashtable
+handlers
+p5s
+ut
+rs
+rsx
+mbrlen.mbs
+mbtowc.mbs
+wctomb.mbs
+ru_reseed
+ru_prf
+tmpnam.tmpcount
+lastenv
+strtok.last
+__stack_chk_guard
+lclptr
+gmtptr
+_ZGVZ14tzset_unlockedE20persist_sys_timezone
+_ZL13g_thread_list
+__atexit
+je_opt_stats_print_opts
+nuls
+precsize_ntoa.retbuf
+__p_secstodate.output
+_ZL13g_atfork_list
+inet_ntoa.b
+ether_ntoa.buf
+__sym_ntos.unname
+__sym_ntop.unname
+__p_type.typebuf
+__p_class.classbuf
+malloc_disabled_lock
+_ZL11g_arc4_lock
+_res_cache_list_lock
+__p_option.nbuf
+__p_time.nbuf
+atexit_mutex
+random_mutex
+__res_randomid.__libc_mutex_random
+locallock
+g_atexit_lock
+_ZL11g_functions
+_ZL13vendor_passwd
+_ZL12vendor_group
+tm
+_ZL18g_thread_list_lock
+buf_asctime
+__dtoa_locks
+freelist
+__loc_ntoa.tmpbuf
+_ZL8g_locale
+je_arenas_lock
+je_background_thread_lock
+init_lock
+ctl_mtx
+tcaches_mtx
+je_tsd_init_head
+_ZZ14tzset_unlockedE20persist_sys_timezone
+arena_binind_div_info
+__hexdig_D2A
+lcl_TZname
+utmp
+inet_nsap_ntoa_tmpbuf
+_ZL17system_properties
+_ZL7key_map
+private_mem
+__libc_globals
+tmpnam.buf
+_res_cache_list
+_nres
+je_extent_mutex_pool
+je_arenas
+je_extents_rtree
diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py
new file mode 100755
index 0000000..6a456f2
--- /dev/null
+++ b/libc/tools/genfunctosyscallnrs.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import argparse
+import collections
+import logging
+import os
+import re
+import subprocess
+import textwrap
+
+from gensyscalls import SysCallsTxtParser
+from genseccomp import parse_syscall_NRs
+
+def load_syscall_names_from_file(file_path, architecture):
+ parser = SysCallsTxtParser()
+ parser.parse_open_file(open(file_path))
+ arch_map = {}
+ for syscall in parser.syscalls:
+ if syscall.get(architecture):
+ arch_map[syscall["func"]] = syscall["name"];
+
+ return arch_map
+
+def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs):
+ for arch in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'):
+ base_names = load_syscall_names_from_file(base_syscall_file, arch)
+
+ for func,syscall in base_names.iteritems():
+ out_file.write("#define __" + arch + "_" + func + " " + str(syscall_NRs[arch][syscall]) + ";\n")
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generates a mapping of bionic functions to system call numbers per architecture.")
+ parser.add_argument("--verbose", "-v", help="Enables verbose logging.")
+ parser.add_argument("--out-dir",
+ help="The output directory for the output files")
+ parser.add_argument("base_file", metavar="base-file", type=str,
+ help="The path of the base syscall list (SYSCALLS.TXT).")
+ parser.add_argument("files", metavar="FILE", type=str, nargs="+",
+ help=("A syscall name-number mapping file for an architecture.\n"))
+ args = parser.parse_args()
+
+ if args.verbose:
+ logging.basicConfig(level=logging.DEBUG)
+ else:
+ logging.basicConfig(level=logging.INFO)
+
+ syscall_files = []
+ syscall_NRs = {}
+ for filename in args.files:
+ m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
+ syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
+
+ output_path = os.path.join(args.out_dir, "func_to_syscall_nrs.h")
+ with open(output_path, "w") as output_file:
+ gen_syscall_nrs(out_file=output_file,
+ syscall_NRs=syscall_NRs, base_syscall_file=args.base_file)
+
+if __name__ == "__main__":
+ main()
diff --git a/linker/Android.bp b/linker/Android.bp
index 38a53f8..033860a 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -4,7 +4,6 @@
recovery_available: true,
srcs: [
- "linker_allocator.cpp",
"linker_memory.cpp",
],
cflags: [
@@ -104,6 +103,7 @@
name: "linker_sources_arm64",
srcs: [
"arch/arm64/begin.S",
+ "arch/arm64/tlsdesc_resolver.S",
],
}
@@ -176,6 +176,11 @@
"-Wextra",
"-Wunused",
"-Werror",
+
+ // Define _USING_LIBCXX so <stdatomic.h> defers to the <atomic> header. When a Soong module
+ // uses the platform libc++, Soong automatically passes this macro, but the dynamic linker
+ // links against libc++ manually.
+ "-D_USING_LIBCXX",
],
// TODO: split out the asflags.
@@ -280,7 +285,11 @@
symlinks: ["linker_asan"],
recovery_available: true,
multilib: {
+ lib32: {
+ cflags: ["-DLIB_PATH=\"lib\""],
+ },
lib64: {
+ cflags: ["-DLIB_PATH=\"lib64\""],
suffix: "64",
},
},
diff --git a/linker/arch/arm64/tlsdesc_resolver.S b/linker/arch/arm64/tlsdesc_resolver.S
new file mode 100644
index 0000000..ef46839
--- /dev/null
+++ b/linker/arch/arm64/tlsdesc_resolver.S
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+#include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
+
+.globl __tls_get_addr
+
+// These resolver functions must preserve every register except x0. They set x0
+// to the offset of the TLS symbol relative to the thread pointer.
+
+ENTRY_PRIVATE(tlsdesc_resolver_static)
+ ldr x0, [x0, #8]
+ ret
+END(tlsdesc_resolver_static)
+
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic)
+ stp x19, x20, [sp, #-32]!
+ .cfi_def_cfa_offset 32
+ .cfi_rel_offset x19, 0
+ .cfi_rel_offset x20, 8
+ stp x21, x22, [sp, #16]
+ .cfi_rel_offset x21, 16
+ .cfi_rel_offset x22, 24
+
+ mrs x19, tpidr_el0 // __get_tls()
+ ldr x20, [x19, #(TLS_SLOT_DTV * 8)]
+ ldr x21, [x20] // TlsDtv::generation
+
+ ldr x0, [x0, #8] // TlsDynamicResolverArg*
+ ldr x22, [x0] // TlsDynamicResolverArg::generation
+
+ cmp x21, x22
+ b.lo .fallback
+
+ ldr x21, [x0, #8] // TlsIndex::module
+ ldr x22, [x0, #16] // TlsIndex::offset
+ ldr x21, [x20, x21, lsl #3] // TlsDtv::modules[module]
+ cbz x21, .fallback
+ add x0, x21, x22
+ sub x0, x0, x19
+
+ ldp x21, x22, [sp, #16]
+ .cfi_remember_state
+ .cfi_restore x21
+ .cfi_restore x22
+ ldp x19, x20, [sp], #32
+ .cfi_adjust_cfa_offset -32
+ .cfi_restore x19
+ .cfi_restore x20
+ ret
+
+.fallback:
+ .cfi_restore_state
+ ldp x21, x22, [sp, #16]
+ .cfi_restore x21
+ .cfi_restore x22
+ ldp x19, x20, [sp], #32
+ .cfi_adjust_cfa_offset -32
+ .cfi_restore x19
+ .cfi_restore x20
+ b tlsdesc_resolver_dynamic_slow_path
+END(tlsdesc_resolver_dynamic)
+
+#define SAVE_REG(x, slot) \
+ str x, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+
+#define SAVE_GPR_PAIR(x, y, slot) \
+ stp x, y, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+ .cfi_rel_offset y, ((slot) + 1) * 8; \
+
+#define SAVE_VEC_PAIR(x, y, slot) \
+ stp x, y, [sp, #((slot) * 8)]; \
+ .cfi_rel_offset x, (slot) * 8; \
+ .cfi_rel_offset y, ((slot) + 2) * 8; \
+
+#define RESTORE_REG(x, slot) \
+ ldr x, [sp, #((slot) * 8)]; \
+ .cfi_restore x; \
+
+#define RESTORE_REG_PAIR(x, y, slot) \
+ ldp x, y, [sp, #((slot) * 8)]; \
+ .cfi_restore x; \
+ .cfi_restore y; \
+
+// On entry, x0 is the address of a TlsDynamicResolverArg object rather than
+// the TlsDescriptor address passed to the original resolver function.
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic_slow_path)
+ sub sp, sp, #(8 * 84)
+ .cfi_def_cfa_offset (8 * 84)
+ SAVE_GPR_PAIR(x29, x30, 0)
+ mov x29, sp
+
+ // Avoid leaking the contents of the shadow call stack register (x18) into
+ // memory. x19 through x29 are callee-save registers, so we do not need to
+ // save them.
+ SAVE_GPR_PAIR(x1, x2, 2)
+ SAVE_GPR_PAIR(x3, x4, 4)
+ SAVE_GPR_PAIR(x5, x6, 6)
+ SAVE_GPR_PAIR(x7, x8, 8)
+ SAVE_GPR_PAIR(x9, x10, 10)
+ SAVE_GPR_PAIR(x11, x12, 12)
+ SAVE_GPR_PAIR(x13, x14, 14)
+ SAVE_GPR_PAIR(x15, x16, 16)
+ SAVE_REG(x17, 18)
+
+ SAVE_VEC_PAIR(q0, q1, 20)
+ SAVE_VEC_PAIR(q2, q3, 24)
+ SAVE_VEC_PAIR(q4, q5, 28)
+ SAVE_VEC_PAIR(q6, q7, 32)
+ SAVE_VEC_PAIR(q8, q9, 36)
+ SAVE_VEC_PAIR(q10, q11, 40)
+ SAVE_VEC_PAIR(q12, q13, 44)
+ SAVE_VEC_PAIR(q14, q15, 48)
+ SAVE_VEC_PAIR(q16, q17, 52)
+ SAVE_VEC_PAIR(q18, q19, 56)
+ SAVE_VEC_PAIR(q20, q21, 60)
+ SAVE_VEC_PAIR(q22, q23, 64)
+ SAVE_VEC_PAIR(q24, q25, 68)
+ SAVE_VEC_PAIR(q26, q27, 72)
+ SAVE_VEC_PAIR(q28, q29, 76)
+ SAVE_VEC_PAIR(q30, q31, 80)
+
+ add x0, x0, #8
+ bl __tls_get_addr
+ mrs x1, tpidr_el0 // __get_tls()
+ sub x0, x0, x1
+
+ RESTORE_REG_PAIR(q30, q31, 80)
+ RESTORE_REG_PAIR(q28, q29, 76)
+ RESTORE_REG_PAIR(q26, q27, 72)
+ RESTORE_REG_PAIR(q24, q25, 68)
+ RESTORE_REG_PAIR(q22, q23, 64)
+ RESTORE_REG_PAIR(q20, q21, 60)
+ RESTORE_REG_PAIR(q18, q19, 56)
+ RESTORE_REG_PAIR(q16, q17, 52)
+ RESTORE_REG_PAIR(q14, q15, 48)
+ RESTORE_REG_PAIR(q12, q13, 44)
+ RESTORE_REG_PAIR(q10, q11, 40)
+ RESTORE_REG_PAIR(q8, q9, 36)
+ RESTORE_REG_PAIR(q6, q7, 32)
+ RESTORE_REG_PAIR(q4, q5, 28)
+ RESTORE_REG_PAIR(q2, q3, 24)
+ RESTORE_REG_PAIR(q0, q1, 20)
+
+ RESTORE_REG(x17, 18)
+ RESTORE_REG_PAIR(x15, x16, 16)
+ RESTORE_REG_PAIR(x13, x14, 14)
+ RESTORE_REG_PAIR(x11, x12, 12)
+ RESTORE_REG_PAIR(x9, x10, 10)
+ RESTORE_REG_PAIR(x7, x8, 8)
+ RESTORE_REG_PAIR(x5, x6, 6)
+ RESTORE_REG_PAIR(x3, x4, 4)
+ RESTORE_REG_PAIR(x1, x2, 2)
+
+ RESTORE_REG_PAIR(x29, x30, 0)
+ add sp, sp, #(8 * 84)
+ .cfi_def_cfa_offset 0
+ ret
+END(tlsdesc_resolver_dynamic_slow_path)
+
+// The address of an unresolved weak TLS symbol evaluates to NULL with TLSDESC.
+// The value returned by this function is added to the thread pointer, so return
+// a negated thread pointer to cancel it out.
+ENTRY_PRIVATE(tlsdesc_resolver_unresolved_weak)
+ str x19, [sp, #-16]!
+ .cfi_def_cfa_offset 16
+ .cfi_rel_offset x19, 0
+ ldr x19, [x0, #8]
+ mrs x0, tpidr_el0 // __get_tls()
+ sub x0, x19, x0
+ ldr x19, [sp], #16
+ .cfi_def_cfa_offset 0
+ .cfi_restore x19
+ ret
+END(tlsdesc_resolver_unresolved_weak)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 1f259e1..d0c740b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -65,8 +65,10 @@
#include "linker_phdr.h"
#include "linker_relocs.h"
#include "linker_reloc_iterators.h"
+#include "linker_tls.h"
#include "linker_utils.h"
+#include "private/bionic_globals.h"
#include "android-base/macros.h"
#include "android-base/strings.h"
#include "android-base/stringprintf.h"
@@ -601,6 +603,9 @@
}
void set_fd(int fd, bool assume_ownership) {
+ if (fd_ != -1 && close_fd_) {
+ close(fd_);
+ }
fd_ = fd;
close_fd_ = assume_ownership;
}
@@ -1652,6 +1657,7 @@
if (!si->is_linked() && !si->prelink_image()) {
return false;
}
+ register_soinfo_tls(si);
}
// Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
@@ -1887,6 +1893,7 @@
si->get_realpath(),
si);
notify_gdb_of_unload(si);
+ unregister_soinfo_tls(si);
get_cfi_shadow()->BeforeUnload(si);
soinfo_free(si);
}
@@ -2666,16 +2673,33 @@
#else
static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE ||
- ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE ||
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_DTPREL ||
+ ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_TPREL) {
return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
}
return 0;
}
#endif
+static bool is_tls_reloc(ElfW(Word) type) {
+ switch (type) {
+ case R_GENERIC_TLS_DTPMOD:
+ case R_GENERIC_TLS_DTPREL:
+ case R_GENERIC_TLS_TPREL:
+ case R_GENERIC_TLSDESC:
+ return true;
+ default:
+ return false;
+ }
+}
+
template<typename ElfRelIteratorT>
bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ const size_t tls_tp_base = __libc_shared_globals()->static_tls_layout.offset_thread_pointer();
+ std::vector<std::pair<TlsDescriptor*, size_t>> deferred_tlsdesc_relocs;
+
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
@@ -2698,7 +2722,26 @@
const ElfW(Sym)* s = nullptr;
soinfo* lsi = nullptr;
- if (sym != 0) {
+ if (sym == 0) {
+ // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation
+ // is a reference to the current module.
+ if (is_tls_reloc(type)) {
+ lsi = this;
+ }
+ } else if (ELF_ST_BIND(symtab_[sym].st_info) == STB_LOCAL && is_tls_reloc(type)) {
+ // In certain situations, the Gold linker accesses a TLS symbol using a
+ // relocation to an STB_LOCAL symbol in .dynsym of either STT_SECTION or
+ // STT_TLS type. Bionic doesn't support these relocations, so issue an
+ // error. References:
+ // - https://groups.google.com/d/topic/generic-abi/dJ4_Y78aQ2M/discussion
+ // - https://sourceware.org/bugzilla/show_bug.cgi?id=17699
+ s = &symtab_[sym];
+ sym_name = get_string(s->st_name);
+ DL_ERR("unexpected TLS reference to local symbol \"%s\": "
+ "sym type %d, rel type %u (idx %zu of \"%s\")",
+ sym_name, ELF_ST_TYPE(s->st_info), type, idx, get_realpath());
+ return false;
+ } else {
sym_name = get_string(symtab_[sym].st_name);
const version_info* vi = nullptr;
@@ -2735,6 +2778,10 @@
case R_GENERIC_GLOB_DAT:
case R_GENERIC_RELATIVE:
case R_GENERIC_IRELATIVE:
+ case R_GENERIC_TLS_DTPMOD:
+ case R_GENERIC_TLS_DTPREL:
+ case R_GENERIC_TLS_TPREL:
+ case R_GENERIC_TLSDESC:
#if defined(__aarch64__)
case R_AARCH64_ABS64:
case R_AARCH64_ABS32:
@@ -2782,12 +2829,26 @@
}
}
#endif
- if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
- DL_ERR("unsupported ELF TLS symbol \"%s\" referenced by \"%s\"",
- sym_name, get_realpath());
- return false;
+ if (is_tls_reloc(type)) {
+ if (ELF_ST_TYPE(s->st_info) != STT_TLS) {
+ DL_ERR("reference to non-TLS symbol \"%s\" from TLS relocation in \"%s\"",
+ sym_name, get_realpath());
+ return false;
+ }
+ if (lsi->get_tls() == nullptr) {
+ DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
+ sym_name, lsi->get_realpath());
+ return false;
+ }
+ sym_addr = s->st_value;
+ } else {
+ if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
+ DL_ERR("reference to TLS symbol \"%s\" from non-TLS relocation in \"%s\"",
+ sym_name, get_realpath());
+ return false;
+ }
+ sym_addr = lsi->resolve_symbol_address(s);
}
- sym_addr = lsi->resolve_symbol_address(s);
#if !defined(__LP64__)
if (protect_segments) {
if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
@@ -2860,6 +2921,104 @@
*reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_addr;
}
break;
+ case R_GENERIC_TLS_TPREL:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ ElfW(Addr) tpoff = 0;
+ if (lsi == nullptr) {
+ // Unresolved weak relocation. Leave tpoff at 0 to resolve
+ // &weak_tls_symbol to __get_tls().
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ const TlsModule& mod = get_tls_module(lsi->get_tls()->module_id);
+ if (mod.static_offset != SIZE_MAX) {
+ tpoff += mod.static_offset - tls_tp_base;
+ } else {
+ DL_ERR("TLS symbol \"%s\" in dlopened \"%s\" referenced from \"%s\" using IE access model",
+ sym_name, lsi->get_realpath(), get_realpath());
+ return false;
+ }
+ }
+ tpoff += sym_addr + addend;
+ TRACE_TYPE(RELO, "RELO TLS_TPREL %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(tpoff), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = tpoff;
+ }
+ break;
+
+#if !defined(__aarch64__)
+ // Omit support for DTPMOD/DTPREL on arm64, at least until
+ // http://b/123385182 is fixed. arm64 uses TLSDESC instead.
+ case R_GENERIC_TLS_DTPMOD:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ size_t module_id = 0;
+ if (lsi == nullptr) {
+ // Unresolved weak relocation. Evaluate the module ID to 0.
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ module_id = lsi->get_tls()->module_id;
+ }
+ TRACE_TYPE(RELO, "RELO TLS_DTPMOD %16p <- %zu %s\n",
+ reinterpret_cast<void*>(reloc), module_id, sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = module_id;
+ }
+ break;
+ case R_GENERIC_TLS_DTPREL:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO TLS_DTPREL %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+ break;
+#endif // !defined(__aarch64__)
+
+#if defined(__aarch64__)
+ // Bionic currently only implements TLSDESC for arm64. This implementation should work with
+ // other architectures, as long as the resolver functions are implemented.
+ case R_GENERIC_TLSDESC:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ {
+ TlsDescriptor* desc = reinterpret_cast<TlsDescriptor*>(reloc);
+ if (lsi == nullptr) {
+ // Unresolved weak relocation.
+ desc->func = tlsdesc_resolver_unresolved_weak;
+ desc->arg = addend;
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- unresolved weak 0x%zx %s\n",
+ reinterpret_cast<void*>(reloc), static_cast<size_t>(addend), sym_name);
+ } else {
+ CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+ size_t module_id = lsi->get_tls()->module_id;
+ const TlsModule& mod = get_tls_module(module_id);
+ if (mod.static_offset != SIZE_MAX) {
+ desc->func = tlsdesc_resolver_static;
+ desc->arg = mod.static_offset - tls_tp_base + sym_addr + addend;
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- static (0x%zx - 0x%zx + 0x%zx + 0x%zx) %s\n",
+ reinterpret_cast<void*>(reloc), mod.static_offset, tls_tp_base,
+ static_cast<size_t>(sym_addr), static_cast<size_t>(addend), sym_name);
+ } else {
+ tlsdesc_args_.push_back({
+ .generation = mod.first_generation,
+ .index.module_id = module_id,
+ .index.offset = sym_addr + addend,
+ });
+ // Defer the TLSDESC relocation until the address of the TlsDynamicResolverArg object
+ // is finalized.
+ deferred_tlsdesc_relocs.push_back({ desc, tlsdesc_args_.size() - 1 });
+ const TlsDynamicResolverArg& desc_arg = tlsdesc_args_.back();
+ TRACE_TYPE(RELO, "RELO TLSDESC %16p <- dynamic (gen %zu, mod %zu, off %zu) %s",
+ reinterpret_cast<void*>(reloc), desc_arg.generation,
+ desc_arg.index.module_id, desc_arg.index.offset, sym_name);
+ }
+ }
+ }
+ break;
+#endif // defined(R_GENERIC_TLSDESC)
#if defined(__aarch64__)
case R_AARCH64_ABS64:
@@ -2961,14 +3120,6 @@
*/
DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_realpath());
return false;
- case R_AARCH64_TLS_TPREL64:
- TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + addend), rel->r_offset);
- break;
- case R_AARCH64_TLSDESC:
- TRACE_TYPE(RELO, "RELO TLSDESC *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + addend), rel->r_offset);
- break;
#elif defined(__x86_64__)
case R_X86_64_32:
count_relocation(kRelocRelative);
@@ -3038,6 +3189,13 @@
return false;
}
}
+
+ for (const std::pair<TlsDescriptor*, size_t>& pair : deferred_tlsdesc_relocs) {
+ TlsDescriptor* desc = pair.first;
+ desc->func = tlsdesc_resolver_dynamic;
+ desc->arg = reinterpret_cast<size_t>(&tlsdesc_args_[pair.second]);
+ }
+
return true;
}
#endif // !defined(__mips__)
@@ -3073,6 +3231,19 @@
&ARM_exidx, &ARM_exidx_count);
#endif
+ TlsSegment tls_segment;
+ if (__bionic_get_tls_segment(phdr, phnum, load_bias, &tls_segment)) {
+ if (!__bionic_check_tls_alignment(&tls_segment.alignment)) {
+ if (!relocating_linker) {
+ DL_ERR("TLS segment alignment in \"%s\" is not a power of 2: %zu",
+ get_realpath(), tls_segment.alignment);
+ }
+ return false;
+ }
+ tls_ = std::make_unique<soinfo_tls>();
+ tls_->segment = tls_segment;
+ }
+
// Extract useful information from dynamic section.
// Note that: "Except for the DT_NULL element at the end of the array,
// and the relative order of DT_NEEDED elements, entries may appear in any order."
@@ -3438,13 +3609,14 @@
// this is parsed after we have strtab initialized (see below).
break;
+ case DT_TLSDESC_GOT:
+ case DT_TLSDESC_PLT:
+ // These DT entries are used for lazy TLSDESC relocations. Bionic
+ // resolves everything eagerly, so these can be ignored.
+ break;
+
default:
if (!relocating_linker) {
- if (d->d_tag == DT_TLSDESC_GOT || d->d_tag == DT_TLSDESC_PLT) {
- DL_ERR("unsupported ELF TLS DT entry in \"%s\"", get_realpath());
- return false;
- }
-
const char* tag_name;
if (d->d_tag == DT_RPATH) {
tag_name = "DT_RPATH";
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp
index dca944e..27f1e38 100644
--- a/linker/linker_block_allocator.cpp
+++ b/linker/linker_block_allocator.cpp
@@ -33,6 +33,9 @@
#include <sys/prctl.h>
#include <unistd.h>
+static constexpr size_t kAllocateSize = PAGE_SIZE * 100;
+static_assert(kAllocateSize % PAGE_SIZE == 0, "Invalid kAllocateSize.");
+
// the multiplier should be power of 2
static constexpr size_t round_up(size_t size, size_t multiplier) {
return (size + (multiplier - 1)) & ~(multiplier-1);
@@ -40,7 +43,7 @@
struct LinkerBlockAllocatorPage {
LinkerBlockAllocatorPage* next;
- uint8_t bytes[PAGE_SIZE - 16] __attribute__((aligned(16)));
+ uint8_t bytes[kAllocateSize - 16] __attribute__((aligned(16)));
};
struct FreeBlockInfo {
@@ -52,7 +55,8 @@
: block_size_(
round_up(block_size < sizeof(FreeBlockInfo) ? sizeof(FreeBlockInfo) : block_size, 16)),
page_list_(nullptr),
- free_block_list_(nullptr)
+ free_block_list_(nullptr),
+ allocated_(0)
{}
void* LinkerBlockAllocator::alloc() {
@@ -73,6 +77,8 @@
memset(block_info, 0, block_size_);
+ ++allocated_;
+
return block_info;
}
@@ -101,32 +107,37 @@
block_info->num_free_blocks = 1;
free_block_list_ = block_info;
+
+ --allocated_;
+ if (allocated_ == 0) {
+ free_all_pages();
+ }
}
void LinkerBlockAllocator::protect_all(int prot) {
for (LinkerBlockAllocatorPage* page = page_list_; page != nullptr; page = page->next) {
- if (mprotect(page, PAGE_SIZE, prot) == -1) {
+ if (mprotect(page, kAllocateSize, prot) == -1) {
abort();
}
}
}
void LinkerBlockAllocator::create_new_page() {
- static_assert(sizeof(LinkerBlockAllocatorPage) == PAGE_SIZE,
+ static_assert(sizeof(LinkerBlockAllocatorPage) == kAllocateSize,
"Invalid sizeof(LinkerBlockAllocatorPage)");
LinkerBlockAllocatorPage* page = reinterpret_cast<LinkerBlockAllocatorPage*>(
- mmap(nullptr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
+ mmap(nullptr, kAllocateSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
if (page == MAP_FAILED) {
abort(); // oom
}
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, PAGE_SIZE, "linker_alloc");
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, kAllocateSize, "linker_alloc");
FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
first_block->next_block = free_block_list_;
- first_block->num_free_blocks = (PAGE_SIZE - sizeof(LinkerBlockAllocatorPage*))/block_size_;
+ first_block->num_free_blocks = (kAllocateSize - sizeof(LinkerBlockAllocatorPage*))/block_size_;
free_block_list_ = first_block;
@@ -142,7 +153,7 @@
LinkerBlockAllocatorPage* page = page_list_;
while (page != nullptr) {
const uint8_t* page_ptr = reinterpret_cast<const uint8_t*>(page);
- if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + PAGE_SIZE)) {
+ if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + kAllocateSize)) {
return page;
}
@@ -151,3 +162,18 @@
abort();
}
+
+void LinkerBlockAllocator::free_all_pages() {
+ if (allocated_) {
+ abort();
+ }
+
+ LinkerBlockAllocatorPage* page = page_list_;
+ while (page) {
+ LinkerBlockAllocatorPage* next = page->next;
+ munmap(page, kAllocateSize);
+ page = next;
+ }
+ page_list_ = nullptr;
+ free_block_list_ = nullptr;
+}
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h
index bd44fc8..458d092 100644
--- a/linker/linker_block_allocator.h
+++ b/linker/linker_block_allocator.h
@@ -53,10 +53,12 @@
private:
void create_new_page();
LinkerBlockAllocatorPage* find_page(void* block);
+ void free_all_pages();
size_t block_size_;
LinkerBlockAllocatorPage* page_list_;
void* free_block_list_;
+ size_t allocated_;
DISALLOW_COPY_AND_ASSIGN(LinkerBlockAllocator);
};
@@ -66,17 +68,18 @@
* of a single fixed-size type. Allocations are backed by page-sized private
* anonymous mmaps.
*
- * The differences between this allocator and LinkerMemoryAllocator are:
- * 1. This allocator manages space more efficiently. LinkerMemoryAllocator
- * operates in power-of-two sized blocks up to 1k, when this implementation
- * splits the page to aligned size of structure; For example for structures
- * with size 513 this allocator will use 516 (520 for lp64) bytes of data
- * where generalized implementation is going to use 1024 sized blocks.
+ * The differences between this allocator and BionicAllocator are:
+ * 1. This allocator manages space more efficiently. BionicAllocator operates in
+ * power-of-two sized blocks up to 1k, when this implementation splits the
+ * page to aligned size of structure; For example for structures with size
+ * 513 this allocator will use 516 (520 for lp64) bytes of data where
+ * generalized implementation is going to use 1024 sized blocks.
*
- * 2. This allocator does not munmap allocated memory, where LinkerMemoryAllocator does.
+ * 2. Unless all allocated memory is freed, this allocator does not munmap
+ * allocated memory, where BionicAllocator does.
*
- * 3. This allocator provides mprotect services to the user, where LinkerMemoryAllocator
- * always treats it's memory as READ|WRITE.
+ * 3. This allocator provides mprotect services to the user, where BionicAllocator
+ * always treats its memory as READ|WRITE.
*/
template<typename T>
class LinkerTypeAllocator {
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 0e75c85..5a728d3 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -41,6 +41,7 @@
#include <limits.h>
#include <stdlib.h>
+#include <unistd.h>
#include <string>
#include <unordered_map>
@@ -238,9 +239,17 @@
// If the path can be resolved, resolve it
char buf[PATH_MAX];
std::string resolved_path;
- if (realpath(value.c_str(), buf)) {
+ if (access(value.c_str(), R_OK) != 0) {
+ if (errno == ENOENT) {
+ // no need to test for non-existing path. skip.
+ continue;
+ }
+ // If not accessible, don't call realpath as it will just cause
+ // SELinux denial spam. Use the path unresolved.
+ resolved_path = value;
+ } else if (realpath(value.c_str(), buf)) {
resolved_path = buf;
- } else if (errno != ENOENT) {
+ } else {
// realpath is expected to fail with EPERM in some situations, so log
// the failure with INFO rather than DL_WARN. e.g. A binary in
// /data/local/tmp may attempt to stat /postinstall. See
@@ -251,9 +260,6 @@
value.c_str(),
strerror(errno));
resolved_path = value;
- } else {
- // ENOENT: no need to test if binary is under the path
- continue;
}
if (file_is_under_dir(binary_realpath, resolved_path)) {
diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp
index d0e5072..ec07a55 100644
--- a/linker/linker_logger.cpp
+++ b/linker/linker_logger.cpp
@@ -118,11 +118,7 @@
flags_ |= ParseProperty(debug_ld_app);
}
-void LinkerLogger::Log(uint32_t type, const char* format, ...) {
- if ((flags_ & type) == 0) {
- return;
- }
-
+void LinkerLogger::Log(const char* format, ...) {
va_list ap;
va_start(ap, format);
async_safe_format_log_va_list(ANDROID_LOG_DEBUG, "linker", format, ap);
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index 1828799..f9fc38e 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -35,10 +35,10 @@
#include <android-base/macros.h>
-#define LD_LOG(type, x...) \
- { \
- g_linker_logger.Log(type, x); \
- }
+#define LD_LOG(type, x...) \
+ do { \
+ if (g_linker_logger.IsEnabled(type)) g_linker_logger.Log(x); \
+ } while (0)
constexpr const uint32_t kLogErrors = 1 << 0;
constexpr const uint32_t kLogDlopen = 1 << 1;
@@ -49,7 +49,12 @@
LinkerLogger() : flags_(0) { }
void ResetState();
- void Log(uint32_t type, const char* format, ...);
+ void Log(const char* format, ...);
+
+ uint32_t IsEnabled(uint32_t type) {
+ return flags_ & type;
+ }
+
private:
uint32_t flags_;
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 9b4ce47..b0c27dc 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -415,6 +415,8 @@
}
}
+ linker_setup_exe_static_tls(g_argv[0]);
+
// Load ld_preloads and dependencies.
std::vector<const char*> needed_library_name_list;
size_t ld_preloads_count = 0;
@@ -452,8 +454,7 @@
si->increment_ref_count();
}
- layout_linker_static_tls();
-
+ linker_finalize_static_tls();
__libc_init_main_thread_final();
if (!get_cfi_shadow()->InitialLinkDone(solist)) __linker_cannot_link(g_argv[0]);
diff --git a/linker/linker_memory.cpp b/linker/linker_memory.cpp
index f2cce01..ce29997 100644
--- a/linker/linker_memory.cpp
+++ b/linker/linker_memory.cpp
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
-#include "linker_allocator.h"
+#include "private/bionic_allocator.h"
#include <stdlib.h>
#include <sys/cdefs.h>
@@ -36,7 +36,7 @@
#include <async_safe/log.h>
-static LinkerMemoryAllocator g_linker_allocator;
+static BionicAllocator g_bionic_allocator;
static std::atomic<pid_t> fallback_tid(0);
// Used by libdebuggerd_handler to switch allocators during a crash dump, in
@@ -56,16 +56,16 @@
}
}
-static LinkerMemoryAllocator& get_fallback_allocator() {
- static LinkerMemoryAllocator fallback_allocator;
+static BionicAllocator& get_fallback_allocator() {
+ static BionicAllocator fallback_allocator;
return fallback_allocator;
}
-static LinkerMemoryAllocator& get_allocator() {
+static BionicAllocator& get_allocator() {
if (__predict_false(fallback_tid) && __predict_false(gettid() == fallback_tid)) {
return get_fallback_allocator();
}
- return g_linker_allocator;
+ return g_bionic_allocator;
}
void* malloc(size_t byte_count) {
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 93079ca..89119aa 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -82,8 +82,15 @@
split_path(path, ":", &runpaths);
std::string origin = dirname(get_realpath());
- // FIXME: add $LIB and $PLATFORM.
- std::vector<std::pair<std::string, std::string>> params = {{"ORIGIN", origin}};
+ // FIXME: add $PLATFORM.
+ std::vector<std::pair<std::string, std::string>> params = {
+ {"ORIGIN", origin},
+#if defined(LIB_PATH)
+ {"LIB", LIB_PATH},
+#else
+#error "LIB_PATH not defined"
+#endif
+ };
for (auto&& s : runpaths) {
format_string(&s, params);
}
@@ -628,6 +635,10 @@
return secondary_namespaces_;
}
+soinfo_tls* soinfo::get_tls() const {
+ return has_min_version(5) ? tls_.get() : nullptr;
+}
+
ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
return call_ifunc_resolver(s->st_value + load_bias);
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 44bff28..dd9c6aa 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -30,9 +30,13 @@
#include <link.h>
+#include <memory>
#include <string>
+#include <vector>
+#include "private/bionic_elf_tls.h"
#include "linker_namespaces.h"
+#include "linker_tls.h"
#define FLAG_LINKED 0x00000001
#define FLAG_EXE 0x00000004 // The main executable
@@ -61,7 +65,7 @@
// unset.
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
-#define SOINFO_VERSION 4
+#define SOINFO_VERSION 5
typedef void (*linker_dtor_function_t)();
typedef void (*linker_ctor_function_t)(int, char**, char**);
@@ -100,6 +104,11 @@
// TODO(dimitry): remove reference from soinfo member functions to this class.
class VersionTracker;
+struct soinfo_tls {
+ TlsSegment segment;
+ size_t module_id = kTlsUninitializedModuleId;
+};
+
#if defined(__work_around_b_24465209__)
#define SOINFO_NAME_LEN 128
#endif
@@ -284,6 +293,8 @@
void add_secondary_namespace(android_namespace_t* secondary_ns);
android_namespace_list_t& get_secondary_namespaces();
+ soinfo_tls* get_tls() const;
+
void set_mapped_by_caller(bool reserved_map);
bool is_mapped_by_caller() const;
@@ -366,6 +377,10 @@
// version >= 4
ElfW(Relr)* relr_;
size_t relr_count_;
+
+ // version >= 5
+ std::unique_ptr<soinfo_tls> tls_;
+ std::vector<TlsDynamicResolverArg> tlsdesc_args_;
};
// This function is used by dlvsym() to calculate hash of sym_ver
diff --git a/linker/linker_tls.cpp b/linker/linker_tls.cpp
index 3327453..a3aa9bf 100644
--- a/linker/linker_tls.cpp
+++ b/linker/linker_tls.cpp
@@ -28,20 +28,91 @@
#include "linker_tls.h"
+#include <vector>
+
+#include "private/ScopedRWLock.h"
+#include "private/ScopedSignalBlocker.h"
#include "private/bionic_defs.h"
#include "private/bionic_elf_tls.h"
#include "private/bionic_globals.h"
#include "private/linker_native_bridge.h"
+#include "linker_main.h"
+#include "linker_soinfo.h"
+
+static bool g_static_tls_finished;
+static std::vector<TlsModule> g_tls_modules;
+
+static size_t get_unused_module_index() {
+ for (size_t i = 0; i < g_tls_modules.size(); ++i) {
+ if (g_tls_modules[i].soinfo_ptr == nullptr) {
+ return i;
+ }
+ }
+ g_tls_modules.push_back({});
+ __libc_shared_globals()->tls_modules.module_count = g_tls_modules.size();
+ __libc_shared_globals()->tls_modules.module_table = g_tls_modules.data();
+ return g_tls_modules.size() - 1;
+}
+
+static void register_tls_module(soinfo* si, size_t static_offset) {
+ TlsModules& libc_modules = __libc_shared_globals()->tls_modules;
+
+ // The global TLS module table points at the std::vector of modules declared
+ // in this file, so acquire a write lock before modifying the std::vector.
+ ScopedSignalBlocker ssb;
+ ScopedWriteLock locker(&libc_modules.rwlock);
+
+ size_t module_idx = get_unused_module_index();
+
+ soinfo_tls* si_tls = si->get_tls();
+ si_tls->module_id = __tls_module_idx_to_id(module_idx);
+
+ const size_t new_generation = ++libc_modules.generation;
+ __libc_tls_generation_copy = new_generation;
+ if (libc_modules.generation_libc_so != nullptr) {
+ *libc_modules.generation_libc_so = new_generation;
+ }
+
+ g_tls_modules[module_idx] = {
+ .segment = si_tls->segment,
+ .static_offset = static_offset,
+ .first_generation = new_generation,
+ .soinfo_ptr = si,
+ };
+}
+
+static void unregister_tls_module(soinfo* si) {
+ ScopedSignalBlocker ssb;
+ ScopedWriteLock locker(&__libc_shared_globals()->tls_modules.rwlock);
+
+ soinfo_tls* si_tls = si->get_tls();
+ TlsModule& mod = g_tls_modules[__tls_module_id_to_idx(si_tls->module_id)];
+ CHECK(mod.static_offset == SIZE_MAX);
+ CHECK(mod.soinfo_ptr == si);
+ mod = {};
+ si_tls->module_id = kTlsUninitializedModuleId;
+}
+
+// The reference is valid until a TLS module is registered or unregistered.
+const TlsModule& get_tls_module(size_t module_id) {
+ size_t module_idx = __tls_module_id_to_idx(module_id);
+ CHECK(module_idx < g_tls_modules.size());
+ return g_tls_modules[module_idx];
+}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
extern "C" void __linker_reserve_bionic_tls_in_static_tls() {
__libc_shared_globals()->static_tls_layout.reserve_bionic_tls();
}
-// Stub for linker static TLS layout.
-void layout_linker_static_tls() {
+void linker_setup_exe_static_tls(const char* progname) {
+ soinfo* somain = solist_get_somain();
StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
- layout.reserve_tcb();
+ if (somain->get_tls() == nullptr) {
+ layout.reserve_exe_segment_and_tcb(nullptr, progname);
+ } else {
+ register_tls_module(somain, layout.reserve_exe_segment_and_tcb(&somain->get_tls()->segment, progname));
+ }
// The pthread key data is located at the very front of bionic_tls. As a
// temporary workaround, allocate bionic_tls just after the thread pointer so
@@ -49,8 +120,32 @@
// small enough. Specifically, Golang scans forward 384 words from the TP on
// ARM.
// - http://b/118381796
- // - https://groups.google.com/d/msg/golang-dev/yVrkFnYrYPE/2G3aFzYqBgAJ
+ // - https://github.com/golang/go/issues/29674
__linker_reserve_bionic_tls_in_static_tls();
+}
- layout.finish_layout();
+void linker_finalize_static_tls() {
+ g_static_tls_finished = true;
+ __libc_shared_globals()->static_tls_layout.finish_layout();
+}
+
+void register_soinfo_tls(soinfo* si) {
+ soinfo_tls* si_tls = si->get_tls();
+ if (si_tls == nullptr || si_tls->module_id != kTlsUninitializedModuleId) {
+ return;
+ }
+ size_t static_offset = SIZE_MAX;
+ if (!g_static_tls_finished) {
+ StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+ static_offset = layout.reserve_solib_segment(si_tls->segment);
+ }
+ register_tls_module(si, static_offset);
+}
+
+void unregister_soinfo_tls(soinfo* si) {
+ soinfo_tls* si_tls = si->get_tls();
+ if (si_tls == nullptr || si_tls->module_id == kTlsUninitializedModuleId) {
+ return;
+ }
+ return unregister_tls_module(si);
}
diff --git a/linker/linker_tls.h b/linker/linker_tls.h
index 2f0a57d..87e1f0d 100644
--- a/linker/linker_tls.h
+++ b/linker/linker_tls.h
@@ -28,4 +28,38 @@
#pragma once
-void layout_linker_static_tls();
+#include <stdlib.h>
+
+#include "private/bionic_elf_tls.h"
+
+struct TlsModule;
+struct soinfo;
+
+void linker_setup_exe_static_tls(const char* progname);
+void linker_finalize_static_tls();
+
+void register_soinfo_tls(soinfo* si);
+void unregister_soinfo_tls(soinfo* si);
+
+const TlsModule& get_tls_module(size_t module_id);
+
+typedef size_t TlsDescResolverFunc(size_t);
+
+struct TlsDescriptor {
+#if defined(__arm__)
+ size_t arg;
+ TlsDescResolverFunc* func;
+#else
+ TlsDescResolverFunc* func;
+ size_t arg;
+#endif
+};
+
+struct TlsDynamicResolverArg {
+ size_t generation;
+ TlsIndex index;
+};
+
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_static(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_dynamic(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_unresolved_weak(size_t);
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index 9268e31..63e0555 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -43,10 +43,8 @@
linker_config_test.cpp \
linker_globals.cpp \
linked_list_test.cpp \
- linker_memory_allocator_test.cpp \
linker_sleb128_test.cpp \
linker_utils_test.cpp \
- ../linker_allocator.cpp \
../linker_block_allocator.cpp \
../linker_config.cpp \
../linker_utils.cpp \
diff --git a/tests/Android.bp b/tests/Android.bp
index beed07a..8b921d8 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -218,6 +218,44 @@
generated_headers: ["generated_android_ids"],
}
+cc_test_library {
+ name: "libBionicElfTlsTests",
+ defaults: ["bionic_tests_defaults"],
+ srcs: [
+ "elftls_test.cpp",
+ ],
+ include_dirs: [
+ "bionic/libc",
+ ],
+ shared: {
+ enabled: false,
+ },
+ cflags: [
+ "-fno-emulated-tls",
+ ],
+}
+
+cc_test_library {
+ name: "libBionicElfTlsLoaderTests",
+ defaults: ["bionic_tests_defaults"],
+ srcs: [
+ "elftls_dl_test.cpp",
+ ],
+ include_dirs: [
+ "bionic/libc",
+ ],
+ static_libs: [
+ "liblog",
+ "libbase",
+ ],
+ shared: {
+ enabled: false,
+ },
+ cflags: [
+ "-fno-emulated-tls",
+ ],
+}
+
// -----------------------------------------------------------------------------
// Fortify tests.
// -----------------------------------------------------------------------------
@@ -306,6 +344,7 @@
defaults: ["bionic_tests_defaults"],
whole_static_libs: [
"libBionicStandardTests",
+ "libBionicElfTlsTests",
"libfortify1-tests-clang",
"libfortify2-tests-clang",
],
@@ -345,7 +384,7 @@
"libdl_test.cpp",
],
static_libs: [
- "libpagemap",
+ "libmeminfo",
"libziparchive",
"libLLVMObject",
"libLLVMBitReader",
@@ -389,6 +428,7 @@
whole_static_libs: [
"libBionicTests",
"libBionicLoaderTests",
+ "libBionicElfTlsLoaderTests",
],
static_libs: [
@@ -423,11 +463,13 @@
"libdl_preempt_test_1",
"libdl_preempt_test_2",
"libdl_test_df_1_global",
+ "libtest_elftls_shared_var",
+ "libtest_elftls_tprel",
],
static_libs: [
// The order of these libraries matters, do not shuffle them.
"libbase",
- "libpagemap",
+ "libmeminfo",
"libziparchive",
"libz",
"libutils",
@@ -471,6 +513,7 @@
required: [
"cfi_test_helper",
"cfi_test_helper2",
+ "elftls_dlopen_ie_error_helper",
"exec_linker_helper",
"exec_linker_helper_lib",
"libtest_dt_runpath_a",
@@ -528,6 +571,9 @@
"libtest_dlsym_from_this",
"libtest_dlsym_weak_func",
"libtest_dt_runpath_d",
+ "libtest_elftls_shared_var",
+ "libtest_elftls_shared_var_ie",
+ "libtest_elftls_tprel",
"libtest_empty",
"libtest_ifunc_variable_impl",
"libtest_ifunc_variable",
@@ -615,6 +661,13 @@
"gtest_preinit_debuggerd.cpp",
"gtest_globals.cpp",
"gtest_main.cpp",
+
+ // The Bionic allocator has its own C++ API. It isn't packaged into its
+ // own library, so it can only be tested when it's part of libc.a.
+ "bionic_allocator_test.cpp",
+ ],
+ include_dirs: [
+ "bionic/libc",
],
whole_static_libs: [
"libBionicTests",
@@ -629,6 +682,8 @@
"libbase",
"libdebuggerd_handler",
"libgtest_isolated",
+ "libtest_elftls_shared_var",
+ "libtest_elftls_tprel",
],
static_executable: true,
@@ -662,12 +717,15 @@
shared_libs: [
"libdl_preempt_test_1",
"libdl_preempt_test_2",
-
"libdl_test_df_1_global",
+ "libtest_elftls_shared_var",
+ "libtest_elftls_tprel",
],
whole_static_libs: [
"libBionicStandardTests",
+ "libBionicElfTlsTests",
+ "libBionicElfTlsLoaderTests",
"libfortify1-tests-clang",
"libfortify2-tests-clang",
],
diff --git a/tests/__aeabi_read_tp_test.cpp b/tests/__aeabi_read_tp_test.cpp
index ab96af9..6974658 100644
--- a/tests/__aeabi_read_tp_test.cpp
+++ b/tests/__aeabi_read_tp_test.cpp
@@ -32,7 +32,12 @@
#if defined(__arm__)
extern "C" void* __aeabi_read_tp();
-TEST(aeabi, read_tp) {
- ASSERT_EQ(__aeabi_read_tp(), static_cast<void*>(__get_tls()));
-}
#endif
+
+TEST(aeabi, read_tp) {
+#if defined(__arm__)
+ ASSERT_EQ(__aeabi_read_tp(), static_cast<void*>(__get_tls()));
+#else
+ GTEST_LOG_(INFO) << "__aeabi_read_tp is only available on arm32.\n";
+#endif
+}
diff --git a/linker/tests/linker_memory_allocator_test.cpp b/tests/bionic_allocator_test.cpp
similarity index 75%
rename from linker/tests/linker_memory_allocator_test.cpp
rename to tests/bionic_allocator_test.cpp
index c284eaa..f710907 100644
--- a/linker/tests/linker_memory_allocator_test.cpp
+++ b/tests/bionic_allocator_test.cpp
@@ -32,7 +32,7 @@
#include <gtest/gtest.h>
-#include "../linker_allocator.h"
+#include "private/bionic_allocator.h"
#include <unistd.h>
@@ -61,20 +61,20 @@
static size_t kPageSize = sysconf(_SC_PAGE_SIZE);
-TEST(linker_memory, test_alloc_0) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_alloc_0) {
+ BionicAllocator allocator;
void* ptr = allocator.alloc(0);
ASSERT_TRUE(ptr != nullptr);
allocator.free(ptr);
}
-TEST(linker_memory, test_free_nullptr) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_free_nullptr) {
+ BionicAllocator allocator;
allocator.free(nullptr);
}
-TEST(linker_memory, test_realloc) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_realloc) {
+ BionicAllocator allocator;
uint32_t* array = reinterpret_cast<uint32_t*>(allocator.alloc(512));
const size_t array_size = 512 / sizeof(uint32_t);
@@ -127,8 +127,8 @@
ASSERT_EQ(nullptr, allocator.realloc(reallocated_ptr, 0));
}
-TEST(linker_memory, test_small_smoke) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_small_smoke) {
+ BionicAllocator allocator;
uint8_t zeros[16];
memset(zeros, 0, sizeof(zeros));
@@ -150,8 +150,8 @@
allocator.free(ptr2);
}
-TEST(linker_memory, test_huge_smoke) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_huge_smoke) {
+ BionicAllocator allocator;
// this should trigger proxy-to-mmap
test_struct_huge* ptr1 =
@@ -170,8 +170,8 @@
allocator.free(ptr1);
}
-TEST(linker_memory, test_large) {
- LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_large) {
+ BionicAllocator allocator;
test_struct_large* ptr1 =
reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large)));
@@ -212,4 +212,49 @@
allocator.free(ptr_to_free);
}
+TEST(bionic_allocator, test_memalign_small) {
+ BionicAllocator allocator;
+ void* ptr;
+ // simple case
+ ptr = allocator.memalign(0x100, 0x100);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+ allocator.free(ptr);
+
+ // small objects are automatically aligned to their size.
+ ptr = allocator.alloc(0x200);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x200);
+ allocator.free(ptr);
+
+ // the size (0x10) is bumped up to the alignment (0x100)
+ ptr = allocator.memalign(0x100, 0x10);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+ allocator.free(ptr);
+}
+
+TEST(bionic_allocator, test_memalign_large) {
+ BionicAllocator allocator;
+ void* ptr;
+
+ // a large object with alignment < PAGE_SIZE
+ ptr = allocator.memalign(0x100, 0x2000);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+ allocator.free(ptr);
+
+ // a large object with alignment == PAGE_SIZE
+ ptr = allocator.memalign(0x1000, 0x2000);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x1000);
+ allocator.free(ptr);
+
+ // A large object with alignment > PAGE_SIZE is only guaranteed to have page
+ // alignment.
+ ptr = allocator.memalign(0x2000, 0x4000);
+ ASSERT_TRUE(ptr != nullptr);
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x1000);
+ allocator.free(ptr);
+}
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 34013a7..c9ecd2e 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -36,7 +36,7 @@
#include <sys/vfs.h>
#include <sys/wait.h>
-#include <pagemap/pagemap.h>
+#include <meminfo/procmeminfo.h>
#include <ziparchive/zip_archive.h>
#include "gtest_globals.h"
@@ -488,33 +488,23 @@
void GetPss(bool shared_relro, const char* lib, const char* relro_file, pid_t pid,
size_t* total_pss) {
- pm_kernel_t* kernel;
- ASSERT_EQ(0, pm_kernel_create(&kernel));
-
- pm_process_t* process;
- ASSERT_EQ(0, pm_process_create(kernel, pid, &process));
-
- pm_map_t** maps;
- size_t num_maps;
- ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
+ android::meminfo::ProcMemInfo proc_mem(pid);
+ const std::vector<android::meminfo::Vma>& maps = proc_mem.Maps();
+ ASSERT_GT(maps.size(), 0UL);
// Calculate total PSS of the library.
*total_pss = 0;
bool saw_relro_file = false;
- for (size_t i = 0; i < num_maps; ++i) {
- if (android::base::EndsWith(maps[i]->name, lib) || strcmp(maps[i]->name, relro_file) == 0) {
- if (strcmp(maps[i]->name, relro_file) == 0) saw_relro_file = true;
+ for (auto& vma : maps) {
+ if (android::base::EndsWith(vma.name, lib) || (vma.name == relro_file)) {
+ if (vma.name == relro_file) {
+ saw_relro_file = true;
+ }
- pm_memusage_t usage;
- ASSERT_EQ(0, pm_map_usage(maps[i], &usage));
- *total_pss += usage.pss;
+ *total_pss += vma.usage.pss;
}
}
- free(maps);
- pm_process_destroy(process);
- pm_kernel_destroy(kernel);
-
if (shared_relro) ASSERT_TRUE(saw_relro_file);
}
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 5f48e67..e3ba227 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1083,10 +1083,16 @@
}
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("unsupported ELF TLS", dlerror());
+ 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) {
diff --git a/tests/elftls_dl_test.cpp b/tests/elftls_dl_test.cpp
new file mode 100644
index 0000000..0a97c28
--- /dev/null
+++ b/tests/elftls_dl_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <gtest/gtest.h>
+
+#include <thread>
+
+#include "gtest_globals.h"
+#include "utils.h"
+
+// 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;
+
+TEST(elftls_dl, dlopen_shared_var_ie) {
+ // libtest_elftls_shared_var_ie.so can be dlopen'ed, even though it contains a
+ // TLS IE access, because its IE access references a TLS variable from
+ // libtest_elftls_shared_var.so, which is DT_NEEDED by the executable. This
+ // pattern appears in sanitizers, which use TLS IE instrumentation in shared
+ // objects to access special variables exported from the executable or from a
+ // preloaded solib.
+ void* lib = dlopen("libtest_elftls_shared_var_ie.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, dlopen_ie_error) {
+ std::string helper = GetTestlibRoot() +
+ "/elftls_dlopen_ie_error_helper/elftls_dlopen_ie_error_helper";
+ std::string src_path = GetTestlibRoot() + "/libtest_elftls_shared_var_ie.so";
+ std::string dst_path = GetTestlibRoot() + "/libtest_elftls_shared_var.so";
+#if defined(__BIONIC__)
+ std::string error =
+ "dlerror: dlopen failed: TLS symbol \"elftls_shared_var\" in dlopened \"" + dst_path + "\" " +
+ "referenced from \"" + src_path + "\" using IE access model\n";
+#else
+ // glibc will reserve some surplus static TLS memory, allowing this test to pass.
+ std::string error = "success\n";
+#endif
+
+ 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, error.c_str());
+}
diff --git a/tests/elftls_test.cpp b/tests/elftls_test.cpp
new file mode 100644
index 0000000..11d41ce
--- /dev/null
+++ b/tests/elftls_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <thread>
+
+#include "private/__get_tls.h"
+
+// Specify the LE access model explicitly. This file is compiled into the
+// bionic-unit-tests executable, but the compiler sees an -fpic object file
+// output into a static library, so it defaults to dynamic TLS accesses.
+
+// This variable will be zero-initialized (.tbss)
+__attribute__((tls_model("local-exec"))) static __thread int tlsvar_le_zero;
+
+// This variable will have an initializer (.tdata)
+__attribute__((tls_model("local-exec"))) static __thread int tlsvar_le_init = 10;
+
+// Access libtest_elftls_shared_var's TLS variable using an IE access.
+__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
+
+TEST(elftls, basic_le) {
+ // Check the variables on the main thread.
+ ASSERT_EQ(11, ++tlsvar_le_init);
+ ASSERT_EQ(1, ++tlsvar_le_zero);
+
+ // Check variables on a new thread.
+ std::thread([] {
+ ASSERT_EQ(11, ++tlsvar_le_init);
+ ASSERT_EQ(1, ++tlsvar_le_zero);
+ }).join();
+}
+
+TEST(elftls, shared_ie) {
+ ASSERT_EQ(21, ++elftls_shared_var);
+ std::thread([] {
+ ASSERT_EQ(21, ++elftls_shared_var);
+ }).join();
+}
+
+extern "C" int* missing_weak_tls_addr();
+extern "C" int bump_static_tls_var_1();
+extern "C" int bump_static_tls_var_2();
+
+TEST(elftls, tprel_missing_weak) {
+ ASSERT_EQ(static_cast<void*>(__get_tls()), missing_weak_tls_addr());
+ std::thread([] {
+ ASSERT_EQ(static_cast<void*>(__get_tls()), missing_weak_tls_addr());
+ }).join();
+}
+
+TEST(elftls, tprel_addend) {
+ ASSERT_EQ(4, bump_static_tls_var_1());
+ ASSERT_EQ(8, bump_static_tls_var_2());
+ std::thread([] {
+ ASSERT_EQ(4, bump_static_tls_var_1());
+ ASSERT_EQ(8, bump_static_tls_var_2());
+ }).join();
+}
diff --git a/tests/grp_pwd_test.cpp b/tests/grp_pwd_test.cpp
index ca34205..4b207b6 100644
--- a/tests/grp_pwd_test.cpp
+++ b/tests/grp_pwd_test.cpp
@@ -198,7 +198,7 @@
}
TEST(pwd, getpwnam_app_id_u0_i1) {
- check_get_passwd("u0_i1", 99001, TYPE_APP);
+ check_get_passwd("u0_i1", 90001, TYPE_APP);
}
TEST(pwd, getpwnam_app_id_u1_root) {
@@ -218,9 +218,8 @@
}
TEST(pwd, getpwnam_app_id_u1_i0) {
- check_get_passwd("u1_i0", 199000, TYPE_APP);
+ check_get_passwd("u1_i0", 190000, TYPE_APP);
}
-
#if defined(__BIONIC__)
template <typename T>
static void expect_ids(const T& ids) {
@@ -464,7 +463,7 @@
}
TEST(grp, getgrnam_app_id_u0_i1) {
- check_get_group("u0_i1", 99001);
+ check_get_group("u0_i1", 90001);
}
TEST(grp, getgrnam_app_id_u1_root) {
@@ -484,7 +483,7 @@
}
TEST(grp, getgrnam_app_id_u1_i0) {
- check_get_group("u1_i0", 199000);
+ check_get_group("u1_i0", 190000);
}
TEST(grp, getgrnam_r_reentrancy) {
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 79c9a06..05d1ed2 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -40,7 +40,7 @@
}
// -----------------------------------------------------------------------------
-// Library to test ELF TLS
+// Libraries and helper binaries for ELF TLS
// -----------------------------------------------------------------------------
cc_test_library {
name: "libelf-tls-library",
@@ -50,6 +50,35 @@
allow_undefined_symbols: true, // __tls_get_addr is undefined.
}
+cc_test_library {
+ name: "libtest_elftls_shared_var",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_shared_var.cpp"],
+ cflags: ["-fno-emulated-tls"],
+}
+
+cc_test_library {
+ name: "libtest_elftls_shared_var_ie",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_shared_var_ie.cpp"],
+ cflags: ["-fno-emulated-tls"],
+ shared_libs: ["libtest_elftls_shared_var"],
+}
+
+cc_test_library {
+ name: "libtest_elftls_tprel",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_tprel.cpp"],
+ cflags: ["-fno-emulated-tls"],
+}
+
+cc_test {
+ name: "elftls_dlopen_ie_error_helper",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["elftls_dlopen_ie_error_helper.cpp"],
+ ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
// -----------------------------------------------------------------------------
// Library to test gnu-styled hash
// -----------------------------------------------------------------------------
@@ -237,6 +266,10 @@
"libnstest_public",
"libnstest_private",
],
+ // The dlext.ns_anonymous test copies the loaded segments of this shared
+ // object into a new mapping, so every segment must be readable. Turn off
+ // eXecute-Only-Memory. See http://b/123034666.
+ xom: false,
}
cc_test_library {
diff --git a/tests/libs/elftls_dlopen_ie_error_helper.cpp b/tests/libs/elftls_dlopen_ie_error_helper.cpp
new file mode 100644
index 0000000..5902e07
--- /dev/null
+++ b/tests/libs/elftls_dlopen_ie_error_helper.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+// This helper executable attempts to load libtest_elftls_shared_var_ie.so,
+// then reports success or failure. With Bionic, it is expected to fail, because
+// libtest_elftls_shared_var_ie.so tries to access a dynamically-allocated TLS
+// variable using the IE access model intended for static TLS.
+
+int main() {
+ void* lib = dlopen("libtest_elftls_shared_var_ie.so", RTLD_LOCAL | RTLD_NOW);
+ if (lib) {
+ printf("success\n");
+ } else {
+ printf("dlerror: %s\n", dlerror());
+ }
+ return 0;
+}
diff --git a/tests/libs/elftls_shared_var.cpp b/tests/libs/elftls_shared_var.cpp
new file mode 100644
index 0000000..27a15f0
--- /dev/null
+++ b/tests/libs/elftls_shared_var.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 merely declares a global TLS variable without accessing
+// it.
+
+extern "C" __thread int elftls_shared_var = 20;
diff --git a/tests/libs/elftls_shared_var_ie.cpp b/tests/libs/elftls_shared_var_ie.cpp
new file mode 100644
index 0000000..14e2ab0
--- /dev/null
+++ b/tests/libs/elftls_shared_var_ie.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+// Accessing a symbol in libtest_elftls_shared_var.so using an IE access should
+// work iff the solib is part of static TLS.
+__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
+
+extern "C" int bump_shared_var() {
+ return ++elftls_shared_var;
+}
diff --git a/tests/libs/elftls_tprel.cpp b/tests/libs/elftls_tprel.cpp
new file mode 100644
index 0000000..eb2fd93
--- /dev/null
+++ b/tests/libs/elftls_tprel.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 tests TPREL relocations in the dynamic linker. It's always
+// part of static TLS.
+
+// For accesses to these variables, the bfd and lld linkers generate a TPREL
+// relocation with no symbol but a non-zero addend.
+__attribute__((tls_model("initial-exec"))) static __thread int tls_var_1 = 3;
+__attribute__((tls_model("initial-exec"))) static __thread int tls_var_2 = 7;
+
+extern "C" int bump_static_tls_var_1() {
+ return ++tls_var_1;
+}
+
+extern "C" int bump_static_tls_var_2() {
+ return ++tls_var_2;
+}
+
+__attribute__((tls_model("initial-exec"), weak)) extern "C" __thread int missing_weak_tls;
+
+extern "C" int* missing_weak_tls_addr() {
+ // The dynamic linker should resolve a TPREL relocation to this symbol to 0,
+ // which this function adds to the thread pointer.
+ return &missing_weak_tls;
+}
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 4a01278..2506691 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -25,6 +25,7 @@
#include <tinyxml2.h>
#include "private/bionic_config.h"
+#include "private/bionic_malloc.h"
#include "utils.h"
#if defined(__BIONIC__)
@@ -601,3 +602,32 @@
GTEST_LOG_(INFO) << "Host glibc does not pass this test, skipping.\n";
#endif
}
+
+TEST(android_mallopt, error_on_unexpected_option) {
+#if defined(__BIONIC__)
+ const int unrecognized_option = -1;
+ errno = 0;
+ EXPECT_EQ(false, android_mallopt(unrecognized_option, nullptr, 0));
+ EXPECT_EQ(ENOTSUP, errno);
+#else
+ GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
+
+TEST(android_mallopt, init_zygote_child_profiling) {
+#if defined(__BIONIC__)
+ // Successful call.
+ errno = 0;
+ EXPECT_EQ(true, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0));
+ EXPECT_EQ(0, errno);
+
+ // Unexpected arguments rejected.
+ errno = 0;
+ char unexpected = 0;
+ EXPECT_EQ(false, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, &unexpected, 1));
+ EXPECT_EQ(EINVAL, errno);
+#else
+ GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
+
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index dd27aef..77b004f 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -392,11 +392,19 @@
static uint64_t sigset;
struct sigaction sa = {};
sa.sa_handler = [](int) { sigset = GetSignalMask(); };
+ sa.sa_flags = SA_ONSTACK | SA_NODEFER;
sigfillset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, nullptr);
raise(SIGUSR1);
- ASSERT_NE(0ULL, sigset);
- TestSignalMaskFiltered(sigset);
+
+ // On LP32, struct sigaction::sa_mask is only 32-bits wide.
+ unsigned long expected_sigset = ~0UL;
+
+ // SIGKILL and SIGSTOP are always blocked.
+ expected_sigset &= ~(1UL << (SIGKILL - 1));
+ expected_sigset &= ~(1UL << (SIGSTOP - 1));
+
+ ASSERT_EQ(static_cast<uint64_t>(expected_sigset), sigset);
}
TEST(signal, sigaction64_filter) {
@@ -404,11 +412,18 @@
static uint64_t sigset;
struct sigaction64 sa = {};
sa.sa_handler = [](int) { sigset = GetSignalMask(); };
+ sa.sa_flags = SA_ONSTACK | SA_NODEFER;
sigfillset64(&sa.sa_mask);
sigaction64(SIGUSR1, &sa, nullptr);
raise(SIGUSR1);
- ASSERT_NE(0ULL, sigset);
- TestSignalMaskFiltered(sigset);
+
+ uint64_t expected_sigset = ~0ULL;
+
+ // SIGKILL and SIGSTOP are always blocked.
+ expected_sigset &= ~(1ULL << (SIGKILL - 1));
+ expected_sigset &= ~(1ULL << (SIGSTOP - 1));
+
+ ASSERT_EQ(expected_sigset, sigset);
}
TEST(signal, sigprocmask_setmask_filter) {
diff --git a/tests/sys_time_test.cpp b/tests/sys_time_test.cpp
index d033364..5dda7ab 100644
--- a/tests/sys_time_test.cpp
+++ b/tests/sys_time_test.cpp
@@ -147,7 +147,7 @@
tv2.tv_usec += 1000000;
}
- // Should be less than (a very generous, to try to avoid flakiness) 5ms (5000us).
+ // To try to avoid flakiness we'll accept answers within 10,000us (0.01s).
ASSERT_EQ(0, tv2.tv_sec);
- ASSERT_LT(tv2.tv_usec, 5000);
+ ASSERT_LT(tv2.tv_usec, 10'000);
}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 4ec5976..50830ee 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -607,9 +607,9 @@
ts2.tv_nsec += NS_PER_S;
}
- // Should be less than (a very generous, to try to avoid flakiness) 1000000ns.
+ // To try to avoid flakiness we'll accept answers within 10,000,000ns (0.01s).
ASSERT_EQ(0, ts2.tv_sec);
- ASSERT_LT(ts2.tv_nsec, 1000000);
+ ASSERT_LT(ts2.tv_nsec, 10'000'000);
}
TEST(time, clock_gettime_CLOCK_REALTIME) {