Merge "bionic: CFIShadow: Remove use of PAGE_SIZE macro." into main
diff --git a/docs/status.md b/docs/status.md
index 5514935..de2fa10 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -65,6 +65,10 @@
* New system call wrappers: `__riscv_flush_icache` (`<sys/cachectl.h>`),
`__riscv_hwprobe` (`<sys/hwprobe.h>`).
+New libc behavior in V (API level 35):
+ * Added `LD_SHOW_AUXV` to the dynamic linker to dump the ELF auxiliary
+ vector if the environment variable is set.
+
New libc functions in U (API level 34):
* `close_range` and `copy_file_range` (Linux-specific GNU extensions).
* `memset_explicit` in <string.h> (C23 addition).
diff --git a/libc/async_safe/async_safe_log.cpp b/libc/async_safe/async_safe_log.cpp
index 420560f..2bff616 100644
--- a/libc/async_safe/async_safe_log.cpp
+++ b/libc/async_safe/async_safe_log.cpp
@@ -207,10 +207,12 @@
// Decode the conversion specifier.
int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
int base = 10;
- if (conversion == 'x' || conversion == 'X') {
+ if (tolower(conversion) == 'x') {
base = 16;
} else if (conversion == 'o') {
base = 8;
+ } else if (tolower(conversion) == 'b') {
+ base = 2;
}
bool caps = (conversion == 'X');
@@ -360,7 +362,8 @@
format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
} else if (c == 'm') {
strerror_r(errno, buffer, sizeof(buffer));
- } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
+ } else if (tolower(c) == 'b' || c == 'd' || c == 'i' || c == 'o' || c == 'u' ||
+ tolower(c) == 'x') {
/* integers - first read value from stack */
uint64_t value;
int is_signed = (c == 'd' || c == 'i' || c == 'o');
@@ -391,10 +394,10 @@
value = static_cast<uint64_t>((static_cast<int64_t>(value << shift)) >> shift);
}
- if (alternate && value != 0 && (c == 'x' || c == 'o')) {
- if (c == 'x') {
+ if (alternate && value != 0 && (tolower(c) == 'x' || c == 'o' || tolower(c) == 'b')) {
+ if (tolower(c) == 'x' || tolower(c) == 'b') {
buffer[0] = '0';
- buffer[1] = 'x';
+ buffer[1] = c;
format_integer(buffer + 2, sizeof(buffer) - 2, value, c);
} else {
buffer[0] = '0';
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
index 410eb78..3cfb8b5 100644
--- a/libc/bionic/bionic_call_ifunc_resolver.cpp
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -58,12 +58,12 @@
}
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
#elif defined(__riscv)
- // This argument and its value is just a placeholder for now,
- // but it means that if we do pass something in future (such as
- // getauxval() and/or hwprobe key/value pairs), callees will be able to
- // recognize what they're being given.
- typedef ElfW(Addr) (*ifunc_resolver_t)(void*);
- return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(nullptr);
+ // The pointer argument is currently unused, but reserved for future
+ // expansion. If we pass nullptr from the beginning, it'll be easier
+ // to recognize if/when we pass actual data (and matches glibc).
+ typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, void*);
+ static uint64_t hwcap = getauxval(AT_HWCAP);
+ return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap, nullptr);
#else
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
return reinterpret_cast<ifunc_resolver_t>(resolver_addr)();
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index d7a2856..a3c66d4 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -356,7 +356,7 @@
if (memtag_stack) {
void* pg_start =
reinterpret_cast<void*>(page_start(reinterpret_cast<uintptr_t>(stack_top)));
- if (mprotect(pg_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_MTE | PROT_GROWSDOWN)) {
+ if (mprotect(pg_start, page_size(), PROT_READ | PROT_WRITE | PROT_MTE | PROT_GROWSDOWN)) {
async_safe_fatal("error: failed to set PROT_MTE on main thread stack: %m");
}
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 7efbf6d..3b9e6a4 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -38,6 +38,8 @@
#define __hwasan_thread_exit()
#endif
+#include "platform/bionic/page.h"
+
#include "private/bionic_elf_tls.h"
#include "private/bionic_lock.h"
#include "private/bionic_tls.h"
@@ -236,7 +238,7 @@
// On LP64, we could use more but there's no obvious advantage to doing
// so, and the various media processes use RLIMIT_AS as a way to limit
// the amount of allocation they'll do.
-#define PTHREAD_GUARD_SIZE PAGE_SIZE
+#define PTHREAD_GUARD_SIZE max_page_size()
// SIGSTKSZ (8KiB) is not big enough.
// An snprintf to a stack buffer of size PATH_MAX consumes ~7KiB of stack.
diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp
index 3906e2e..edbdef1 100644
--- a/libc/bionic/sysconf.cpp
+++ b/libc/bionic/sysconf.cpp
@@ -41,6 +41,107 @@
#include "platform/bionic/page.h"
#include "private/bionic_tls.h"
+struct sysconf_cache {
+ long size, assoc, linesize;
+
+ static sysconf_cache from_size_and_geometry(int size_id, int geometry_id) {
+ sysconf_cache result;
+ result.size = getauxval(size_id);
+ unsigned long geometry = getauxval(geometry_id);
+ result.assoc = geometry >> 16;
+ result.linesize = geometry & 0xffff;
+ return result;
+ }
+};
+
+struct sysconf_caches {
+ sysconf_cache l1_i, l1_d, l2, l3, l4;
+};
+
+#if defined(__riscv)
+
+static sysconf_caches* __sysconf_caches() {
+ static sysconf_caches cached = []{
+ sysconf_caches info = {};
+ // riscv64 kernels conveniently hand us all this information.
+ info.l1_i = sysconf_cache::from_size_and_geometry(AT_L1I_CACHESIZE, AT_L1I_CACHEGEOMETRY);
+ info.l1_d = sysconf_cache::from_size_and_geometry(AT_L1D_CACHESIZE, AT_L1D_CACHEGEOMETRY);
+ info.l2 = sysconf_cache::from_size_and_geometry(AT_L2_CACHESIZE, AT_L2_CACHEGEOMETRY);
+ info.l3 = sysconf_cache::from_size_and_geometry(AT_L3_CACHESIZE, AT_L3_CACHEGEOMETRY);
+ return info;
+ }();
+ return &cached;
+}
+
+#elif defined(__aarch64__)
+
+static sysconf_caches* __sysconf_caches() {
+ static sysconf_caches cached = []{
+ sysconf_caches info = {};
+ // arm64 is especially limited. We can infer the L1 line sizes, but that's it.
+ uint64_t ctr_el0;
+ __asm__ __volatile__("mrs %0, ctr_el0" : "=r"(ctr_el0));
+ info.l1_i.linesize = 4 << (ctr_el0 & 0xf);
+ info.l1_d.linesize = 4 << ((ctr_el0 >> 16) & 0xf);
+ return info;
+ }();
+ return &cached;
+}
+
+#else
+
+long __sysconf_fread_long(const char* path) {
+ long result = 0;
+ FILE* fp = fopen(path, "re");
+ if (fp != nullptr) {
+ fscanf(fp, "%ld", &result);
+ fclose(fp);
+ }
+ return result;
+}
+
+static sysconf_caches* __sysconf_caches() {
+ static sysconf_caches cached = []{
+ sysconf_caches info = {};
+ char path[64];
+ for (int i = 0; i < 4; i++) {
+ sysconf_cache c;
+
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/size", i);
+ c.size = __sysconf_fread_long(path) * 1024;
+ if (c.size == 0) break;
+
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/ways_of_associativity", i);
+ c.assoc = __sysconf_fread_long(path);
+
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/coherency_line_size", i);
+ c.linesize = __sysconf_fread_long(path);
+
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/level", i);
+ int level = __sysconf_fread_long(path);
+ if (level == 1) {
+ snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/type", i);
+ FILE* fp = fopen(path, "re");
+ char type = fgetc(fp);
+ fclose(fp);
+ if (type == 'D') {
+ info.l1_d = c;
+ } else if (type == 'I') {
+ info.l1_i = c;
+ }
+ } else if (level == 2) {
+ info.l2 = c;
+ } else if (level == 3) {
+ info.l3 = c;
+ }
+ }
+ return info;
+ }();
+ return &cached;
+}
+
+#endif
+
static long __sysconf_rlimit(int resource) {
rlimit rl;
getrlimit(resource, &rl);
@@ -218,23 +319,21 @@
case _SC_XOPEN_STREAMS: return -1; // Obsolescent in POSIX.1-2008.
case _SC_XOPEN_UUCP: return -1;
- // We do not have actual implementations for cache queries.
- // It's valid to return 0 as the result is unknown.
- case _SC_LEVEL1_ICACHE_SIZE: return 0;
- case _SC_LEVEL1_ICACHE_ASSOC: return 0;
- case _SC_LEVEL1_ICACHE_LINESIZE: return 0;
- case _SC_LEVEL1_DCACHE_SIZE: return 0;
- case _SC_LEVEL1_DCACHE_ASSOC: return 0;
- case _SC_LEVEL1_DCACHE_LINESIZE: return 0;
- case _SC_LEVEL2_CACHE_SIZE: return 0;
- case _SC_LEVEL2_CACHE_ASSOC: return 0;
- case _SC_LEVEL2_CACHE_LINESIZE: return 0;
- case _SC_LEVEL3_CACHE_SIZE: return 0;
- case _SC_LEVEL3_CACHE_ASSOC: return 0;
- case _SC_LEVEL3_CACHE_LINESIZE: return 0;
- case _SC_LEVEL4_CACHE_SIZE: return 0;
- case _SC_LEVEL4_CACHE_ASSOC: return 0;
- case _SC_LEVEL4_CACHE_LINESIZE: return 0;
+ case _SC_LEVEL1_ICACHE_SIZE: return __sysconf_caches()->l1_i.size;
+ case _SC_LEVEL1_ICACHE_ASSOC: return __sysconf_caches()->l1_i.assoc;
+ case _SC_LEVEL1_ICACHE_LINESIZE: return __sysconf_caches()->l1_i.linesize;
+ case _SC_LEVEL1_DCACHE_SIZE: return __sysconf_caches()->l1_d.size;
+ case _SC_LEVEL1_DCACHE_ASSOC: return __sysconf_caches()->l1_d.assoc;
+ case _SC_LEVEL1_DCACHE_LINESIZE: return __sysconf_caches()->l1_d.linesize;
+ case _SC_LEVEL2_CACHE_SIZE: return __sysconf_caches()->l2.size;
+ case _SC_LEVEL2_CACHE_ASSOC: return __sysconf_caches()->l2.assoc;
+ case _SC_LEVEL2_CACHE_LINESIZE: return __sysconf_caches()->l2.linesize;
+ case _SC_LEVEL3_CACHE_SIZE: return __sysconf_caches()->l3.size;
+ case _SC_LEVEL3_CACHE_ASSOC: return __sysconf_caches()->l3.assoc;
+ case _SC_LEVEL3_CACHE_LINESIZE: return __sysconf_caches()->l3.linesize;
+ case _SC_LEVEL4_CACHE_SIZE: return __sysconf_caches()->l4.size;
+ case _SC_LEVEL4_CACHE_ASSOC: return __sysconf_caches()->l4.assoc;
+ case _SC_LEVEL4_CACHE_LINESIZE: return __sysconf_caches()->l4.linesize;
default:
errno = EINVAL;
diff --git a/libc/include/bits/page_size.h b/libc/include/bits/page_size.h
new file mode 100644
index 0000000..ca434e5
--- /dev/null
+++ b/libc/include/bits/page_size.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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 <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+#if !defined(__BIONIC_NO_PAGE_SIZE_MACRO)
+#define PAGE_SIZE 4096
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+#endif
+
+__END_DECLS
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 1f08628..4feade5 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -34,6 +34,7 @@
*/
#include <limits.h>
+#include <bits/page_size.h>
#include <bits/pthread_types.h>
#include <sched.h>
#include <sys/cdefs.h>
@@ -73,9 +74,14 @@
#define PTHREAD_BARRIER_SERIAL_THREAD (-1)
#endif
+
#if defined(__LP64__)
+#if defined(PAGE_SIZE)
#define PTHREAD_STACK_MIN (4 * PAGE_SIZE)
#else
+#define PTHREAD_STACK_MIN 65536
+#endif
+#else
#define PTHREAD_STACK_MIN (2 * PAGE_SIZE)
#endif
diff --git a/libc/include/setjmp.h b/libc/include/setjmp.h
index 6d047ae..0aaaac5 100644
--- a/libc/include/setjmp.h
+++ b/libc/include/setjmp.h
@@ -66,12 +66,14 @@
/**
* The size in words of a riscv64 jmp_buf. Room for callee-saved registers,
* including floating point, stack pointer and program counter, various
- * internal implementation details, and leaving some free space.
+ * internal implementation details, and leaving lots of free space.
*
- * Coincidentally matches OpenBSD, though they also save/restore the
- * floating point status register too.
+ * Deliberately very large given the uncertainty around the final form of
+ * hardware shadow stack, and the fact that x86-64 glibc needed to steal
+ * space from their enormous sigset_t (which we don't have) to be able to
+ * implement the CET shadow stack.
*/
-#define _JBLEN 32
+#define _JBLEN 64
#elif defined(__x86_64__)
/** The size in words of an x86-64 jmp_buf. Inherited from OpenBSD. */
#define _JBLEN 11
diff --git a/libc/include/sys/user.h b/libc/include/sys/user.h
index 0ea0285..1d20034 100644
--- a/libc/include/sys/user.h
+++ b/libc/include/sys/user.h
@@ -32,12 +32,9 @@
#include <stddef.h> /* For size_t. */
#include <stdint.h>
-__BEGIN_DECLS
+#include <bits/page_size.h>
-#if !defined(__BIONIC_NO_PAGE_SIZE_MACRO)
-#define PAGE_SIZE 4096
-#define PAGE_MASK (~(PAGE_SIZE - 1))
-#endif
+__BEGIN_DECLS
#if defined(__i386__)
diff --git a/libdl/libdl_cfi.cpp b/libdl/libdl_cfi.cpp
index 3b68fc7..23cd7f5 100644
--- a/libdl/libdl_cfi.cpp
+++ b/libdl/libdl_cfi.cpp
@@ -26,15 +26,15 @@
// dlopen/dlclose.
static struct {
uintptr_t v;
- char padding[PAGE_SIZE - sizeof(v)];
-} shadow_base_storage alignas(PAGE_SIZE);
+ char padding[max_page_size() - sizeof(v)];
+} shadow_base_storage alignas(max_page_size());
// __cfi_init is called by the loader as soon as the shadow is mapped. This may happen very early
// during startup, before libdl.so global constructors, and, on i386, even before __libc_sysinfo is
// initialized. This function should not do any system calls.
extern "C" uintptr_t* __cfi_init(uintptr_t shadow_base) {
shadow_base_storage.v = shadow_base;
- static_assert(sizeof(shadow_base_storage) == PAGE_SIZE, "");
+ static_assert(sizeof(shadow_base_storage) == max_page_size(), "");
return &shadow_base_storage.v;
}
diff --git a/linker/Android.bp b/linker/Android.bp
index 020bd7d..0ccd16d 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -169,6 +169,7 @@
srcs: [
"dlfcn.cpp",
"linker.cpp",
+ "linker_auxv.cpp",
"linker_block_allocator.cpp",
"linker_dlwarning.cpp",
"linker_cfi.cpp",
diff --git a/linker/NOTICE b/linker/NOTICE
index d61a193..7fd1877 100644
--- a/linker/NOTICE
+++ b/linker/NOTICE
@@ -362,3 +362,31 @@
-------------------------------------------------------------------
+Copyright (C) 2023 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.
+
+-------------------------------------------------------------------
+
diff --git a/linker/linker_auxv.cpp b/linker/linker_auxv.cpp
new file mode 100644
index 0000000..d8e4a3e
--- /dev/null
+++ b/linker/linker_auxv.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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 "linker_auxv.h"
+
+#include <elf.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+#include <unistd.h>
+
+#include <async_safe/log.h>
+
+static const char* auxv_name(int at) {
+ switch (at) {
+ case AT_NULL: return "AT_NULL";
+ case AT_IGNORE: return "AT_IGNORE";
+ case AT_EXECFD: return "AT_EXECFD";
+ case AT_PHDR: return "AT_PHDR";
+ case AT_PHENT: return "AT_PHENT";
+ case AT_PHNUM: return "AT_PHNUM";
+ case AT_PAGESZ: return "AT_PAGESZ";
+ case AT_BASE: return "AT_BASE";
+ case AT_FLAGS: return "AT_FLAGS";
+ case AT_ENTRY: return "AT_ENTRY";
+ case AT_NOTELF: return "AT_NOTELF";
+ case AT_UID: return "AT_UID";
+ case AT_EUID: return "AT_EUID";
+ case AT_GID: return "AT_GID";
+ case AT_EGID: return "AT_EGID";
+ case AT_PLATFORM: return "AT_PLATFORM";
+ case AT_HWCAP: return "AT_HWCAP";
+ case AT_CLKTCK: return "AT_CLKTCK";
+ case AT_SECURE: return "AT_SECURE";
+ case AT_BASE_PLATFORM: return "AT_BASE_PLATFORM";
+ case AT_RANDOM: return "AT_RANDOM";
+ case AT_HWCAP2: return "AT_HWCAP2";
+ case AT_RSEQ_FEATURE_SIZE: return "AT_RSEQ_FEATURE_SIZE";
+ case AT_RSEQ_ALIGN: return "AT_RSEQ_ALIGN";
+ case AT_EXECFN: return "AT_EXECFN";
+ case AT_SYSINFO_EHDR: return "AT_SYSINFO_EHDR";
+#if defined(AT_MINSIGSTKSZ)
+ case AT_MINSIGSTKSZ: return "AT_MINSIGSTKSZ";
+#endif
+#if defined(AT_SYSINFO)
+ case AT_SYSINFO: return "AT_SYSINFO";
+#endif
+#if defined(AT_L1I_CACHESIZE)
+ case AT_L1I_CACHESIZE: return "AT_L1I_CACHESIZE";
+#endif
+#if defined(AT_L1I_CACHEGEOMETRY)
+ case AT_L1I_CACHEGEOMETRY: return "AT_L1I_CACHEGEOMETRY";
+#endif
+#if defined(AT_L1D_CACHESIZE)
+ case AT_L1D_CACHESIZE: return "AT_L1D_CACHESIZE";
+#endif
+#if defined(AT_L1D_CACHEGEOMETRY)
+ case AT_L1D_CACHEGEOMETRY: return "AT_L1D_CACHEGEOMETRY";
+#endif
+#if defined(AT_L2_CACHESIZE)
+ case AT_L2_CACHESIZE: return "AT_L2_CACHESIZE";
+#endif
+#if defined(AT_L2_CACHEGEOMETRY)
+ case AT_L2_CACHEGEOMETRY: return "AT_L2_CACHEGEOMETRY";
+#endif
+ }
+ static char name[32];
+ snprintf(name, sizeof(name), "AT_??? (%d)", at);
+ return name;
+}
+
+void ld_show_auxv(ElfW(auxv_t)* auxv) {
+ for (ElfW(auxv_t)* v = auxv; v->a_type != AT_NULL; ++v) {
+ const char* name = auxv_name(v->a_type);
+ long value = v->a_un.a_val;
+ switch (v->a_type) {
+ case AT_SYSINFO_EHDR:
+ case AT_PHDR:
+ case AT_BASE:
+ case AT_ENTRY:
+ case AT_RANDOM:
+ async_safe_format_fd(STDOUT_FILENO, "%-20s %#lx\n", name, value);
+ break;
+ case AT_FLAGS:
+ case AT_HWCAP:
+ case AT_HWCAP2:
+ async_safe_format_fd(STDOUT_FILENO, "%-20s %#lb\n", name, value);
+ break;
+ case AT_EXECFN:
+ case AT_PLATFORM:
+ async_safe_format_fd(STDOUT_FILENO, "%-20s \"%s\"\n", name, reinterpret_cast<char*>(value));
+ break;
+ default:
+ async_safe_format_fd(STDOUT_FILENO, "%-20s %ld\n", name, value);
+ break;
+ }
+ }
+}
diff --git a/linker/linker_auxv.h b/linker/linker_auxv.h
new file mode 100644
index 0000000..c093283
--- /dev/null
+++ b/linker/linker_auxv.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 <elf.h>
+#include <link.h>
+
+void ld_show_auxv(ElfW(auxv_t)* auxv);
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index e92aada..5a33a63 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -32,6 +32,7 @@
#include <sys/auxv.h>
#include "linker.h"
+#include "linker_auxv.h"
#include "linker_cfi.h"
#include "linker_debug.h"
#include "linker_debuggerd.h"
@@ -44,7 +45,6 @@
#include "linker_utils.h"
#include "private/KernelArgumentBlock.h"
-#include "private/ScopedPthreadMutexLocker.h"
#include "private/bionic_call_ifunc_resolver.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
@@ -325,12 +325,14 @@
g_linker_logger.ResetState();
- // Get a few environment variables.
+ // Enable debugging logs?
const char* LD_DEBUG = getenv("LD_DEBUG");
if (LD_DEBUG != nullptr) {
g_ld_debug_verbosity = atoi(LD_DEBUG);
}
+ if (getenv("LD_SHOW_AUXV") != nullptr) ld_show_auxv(args.auxv);
+
#if defined(__LP64__)
INFO("[ Android dynamic linker (64-bit) ]");
#else
@@ -499,11 +501,6 @@
if (!get_cfi_shadow()->InitialLinkDone(solist)) __linker_cannot_link(g_argv[0]);
- // A constructor could spawn a thread that calls into the loader, so as soon
- // as we've called a constructor, we need to hold the lock while accessing
- // global loader state.
- ScopedPthreadMutexLocker locker(&g_dl_mutex);
-
si->call_pre_init_constructors();
si->call_constructors();
@@ -696,6 +693,13 @@
* function, or other GOT reference will generate a segfault.
*/
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
+ // Unlock the loader mutex immediately before transferring to the executable's
+ // entry point. This must happen after destructors are called in this function
+ // (e.g. ~soinfo), so declare this variable very early.
+ struct DlMutexUnlocker {
+ ~DlMutexUnlocker() { pthread_mutex_unlock(&g_dl_mutex); }
+ } unlocker;
+
// Initialize TLS early so system calls and errno work.
KernelArgumentBlock args(raw_args);
bionic_tcb temp_tcb __attribute__((uninitialized));
@@ -758,6 +762,11 @@
// Initialize the linker's static libc's globals
__libc_init_globals();
+ // A constructor could spawn a thread that calls into the loader, so as soon
+ // as we've called a constructor, we need to hold the lock until transferring
+ // to the entry point.
+ pthread_mutex_lock(&g_dl_mutex);
+
// Initialize the linker's own global variables
tmp_linker_so.call_constructors();
diff --git a/tests/async_safe_test.cpp b/tests/async_safe_test.cpp
index dc4db07..cc1b598 100644
--- a/tests/async_safe_test.cpp
+++ b/tests/async_safe_test.cpp
@@ -45,6 +45,15 @@
async_safe_format_buffer(buf, sizeof(buf), "aa%scc", "bb");
EXPECT_STREQ("aabbcc", buf);
+ async_safe_format_buffer(buf, sizeof(buf), "a%bb", 1234);
+ EXPECT_STREQ("a10011010010b", buf);
+
+ async_safe_format_buffer(buf, sizeof(buf), "a%#bb", 1234);
+ EXPECT_STREQ("a0b10011010010b", buf);
+
+ async_safe_format_buffer(buf, sizeof(buf), "a%#Bb", 1234);
+ EXPECT_STREQ("a0B10011010010b", buf);
+
async_safe_format_buffer(buf, sizeof(buf), "a%cc", 'b');
EXPECT_STREQ("abc", buf);
@@ -76,9 +85,15 @@
async_safe_format_buffer(buf, sizeof(buf), "a%xz", 0x12ab);
EXPECT_STREQ("a12abz", buf);
+ async_safe_format_buffer(buf, sizeof(buf), "a%#xz", 0x12ab);
+ EXPECT_STREQ("a0x12abz", buf);
+
async_safe_format_buffer(buf, sizeof(buf), "a%Xz", 0x12ab);
EXPECT_STREQ("a12ABz", buf);
+ async_safe_format_buffer(buf, sizeof(buf), "a%#Xz", 0x12ab);
+ EXPECT_STREQ("a0X12ABz", buf);
+
async_safe_format_buffer(buf, sizeof(buf), "a%08xz", 0x123456);
EXPECT_STREQ("a00123456z", buf);
diff --git a/tests/ifunc_test.cpp b/tests/ifunc_test.cpp
index e3c437e..1fdbf1a 100644
--- a/tests/ifunc_test.cpp
+++ b/tests/ifunc_test.cpp
@@ -60,6 +60,26 @@
return ret42;
}
+#elif defined(__riscv)
+
+#include <sys/hwprobe.h>
+
+static uint64_t g_hwcap;
+
+static riscv_hwprobe g_hwprobes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0}};
+
+extern "C" fn_ptr_t hwcap_resolver(uint64_t hwcap, void* null) {
+ // Check hwcap like arm32/arm64.
+ g_hwcap = hwcap;
+
+ // For now, the pointer argument is reserved for future expansion.
+ if (null != NULL) abort();
+
+ // Ensure that __riscv_hwprobe() can be called from an ifunc.
+ if (__riscv_hwprobe(g_hwprobes, 1, 0, nullptr, 0) != 0) return nullptr;
+ return ret42;
+}
+
#else
extern "C" fn_ptr_t hwcap_resolver() {
@@ -81,6 +101,12 @@
EXPECT_EQ(getauxval(AT_HWCAP2), g_arg._hwcap2);
#elif defined(__arm__)
EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap);
+#elif defined(__riscv)
+ EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap);
+
+ riscv_hwprobe probes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0}};
+ ASSERT_EQ(0, __riscv_hwprobe(probes, 1, 0, nullptr, 0));
+ EXPECT_EQ(probes[0].value, g_hwprobes[0].value);
#endif
}
diff --git a/tests/sys_hwprobe_test.cpp b/tests/sys_hwprobe_test.cpp
index 15028ff..a4b47c7 100644
--- a/tests/sys_hwprobe_test.cpp
+++ b/tests/sys_hwprobe_test.cpp
@@ -30,10 +30,11 @@
#if __has_include(<sys/hwprobe.h>)
#include <sys/hwprobe.h>
+#include <sys/syscall.h>
#endif
TEST(sys_hwprobe, __riscv_hwprobe) {
-#if defined(__riscv) && __has_include(<sys/cachectl.h>)
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
riscv_hwprobe probes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
{.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
ASSERT_EQ(0, __riscv_hwprobe(probes, 2, 0, nullptr, 0));
@@ -60,3 +61,25 @@
GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
#endif
}
+
+TEST(sys_hwprobe, __riscv_hwprobe_syscall_vdso) {
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
+ riscv_hwprobe probes_vdso[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
+ {.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
+ ASSERT_EQ(0, __riscv_hwprobe(probes_vdso, 2, 0, nullptr, 0));
+
+ riscv_hwprobe probes_syscall[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
+ {.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
+ ASSERT_EQ(0, syscall(SYS_riscv_hwprobe, probes_syscall, 2, 0, nullptr, 0));
+
+ // Check we got the same answers from the vdso and the syscall.
+ EXPECT_EQ(RISCV_HWPROBE_KEY_IMA_EXT_0, probes_syscall[0].key);
+ EXPECT_EQ(probes_vdso[0].key, probes_syscall[0].key);
+ EXPECT_EQ(probes_vdso[0].value, probes_syscall[0].value);
+ EXPECT_EQ(RISCV_HWPROBE_KEY_CPUPERF_0, probes_syscall[1].key);
+ EXPECT_EQ(probes_vdso[1].key, probes_syscall[1].key);
+ EXPECT_EQ(probes_vdso[1].value, probes_syscall[1].value);
+#else
+ GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 4c21627..b639a4e 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -1166,6 +1166,26 @@
VERIFY_SYSCONF_UNKNOWN(666);
}
+static void show_cache(const char* name, long size, long assoc, long line_size) {
+ printf("%s cache size: %ld bytes, line size %ld bytes, ", name, size, line_size);
+ if (assoc == 0) {
+ printf("fully");
+ } else {
+ printf("%ld-way", assoc);
+ }
+ printf(" associative\n");
+}
+
+TEST(UNISTD_TEST, sysconf_cache) {
+ // It's not obvious we can _test_ any of these, but we can at least
+ // show the output for humans to inspect.
+ show_cache("L1D", sysconf(_SC_LEVEL1_DCACHE_SIZE), sysconf(_SC_LEVEL1_DCACHE_ASSOC), sysconf(_SC_LEVEL1_DCACHE_LINESIZE));
+ show_cache("L1I", sysconf(_SC_LEVEL1_ICACHE_SIZE), sysconf(_SC_LEVEL1_ICACHE_ASSOC), sysconf(_SC_LEVEL1_ICACHE_LINESIZE));
+ show_cache("L2", sysconf(_SC_LEVEL2_CACHE_SIZE), sysconf(_SC_LEVEL2_CACHE_ASSOC), sysconf(_SC_LEVEL2_CACHE_LINESIZE));
+ show_cache("L3", sysconf(_SC_LEVEL3_CACHE_SIZE), sysconf(_SC_LEVEL3_CACHE_ASSOC), sysconf(_SC_LEVEL3_CACHE_LINESIZE));
+ show_cache("L4", sysconf(_SC_LEVEL4_CACHE_SIZE), sysconf(_SC_LEVEL4_CACHE_ASSOC), sysconf(_SC_LEVEL4_CACHE_LINESIZE));
+}
+
TEST(UNISTD_TEST, dup2_same) {
// POSIX says of dup2:
// If fildes2 is already a valid open file descriptor ...