Merge "Add epoll_pwait2()." into main
diff --git a/docs/status.md b/docs/status.md
index 4b1b46b..3c5d1ba 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -56,6 +56,7 @@
Current libc symbols: https://android.googlesource.com/platform/bionic/+/master/libc/libc.map.txt
New libc functions in V (API level 35):
+ * `tcgetwinsize`, `tcsetwinsize` (POSIX Issue 8 additions).
* `timespec_getres` (C23 addition).
* `localtime_rz`, `mktime_z`, `tzalloc`, and `tzfree` (NetBSD
extensions implemented in tzcode, and the "least non-standard"
diff --git a/libc/Android.bp b/libc/Android.bp
index fe263fd..99455c1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2282,6 +2282,8 @@
"bionic", // crtbegin.c includes bionic/libc_init_common.h
],
+ cflags: [ "-DCRTBEGIN_STATIC", ],
+
srcs: ["arch-common/bionic/crtbegin.c"],
objs: [
"crtbrand",
diff --git a/libc/SECCOMP_ALLOWLIST_COMMON.TXT b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
index 67b662e..aba8303 100644
--- a/libc/SECCOMP_ALLOWLIST_COMMON.TXT
+++ b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
@@ -17,6 +17,9 @@
int rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t*) all
int restart_syscall() all
+# The public API doesn't set errno, so we call this via inline assembler.
+int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
+
# vfork is used by bionic (and java.lang.ProcessBuilder) on some
# architectures. (The others use clone(2) directly instead.)
pid_t vfork() arm,x86,x86_64
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 5791f19..0db5d79 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -358,7 +358,6 @@
# riscv64-specific
int __riscv_flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
-int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
# x86-specific
int __set_thread_area:set_thread_area(void*) x86
diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c
index b87db64..127896a 100644
--- a/libc/arch-common/bionic/crtbegin.c
+++ b/libc/arch-common/bionic/crtbegin.c
@@ -30,17 +30,53 @@
#include <stddef.h>
#include <stdint.h>
-#define SECTION(name) __attribute__((__section__(name)))
-SECTION(".preinit_array") init_func_t* __PREINIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".init_array.0") init_func_t* __INIT_ARRAY__ = (init_func_t*)-1;
-SECTION(".fini_array.0") fini_func_t* __FINI_ARRAY__ = (fini_func_t*)-1;
-#undef SECTION
+extern init_func_t* __preinit_array_start[];
+extern init_func_t* __preinit_array_end[];
+extern init_func_t* __init_array_start[];
+extern init_func_t* __init_array_end[];
+extern fini_func_t* __fini_array_start[];
+extern fini_func_t* __fini_array_end[];
+
+#if !defined(CRTBEGIN_STATIC)
+/* This function will be called during normal program termination
+ * to run the destructors that are listed in the .fini_array section
+ * of the executable, if any.
+ *
+ * 'fini_array' points to a list of function addresses.
+ */
+static void call_fini_array() {
+ fini_func_t** array = __fini_array_start;
+ size_t count = __fini_array_end - __fini_array_start;
+ // Call fini functions in reverse order.
+ while (count-- > 0) {
+ fini_func_t* function = array[count];
+ (*function)();
+ }
+}
+
+// libc.so needs fini_array with sentinels. So create a fake fini_array with sentinels.
+// It contains a function to call functions in real fini_array.
+static fini_func_t* fini_array_with_sentinels[] = {
+ (fini_func_t*)-1,
+ &call_fini_array,
+ (fini_func_t*)0,
+};
+#endif // !defined(CRTBEGIN_STATIC)
__used static void _start_main(void* raw_args) {
- structors_array_t array;
- array.preinit_array = &__PREINIT_ARRAY__;
- array.init_array = &__INIT_ARRAY__;
- array.fini_array = &__FINI_ARRAY__;
+ structors_array_t array = {};
+#if defined(CRTBEGIN_STATIC)
+ array.preinit_array = __preinit_array_start;
+ array.preinit_array_count = __preinit_array_end - __preinit_array_start;
+ array.init_array = __init_array_start;
+ array.init_array_count = __init_array_end - __init_array_start;
+ array.fini_array = __fini_array_start;
+ array.fini_array_count = __fini_array_end - __fini_array_start;
+#else
+ if (__fini_array_end - __fini_array_start > 0) {
+ array.fini_array = fini_array_with_sentinels;
+ }
+#endif // !defined(CRTBEGIN_STATIC)
__libc_init(raw_args, NULL, &main, &array);
}
diff --git a/libc/arch-common/bionic/crtend.S b/libc/arch-common/bionic/crtend.S
index 49c729f..74b3aa9 100644
--- a/libc/arch-common/bionic/crtend.S
+++ b/libc/arch-common/bionic/crtend.S
@@ -34,18 +34,6 @@
__bionic_asm_custom_note_gnu_section()
#endif
- .section .preinit_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
- .section .init_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
- .section .fini_array, "aw"
- ASM_ALIGN_TO_PTR_SIZE
- ASM_PTR_SIZE(0)
-
.section .note.GNU-stack, "", %progbits
#if !defined(__arm__)
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/libc_init_common.h b/libc/bionic/libc_init_common.h
index 6b39d6d..126f002 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -38,6 +38,10 @@
init_func_t** preinit_array;
init_func_t** init_array;
fini_func_t** fini_array;
+ // Below fields are only available in static executables.
+ size_t preinit_array_count;
+ size_t init_array_count;
+ size_t fini_array_count;
} structors_array_t;
__BEGIN_DECLS
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index a3c66d4..1591785 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -69,10 +69,21 @@
extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
extern "C" const char* __gnu_basename(const char* path);
-static void call_array(init_func_t** list, int argc, char* argv[], char* envp[]) {
- // First element is -1, list is null-terminated
- while (*++list) {
- (*list)(argc, argv, envp);
+static void call_array(init_func_t** list, size_t count, int argc, char* argv[], char* envp[]) {
+ while (count-- > 0) {
+ init_func_t* function = *list++;
+ (*function)(argc, argv, envp);
+ }
+}
+
+static void call_fini_array(void* arg) {
+ structors_array_t* structors = reinterpret_cast<structors_array_t*>(arg);
+ fini_func_t** array = structors->fini_array;
+ size_t count = structors->fini_array_count;
+ // Now call each destructor in reverse order.
+ while (count-- > 0) {
+ fini_func_t* function = array[count];
+ (*function)();
}
}
@@ -413,14 +424,15 @@
// Several Linux ABIs don't pass the onexit pointer, and the ones that
// do never use it. Therefore, we ignore it.
- call_array(structors->preinit_array, args.argc, args.argv, args.envp);
- call_array(structors->init_array, args.argc, args.argv, args.envp);
+ call_array(structors->preinit_array, structors->preinit_array_count, args.argc, args.argv,
+ args.envp);
+ call_array(structors->init_array, structors->init_array_count, args.argc, args.argv, args.envp);
// The executable may have its own destructors listed in its .fini_array
// so we need to ensure that these are called when the program exits
// normally.
- if (structors->fini_array != nullptr) {
- __cxa_atexit(__libc_fini,structors->fini_array,nullptr);
+ if (structors->fini_array_count > 0) {
+ __cxa_atexit(call_fini_array, const_cast<structors_array_t*>(structors), nullptr);
}
__libc_init_mte_late();
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/bionic/termios.cpp b/libc/bionic/termios.cpp
index 5fe8eb0..57b34b7 100644
--- a/libc/bionic/termios.cpp
+++ b/libc/bionic/termios.cpp
@@ -34,6 +34,10 @@
#define __BIONIC_TERMIOS_INLINE /* Out of line. */
#include <bits/termios_inlines.h>
+// POSIX added a couple more functions much later, so do the same for them.
+#define __BIONIC_TERMIOS_WINSIZE_INLINE /* Out of line. */
+#include <bits/termios_winsize_inlines.h>
+
// Actually declared in <unistd.h>, present on all API levels.
pid_t tcgetpgrp(int fd) {
pid_t pid;
diff --git a/libc/bionic/vdso.cpp b/libc/bionic/vdso.cpp
index e834ec7..d0f01d0 100644
--- a/libc/bionic/vdso.cpp
+++ b/libc/bionic/vdso.cpp
@@ -24,13 +24,13 @@
#include <sys/cdefs.h>
#include <sys/hwprobe.h>
#include <sys/time.h>
+#include <syscall.h>
#include <time.h>
#include <unistd.h>
extern "C" int __clock_gettime(int, struct timespec*);
extern "C" int __clock_getres(int, struct timespec*);
extern "C" int __gettimeofday(struct timeval*, struct timezone*);
-extern "C" int riscv_hwprobe(struct riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned);
static inline int vdso_return(int result) {
if (__predict_true(result == 0)) return 0;
@@ -88,9 +88,21 @@
auto vdso_riscv_hwprobe =
reinterpret_cast<decltype(&__riscv_hwprobe)>(__libc_globals->vdso[VDSO_RISCV_HWPROBE].fn);
if (__predict_true(vdso_riscv_hwprobe)) {
- return vdso_return(vdso_riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags));
+ return -vdso_riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags);
}
- return riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags);
+ // Inline the syscall directly in case someone's calling it from an
+ // ifunc resolver where we won't be able to set errno on failure.
+ // (Rather than our usual trick of letting the python-generated
+ // wrapper set errno but saving/restoring errno in cases where the API
+ // is to return an error value rather than setting errno.)
+ register long a0 __asm__("a0") = reinterpret_cast<long>(pairs);
+ register long a1 __asm__("a1") = pair_count;
+ register long a2 __asm__("a2") = cpu_count;
+ register long a3 __asm__("a3") = reinterpret_cast<long>(cpus);
+ register long a4 __asm__("a4") = flags;
+ register long a7 __asm__("a7") = __NR_riscv_hwprobe;
+ __asm__ volatile("ecall" : "=r"(a0) : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a7));
+ return -a0;
}
#endif
diff --git a/libc/include/android/legacy_termios_inlines.h b/libc/include/android/legacy_termios_inlines.h
index 6222786..a816b40 100644
--- a/libc/include/android/legacy_termios_inlines.h
+++ b/libc/include/android/legacy_termios_inlines.h
@@ -43,3 +43,10 @@
#include <bits/termios_inlines.h>
#endif
+
+#if __ANDROID_API__ < 35
+
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
+#include <bits/termios_winsize_inlines.h>
+
+#endif
diff --git a/libc/include/bits/termios_winsize_inlines.h b/libc/include/bits/termios_winsize_inlines.h
new file mode 100644
index 0000000..ae246e4
--- /dev/null
+++ b/libc/include/bits/termios_winsize_inlines.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <errno.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <linux/termios.h>
+
+#if !defined(__BIONIC_TERMIOS_WINSIZE_INLINE)
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
+#endif
+
+__BEGIN_DECLS
+
+__BIONIC_TERMIOS_WINSIZE_INLINE int tcgetwinsize(int __fd, struct winsize* _Nonnull __size) {
+ return ioctl(__fd, TIOCGWINSZ, __size);
+}
+
+__BIONIC_TERMIOS_WINSIZE_INLINE int tcsetwinsize(int __fd, const struct winsize* _Nonnull __size) {
+ return ioctl(__fd, TIOCSWINSZ, __size);
+}
+
+__END_DECLS
diff --git a/libc/include/sys/hwprobe.h b/libc/include/sys/hwprobe.h
index f2fd98d..b1a8400 100644
--- a/libc/include/sys/hwprobe.h
+++ b/libc/include/sys/hwprobe.h
@@ -49,7 +49,7 @@
*
* A `__cpu_count` of 0 and null `__cpus` means "all online cpus".
*
- * Returns 0 on success and returns -1 and sets `errno` on failure.
+ * Returns 0 on success and returns an error number on failure.
*/
int __riscv_hwprobe(struct riscv_hwprobe* _Nonnull __pairs, size_t __pair_count, size_t __cpu_count, unsigned long* _Nullable __cpus, unsigned __flags);
diff --git a/libc/include/termios.h b/libc/include/termios.h
index 853b4eb..7abff5d 100644
--- a/libc/include/termios.h
+++ b/libc/include/termios.h
@@ -149,6 +149,25 @@
#endif
+#if __ANDROID_API__ >= 35
+// These two functions were POSIX Issue 8 additions that we can also trivially
+// implement as inlines for older OS version.
+
+/**
+ * tcgetwinsize(3) gets the window size of the given terminal.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int tcgetwinsize(int __fd, struct winsize* _Nonnull __size);
+
+/**
+ * tcsetwinsize(3) sets the window size of the given terminal.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int tcsetwinsize(int __fd, const struct winsize* _Nonnull __size);
+#endif
+
__END_DECLS
#include <android/legacy_termios_inlines.h>
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 75b3d86..e6ea3c2 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1593,6 +1593,8 @@
mktime_z;
__riscv_flush_icache; # riscv64
__riscv_hwprobe; # riscv64
+ tcgetwinsize;
+ tcsetwinsize;
timespec_getres;
tzalloc;
tzfree;
diff --git a/libc/private/CFIShadow.h b/libc/private/CFIShadow.h
index ec87e3c..cbdf0f7 100644
--- a/libc/private/CFIShadow.h
+++ b/libc/private/CFIShadow.h
@@ -68,8 +68,7 @@
#endif
// Shadow is 2 -> 2**kShadowGranularity.
- static constexpr uintptr_t kShadowSize =
- align_up((kMaxTargetAddr >> (kShadowGranularity - 1)), PAGE_SIZE);
+ static constexpr uintptr_t kShadowSize = kMaxTargetAddr >> (kShadowGranularity - 1);
// Returns offset inside the shadow region for an address.
static constexpr uintptr_t MemToShadowOffset(uintptr_t x) {
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/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/headers/posix/termios_h.c b/tests/headers/posix/termios_h.c
index 1255c16..0a67eaa 100644
--- a/tests/headers/posix/termios_h.c
+++ b/tests/headers/posix/termios_h.c
@@ -42,6 +42,12 @@
STRUCT_MEMBER(struct termios, tcflag_t, c_lflag);
STRUCT_MEMBER_ARRAY(struct termios, cc_t/*[]*/, c_cc);
+#if !defined(__GLIBC__) // Our glibc is too old.
+ TYPE(struct winsize);
+ STRUCT_MEMBER(struct winsize, unsigned short, ws_row);
+ STRUCT_MEMBER(struct winsize, unsigned short, ws_col);
+#endif
+
MACRO(NCCS);
MACRO(VEOF);
@@ -162,6 +168,12 @@
FUNCTION(tcflush, int (*f)(int, int));
FUNCTION(tcgetattr, int (*f)(int, struct termios*));
FUNCTION(tcgetsid, pid_t (*f)(int));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(tcgetwinsize, int (*f)(int, struct winsize*));
+#endif
FUNCTION(tcsendbreak, int (*f)(int, int));
FUNCTION(tcsetattr, int (*f)(int, int, const struct termios*));
+#if !defined(__GLIBC__) // Our glibc is too old.
+ FUNCTION(tcsetwinsize, int (*f)(int, const struct winsize*));
+#endif
}
diff --git a/tests/sys_hwprobe_test.cpp b/tests/sys_hwprobe_test.cpp
index a4b47c7..3c8dce2 100644
--- a/tests/sys_hwprobe_test.cpp
+++ b/tests/sys_hwprobe_test.cpp
@@ -83,3 +83,12 @@
GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
#endif
}
+
+TEST(sys_hwprobe, __riscv_hwprobe_fail) {
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
+ riscv_hwprobe probes[] = {};
+ ASSERT_EQ(EINVAL, __riscv_hwprobe(probes, 0, 0, nullptr, ~0));
+#else
+ GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}
diff --git a/tests/termios_test.cpp b/tests/termios_test.cpp
index 6290771..a8d5890 100644
--- a/tests/termios_test.cpp
+++ b/tests/termios_test.cpp
@@ -29,6 +29,8 @@
#include <termios.h>
#include <errno.h>
+#include <fcntl.h>
+#include <pty.h>
#include <gtest/gtest.h>
@@ -96,3 +98,49 @@
EXPECT_EQ(1, t.c_cc[VMIN]);
EXPECT_EQ(0, t.c_cc[VTIME]);
}
+
+TEST(termios, tcgetwinsize_tcsetwinsize_invalid) {
+#if !defined(__GLIBC__)
+ winsize ws = {};
+
+ errno = 0;
+ ASSERT_EQ(-1, tcgetwinsize(-1, &ws));
+ ASSERT_EQ(EBADF, errno);
+
+ errno = 0;
+ ASSERT_EQ(-1, tcsetwinsize(-1, &ws));
+ ASSERT_EQ(EBADF, errno);
+#else
+ GTEST_SKIP() << "glibc too old";
+#endif
+}
+
+TEST(termios, tcgetwinsize_tcsetwinsize) {
+#if !defined(__GLIBC__)
+ int pty, tty;
+ winsize ws = {123, 456, 9999, 9999};
+ ASSERT_EQ(0, openpty(&pty, &tty, nullptr, nullptr, &ws));
+
+ winsize actual = {};
+ ASSERT_EQ(0, tcgetwinsize(tty, &actual));
+ EXPECT_EQ(ws.ws_xpixel, actual.ws_xpixel);
+ EXPECT_EQ(ws.ws_ypixel, actual.ws_ypixel);
+ EXPECT_EQ(ws.ws_row, actual.ws_row);
+ EXPECT_EQ(ws.ws_col, actual.ws_col);
+
+ ws = {1, 2, 3, 4};
+ ASSERT_EQ(0, tcsetwinsize(tty, &ws));
+
+ actual = {};
+ ASSERT_EQ(0, tcgetwinsize(tty, &actual));
+ EXPECT_EQ(ws.ws_xpixel, actual.ws_xpixel);
+ EXPECT_EQ(ws.ws_ypixel, actual.ws_ypixel);
+ EXPECT_EQ(ws.ws_row, actual.ws_row);
+ EXPECT_EQ(ws.ws_col, actual.ws_col);
+
+ close(pty);
+ close(tty);
+#else
+ GTEST_SKIP() << "glibc too old";
+#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 ...