Merge "Make fork equivalent to vfork when HWASan or MTE stack tagging is enabled."
diff --git a/README.md b/README.md
index 8d8e583..5107f42 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,9 @@
#### tests/ --- unit tests
The `tests/` directory contains unit tests. Roughly arranged as one file per
-publicly-exported header file.
+publicly-exported header file. `tests/headers/` contains compile-only tests
+that just check that things are _in_ the headers, whereas the "real" tests
+check actual _behavior_.
#### benchmarks/ --- benchmarks
diff --git a/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index c5a6ac3..8ffd96f 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -107,7 +107,7 @@
## Correct soname/path handling (Available in API level >= 23)
The dynamic linker now understands the difference
-between a library’s soname and its path (public bug
+between a library’s soname and its path (public bug
https://code.google.com/p/android/issues/detail?id=6670). API level 23
is the first release where search by soname is implemented. Earlier
releases would assume that the basename of the library was the soname,
@@ -326,12 +326,12 @@
## Missing SONAME (Enforced for API level >= 23)
-Each ELF shared object (“native library”) must have a SONAME (Shared
-Object Name) attribute. The NDK toolchain adds this attribute by default,
-so its absence indicates either a misconfigured alternative toolchain
-or a misconfiguration in your build system. A missing SONAME may lead
-to runtime issues such as the wrong library being loaded: the filename
-is used instead when this attribute is missing.
+Each ELF shared object (“native library”) must have a SONAME
+(Shared Object Name) attribute. The NDK build systems add this
+attribute by default, so its absence (or an incorrect soname) indicates
+a misconfiguration in your build system. A missing SONAME may lead to
+runtime issues such as the wrong library being loaded: the filename is
+used instead when this attribute is missing.
```
$ readelf --dynamic libWithSoName.so | grep SONAME
@@ -346,7 +346,7 @@
*Resolution*: the current NDK generates the correct SONAME by
default. Ensure you're using the current NDK and that you haven't
configured your build system to generate incorrect SONAME entries (using
-the -soname linker option).
+the `-soname` linker option).
## `__register_atfork` (Available in API level >= 23)
diff --git a/apex/Android.bp b/apex/Android.bp
index 90a14b2..6201ad1 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -41,6 +41,11 @@
"libc_malloc_debug",
"libc_malloc_hooks",
],
+ arch: {
+ arm64: {
+ native_shared_libs: ["libc_hwasan", "libclang_rt.hwasan"],
+ },
+ },
binaries: [
"linkerconfig",
],
diff --git a/docs/defines.md b/docs/defines.md
index 4775cd2..65a715e 100644
--- a/docs/defines.md
+++ b/docs/defines.md
@@ -54,7 +54,7 @@
work around issues with some of them, use these macros to detect the versinon of
the NDK you're being built with. Usually only `__NDK_MAJOR__` will be necessary.
-## `__arm__`, `__aarch64__`, `__i386__`, `__x86_64__`
+## `__arm__`, `__aarch64__`, `__i386__`, `__x86_64__`, `__riscv`
If your code is specific to a particular processor architecture, use these
macros to conditionally compile. Note that the ABI usually called `arm64` uses
diff --git a/docs/status.md b/docs/status.md
index 3d104e4..411b140 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -55,6 +55,9 @@
Current libc symbols: https://android.googlesource.com/platform/bionic/+/master/libc/libc.map.txt
+New libc functions in V (API level 35):
+ * `timespec_getres` (C23 addition).
+
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).
@@ -67,6 +70,8 @@
* Support for `%b` and `%B` in the printf/wprintf family, `%b` in the
scanf/wscanf family, and `0b` prefixes with base 0 in the strtol/wcstol
family.
+ * Support for `wN` length modifiers in the printf/wprintf family.
+ * tmpfile() now respects $TMPDIR.
New libc functions in T (API level 33):
* `backtrace`, `backtrace_symbols`, `backtrace_symbols_fd` (`<execinfo.h>`).
diff --git a/libc/Android.bp b/libc/Android.bp
index 93f9ce9..ecabb06 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1205,8 +1205,8 @@
"bionic/termios.cpp",
"bionic/thread_private.cpp",
"bionic/threads.cpp",
+ "bionic/time.cpp",
"bionic/time_l.cpp",
- "bionic/timespec_get.cpp",
"bionic/tmpfile.cpp",
"bionic/umount.cpp",
"bionic/unlink.cpp",
@@ -1318,7 +1318,7 @@
// ========================================================
genrule {
- name: "syscalls-arm.S",
+ name: "syscalls-arm",
out: ["syscalls-arm.S"],
srcs: ["SYSCALLS.TXT"],
tools: ["gensyscalls"],
@@ -1326,7 +1326,7 @@
}
genrule {
- name: "syscalls-arm64.S",
+ name: "syscalls-arm64",
out: ["syscalls-arm64.S"],
srcs: ["SYSCALLS.TXT"],
tools: ["gensyscalls"],
@@ -1334,7 +1334,7 @@
}
genrule {
- name: "syscalls-riscv64.S",
+ name: "syscalls-riscv64",
out: ["syscalls-riscv64.S"],
srcs: ["SYSCALLS.TXT"],
tools: ["gensyscalls"],
@@ -1342,7 +1342,7 @@
}
genrule {
- name: "syscalls-x86.S",
+ name: "syscalls-x86",
out: ["syscalls-x86.S"],
srcs: ["SYSCALLS.TXT"],
tools: ["gensyscalls"],
@@ -1350,7 +1350,7 @@
}
genrule {
- name: "syscalls-x86_64.S",
+ name: "syscalls-x86_64",
out: ["syscalls-x86_64.S"],
srcs: ["SYSCALLS.TXT"],
tools: ["gensyscalls"],
@@ -1362,19 +1362,19 @@
srcs: ["bionic/__set_errno.cpp"],
arch: {
arm: {
- srcs: [":syscalls-arm.S"],
+ srcs: [":syscalls-arm"],
},
arm64: {
- srcs: [":syscalls-arm64.S"],
+ srcs: [":syscalls-arm64"],
},
riscv64: {
- srcs: [":syscalls-riscv64.S"],
+ srcs: [":syscalls-riscv64"],
},
x86: {
- srcs: [":syscalls-x86.S"],
+ srcs: [":syscalls-x86"],
},
x86_64: {
- srcs: [":syscalls-x86_64.S"],
+ srcs: [":syscalls-x86_64"],
},
},
name: "libc_syscalls",
@@ -1674,13 +1674,12 @@
// ========================================================
// libc.a + libc.so
// ========================================================
-cc_library {
+cc_defaults {
defaults: [
"libc_defaults",
"libc_native_allocator_defaults",
],
- name: "libc",
- static_ndk_lib: true,
+ name: "libc_library_defaults",
product_variables: {
platform_sdk_version: {
asflags: ["-DPLATFORM_SDK_VERSION=%d"],
@@ -1807,20 +1806,7 @@
},
},
- stubs: {
- symbol_file: "libc.map.txt",
- versions: [
- "29",
- "R",
- "current",
- ],
- },
- llndk: {
- symbol_file: "libc.map.txt",
- export_headers_as_system: true,
- export_preprocessed_headers: ["include"],
- export_llndk_headers: ["libc_llndk_headers"],
- },
+
apex_available: [
"//apex_available:platform",
"com.android.runtime",
@@ -1835,9 +1821,58 @@
},
}
+cc_library {
+ name: "libc",
+ defaults: [
+ "libc_library_defaults",
+ ],
+ stubs: {
+ symbol_file: "libc.map.txt",
+ versions: [
+ "29",
+ "R",
+ "current",
+ ],
+ },
+ static_ndk_lib: true,
+ llndk: {
+ symbol_file: "libc.map.txt",
+ export_headers_as_system: true,
+ export_preprocessed_headers: ["include"],
+ export_llndk_headers: ["libc_llndk_headers"],
+ },
+}
+
+cc_library {
+ name: "libc_hwasan",
+ defaults: [
+ "libc_library_defaults",
+ ],
+ sanitize: {
+ hwaddress: true,
+ },
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
+ stem: "libc",
+ relative_install_path: "hwasan",
+ // We don't really need the stubs, but this needs to stay to trigger the
+ // symlink logic in soong.
+ stubs: {
+ symbol_file: "libc.map.txt",
+ },
+ native_bridge_supported: false,
+ // It is never correct to depend on this directly. This is only
+ // needed for the runtime apex, and in base_system.mk.
+ visibility: ["//bionic/apex"],
+}
+
genrule {
name: "libc.arm.map",
- out: ["libc.arm.map"],
+ out: ["libc.arm.map.txt"],
srcs: ["libc.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm $(in) $(out)",
@@ -1845,7 +1880,7 @@
genrule {
name: "libc.arm64.map",
- out: ["libc.arm64.map"],
+ out: ["libc.arm64.map.txt"],
srcs: ["libc.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm64 $(in) $(out)",
@@ -1853,7 +1888,7 @@
genrule {
name: "libc.riscv64.map",
- out: ["libc.riscv64.map"],
+ out: ["libc.riscv64.map.txt"],
srcs: ["libc.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) riscv64 $(in) $(out)",
@@ -1861,7 +1896,7 @@
genrule {
name: "libc.x86.map",
- out: ["libc.x86.map"],
+ out: ["libc.x86.map.txt"],
srcs: ["libc.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86 $(in) $(out)",
@@ -1869,7 +1904,7 @@
genrule {
name: "libc.x86_64.map",
- out: ["libc.x86_64.map"],
+ out: ["libc.x86_64.map.txt"],
srcs: ["libc.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86_64 $(in) $(out)",
@@ -2074,7 +2109,7 @@
genrule {
name: "libstdc++.arm.map",
- out: ["libstdc++.arm.map"],
+ out: ["libstdc++.arm.map.txt"],
srcs: ["libstdc++.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm $(in) $(out)",
@@ -2082,7 +2117,7 @@
genrule {
name: "libstdc++.arm64.map",
- out: ["libstdc++.arm64.map"],
+ out: ["libstdc++.arm64.map.txt"],
srcs: ["libstdc++.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm64 $(in) $(out)",
@@ -2090,7 +2125,7 @@
genrule {
name: "libstdc++.riscv64.map",
- out: ["libstdc++.riscv64.map"],
+ out: ["libstdc++.riscv64.map.txt"],
srcs: ["libstdc++.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) riscv64 $(in) $(out)",
@@ -2098,7 +2133,7 @@
genrule {
name: "libstdc++.x86.map",
- out: ["libstdc++.x86.map"],
+ out: ["libstdc++.x86.map.txt"],
srcs: ["libstdc++.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86 $(in) $(out)",
@@ -2106,7 +2141,7 @@
genrule {
name: "libstdc++.x86_64.map",
- out: ["libstdc++.x86_64.map"],
+ out: ["libstdc++.x86_64.map.txt"],
srcs: ["libstdc++.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86_64 $(in) $(out)",
@@ -2147,10 +2182,6 @@
name: "crt_defaults",
defaults: ["crt_and_memtag_defaults"],
system_shared_libs: [],
-
- conlyflags: [
- "-mllvm -disable-check-noreturn-call",
- ]
}
cc_defaults {
@@ -2183,6 +2214,8 @@
srcs: ["arch-common/bionic/crtbrand.S"],
defaults: ["crt_so_defaults"],
+ // crtbrand is an intermediate artifact, not a final CRT object.
+ exclude_from_ndk_sysroot: true,
}
cc_object {
diff --git a/libc/arch-riscv64/bionic/setjmp.S b/libc/arch-riscv64/bionic/setjmp.S
index ba3cacf..26f7ec9 100644
--- a/libc/arch-riscv64/bionic/setjmp.S
+++ b/libc/arch-riscv64/bionic/setjmp.S
@@ -36,50 +36,52 @@
// 0 sigflag/cookie setjmp cookie in top 31 bits, signal mask flag in low bit
// 1 sigmask 64-bit signal mask
// 2 ra
-// 3 s0
+// 3 sp
+// 4 gp
+// 5 s0
// ......
-// 14 s11
-// 15 sp
-// 16 fs0
+// 16 s11
+// 17 fs0
// ......
-// 27 fs11
-// 28 checksum
+// 28 fs11
+// 29 checksum
// _JBLEN: defined in bionic/libc/include/setjmp.h
#define _JB_SIGFLAG 0
#define _JB_SIGMASK 1 * 8
#define _JB_RA 2 * 8
-#define _JB_S0 3 * 8
-#define _JB_S1 4 * 8
-#define _JB_S2 5 * 8
-#define _JB_S3 6 * 8
-#define _JB_S4 7 * 8
-#define _JB_S5 8 * 8
-#define _JB_S6 9 * 8
-#define _JB_S7 10 * 8
-#define _JB_S8 11 * 8
-#define _JB_S9 12 * 8
-#define _JB_S10 13 * 8
-#define _JB_S11 14 * 8
-#define _JB_SP 15 * 8
-#define _JB_FS0 16 * 8
-#define _JB_FS1 17 * 8
-#define _JB_FS2 18 * 8
-#define _JB_FS3 19 * 8
-#define _JB_FS4 20 * 8
-#define _JB_FS5 21 * 8
-#define _JB_FS6 22 * 8
-#define _JB_FS7 23 * 8
-#define _JB_FS8 24 * 8
-#define _JB_FS9 25 * 8
-#define _JB_FS10 26 * 8
-#define _JB_FS11 27 * 8
-#define _JB_CHECKSUM 28 * 8
+#define _JB_SP 3 * 8
+#define _JB_GP 4 * 8
+#define _JB_S0 5 * 8
+#define _JB_S1 6 * 8
+#define _JB_S2 7 * 8
+#define _JB_S3 8 * 8
+#define _JB_S4 9 * 8
+#define _JB_S5 10 * 8
+#define _JB_S6 11 * 8
+#define _JB_S7 12 * 8
+#define _JB_S8 13 * 8
+#define _JB_S9 14 * 8
+#define _JB_S10 15 * 8
+#define _JB_S11 16 * 8
+#define _JB_FS0 17 * 8
+#define _JB_FS1 18 * 8
+#define _JB_FS2 19 * 8
+#define _JB_FS3 20 * 8
+#define _JB_FS4 21 * 8
+#define _JB_FS5 22 * 8
+#define _JB_FS6 23 * 8
+#define _JB_FS7 24 * 8
+#define _JB_FS8 25 * 8
+#define _JB_FS9 26 * 8
+#define _JB_FS10 27 * 8
+#define _JB_FS11 28 * 8
+#define _JB_CHECKSUM 29 * 8
.macro m_mangle_registers reg, sp_reg
xor s0, s0, \reg
xor s1, s1, \reg
- xor a4, a4, \reg // a4 is the masked s2 (x18) for SCS.
+ xor s2, s2, \reg
xor s3, s3, \reg
xor s4, s4, \reg
xor s5, s5, \reg
@@ -89,12 +91,13 @@
xor s9, s9, \reg
xor s10, s10, \reg
xor s11, s11, \reg
+ xor a4, a4, \reg // a4 is the masked gp (x3) for SCS.
xor \sp_reg, \sp_reg, \reg
.endm
.macro m_calculate_checksum dst, src, scratch
li \dst, 0
- .irp i,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27
+ .irp i,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
ld \scratch, (\i * 8)(\src)
xor \dst, \dst, \scratch
.endr
@@ -152,19 +155,21 @@
andi a1, a1, -2
// Mask off the high bits of the shadow call stack pointer.
- // We only store the low bits of x18 to avoid leaking the
+ // We only store the low bits of gp to avoid leaking the
// shadow call stack address into memory.
// See the SCS commentary in pthread_internal.h for more detail.
li a4, SCS_MASK
- and a4, a4, x18
+ and a4, a4, gp
// Save core registers.
mv a2, sp
m_mangle_registers a1, sp_reg=a2
sd ra, _JB_RA(a0)
+ sd a4, _JB_GP(a0) // a4 is the masked gp (x3) for SCS.
+ sd a2, _JB_SP(a0)
sd s0, _JB_S0(a0)
sd s1, _JB_S1(a0)
- sd a4, _JB_S2(a0) // a4 is the masked s2 (x18) for SCS.
+ sd s2, _JB_S2(a0)
sd s3, _JB_S3(a0)
sd s4, _JB_S4(a0)
sd s5, _JB_S5(a0)
@@ -174,7 +179,6 @@
sd s9, _JB_S9(a0)
sd s10, _JB_S10(a0)
sd s11, _JB_S11(a0)
- sd a2, _JB_SP(a0)
m_unmangle_registers a1, sp_reg=a2
// Save floating point registers.
@@ -236,9 +240,10 @@
// Restore core registers.
andi a2, a2, -2
ld ra, _JB_RA(a0)
+ ld a4, _JB_GP(a0) // Don't clobber the upper bits of gp (x3) used for SCS yet.
ld s0, _JB_S0(a0)
ld s1, _JB_S1(a0)
- ld a4, _JB_S2(a0) // Don't clobber s2 (x18) used for SCS yet.
+ ld s2, _JB_S2(a0)
ld s3, _JB_S3(a0)
ld s4, _JB_S4(a0)
ld s5, _JB_S5(a0)
@@ -254,8 +259,8 @@
// Restore the low bits of the shadow call stack pointer.
li a5, ~SCS_MASK
- and x18, x18, a5
- or x18, a4, x18
+ and gp, gp, a5
+ or gp, gp, a4
addi sp, sp, -24
sd ra, 0(sp)
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 5d5ecac..59b2ddb 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -289,6 +289,7 @@
"LD_DEBUG",
"LD_DEBUG_OUTPUT",
"LD_DYNAMIC_WEAK",
+ "LD_HWASAN",
"LD_LIBRARY_PATH",
"LD_ORIGIN_PATH",
"LD_PRELOAD",
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 15d6d6d..7bf9b40 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -133,14 +133,14 @@
size_t scs_offset =
(getpid() == 1) ? 0 : (arc4random_uniform(SCS_GUARD_REGION_SIZE / SCS_SIZE - 1) * SCS_SIZE);
- // Make the stack readable and writable and store its address in x18.
- // This is deliberately the only place where the address is stored.
+ // Make the stack read-write, and store its address in the register we're using as the shadow
+ // stack pointer. This is deliberately the only place where the address is stored.
char* scs = scs_aligned_guard_region + scs_offset;
mprotect(scs, SCS_SIZE, PROT_READ | PROT_WRITE);
#if defined(__aarch64__)
__asm__ __volatile__("mov x18, %0" ::"r"(scs));
#elif defined(__riscv)
- __asm__ __volatile__("mv x18, %0" ::"r"(scs));
+ __asm__ __volatile__("mv gp, %0" ::"r"(scs));
#endif
#endif
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 8f2b9ac..7efbf6d 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -110,7 +110,8 @@
// are actually used.
//
// This address is only used to deallocate the shadow call stack on thread
- // exit; the address of the stack itself is stored only in the x18 register.
+ // exit; the address of the stack itself is stored only in the register used
+ // as the shadow stack pointer (x18 on arm64, gp on riscv64).
//
// Because the protection offered by SCS relies on the secrecy of the stack
// address, storing the address here weakens the protection, but only
@@ -119,22 +120,24 @@
// to other allocations), but not the stack itself, which is <0.1% of the size
// of the guard region.
//
- // longjmp()/setjmp() don't store all the bits of x18, only the bottom bits
- // covered by SCS_MASK. Since longjmp()/setjmp() between different threads is
- // undefined behavior (and unsupported on Android), we can retrieve the high
- // bits of x18 from the current value in x18 --- all the jmp_buf needs to store
- // is where exactly the shadow stack pointer is in the thread's shadow stack:
- // the bottom bits of x18.
+ // longjmp()/setjmp() don't store all the bits of the shadow stack pointer,
+ // only the bottom bits covered by SCS_MASK. Since longjmp()/setjmp() between
+ // different threads is undefined behavior (and unsupported on Android), we
+ // can retrieve the high bits of the shadow stack pointer from the current
+ // value in the register --- all the jmp_buf needs to store is where exactly
+ // the shadow stack pointer is *within* the thread's shadow stack: the bottom
+ // bits of the register.
//
// There are at least two other options for discovering the start address of
// the guard region on thread exit, but they are not as simple as storing in
// TLS.
//
- // 1) Derive it from the value of the x18 register. This is only possible in
- // processes that do not contain legacy code that might clobber x18,
- // therefore each process must declare early during process startup whether
- // it might load legacy code.
- // TODO: riscv64 has no legacy code, so we can actually go this route there!
+ // 1) Derive it from the current value of the shadow stack pointer. This is
+ // only possible in processes that do not contain legacy code that might
+ // clobber x18 on arm64, therefore each process must declare early during
+ // process startup whether it might load legacy code.
+ // TODO: riscv64 has no legacy code, so we can actually go this route
+ // there, but hopefully we'll actually get the Zisslpcfi extension instead.
// 2) Mark the guard region as such using prctl(PR_SET_VMA_ANON_NAME) and
// discover its address by reading /proc/self/maps. One issue with this is
// that reading /proc/self/maps can race with allocations, so we may need
diff --git a/libc/bionic/syslog.cpp b/libc/bionic/syslog.cpp
index 6b17d26..a459c6b 100644
--- a/libc/bionic/syslog.cpp
+++ b/libc/bionic/syslog.cpp
@@ -18,18 +18,22 @@
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
+#include <unistd.h>
#include <async_safe/log.h>
static const char* syslog_log_tag = nullptr;
static int syslog_priority_mask = 0xff;
+static int syslog_options = 0;
void closelog() {
syslog_log_tag = nullptr;
+ syslog_options = 0;
}
-void openlog(const char* log_tag, int /*options*/, int /*facility*/) {
+void openlog(const char* log_tag, int options, int /*facility*/) {
syslog_log_tag = log_tag;
+ syslog_options = options;
}
int setlogmask(int new_mask) {
@@ -73,10 +77,16 @@
android_log_priority = ANDROID_LOG_DEBUG;
}
- // We can't let async_safe_format_log do the formatting because it doesn't support
- // all the printf functionality.
+ // We can't let async_safe_format_log do the formatting because it doesn't
+ // support all the printf functionality.
char log_line[1024];
- vsnprintf(log_line, sizeof(log_line), fmt, args);
+ int n = vsnprintf(log_line, sizeof(log_line), fmt, args);
+ if (n < 0) return;
async_safe_format_log(android_log_priority, log_tag, "%s", log_line);
+ if ((syslog_options & LOG_PERROR) != 0) {
+ bool have_newline =
+ (n > 0 && n < static_cast<int>(sizeof(log_line)) && log_line[n - 1] == '\n');
+ dprintf(STDERR_FILENO, "%s: %s%s", log_tag, log_line, have_newline ? "" : "\n");
+ }
}
diff --git a/libc/bionic/timespec_get.cpp b/libc/bionic/time.cpp
similarity index 89%
rename from libc/bionic/timespec_get.cpp
rename to libc/bionic/time.cpp
index 7fc2182..800395e 100644
--- a/libc/bionic/timespec_get.cpp
+++ b/libc/bionic/time.cpp
@@ -29,5 +29,9 @@
#include <time.h>
int timespec_get(timespec* ts, int base) {
- return (base == TIME_UTC && clock_gettime(CLOCK_REALTIME, ts) != -1) ? base : 0;
+ return (clock_gettime(base - 1, ts) != -1) ? base : 0;
+}
+
+int timespec_getres(timespec* ts, int base) {
+ return (clock_getres(base - 1, ts) != -1) ? base : 0;
}
diff --git a/libc/bionic/tmpfile.cpp b/libc/bionic/tmpfile.cpp
index 3d04610..4d6a1fb 100644
--- a/libc/bionic/tmpfile.cpp
+++ b/libc/bionic/tmpfile.cpp
@@ -51,6 +51,9 @@
return nullptr;
}
+// O_TMPFILE isn't available until Linux 3.11, so we fall back to this on
+// older kernels. AOSP was on a new enough kernel in the Lollipop timeframe,
+// so this code should be obsolete by 2025.
static FILE* __tmpfile_dir_legacy(const char* tmp_dir) {
char* path = nullptr;
if (asprintf(&path, "%s/tmp.XXXXXXXXXX", tmp_dir) == -1) {
@@ -79,25 +82,18 @@
return __fd_to_fp(fd);
}
-static FILE* __tmpfile_dir(const char* tmp_dir) {
- int fd = open(tmp_dir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
- if (fd == -1) return __tmpfile_dir_legacy(tmp_dir);
- return __fd_to_fp(fd);
+const char* __get_TMPDIR() {
+ // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
+ // Useless for apps, but good enough for the shell.
+ const char* tmpdir = getenv("TMPDIR");
+ return (tmpdir == nullptr) ? "/data/local/tmp" : tmpdir;
}
FILE* tmpfile() {
- // TODO: get this app's temporary directory from the framework ("/data/data/app/cache").
-
- // $EXTERNAL_STORAGE turns out not to be very useful because it doesn't support hard links.
- // This means we can't do the usual trick of calling unlink before handing the file back.
-
- FILE* fp = __tmpfile_dir("/data/local/tmp");
- if (fp == nullptr) {
- // P_tmpdir is "/tmp/", but POSIX explicitly says that tmpdir(3) should try P_tmpdir before
- // giving up. This is potentially useful for bionic on the host anyway.
- fp = __tmpfile_dir(P_tmpdir);
- }
- return fp;
+ const char* tmpdir = __get_TMPDIR();
+ int fd = open(tmpdir, O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+ if (fd == -1) return __tmpfile_dir_legacy(tmpdir);
+ return __fd_to_fp(fd);
}
__strong_alias(tmpfile64, tmpfile);
@@ -107,7 +103,7 @@
// since we can't easily remove it...
// $TMPDIR overrides any directory passed in.
- char* tmpdir = getenv("TMPDIR");
+ const char* tmpdir = getenv("TMPDIR");
if (tmpdir != nullptr) dir = tmpdir;
// If we still have no directory, we'll give you a default.
@@ -136,12 +132,7 @@
static char buf[L_tmpnam];
if (s == nullptr) s = buf;
- // Use $TMPDIR if set, or fall back to /data/local/tmp otherwise.
- // Useless for apps, but good enough for the shell.
- const char* dir = getenv("TMPDIR");
- if (dir == nullptr) dir = "/data/local/tmp";
-
// Make up a mktemp(3) template and defer to it for the real work.
- snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", dir);
+ snprintf(s, L_tmpnam, "%s/tmpnam.XXXXXXXXXX", __get_TMPDIR());
return mktemp(s);
}
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index f0b731c..f216aab 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -157,7 +157,7 @@
uint64_t flags;
/** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
- void* reserved_addr;
+ void* _Nullable reserved_addr;
/** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
size_t reserved_size;
@@ -170,7 +170,7 @@
off64_t library_fd_offset;
/** Used by `ANDROID_DLEXT_USE_NAMESPACE`. */
- struct android_namespace_t* library_namespace;
+ struct android_namespace_t* _Nullable library_namespace;
} android_dlextinfo;
/**
@@ -180,7 +180,7 @@
*
* Available since API level 21.
*/
-void* android_dlopen_ext(const char* __filename, int __flags, const android_dlextinfo* __info)
+void* _Nullable android_dlopen_ext(const char* _Nullable __filename, int __flags, const android_dlextinfo* _Nullable __info)
__INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/android/fdsan.h b/libc/include/android/fdsan.h
index 59ce133..3de0649 100644
--- a/libc/include/android/fdsan.h
+++ b/libc/include/android/fdsan.h
@@ -159,7 +159,7 @@
*
* The return value points to memory with static lifetime, do not attempt to modify it.
*/
-const char* android_fdsan_get_tag_type(uint64_t tag) __INTRODUCED_IN(29);
+const char* _Nonnull android_fdsan_get_tag_type(uint64_t tag) __INTRODUCED_IN(29);
/*
* Get an owner tag's value, with the type masked off.
diff --git a/libc/include/android/legacy_stdlib_inlines.h b/libc/include/android/legacy_stdlib_inlines.h
index e9b606a..f0985fe 100644
--- a/libc/include/android/legacy_stdlib_inlines.h
+++ b/libc/include/android/legacy_stdlib_inlines.h
@@ -38,15 +38,15 @@
__BEGIN_DECLS
-static __inline double strtod_l(const char* __s, char** __end_ptr, locale_t __l) {
+static __inline double strtod_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
return strtod(__s, __end_ptr);
}
-static __inline float strtof_l(const char* __s, char** __end_ptr, locale_t __l) {
+static __inline float strtof_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
return strtof(__s, __end_ptr);
}
-static __inline long strtol_l(const char* __s, char** __end_ptr, int __base, locale_t __l) {
+static __inline long strtol_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) {
return strtol(__s, __end_ptr, __base);
}
diff --git a/libc/include/android/set_abort_message.h b/libc/include/android/set_abort_message.h
index 5d8d8ee..2be01a9 100644
--- a/libc/include/android/set_abort_message.h
+++ b/libc/include/android/set_abort_message.h
@@ -46,6 +46,6 @@
*
* Available since API level 21.
*/
-void android_set_abort_message(const char* __msg) __INTRODUCED_IN(21);
+void android_set_abort_message(const char* _Nullable __msg) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/bits/ctype_inlines.h b/libc/include/bits/ctype_inlines.h
index 744eb91..089a642 100644
--- a/libc/include/bits/ctype_inlines.h
+++ b/libc/include/bits/ctype_inlines.h
@@ -61,7 +61,7 @@
__BEGIN_DECLS
/** Internal implementation detail. Do not use. */
-extern const char* _ctype_;
+extern const char* _Nonnull _ctype_;
/** Returns true if `ch` is in `[A-Za-z0-9]`. */
__BIONIC_CTYPE_INLINE int isalnum(int __ch) {
@@ -175,33 +175,33 @@
#if __ANDROID_API__ >= 21
/** Like isalnum but with an ignored `locale_t`. */
-int isalnum_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isalnum_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isalpha but with an ignored `locale_t`. */
-int isalpha_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isalpha_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isblank but with an ignored `locale_t`. */
-int isblank_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isblank_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like iscntrl but with an ignored `locale_t`. */
-int iscntrl_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int iscntrl_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isdigit but with an ignored `locale_t`. */
-int isdigit_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isdigit_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isgraph but with an ignored `locale_t`. */
-int isgraph_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isgraph_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like islower but with an ignored `locale_t`. */
-int islower_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int islower_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isprint but with an ignored `locale_t`. */
-int isprint_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isprint_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like ispunct but with an ignored `locale_t`. */
-int ispunct_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int ispunct_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isspace but with an ignored `locale_t`. */
-int isspace_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isspace_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isupper but with an ignored `locale_t`. */
-int isupper_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isupper_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like isxdigit but with an ignored `locale_t`. */
-int isxdigit_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int isxdigit_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like tolower but with an ignored `locale_t`. */
-int tolower_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int tolower_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
/** Like toupper but with an ignored `locale_t`. */
-int toupper_l(int __ch, locale_t __l) __INTRODUCED_IN(21);
+int toupper_l(int __ch, locale_t _Nonnull __l) __INTRODUCED_IN(21);
#else
// Implemented as static inlines in libc++ before 21.
#endif
diff --git a/libc/include/bits/get_device_api_level_inlines.h b/libc/include/bits/get_device_api_level_inlines.h
index d14eb2c..dc5871b 100644
--- a/libc/include/bits/get_device_api_level_inlines.h
+++ b/libc/include/bits/get_device_api_level_inlines.h
@@ -35,8 +35,8 @@
__BEGIN_DECLS
// Avoid circular dependencies since this is exposed from <sys/cdefs.h>.
-int __system_property_get(const char* __name, char* __value);
-int atoi(const char* __s) __attribute_pure__;
+int __system_property_get(const char* _Nonnull __name, char* _Nonnull __value);
+int atoi(const char* _Nonnull __s) __attribute_pure__;
__BIONIC_GET_DEVICE_API_LEVEL_INLINE int android_get_device_api_level() {
char value[92] = { 0 };
diff --git a/libc/include/bits/getopt.h b/libc/include/bits/getopt.h
index 0411716..60a89ed 100644
--- a/libc/include/bits/getopt.h
+++ b/libc/include/bits/getopt.h
@@ -38,12 +38,12 @@
* Returns the next option character on success, returns -1 if all options have been parsed, and
* returns `'?'` on error.
*/
-int getopt(int __argc, char* const __argv[], const char* __options);
+int getopt(int __argc, char* const _Nonnull __argv[_Nullable], const char* _Nonnull __options);
/**
* Points to the text of the corresponding value for options that take an argument.
*/
-extern char* optarg;
+extern char* _Nullable optarg;
/**
* The index of the next element to be processed.
diff --git a/libc/include/bits/glibc-syscalls.h b/libc/include/bits/glibc-syscalls.h
index c144919..79f7da0 100644
--- a/libc/include/bits/glibc-syscalls.h
+++ b/libc/include/bits/glibc-syscalls.h
@@ -906,6 +906,9 @@
#if defined(__NR_restart_syscall)
#define SYS_restart_syscall __NR_restart_syscall
#endif
+#if defined(__NR_riscv_flush_icache)
+ #define SYS_riscv_flush_icache __NR_riscv_flush_icache
+#endif
#if defined(__NR_rmdir)
#define SYS_rmdir __NR_rmdir
#endif
diff --git a/libc/include/bits/strcasecmp.h b/libc/include/bits/strcasecmp.h
index 3994b68..23acbe5 100644
--- a/libc/include/bits/strcasecmp.h
+++ b/libc/include/bits/strcasecmp.h
@@ -46,12 +46,12 @@
* Returns an integer less than, equal to, or greater than zero if the first string is less than,
* equal to, or greater than the second string (ignoring case).
*/
-int strcasecmp(const char* __s1, const char* __s2) __attribute_pure__;
+int strcasecmp(const char* _Nonnull __s1, const char* _Nonnull __s2) __attribute_pure__;
/**
* Like strcasecmp() but taking a `locale_t`.
*/
-int strcasecmp_l(const char* __s1, const char* __s2, locale_t __l) __attribute_pure__ __INTRODUCED_IN(23);
+int strcasecmp_l(const char* _Nonnull __s1, const char* _Nonnull __s2, locale_t _Nonnull __l) __attribute_pure__ __INTRODUCED_IN(23);
/**
* [strncasecmp(3)](http://man7.org/linux/man-pages/man3/strncasecmp.3.html) compares the first
@@ -61,11 +61,11 @@
* first string is less than, equal to, or greater than the first `n` bytes of the second
* string (ignoring case).
*/
-int strncasecmp(const char* __s1, const char* __s2, size_t __n) __attribute_pure__;
+int strncasecmp(const char* _Nonnull __s1, const char* _Nonnull __s2, size_t __n) __attribute_pure__;
/**
* Like strncasecmp() but taking a `locale_t`.
*/
-int strncasecmp_l(const char* __s1, const char* __s2, size_t __n, locale_t __l) __attribute_pure__ __INTRODUCED_IN(23);
+int strncasecmp_l(const char* _Nonnull __s1, const char* _Nonnull __s2, size_t __n, locale_t _Nonnull __l) __attribute_pure__ __INTRODUCED_IN(23);
__END_DECLS
diff --git a/libc/include/bits/swab.h b/libc/include/bits/swab.h
index 63281b6..9591c2e 100644
--- a/libc/include/bits/swab.h
+++ b/libc/include/bits/swab.h
@@ -38,7 +38,7 @@
__BEGIN_DECLS
-__BIONIC_SWAB_INLINE void swab(const void* __void_src, void* __void_dst, ssize_t __byte_count) {
+__BIONIC_SWAB_INLINE void swab(const void* _Nonnull __void_src, void* _Nonnull __void_dst, ssize_t __byte_count) {
const uint8_t* __src = __BIONIC_CAST(static_cast, const uint8_t*, __void_src);
uint8_t* __dst = __BIONIC_CAST(static_cast, uint8_t*, __void_dst);
while (__byte_count > 1) {
diff --git a/libc/include/bits/wait.h b/libc/include/bits/wait.h
index a6a2129..c7f1fb0 100644
--- a/libc/include/bits/wait.h
+++ b/libc/include/bits/wait.h
@@ -53,7 +53,7 @@
#define WIFEXITED(__status) (WTERMSIG(__status) == 0)
/** Returns true if the process was stopped by a signal. */
-#define WIFSTOPPED(__status) (WTERMSIG(__status) == 0x7f)
+#define WIFSTOPPED(__status) (((__status) & 0xff) == 0x7f)
/** Returns true if the process was terminated by a signal. */
#define WIFSIGNALED(__status) (WTERMSIG((__status)+1) >= 2)
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index 6a2d380..91d63b3 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -232,8 +232,9 @@
/**
* mallopt() option for per-thread memory initialization tuning.
* The value argument should be one of:
- * 1: Disable automatic heap initialization and, where possible, memory tagging,
- * on this thread.
+ * 1: Disable automatic heap initialization on this thread only.
+ * If memory tagging is enabled, disable as much as possible of the
+ * memory tagging initialization for this thread.
* 0: Normal behavior.
*
* Available since API level 31.
diff --git a/libc/include/netinet/ether.h b/libc/include/netinet/ether.h
index 480063d..d570c18 100644
--- a/libc/include/netinet/ether.h
+++ b/libc/include/netinet/ether.h
@@ -44,7 +44,7 @@
*
* Returns a pointer to a static buffer.
*/
-char* ether_ntoa(const struct ether_addr* __addr);
+char* _Nonnull ether_ntoa(const struct ether_addr* _Nonnull __addr);
/**
* [ether_ntoa_r(3)](http://man7.org/linux/man-pages/man3/ether_ntoa_r.3.html) returns a string
@@ -52,7 +52,7 @@
*
* Returns a pointer to the given buffer.
*/
-char* ether_ntoa_r(const struct ether_addr* __addr, char* __buf);
+char* _Nonnull ether_ntoa_r(const struct ether_addr* _Nonnull __addr, char* _Nonnull __buf);
/**
* [ether_aton(3)](http://man7.org/linux/man-pages/man3/ether_aton.3.html) returns an `ether_addr`
@@ -60,7 +60,7 @@
*
* Returns a pointer to a static buffer, or NULL if the given string isn't a valid MAC address.
*/
-struct ether_addr* ether_aton(const char* __ascii);
+struct ether_addr* _Nullable ether_aton(const char* _Nonnull __ascii);
/**
* [ether_aton_r(3)](http://man7.org/linux/man-pages/man3/ether_aton_r.3.html) returns an
@@ -68,6 +68,6 @@
*
* Returns a pointer to the given buffer, or NULL if the given string isn't a valid MAC address.
*/
-struct ether_addr* ether_aton_r(const char* __ascii, struct ether_addr* __addr);
+struct ether_addr* _Nullable ether_aton_r(const char* _Nonnull __ascii, struct ether_addr* _Nonnull __addr);
__END_DECLS
diff --git a/libc/include/netinet/in.h b/libc/include/netinet/in.h
index 46e3543..b235e6e 100644
--- a/libc/include/netinet/in.h
+++ b/libc/include/netinet/in.h
@@ -54,7 +54,7 @@
typedef uint16_t in_port_t;
-int bindresvport(int __fd, struct sockaddr_in* __sin);
+int bindresvport(int __fd, struct sockaddr_in* _Nullable __sin);
#if __ANDROID_API__ >= 24
extern const struct in6_addr in6addr_any __INTRODUCED_IN(24);
diff --git a/libc/include/semaphore.h b/libc/include/semaphore.h
index 5d66f7e..6ad9ea3 100644
--- a/libc/include/semaphore.h
+++ b/libc/include/semaphore.h
@@ -45,12 +45,12 @@
#define SEM_FAILED __BIONIC_CAST(reinterpret_cast, sem_t*, 0)
-int sem_clockwait(sem_t* __sem, clockid_t __clock, const struct timespec* __ts) __INTRODUCED_IN(30);
-int sem_destroy(sem_t* __sem);
-int sem_getvalue(sem_t* __sem, int* __value);
-int sem_init(sem_t* __sem, int __shared, unsigned int __value);
-int sem_post(sem_t* __sem);
-int sem_timedwait(sem_t* __sem, const struct timespec* __ts);
+int sem_clockwait(sem_t* _Nonnull __sem, clockid_t __clock, const struct timespec* _Nonnull __ts) __INTRODUCED_IN(30);
+int sem_destroy(sem_t* _Nonnull __sem);
+int sem_getvalue(sem_t* _Nonnull __sem, int* _Nonnull __value);
+int sem_init(sem_t* _Nonnull __sem, int __shared, unsigned int __value);
+int sem_post(sem_t* _Nonnull __sem);
+int sem_timedwait(sem_t* _Nonnull __sem, const struct timespec* _Nonnull __ts);
/*
* POSIX historically only supported using sem_timedwait() with CLOCK_REALTIME, however that is
* typically inappropriate, since that clock can change dramatically, causing the timeout to either
@@ -59,14 +59,14 @@
* Note that sem_clockwait() allows specifying an arbitrary clock and has superseded this
* function.
*/
-int sem_timedwait_monotonic_np(sem_t* __sem, const struct timespec* __ts) __INTRODUCED_IN(28);
-int sem_trywait(sem_t* __sem);
-int sem_wait(sem_t* __sem);
+int sem_timedwait_monotonic_np(sem_t* _Nonnull __sem, const struct timespec* _Nonnull __ts) __INTRODUCED_IN(28);
+int sem_trywait(sem_t* _Nonnull __sem);
+int sem_wait(sem_t* _Nonnull __sem);
/* These aren't actually implemented. */
-sem_t* sem_open(const char* __name, int _flags, ...);
-int sem_close(sem_t* __sem);
-int sem_unlink(const char* __name);
+sem_t* _Nullable sem_open(const char* _Nonnull __name, int _flags, ...);
+int sem_close(sem_t* _Nonnull __sem);
+int sem_unlink(const char* _Nonnull __name);
__END_DECLS
diff --git a/libc/include/setjmp.h b/libc/include/setjmp.h
index 141e925..6d047ae 100644
--- a/libc/include/setjmp.h
+++ b/libc/include/setjmp.h
@@ -47,14 +47,33 @@
#include <sys/cdefs.h>
#if defined(__aarch64__)
+/**
+ * The size in words of an arm64 jmp_buf. Room for callee-saved registers,
+ * including floating point, stack pointer and program counter, various
+ * internal implementation details, and leaving some free space.
+ *
+ * Coincidentally matches OpenBSD, though they also save/restore the
+ * floating point status register too.
+ */
#define _JBLEN 32
#elif defined(__arm__)
+/** The size in words of an arm32 jmp_buf. Inherited from OpenBSD. */
#define _JBLEN 64
#elif defined(__i386__)
+/** The size in words of an x86 jmp_buf. Inherited from OpenBSD. */
#define _JBLEN 10
#elif defined(__riscv)
+/**
+ * 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.
+ *
+ * Coincidentally matches OpenBSD, though they also save/restore the
+ * floating point status register too.
+ */
#define _JBLEN 32
#elif defined(__x86_64__)
+/** The size in words of an x86-64 jmp_buf. Inherited from OpenBSD. */
#define _JBLEN 11
#endif
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index e748faa..d7b65e4 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -143,9 +143,9 @@
(defined(__cplusplus) && __cplusplus <= 201103L)
char* _Nullable gets(char* _Nonnull __buf) __attribute__((deprecated("gets is unsafe, use fgets instead")));
#endif
-int sprintf(char* _Nonnull __s, const char* _Nonnull __fmt, ...)
+int sprintf(char* __BIONIC_COMPLICATED_NULLNESS __s, const char* _Nonnull __fmt, ...)
__printflike(2, 3) __warnattr_strict("sprintf is often misused; please use snprintf");
-int vsprintf(char* _Nonnull __s, const char* _Nonnull __fmt, va_list __args)
+int vsprintf(char* __BIONIC_COMPLICATED_NULLNESS __s, const char* _Nonnull __fmt, va_list __args)
__printflike(2, 0) __warnattr_strict("vsprintf is often misused; please use vsnprintf");
char* _Nullable tmpnam(char* _Nullable __s)
__warnattr("tmpnam is unsafe, use mkstemp or tmpfile instead");
@@ -251,10 +251,10 @@
FILE* _Nullable tmpfile(void);
FILE* _Nullable tmpfile64(void) __INTRODUCED_IN(24);
-int snprintf(char* _Nullable __buf, size_t __size, const char* _Nonnull __fmt, ...) __printflike(3, 4);
+int snprintf(char* __BIONIC_COMPLICATED_NULLNESS __buf, size_t __size, const char* _Nonnull __fmt, ...) __printflike(3, 4);
int vfscanf(FILE* _Nonnull __fp, const char* _Nonnull __fmt, va_list __args) __scanflike(2, 0);
int vscanf(const char* _Nonnull __fmt , va_list __args) __scanflike(1, 0);
-int vsnprintf(char* _Nullable __buf, size_t __size, const char* _Nonnull __fmt, va_list __args) __printflike(3, 0);
+int vsnprintf(char* __BIONIC_COMPLICATED_NULLNESS __buf, size_t __size, const char* _Nonnull __fmt, va_list __args) __printflike(3, 0);
int vsscanf(const char* _Nonnull __s, const char* _Nonnull __fmt, va_list __args) __scanflike(2, 0);
#define L_ctermid 1024 /* size for ctermid() */
diff --git a/libc/include/sys/capability.h b/libc/include/sys/capability.h
index 4cb698f..b43bbf0 100644
--- a/libc/include/sys/capability.h
+++ b/libc/include/sys/capability.h
@@ -44,7 +44,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int capget(cap_user_header_t __hdr_ptr, cap_user_data_t __data_ptr);
+int capget(cap_user_header_t _Nonnull __hdr_ptr, cap_user_data_t _Nullable __data_ptr);
/**
* [capset(2)](http://man7.org/linux/man-pages/man2/capset.2.html) sets the calling
@@ -52,6 +52,6 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int capset(cap_user_header_t __hdr_ptr, const cap_user_data_t __data_ptr);
+int capset(cap_user_header_t _Nonnull __hdr_ptr, const cap_user_data_t _Nullable __data_ptr);
__END_DECLS
diff --git a/libc/include/sys/inotify.h b/libc/include/sys/inotify.h
index c3cdc85..e834d07 100644
--- a/libc/include/sys/inotify.h
+++ b/libc/include/sys/inotify.h
@@ -42,7 +42,7 @@
int inotify_init(void);
int inotify_init1(int __flags) __INTRODUCED_IN(21);
-int inotify_add_watch(int __fd, const char* __path, uint32_t __mask);
+int inotify_add_watch(int __fd, const char* _Nonnull __path, uint32_t __mask);
int inotify_rm_watch(int __fd, uint32_t __watch_descriptor);
__END_DECLS
diff --git a/libc/include/sys/ipc.h b/libc/include/sys/ipc.h
index c81ec1a..2e2b8cf 100644
--- a/libc/include/sys/ipc.h
+++ b/libc/include/sys/ipc.h
@@ -52,6 +52,6 @@
*
* Returns a key on success, and returns -1 and sets `errno` on failure.
*/
-key_t ftok(const char* __path, int __id);
+key_t ftok(const char* _Nonnull __path, int __id);
__END_DECLS
diff --git a/libc/include/sys/mount.h b/libc/include/sys/mount.h
index 4db1ac1..aace205 100644
--- a/libc/include/sys/mount.h
+++ b/libc/include/sys/mount.h
@@ -55,7 +55,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int mount(const char* __source, const char* __target, const char* __fs_type, unsigned long __flags, const void* __data);
+int mount(const char* __BIONIC_COMPLICATED_NULLNESS __source, const char* _Nonnull __target, const char* __BIONIC_COMPLICATED_NULLNESS __fs_type, unsigned long __flags, const void* _Nullable __data);
/**
* [umount(2)](http://man7.org/linux/man-pages/man2/umount.2.html) unmounts the filesystem at
@@ -63,7 +63,7 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int umount(const char* __target);
+int umount(const char* _Nonnull __target);
/**
* [umount2(2)](http://man7.org/linux/man-pages/man2/umount2.2.html) unmounts the filesystem at
@@ -71,6 +71,6 @@
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*/
-int umount2(const char* __target, int __flags);
+int umount2(const char* _Nonnull __target, int __flags);
__END_DECLS
diff --git a/libc/include/sys/msg.h b/libc/include/sys/msg.h
index e19452c..ad481a0 100644
--- a/libc/include/sys/msg.h
+++ b/libc/include/sys/msg.h
@@ -46,12 +46,12 @@
typedef __kernel_ulong_t msglen_t;
/** Not useful on Android; disallowed by SELinux. */
-int msgctl(int __msg_id, int __cmd, struct msqid_ds* __buf) __INTRODUCED_IN(26);
+int msgctl(int __msg_id, int __cmd, struct msqid_ds* _Nullable __buf) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
int msgget(key_t __key, int __flags) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
-ssize_t msgrcv(int __msg_id, void* __msgbuf_ptr, size_t __size, long __type, int __flags) __INTRODUCED_IN(26);
+ssize_t msgrcv(int __msg_id, void* _Nonnull __msgbuf_ptr, size_t __size, long __type, int __flags) __INTRODUCED_IN(26);
/** Not useful on Android; disallowed by SELinux. */
-int msgsnd(int __msg_id, const void* __msgbuf_ptr, size_t __size, int __flags) __INTRODUCED_IN(26);
+int msgsnd(int __msg_id, const void* _Nonnull __msgbuf_ptr, size_t __size, int __flags) __INTRODUCED_IN(26);
__END_DECLS
diff --git a/libc/include/sys/random.h b/libc/include/sys/random.h
index be52bd9..0251176 100644
--- a/libc/include/sys/random.h
+++ b/libc/include/sys/random.h
@@ -50,7 +50,7 @@
*
* See also arc4random_buf() which is available in all API levels.
*/
-int getentropy(void* __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
+int getentropy(void* _Nonnull __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
/**
* [getrandom(2)](http://man7.org/linux/man-pages/man2/getrandom.2.html) fills the given buffer
@@ -62,6 +62,6 @@
*
* See also arc4random_buf() which is available in all API levels.
*/
-ssize_t getrandom(void* __buffer, size_t __buffer_size, unsigned int __flags) __wur __INTRODUCED_IN(28);
+ssize_t getrandom(void* _Nonnull __buffer, size_t __buffer_size, unsigned int __flags) __wur __INTRODUCED_IN(28);
__END_DECLS
diff --git a/libc/include/sys/resource.h b/libc/include/sys/resource.h
index ccb267d..0b540de 100644
--- a/libc/include/sys/resource.h
+++ b/libc/include/sys/resource.h
@@ -43,19 +43,19 @@
typedef unsigned long rlim_t;
typedef unsigned long long rlim64_t;
-int getrlimit(int __resource, struct rlimit* __limit);
-int setrlimit(int __resource, const struct rlimit* __limit);
+int getrlimit(int __resource, struct rlimit* _Nonnull __limit);
+int setrlimit(int __resource, const struct rlimit* _Nonnull __limit);
-int getrlimit64(int __resource, struct rlimit64* __limit) __INTRODUCED_IN(21);
-int setrlimit64(int __resource, const struct rlimit64* __limit) __INTRODUCED_IN(21);
+int getrlimit64(int __resource, struct rlimit64* _Nonnull __limit) __INTRODUCED_IN(21);
+int setrlimit64(int __resource, const struct rlimit64* _Nonnull __limit) __INTRODUCED_IN(21);
int getpriority(int __which, id_t __who);
int setpriority(int __which, id_t __who, int __priority);
-int getrusage(int __who, struct rusage* __usage);
+int getrusage(int __who, struct rusage* _Nonnull __usage);
-int prlimit(pid_t __pid, int __resource, const struct rlimit* __new_limit, struct rlimit* __old_limit) __INTRODUCED_IN_32(24) __INTRODUCED_IN_64(21);
-int prlimit64(pid_t __pid, int __resource, const struct rlimit64* __new_limit, struct rlimit64* __old_limit) __INTRODUCED_IN(21);
+int prlimit(pid_t __pid, int __resource, const struct rlimit* _Nullable __new_limit, struct rlimit* _Nullable __old_limit) __INTRODUCED_IN_32(24) __INTRODUCED_IN_64(21);
+int prlimit64(pid_t __pid, int __resource, const struct rlimit64* _Nullable __new_limit, struct rlimit64* _Nullable __old_limit) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index 06914a6..8c6c2ff 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -71,9 +71,9 @@
} \
} while (0)
-void __FD_CLR_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-void __FD_SET_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
-int __FD_ISSET_chk(int, const fd_set*, size_t) __INTRODUCED_IN(21);
+void __FD_CLR_chk(int, fd_set* _Nonnull , size_t) __INTRODUCED_IN(21);
+void __FD_SET_chk(int, fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
+int __FD_ISSET_chk(int, const fd_set* _Nonnull, size_t) __INTRODUCED_IN(21);
#define __FD_CLR(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] &= ~__FDMASK(fd))
#define __FD_SET(fd, set) (__FDS_BITS(fd_set*,set)[__FDELT(fd)] |= __FDMASK(fd))
@@ -95,7 +95,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int select(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, struct timeval* __timeout);
+int select(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, struct timeval* _Nullable __timeout);
/**
* [pselect(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -106,7 +106,7 @@
* Returns the number of ready file descriptors on success, 0 for timeout,
* and returns -1 and sets `errno` on failure.
*/
-int pselect(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset_t* __mask);
+int pselect(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset_t* _Nullable __mask);
/**
* [pselect64(2)](http://man7.org/linux/man-pages/man2/select.2.html) waits on a
@@ -119,6 +119,6 @@
*
* Available since API level 28.
*/
-int pselect64(int __max_fd_plus_one, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset64_t* __mask) __INTRODUCED_IN(28);
+int pselect64(int __max_fd_plus_one, fd_set* _Nullable __read_fds, fd_set* _Nullable __write_fds, fd_set* _Nullable __exception_fds, const struct timespec* _Nullable __timeout, const sigset64_t* _Nullable __mask) __INTRODUCED_IN(28);
__END_DECLS
diff --git a/libc/include/sys/sem.h b/libc/include/sys/sem.h
index cd62242..f4256e2 100644
--- a/libc/include/sys/sem.h
+++ b/libc/include/sys/sem.h
@@ -45,18 +45,18 @@
union semun {
int val;
- struct semid_ds* buf;
- unsigned short* array;
- struct seminfo* __buf;
- void* __pad;
+ struct semid_ds* _Nullable buf;
+ unsigned short* _Nullable array;
+ struct seminfo* _Nullable __buf;
+ void* _Nullable __pad;
};
int semctl(int __sem_id, int __sem_num, int __cmd, ...) __INTRODUCED_IN(26);
int semget(key_t __key, int __sem_count, int __flags) __INTRODUCED_IN(26);
-int semop(int __sem_id, struct sembuf* __ops, size_t __op_count) __INTRODUCED_IN(26);
+int semop(int __sem_id, struct sembuf* _Nonnull __ops, size_t __op_count) __INTRODUCED_IN(26);
#if defined(__USE_GNU)
-int semtimedop(int __sem_id, struct sembuf* __ops, size_t __op_count, const struct timespec* __timeout) __INTRODUCED_IN(26);
+int semtimedop(int __sem_id, struct sembuf* _Nonnull __ops, size_t __op_count, const struct timespec* _Nullable __timeout) __INTRODUCED_IN(26);
#endif
__END_DECLS
diff --git a/libc/include/sys/sendfile.h b/libc/include/sys/sendfile.h
index 60bbde8..4b00d5d 100644
--- a/libc/include/sys/sendfile.h
+++ b/libc/include/sys/sendfile.h
@@ -40,7 +40,7 @@
/* See https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
#if defined(__USE_FILE_OFFSET64)
-ssize_t sendfile(int __out_fd, int __in_fd, off_t* __offset, size_t __count) __RENAME(sendfile64) __INTRODUCED_IN(21);
+ssize_t sendfile(int __out_fd, int __in_fd, off_t* _Nullable __offset, size_t __count) __RENAME(sendfile64) __INTRODUCED_IN(21);
#else
/**
* [sendfile(2)](http://man7.org/linux/man-pages/man2/sendfile.2.html) copies data directly
@@ -50,13 +50,13 @@
*
* Available since API level 21.
*/
-ssize_t sendfile(int __out_fd, int __in_fd, off_t* __offset, size_t __count);
+ssize_t sendfile(int __out_fd, int __in_fd, off_t* _Nullable __offset, size_t __count);
#endif
/**
* Like sendfile() but allows using a 64-bit offset
* even from a 32-bit process without `__FILE_OFFSET_BITS=64`.
*/
-ssize_t sendfile64(int __out_fd, int __in_fd, off64_t* __offset, size_t __count) __INTRODUCED_IN(21);
+ssize_t sendfile64(int __out_fd, int __in_fd, off64_t* _Nullable __offset, size_t __count) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/sys/statvfs.h b/libc/include/sys/statvfs.h
index 793ee13..d81f836 100644
--- a/libc/include/sys/statvfs.h
+++ b/libc/include/sys/statvfs.h
@@ -96,7 +96,7 @@
*
* Available since API level 19.
*/
-int statvfs(const char* __path, struct statvfs* __buf) __INTRODUCED_IN(19);
+int statvfs(const char* _Nonnull __path, struct statvfs* _Nonnull __buf) __INTRODUCED_IN(19);
/**
* [fstatvfs(3)](http://man7.org/linux/man-pages/man3/fstatvfs.3.html)
@@ -106,12 +106,12 @@
*
* Available since API level 19.
*/
-int fstatvfs(int __fd, struct statvfs* __buf) __INTRODUCED_IN(19);
+int fstatvfs(int __fd, struct statvfs* _Nonnull __buf) __INTRODUCED_IN(19);
/** Equivalent to statvfs() . */
-int statvfs64(const char* __path, struct statvfs64* __buf) __INTRODUCED_IN(21);
+int statvfs64(const char* _Nonnull __path, struct statvfs64* _Nonnull __buf) __INTRODUCED_IN(21);
/** Equivalent to fstatvfs(). */
-int fstatvfs64(int __fd, struct statvfs64* __buf) __INTRODUCED_IN(21);
+int fstatvfs64(int __fd, struct statvfs64* _Nonnull __buf) __INTRODUCED_IN(21);
__END_DECLS
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index bb6443b..4f4d5ce 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -318,12 +318,20 @@
#if defined(__USE_GNU)
-#define REG_PC 0
-#define REG_RA 1
-#define REG_SP 2
-#define REG_TP 4
-#define REG_S0 8
-#define REG_A0 10
+enum {
+ REG_PC = 0,
+#define REG_PC REG_PC
+ REG_RA = 1,
+#define REG_RA REG_RA
+ REG_SP = 2,
+#define REG_SP REG_SP
+ REG_TP = 4,
+#define REG_TP REG_TP
+ REG_S0 = 8,
+#define REG_S0 REG_S0
+ REG_A0 = 10,
+#define REG_A0 REG_A0
+};
#endif // defined(__USE_GNU)
diff --git a/libc/include/sys/uio.h b/libc/include/sys/uio.h
index 583cfc6..c8c64ae 100644
--- a/libc/include/sys/uio.h
+++ b/libc/include/sys/uio.h
@@ -46,7 +46,7 @@
* Returns the number of bytes read on success,
* and returns -1 and sets `errno` on failure.
*/
-ssize_t readv(int __fd, const struct iovec* __iov, int __count);
+ssize_t readv(int __fd, const struct iovec* _Nonnull __iov, int __count);
/**
* [writev(2)](http://man7.org/linux/man-pages/man2/writev.2.html) writes
@@ -55,7 +55,7 @@
* Returns the number of bytes written on success,
* and returns -1 and sets `errno` on failure.
*/
-ssize_t writev(int __fd, const struct iovec* __iov, int __count);
+ssize_t writev(int __fd, const struct iovec* _Nonnull __iov, int __count);
#if defined(__USE_GNU)
@@ -69,7 +69,7 @@
*
* Available since API level 24.
*/
-ssize_t preadv(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(preadv64) __INTRODUCED_IN(24);
+ssize_t preadv(int __fd, const struct iovec* _Nonnull __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(preadv64) __INTRODUCED_IN(24);
/**
* [pwritev(2)](http://man7.org/linux/man-pages/man2/pwritev.2.html) writes
@@ -81,21 +81,21 @@
*
* Available since API level 24.
*/
-ssize_t pwritev(int __fd, const struct iovec* __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(pwritev64) __INTRODUCED_IN(24);
+ssize_t pwritev(int __fd, const struct iovec* _Nonnull __iov, int __count, off_t __offset) __RENAME_IF_FILE_OFFSET64(pwritev64) __INTRODUCED_IN(24);
/**
* Like preadv() but with a 64-bit offset even in a 32-bit process.
*
* Available since API level 24.
*/
-ssize_t preadv64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
+ssize_t preadv64(int __fd, const struct iovec* _Nonnull __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
/**
* Like pwritev() but with a 64-bit offset even in a 32-bit process.
*
* Available since API level 24.
*/
-ssize_t pwritev64(int __fd, const struct iovec* __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
+ssize_t pwritev64(int __fd, const struct iovec* _Nonnull __iov, int __count, off64_t __offset) __INTRODUCED_IN(24);
/**
* [preadv2(2)](http://man7.org/linux/man-pages/man2/preadv2.2.html) reads
@@ -107,7 +107,7 @@
*
* Available since API level 33.
*/
-ssize_t preadv2(int __fd, const struct iovec* __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(preadv64v2) __INTRODUCED_IN(33);
+ssize_t preadv2(int __fd, const struct iovec* _Nonnull __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(preadv64v2) __INTRODUCED_IN(33);
/**
* [pwritev2(2)](http://man7.org/linux/man-pages/man2/pwritev2.2.html) writes
@@ -119,21 +119,21 @@
*
* Available since API level 33.
*/
-ssize_t pwritev2(int __fd, const struct iovec* __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(pwritev64v2) __INTRODUCED_IN(33);
+ssize_t pwritev2(int __fd, const struct iovec* _Nonnull __iov, int __count, off_t __offset, int __flags) __RENAME_IF_FILE_OFFSET64(pwritev64v2) __INTRODUCED_IN(33);
/**
* Like preadv2() but with a 64-bit offset even in a 32-bit process.
*
* Available since API level 33.
*/
-ssize_t preadv64v2(int __fd, const struct iovec* __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
+ssize_t preadv64v2(int __fd, const struct iovec* _Nonnull __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
/**
* Like pwritev2() but with a 64-bit offset even in a 32-bit process.
*
* Available since API level 33.
*/
-ssize_t pwritev64v2(int __fd, const struct iovec* __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
+ssize_t pwritev64v2(int __fd, const struct iovec* _Nonnull __iov, int __count, off64_t __offset, int __flags) __INTRODUCED_IN(33);
/**
* [process_vm_readv(2)](http://man7.org/linux/man-pages/man2/process_vm_readv.2.html)
@@ -144,7 +144,7 @@
*
* Available since API level 23.
*/
-ssize_t process_vm_readv(pid_t __pid, const struct iovec* __local_iov, unsigned long __local_iov_count, const struct iovec* __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
+ssize_t process_vm_readv(pid_t __pid, const struct iovec* __BIONIC_COMPLICATED_NULLNESS __local_iov, unsigned long __local_iov_count, const struct iovec* __BIONIC_COMPLICATED_NULLNESS __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
/**
* [process_vm_writev(2)](http://man7.org/linux/man-pages/man2/process_vm_writev.2.html)
@@ -155,7 +155,7 @@
*
* Available since API level 23.
*/
-ssize_t process_vm_writev(pid_t __pid, const struct iovec* __local_iov, unsigned long __local_iov_count, const struct iovec* __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
+ssize_t process_vm_writev(pid_t __pid, const struct iovec* __BIONIC_COMPLICATED_NULLNESS __local_iov, unsigned long __local_iov_count, const struct iovec* __BIONIC_COMPLICATED_NULLNESS __remote_iov, unsigned long __remote_iov_count, unsigned long __flags) __INTRODUCED_IN(23);
#endif
diff --git a/libc/include/sys/wait.h b/libc/include/sys/wait.h
index 96974a2..e6fb855 100644
--- a/libc/include/sys/wait.h
+++ b/libc/include/sys/wait.h
@@ -37,9 +37,9 @@
__BEGIN_DECLS
-pid_t wait(int* __status);
-pid_t waitpid(pid_t __pid, int* __status, int __options);
-pid_t wait4(pid_t __pid, int* __status, int __options, struct rusage* __rusage) __INTRODUCED_IN(18);
+pid_t wait(int* _Nullable __status);
+pid_t waitpid(pid_t __pid, int* _Nullable __status, int __options);
+pid_t wait4(pid_t __pid, int* _Nullable __status, int __options, struct rusage* _Nullable __rusage) __INTRODUCED_IN(18);
/* Posix states that idtype_t should be an enumeration type, but
* the kernel headers define P_ALL, P_PID and P_PGID as constant macros
@@ -47,6 +47,6 @@
*/
typedef int idtype_t;
-int waitid(idtype_t __type, id_t __id, siginfo_t* __info, int __options);
+int waitid(idtype_t __type, id_t __id, siginfo_t* _Nullable __info, int __options);
__END_DECLS
diff --git a/libc/include/syslog.h b/libc/include/syslog.h
index d89d769..90ea76e 100644
--- a/libc/include/syslog.h
+++ b/libc/include/syslog.h
@@ -112,17 +112,21 @@
*/
#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1)
-/** openlog() options are currently ignored on Android. */
+/** openlog() option ignored on Android. */
#define LOG_PID 0x01
-/** openlog() options are currently ignored on Android. */
+/** openlog() option ignored on Android. */
#define LOG_CONS 0x02
-/** openlog() options are currently ignored on Android. */
+/** openlog() option ignored on Android. */
#define LOG_ODELAY 0x04
-/** openlog() options are currently ignored on Android. */
+/** openlog() option ignored on Android. */
#define LOG_NDELAY 0x08
-/** openlog() options are currently ignored on Android. */
+/** openlog() option ignored on Android. */
#define LOG_NOWAIT 0x10
-/** openlog() options are currently ignored on Android. */
+/**
+ * openlog() option to log to stderr as well as the system log.
+ *
+ * Available since API level 34 (ignored before then).
+ */
#define LOG_PERROR 0x20
/**
diff --git a/libc/include/time.h b/libc/include/time.h
index 5339540..1c3ae4b 100644
--- a/libc/include/time.h
+++ b/libc/include/time.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _TIME_H_
-#define _TIME_H_
+#pragma once
#include <sys/cdefs.h>
#include <sys/time.h>
@@ -105,9 +104,52 @@
time_t timelocal(struct tm* _Nonnull __tm);
time_t timegm(struct tm* _Nonnull __tm);
-#define TIME_UTC 1
+/**
+ * The timebase for timespec_get() and timespec_getres() corresponding to CLOCK_REALTIME.
+ *
+ * Available since API level 29.
+ */
+#define TIME_UTC (CLOCK_REALTIME+1)
+
+/**
+ * The timebase for timespec_get() and timespec_getres() corresponding to CLOCK_MONOTONIC.
+ *
+ * Available since API level 35.
+ */
+#define TIME_MONOTONIC (CLOCK_MONOTONIC+1)
+
+/**
+ * The timebase for timespec_get() and timespec_getres() corresponding to CLOCK_PROCESS_CPUTIME_ID.
+ *
+ * Available since API level 35.
+ */
+#define TIME_ACTIVE (CLOCK_PROCESS_CPUTIME_ID+1)
+
+/**
+ * The timebase for timespec_get() and timespec_getres() corresponding to CLOCK_THREAD_CPUTIME_ID.
+ *
+ * Available since API level 35.
+ */
+#define TIME_THREAD_ACTIVE (CLOCK_THREAD_CPUTIME_ID+1)
+
+/**
+ * timespec_get(3) is equivalent to clock_gettime() for the clock corresponding to the given base.
+ *
+ * Returns the base on success and returns 0 on failure.
+ *
+ * Available since API level 29 for TIME_UTC; other bases arrived later.
+ * Code for Android should prefer clock_gettime().
+ */
int timespec_get(struct timespec* _Nonnull __ts, int __base) __INTRODUCED_IN(29);
-__END_DECLS
+/**
+ * timespec_getres(3) is equivalent to clock_getres() for the clock corresponding to the given base.
+ *
+ * Returns the base on success and returns 0 on failure.
+ *
+ * Available since API level 35.
+ * Code for Android should prefer clock_gettime().
+ */
+int timespec_getres(struct timespec* _Nonnull __ts, int __base) __INTRODUCED_IN(35);
-#endif
+__END_DECLS
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index add3606..39f9374 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -44,97 +44,96 @@
__BEGIN_DECLS
wint_t btowc(int __ch);
-int fwprintf(FILE* __fp, const wchar_t* __fmt, ...);
-int fwscanf(FILE* __fp, const wchar_t* __fmt, ...);
-wint_t fgetwc(FILE* __fp);
-wchar_t* fgetws(wchar_t* __buf, int __size, FILE* __fp);
-wint_t fputwc(wchar_t __wc, FILE* __fp);
-int fputws(const wchar_t* __s, FILE* __fp);
-int fwide(FILE* __fp, int __mode);
-wint_t getwc(FILE* __fp);
+int fwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+int fwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, ...);
+wint_t fgetwc(FILE* _Nonnull __fp);
+wchar_t* _Nullable fgetws(wchar_t* _Nonnull __buf, int __size, FILE* _Nonnull __fp);
+wint_t fputwc(wchar_t __wc, FILE* _Nonnull __fp);
+int fputws(const wchar_t* _Nonnull __s, FILE* _Nonnull __fp);
+int fwide(FILE* _Nonnull __fp, int __mode);
+wint_t getwc(FILE* _Nonnull __fp);
wint_t getwchar(void);
-int mbsinit(const mbstate_t* __ps);
-size_t mbrlen(const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbrtowc(wchar_t* __buf, const char* __s, size_t __n, mbstate_t* __ps);
-size_t mbsrtowcs(wchar_t* __dst, const char** __src, size_t __dst_n, mbstate_t* __ps);
-size_t mbsnrtowcs(wchar_t* __dst, const char** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wint_t putwc(wchar_t __wc, FILE* __fp);
+int mbsinit(const mbstate_t* _Nullable __ps);
+size_t mbrlen(const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbrtowc(wchar_t* _Nullable __buf, const char* _Nullable __s, size_t __n, mbstate_t* _Nullable __ps);
+size_t mbsrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nonnull __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t mbsnrtowcs(wchar_t* _Nullable __dst, const char* _Nullable * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wint_t putwc(wchar_t __wc, FILE* _Nonnull __fp);
wint_t putwchar(wchar_t __wc);
-int swprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, ...);
-int swscanf(const wchar_t* __s, const wchar_t* __fmt, ...);
-wint_t ungetwc(wint_t __wc, FILE* __fp);
-int vfwprintf(FILE* __fp, const wchar_t* __fmt, va_list __args);
-int vfwscanf(FILE* __fp, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vswprintf(wchar_t* __buf, size_t __n, const wchar_t* __fmt, va_list __args);
-int vswscanf(const wchar_t* __s, const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-int vwprintf(const wchar_t* __fmt, va_list __args);
-int vwscanf(const wchar_t* __fmt, va_list __args) __INTRODUCED_IN(21);
-wchar_t* wcpcpy(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcpncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcrtomb(char* __buf, wchar_t __wc, mbstate_t* __ps);
-int wcscasecmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __INTRODUCED_IN(23);
-wchar_t* wcscat(wchar_t* __dst, const wchar_t* __src);
-wchar_t* wcschr(const wchar_t* __s, wchar_t __wc);
-int wcscmp(const wchar_t* __lhs, const wchar_t* __rhs);
-int wcscoll(const wchar_t* __lhs, const wchar_t* __rhs);
-wchar_t* wcscpy(wchar_t* __dst, const wchar_t* __src);
-size_t wcscspn(const wchar_t* __s, const wchar_t* __accept);
-size_t wcsftime(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm);
-size_t wcsftime_l(wchar_t* __buf, size_t __n, const wchar_t* __fmt, const struct tm* __tm, locale_t __l) __INTRODUCED_IN(28);
-size_t wcslen(const wchar_t* __s);
-int wcsncasecmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-int wcsncasecmp_l(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n, locale_t __l) __INTRODUCED_IN(23);
-wchar_t* wcsncat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-int wcsncmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wcsncpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcsnrtombs(char* __dst, const wchar_t** __src, size_t __src_n, size_t __dst_n, mbstate_t* __ps) __INTRODUCED_IN(21);
-wchar_t* wcspbrk(const wchar_t* __s, const wchar_t* __accept);
-wchar_t* wcsrchr(const wchar_t* __s, wchar_t __wc);
-size_t wcsrtombs(char* __dst, const wchar_t** __src, size_t __dst_n, mbstate_t* __ps);
-size_t wcsspn(const wchar_t* __s, const wchar_t* __accept);
-wchar_t* wcsstr(const wchar_t* __haystack, const wchar_t* __needle);
-double wcstod(const wchar_t* __s, wchar_t** __end_ptr);
-double wcstod_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
-float wcstof(const wchar_t* __s, wchar_t** __end_ptr) __INTRODUCED_IN(21);
-float wcstof_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(28);
-wchar_t* wcstok(wchar_t* __s, const wchar_t* __delimiter, wchar_t** __ptr);
-long wcstol(const wchar_t* __s, wchar_t** __end_ptr, int __base);
-long wcstol_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
-long long wcstoll(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
-long double wcstold(const wchar_t* __s, wchar_t** __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
-unsigned long wcstoul(const wchar_t* __s, wchar_t** __end_ptr, int __base);
-unsigned long wcstoul_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(28);
-unsigned long long wcstoull(const wchar_t* __s, wchar_t** __end_ptr, int __base) __INTRODUCED_IN(21);
-int wcswidth(const wchar_t* __s, size_t __n);
-size_t wcsxfrm(wchar_t* __dst, const wchar_t* __src, size_t __n);
+int swprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, ...);
+int swscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, ...);
+wint_t ungetwc(wint_t __wc, FILE* _Nonnull __fp);
+int vfwprintf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args);
+int vfwscanf(FILE* _Nonnull __fp, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vswprintf(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nonnull __fmt, va_list __args);
+int vswscanf(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+int vwprintf(const wchar_t* _Nonnull __fmt, va_list __args);
+int vwscanf(const wchar_t* _Nonnull __fmt, va_list __args) __INTRODUCED_IN(21);
+wchar_t* _Nonnull wcpcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nonnull wcpncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcrtomb(char* _Nullable __buf, wchar_t __wc, mbstate_t* _Nullable __ps);
+int wcscasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcscat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+wchar_t* _Nullable wcschr(const wchar_t * _Nonnull __s, wchar_t __wc);
+int wcscmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+int wcscoll(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs);
+wchar_t* _Nonnull wcscpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src);
+size_t wcscspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+size_t wcsftime(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm);
+size_t wcsftime_l(wchar_t* _Nonnull __buf, size_t __n, const wchar_t* _Nullable __fmt, const struct tm* _Nonnull __tm, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+size_t wcslen(const wchar_t* _Nonnull __s);
+int wcsncasecmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+int wcsncasecmp_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wcsncat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+int wcsncmp(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, size_t __n);
+wchar_t* _Nonnull wcsncpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcsnrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __src_n, size_t __dst_n, mbstate_t* _Nullable __ps) __INTRODUCED_IN(21);
+wchar_t* _Nullable wcspbrk(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsrchr(const wchar_t* _Nonnull __s, wchar_t __wc);
+size_t wcsrtombs(char* _Nullable __dst, const wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __src, size_t __dst_n, mbstate_t* _Nullable __ps);
+size_t wcsspn(const wchar_t* _Nonnull __s, const wchar_t* _Nonnull __accept);
+wchar_t* _Nullable wcsstr(const wchar_t* _Nonnull __haystack, const wchar_t* _Nonnull __needle);
+double wcstod(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr);
+double wcstod_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+float wcstof(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __INTRODUCED_IN(21);
+float wcstof_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+wchar_t* _Nullable wcstok(wchar_t* _Nullable __s, const wchar_t* _Nonnull __delimiter, wchar_t* _Nonnull * _Nonnull __ptr);
+long wcstol(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+long wcstol_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+long long wcstoll(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+long double wcstold(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr) __RENAME_LDBL(wcstod, 3, 21);
+unsigned long wcstoul(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base);
+unsigned long wcstoul_l(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(28);
+unsigned long long wcstoull(const wchar_t* _Nonnull __s, wchar_t* __BIONIC_COMPLICATED_NULLNESS * _Nullable __end_ptr, int __base) __INTRODUCED_IN(21);
+int wcswidth(const wchar_t* _Nonnull __s, size_t __n);
+size_t wcsxfrm(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n);
int wctob(wint_t __wc);
int wcwidth(wchar_t __wc);
-wchar_t* wmemchr(const wchar_t* __src, wchar_t __wc, size_t __n);
-int wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __n);
-wchar_t* wmemcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
+wchar_t* _Nullable wmemchr(const wchar_t* _Nonnull __src, wchar_t __wc, size_t __n);
+int wmemcmp(const wchar_t* _Nullable __lhs, const wchar_t* _Nullable __rhs, size_t __n);
+wchar_t* _Nonnull wmemcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
#if defined(__USE_GNU)
-wchar_t* wmempcpy(wchar_t* __dst, const wchar_t* __src, size_t __n) __INTRODUCED_IN(23);
+wchar_t* _Nonnull wmempcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n) __INTRODUCED_IN(23);
#endif
-wchar_t* wmemmove(wchar_t* __dst, const wchar_t* __src, size_t __n);
-wchar_t* wmemset(wchar_t* __dst, wchar_t __wc, size_t __n);
-int wprintf(const wchar_t* __fmt, ...);
-int wscanf(const wchar_t* __fmt, ...);
+wchar_t* _Nonnull wmemmove(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+wchar_t* _Nonnull wmemset(wchar_t* _Nonnull __dst, wchar_t __wc, size_t __n);
+int wprintf(const wchar_t* _Nonnull __fmt, ...);
+int wscanf(const wchar_t* _Nonnull __fmt, ...);
-long long wcstoll_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-unsigned long long wcstoull_l(const wchar_t* __s, wchar_t** __end_ptr, int __base, locale_t __l) __INTRODUCED_IN(21);
-long double wcstold_l(const wchar_t* __s, wchar_t** __end_ptr, locale_t __l) __INTRODUCED_IN(21);
+long long wcstoll_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+unsigned long long wcstoull_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+long double wcstold_l(const wchar_t* _Nonnull __s, wchar_t* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) __INTRODUCED_IN(21);
-int wcscoll_l(const wchar_t* __lhs, const wchar_t* __rhs, locale_t __l) __attribute_pure__
+int wcscoll_l(const wchar_t* _Nonnull __lhs, const wchar_t* _Nonnull __rhs, locale_t _Nonnull __l) __attribute_pure__
__INTRODUCED_IN(21);
-size_t wcsxfrm_l(wchar_t* __dst, const wchar_t* __src, size_t __n, locale_t __l) __INTRODUCED_IN(21);
+size_t wcsxfrm_l(wchar_t* __BIONIC_COMPLICATED_NULLNESS __dst, const wchar_t* _Nonnull __src, size_t __n, locale_t _Nonnull __l) __INTRODUCED_IN(21);
+size_t wcslcat(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
+size_t wcslcpy(wchar_t* _Nonnull __dst, const wchar_t* _Nonnull __src, size_t __n);
-size_t wcslcat(wchar_t* __dst, const wchar_t* __src, size_t __n);
-size_t wcslcpy(wchar_t* __dst, const wchar_t* __src, size_t __n);
-
-FILE* open_wmemstream(wchar_t** __ptr, size_t* __size_ptr) __INTRODUCED_IN(23);
-wchar_t* wcsdup(const wchar_t* __s);
-size_t wcsnlen(const wchar_t* __s, size_t __n);
+FILE* _Nullable open_wmemstream(wchar_t* _Nonnull * _Nonnull __ptr, size_t* _Nonnull __size_ptr) __INTRODUCED_IN(23);
+wchar_t* _Nullable wcsdup(const wchar_t* _Nonnull __s);
+size_t wcsnlen(const wchar_t* _Nonnull __s, size_t __n);
__END_DECLS
diff --git a/libc/kernel/tools/update_all.py b/libc/kernel/tools/update_all.py
index abc72a4..ae89a80 100755
--- a/libc/kernel/tools/update_all.py
+++ b/libc/kernel/tools/update_all.py
@@ -92,6 +92,7 @@
'kernel/uapi/asm-arm/asm/unistd.h',
'kernel/uapi/asm-arm/asm/unistd-eabi.h',
'kernel/uapi/asm-arm/asm/unistd-oabi.h',
+ 'kernel/uapi/asm-riscv/asm/unistd.h',
'kernel/uapi/asm-x86/asm/unistd_32.h',
'kernel/uapi/asm-x86/asm/unistd_64.h',
'kernel/uapi/asm-x86/asm/unistd_x32.h']:
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index c75b13a..0102b30 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1584,6 +1584,11 @@
posix_spawn_file_actions_addfchdir_np;
} LIBC_T;
+LIBC_V { # introduced=VanillaIceCream
+ global:
+ timespec_getres;
+} LIBC_U;
+
LIBC_PRIVATE {
global:
__accept4; # arm x86
diff --git a/libc/stdio/printf_common.h b/libc/stdio/printf_common.h
index e761835..365728b 100644
--- a/libc/stdio/printf_common.h
+++ b/libc/stdio/printf_common.h
@@ -528,6 +528,29 @@
case 'b':
ADDUARG();
break;
+ case 'w': {
+ n = 0;
+ bool fast = false;
+ ch = *fmt++;
+ if (ch == 'f') {
+ fast = true;
+ ch = *fmt++;
+ }
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ if (n == 64) {
+ flags |= LLONGINT;
+ } else {
+ if (n != 8 && fast) {
+#if defined(__LP64__)
+ flags |= LLONGINT;
+#endif
+ }
+ }
+ goto reswitch;
+ }
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0') goto done;
break;
@@ -813,4 +836,15 @@
return convbuf;
}
+ // Trasnlate a fixed size integer argument for the %w/%wf format to a
+ // flag representation. Supported sizes are 8, 16, 32, and 64 so far.
+ // See details in bionic/libc/include/stdint.h
+ static int w_to_flag(int size, bool fast) {
+ static constexpr int fast_size = sizeof(void*) == 8 ? LLONGINT : 0;
+ if (size == 8) return CHARINT;
+ if (size == 16) return fast ? fast_size : SHORTINT;
+ if (size == 32) return fast ? fast_size : 0;
+ if (size == 64) return LLONGINT;
+ __fortify_fatal("%%w%s%d is unsupported", fast ? "f" : "", size);
+ }
};
diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp
index d83a5bf..994269b 100644
--- a/libc/stdio/vfprintf.cpp
+++ b/libc/stdio/vfprintf.cpp
@@ -521,6 +521,21 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w': {
+ n = 0;
+ bool fast = false;
+ ch = *fmt++;
+ if (ch == 'f') {
+ fast = true;
+ ch = *fmt++;
+ }
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ flags |= helpers::w_to_flag(n, fast);
+ goto reswitch;
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/libc/stdio/vfwprintf.cpp b/libc/stdio/vfwprintf.cpp
index 9819a73..0caeb2d 100644
--- a/libc/stdio/vfwprintf.cpp
+++ b/libc/stdio/vfwprintf.cpp
@@ -510,6 +510,21 @@
_umax = UARG();
base = DEC;
goto nosign;
+ case 'w': {
+ n = 0;
+ bool fast = false;
+ ch = *fmt++;
+ if (ch == 'f') {
+ fast = true;
+ ch = *fmt++;
+ }
+ while (is_digit(ch)) {
+ APPEND_DIGIT(n, ch);
+ ch = *fmt++;
+ }
+ flags |= helpers::w_to_flag(n, fast);
+ goto reswitch;
+ }
case 'X':
xdigs = xdigs_upper;
goto hex;
diff --git a/libc/tzcode/asctime.c b/libc/tzcode/asctime.c
index ce5d4be..4cdfd13 100644
--- a/libc/tzcode/asctime.c
+++ b/libc/tzcode/asctime.c
@@ -17,12 +17,6 @@
#include <stdio.h>
/*
-** Some systems only handle "%.2d"; others only handle "%02d";
-** "%02.2d" makes (most) everybody happy.
-** At least some versions of gcc warn about the %02.2d;
-** we conditionalize below to avoid the warning.
-*/
-/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
@@ -34,24 +28,16 @@
** The ISO C and POSIX standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
-#ifdef __GNUC__
-#define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n"
-#else /* !defined __GNUC__ */
-#define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n"
-#endif /* !defined __GNUC__ */
+static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
-#ifdef __GNUC__
-#define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n"
-#else /* !defined __GNUC__ */
-#define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n"
-#endif /* !defined __GNUC__ */
+static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
-#define STD_ASCTIME_BUF_SIZE 26
+enum { STD_ASCTIME_BUF_SIZE = 26 };
/*
** Big enough for something such as
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
@@ -59,15 +45,23 @@
** seven explicit spaces, two explicit colons, a newline,
** and a trailing NUL byte).
** The values above are for systems where an int is 32 bits and are provided
-** as an example; the define below calculates the maximum for the system at
+** as an example; the size expression below is a bound for the system at
** hand.
*/
-#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
+static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
-static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
+/* A similar buffer for ctime.
+ C89 requires that they be the same buffer.
+ This requirement was removed in C99, so support it only if requested,
+ as support is more likely to lead to bugs in badly written programs. */
+#if SUPPORT_C89
+# define buf_ctime buf_asctime
+#else
+static char buf_ctime[sizeof buf_asctime];
+#endif
char *
-asctime_r(register const struct tm *timeptr, char *buf)
+asctime_r(struct tm const *restrict timeptr, char *restrict buf)
{
static const char wday_name[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@@ -79,7 +73,7 @@
register const char * wn;
register const char * mn;
char year[INT_STRLEN_MAXIMUM(int) + 2];
- char result[MAX_ASCTIME_BUF_SIZE];
+ char result[sizeof buf_asctime];
if (timeptr == NULL) {
errno = EINVAL;
@@ -107,7 +101,8 @@
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
- if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
+ if (strlen(result) < STD_ASCTIME_BUF_SIZE
+ || buf == buf_ctime || buf == buf_asctime)
return strcpy(buf, result);
else {
errno = EOVERFLOW;
@@ -120,3 +115,17 @@
{
return asctime_r(timeptr, buf_asctime);
}
+
+char *
+ctime_r(const time_t *timep, char *buf)
+{
+ struct tm mytm;
+ struct tm *tmp = localtime_r(timep, &mytm);
+ return tmp ? asctime_r(tmp, buf) : NULL;
+}
+
+char *
+ctime(const time_t *timep)
+{
+ return ctime_r(timep, buf_ctime);
+}
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 8ff5cee..5e1181f 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -28,29 +28,22 @@
static void unlock(void) { }
#endif
-#ifndef TZ_ABBR_MAX_LEN
-#define TZ_ABBR_MAX_LEN 16
-#endif /* !defined TZ_ABBR_MAX_LEN */
-
#ifndef TZ_ABBR_CHAR_SET
-#define TZ_ABBR_CHAR_SET \
+# define TZ_ABBR_CHAR_SET \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
#endif /* !defined TZ_ABBR_CHAR_SET */
#ifndef TZ_ABBR_ERR_CHAR
-#define TZ_ABBR_ERR_CHAR '_'
+# define TZ_ABBR_ERR_CHAR '_'
#endif /* !defined TZ_ABBR_ERR_CHAR */
/*
-** SunOS 4.1.1 headers lack O_BINARY.
++** Support non-POSIX platforms that distinguish between text and binary files.
*/
-#ifdef O_BINARY
-#define OPEN_MODE (O_RDONLY | O_BINARY)
-#endif /* defined O_BINARY */
#ifndef O_BINARY
-#define OPEN_MODE O_RDONLY
-#endif /* !defined O_BINARY */
+# define O_BINARY 0
+#endif
#ifndef WILDABBR
/*
@@ -72,12 +65,13 @@
** manual page of what this "time zone abbreviation" means (doing this so
** that tzname[0] has the "normal" length of three characters).
*/
-#define WILDABBR " "
+# define WILDABBR " "
#endif /* !defined WILDABBR */
static const char wildabbr[] = WILDABBR;
-static const char gmt[] = "GMT";
+static char const etc_utc[] = "Etc/UTC";
+static char const *utc = etc_utc + sizeof "Etc/" - 1;
/*
** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
@@ -86,7 +80,7 @@
** for historical reasons, US rules are a common default.
*/
#ifndef TZDEFRULESTRING
-#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
+# define TZDEFRULESTRING ",M3.2.0,M11.1.0"
#endif
struct ttinfo { /* time type information */
@@ -102,9 +96,6 @@
int_fast32_t ls_corr; /* correction to apply */
};
-#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
-#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
-
/* This abbreviation means local time is unspecified. */
static char const UNSPEC[] = "-00";
@@ -112,14 +103,13 @@
This needs to be at least 1 for null termination in case the input
data isn't properly terminated, and it also needs to be big enough
for ttunspecified to work without crashing. */
-enum { CHARS_EXTRA = BIGGEST(sizeof UNSPEC, 2) - 1 };
+enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-#ifdef TZNAME_MAX
-#define MY_TZNAME_MAX TZNAME_MAX
-#endif /* defined TZNAME_MAX */
-#ifndef TZNAME_MAX
-#define MY_TZNAME_MAX 255
-#endif /* !defined TZNAME_MAX */
+/* Limit to time zone abbreviation length in POSIX-style TZ strings.
+ This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
+#ifndef TZNAME_MAXIMUM
+# define TZNAME_MAXIMUM 255
+#endif
struct state {
int leapcnt;
@@ -131,9 +121,8 @@
time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + CHARS_EXTRA,
- sizeof gmt),
- (2 * (MY_TZNAME_MAX + 1)))];
+ char chars[max(max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof "UTC"),
+ 2 * (TZNAME_MAXIMUM + 1))];
struct lsinfo lsis[TZ_MAX_LEAPS];
/* The time type to use for early times or if no transitions.
It is always zero for recent tzdb releases.
@@ -174,12 +163,12 @@
#ifndef ALL_STATE
static struct state lclmem;
static struct state gmtmem;
-#define lclptr (&lclmem)
-#define gmtptr (&gmtmem)
+static struct state *const lclptr = &lclmem;
+static struct state *const gmtptr = &gmtmem;
#endif /* State Farm */
#ifndef TZ_STRLEN_MAX
-#define TZ_STRLEN_MAX 255
+# define TZ_STRLEN_MAX 255
#endif /* !defined TZ_STRLEN_MAX */
static char lcl_TZname[TZ_STRLEN_MAX + 1];
@@ -191,9 +180,14 @@
** ctime, gmtime, localtime] return values in one of two static
** objects: a broken-down time structure and an array of char.
** Thanks to Paul Eggert for noting this.
+**
+** This requirement was removed in C99, so support it only if requested,
+** as support is more likely to lead to bugs in badly written programs.
*/
+#if SUPPORT_C89
static struct tm tm;
+#endif
#if 2 <= HAVE_TZNAME + TZ_TIME_T
char * tzname[2] = {
@@ -321,7 +315,7 @@
int stddst_mask = 0;
#if HAVE_TZNAME
- tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt);
+ tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc);
stddst_mask = 3;
#endif
#if USG_COMPAT
@@ -346,27 +340,28 @@
#endif
}
-static void
+/* Replace bogus characters in time zone abbreviations.
+ Return 0 on success, an errno value if a time zone abbreviation is
+ too long. */
+static int
scrub_abbrs(struct state *sp)
{
int i;
- /*
- ** First, replace bogus characters.
- */
+
+ /* Reject overlong abbreviations. */
+ for (i = 0; i < sp->charcnt - (TZNAME_MAXIMUM + 1); ) {
+ int len = strlen(&sp->chars[i]);
+ if (TZNAME_MAXIMUM < len)
+ return EOVERFLOW;
+ i += len + 1;
+ }
+
+ /* Replace bogus characters. */
for (i = 0; i < sp->charcnt; ++i)
if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
sp->chars[i] = TZ_ABBR_ERR_CHAR;
- /*
- ** Second, truncate long abbreviations.
- */
- for (i = 0; i < sp->typecnt; ++i) {
- register const struct ttinfo * const ttisp = &sp->ttis[i];
- char *cp = &sp->chars[ttisp->tt_desigidx];
- if (strlen(cp) > TZ_ABBR_MAX_LEN &&
- strcmp(cp, GRANDPARENTED) != 0)
- *(cp + TZ_ABBR_MAX_LEN) = '\0';
- }
+ return 0;
}
/* Input buffer for data read from a compiled tz file. */
@@ -399,8 +394,7 @@
// Android-removed: There is no directory with file-per-time zone on Android.
#ifndef __BIONIC__
/* The file name to be opened. */
- char fullname[BIGGEST(sizeof(struct file_analysis),
- sizeof tzdirslash + 1024)];
+ char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)];
#endif
};
@@ -446,8 +440,7 @@
#endif
if (!doaccess) {
char const *dot;
- size_t namelen = strlen(name);
- if (sizeof lsp->fullname - sizeof tzdirslash <= namelen)
+ if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name))
return ENAMETOOLONG;
/* Create a string "TZDIR/NAME". Using sprintf here
@@ -470,7 +463,7 @@
}
if (doaccess && access(name, R_OK) != 0)
return errno;
- fid = open(name, OPEN_MODE);
+ fid = open(name, O_RDONLY | O_BINARY);
#endif
if (fid < 0)
return errno;
@@ -828,12 +821,14 @@
b < 0 || b >= sp->typecnt)
result = false;
else {
+ /* Compare the relevant members of *AP and *BP.
+ Ignore tt_ttisstd and tt_ttisut, as they are
+ irrelevant now and counting them could cause
+ sp->goahead to mistakenly remain false. */
register const struct ttinfo * ap = &sp->ttis[a];
register const struct ttinfo * bp = &sp->ttis[b];
result = (ap->tt_utoff == bp->tt_utoff
&& ap->tt_isdst == bp->tt_isdst
- && ap->tt_ttisstd == bp->tt_ttisstd
- && ap->tt_ttisut == bp->tt_ttisut
&& (strcmp(&sp->chars[ap->tt_desigidx],
&sp->chars[bp->tt_desigidx])
== 0));
@@ -863,7 +858,7 @@
** Return a pointer to that character.
*/
-static ATTRIBUTE_PURE const char *
+ATTRIBUTE_REPRODUCIBLE static const char *
getzname(register const char *strp)
{
register char c;
@@ -884,7 +879,7 @@
** We don't do any checking here; checking is done later in common-case code.
*/
-static ATTRIBUTE_PURE const char *
+ATTRIBUTE_REPRODUCIBLE static const char *
getqzname(register const char *strp, const int delim)
{
register int c;
@@ -1122,7 +1117,7 @@
value += mon_lengths[leapyear][i] * SECSPERDAY;
break;
- default: UNREACHABLE();
+ default: unreachable();
}
/*
@@ -1144,13 +1139,11 @@
{
const char * stdname;
const char * dstname;
- size_t stdlen;
- size_t dstlen;
- size_t charcnt;
int_fast32_t stdoffset;
int_fast32_t dstoffset;
register char * cp;
register bool load_ok;
+ ptrdiff_t stdlen, dstlen, charcnt;
time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
stdname = name;
@@ -1166,14 +1159,12 @@
name = getzname(name);
stdlen = name - stdname;
}
- if (!stdlen)
+ if (! (0 < stdlen && stdlen <= TZNAME_MAXIMUM))
return false;
name = getoffset(name, &stdoffset);
if (name == NULL)
return false;
charcnt = stdlen + 1;
- if (sizeof sp->chars < charcnt)
- return false;
if (basep) {
if (0 < basep->timecnt)
atlo = basep->ats[basep->timecnt - 1];
@@ -1200,11 +1191,9 @@
name = getzname(name);
dstlen = name - dstname; /* length of DST abbr. */
}
- if (!dstlen)
+ if (! (0 < dstlen && dstlen <= TZNAME_MAXIMUM))
return false;
charcnt += dstlen + 1;
- if (sizeof sp->chars < charcnt)
- return false;
if (*name != '\0' && *name != ',' && *name != ';') {
name = getoffset(name, &dstoffset);
if (name == NULL)
@@ -1420,8 +1409,8 @@
static void
gmtload(struct state *const sp)
{
- if (tzload(gmt, sp, true) != 0)
- tzparse("GMT0", sp, NULL);
+ if (tzload(etc_utc, sp, true) != 0)
+ tzparse("UTC0", sp, NULL);
}
/* Initialize *SP to a value appropriate for the TZ setting NAME.
@@ -1439,7 +1428,7 @@
sp->charcnt = 0;
sp->goback = sp->goahead = false;
init_ttinfo(&sp->ttis[0], 0, false, 0);
- strcpy(sp->chars, gmt);
+ strcpy(sp->chars, utc);
sp->defaulttype = 0;
return 0;
} else {
@@ -1447,7 +1436,7 @@
if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
- scrub_abbrs(sp);
+ err = scrub_abbrs(sp);
return err;
}
}
@@ -1557,7 +1546,7 @@
** set the applicable parts of tzname, timezone and altzone;
** however, it's OK to omit this step if the timezone is POSIX-compatible,
** since in that case tzset should have already done this step correctly.
-** SETNAME's type is intfast32_t for compatibility with gmtsub,
+** SETNAME's type is int_fast32_t for compatibility with gmtsub,
** but it is actually a boolean and its value should be 0 or 1.
*/
@@ -1601,6 +1590,14 @@
return NULL; /* "cannot happen" */
result = localsub(sp, &newt, setname, tmp);
if (result) {
+#if defined ckd_add && defined ckd_sub
+ if (t < sp->ats[0]
+ ? ckd_sub(&result->tm_year,
+ result->tm_year, years)
+ : ckd_add(&result->tm_year,
+ result->tm_year, years))
+ return NULL;
+#else
register int_fast64_t newy;
newy = result->tm_year;
@@ -1610,6 +1607,7 @@
if (! (INT_MIN <= newy && newy <= INT_MAX))
return NULL;
result->tm_year = newy;
+#endif
}
return result;
}
@@ -1650,7 +1648,8 @@
#if NETBSD_INSPIRED
struct tm *
-localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp)
+localtime_rz(struct state *restrict sp, time_t const *restrict timep,
+ struct tm *restrict tmp)
{
return localsub(sp, timep, 0, tmp);
}
@@ -1681,11 +1680,14 @@
struct tm *
localtime(const time_t *timep)
{
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return localtime_tzset(timep, &tm);
}
struct tm *
-localtime_r(const time_t *timep, struct tm *tmp)
+localtime_r(const time_t *restrict timep, struct tm *restrict tmp)
{
return localtime_tzset(timep, tmp);
}
@@ -1695,8 +1697,8 @@
*/
static struct tm *
-gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
- struct tm *tmp)
+gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
+ int_fast32_t offset, struct tm *tmp)
{
register struct tm * result;
@@ -1708,7 +1710,7 @@
** but this is no time for a treasure hunt.
*/
tmp->TM_ZONE = ((char *)
- (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt));
+ (offset ? wildabbr : gmtptr ? gmtptr->chars : utc));
#endif /* defined TM_ZONE */
return result;
}
@@ -1718,7 +1720,7 @@
*/
struct tm *
-gmtime_r(const time_t *timep, struct tm *tmp)
+gmtime_r(time_t const *restrict timep, struct tm *restrict tmp)
{
gmtcheck();
return gmtsub(gmtptr, timep, 0, tmp);
@@ -1727,19 +1729,26 @@
struct tm *
gmtime(const time_t *timep)
{
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return gmtime_r(timep, &tm);
}
-#ifdef STD_INSPIRED
+#if STD_INSPIRED
struct tm *
offtime(const time_t *timep, long offset)
{
gmtcheck();
+
+#if !SUPPORT_C89
+ static struct tm tm;
+#endif
return gmtsub(gmtptr, timep, offset, &tm);
}
-#endif /* defined STD_INSPIRED */
+#endif
/*
** Return the number of leap years through the end of the given year
@@ -1825,6 +1834,12 @@
y = newy;
}
+#ifdef ckd_add
+ if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+#else
if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) {
int signed_y = y;
tmp->tm_year = signed_y - TM_YEAR_BASE;
@@ -1835,6 +1850,7 @@
errno = EOVERFLOW;
return NULL;
}
+#endif
tmp->tm_yday = idays;
/*
** The "extra" mods below avoid overflow problems.
@@ -1868,27 +1884,6 @@
return tmp;
}
-char *
-ctime(const time_t *timep)
-{
-/*
-** Section 4.12.3.2 of X3.159-1989 requires that
-** The ctime function converts the calendar time pointed to by timer
-** to local time in the form of a string. It is equivalent to
-** asctime(localtime(timer))
-*/
- struct tm *tmp = localtime(timep);
- return tmp ? asctime(tmp) : NULL;
-}
-
-char *
-ctime_r(const time_t *timep, char *buf)
-{
- struct tm mytm;
- struct tm *tmp = localtime_r(timep, &mytm);
- return tmp ? asctime_r(tmp, buf) : NULL;
-}
-
/*
** Adapted from code provided by Robert Elz, who writes:
** The "best" way to do mktime I think is based on an idea of Bob
@@ -1899,7 +1894,7 @@
*/
#ifndef WRONG
-#define WRONG (-1)
+# define WRONG (-1)
#endif /* !defined WRONG */
/*
@@ -1909,6 +1904,9 @@
static bool
increment_overflow(int *ip, int j)
{
+#ifdef ckd_add
+ return ckd_add(ip, *ip, j);
+#else
register int const i = *ip;
/*
@@ -1921,22 +1919,30 @@
return true;
*ip += j;
return false;
+#endif
}
static bool
increment_overflow32(int_fast32_t *const lp, int const m)
{
+#ifdef ckd_add
+ return ckd_add(lp, *lp, m);
+#else
register int_fast32_t const l = *lp;
if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
return true;
*lp += m;
return false;
+#endif
}
static bool
increment_overflow_time(time_t *tp, int_fast32_t j)
{
+#ifdef ckd_add
+ return ckd_add(tp, *tp, j);
+#else
/*
** This is like
** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
@@ -1948,6 +1954,7 @@
return true;
*tp += j;
return false;
+#endif
}
static bool
@@ -1990,6 +1997,23 @@
return result;
}
+/* Copy to *DEST from *SRC. Copy only the members needed for mktime,
+ as other members might not be initialized. */
+static void
+mktmcpy(struct tm *dest, struct tm const *src)
+{
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = src->tm_year;
+ dest->tm_isdst = src->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ dest->TM_GMTOFF = src->TM_GMTOFF;
+#endif
+}
+
static time_t
time2sub(struct tm *const tmp,
struct tm *(*funcp)(struct state const *, time_t const *,
@@ -2011,7 +2035,8 @@
struct tm yourtm, mytm;
*okayp = false;
- yourtm = *tmp;
+ mktmcpy(&yourtm, tmp);
+
if (do_norm_secs) {
if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
SECSPERMIN))
@@ -2053,14 +2078,19 @@
return WRONG;
}
}
+#ifdef ckd_add
+ if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE))
+ return WRONG;
+#else
if (increment_overflow32(&y, -TM_YEAR_BASE))
return WRONG;
if (! (INT_MIN <= y && y <= INT_MAX))
return WRONG;
yourtm.tm_year = y;
+#endif
if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
saved_seconds = 0;
- else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
/*
** We can't set tm_sec to 0, because that might push the
** time below the minimum representable time.
@@ -2120,10 +2150,10 @@
&& (yourtm.TM_GMTOFF < 0
? (-SECSPERDAY <= yourtm.TM_GMTOFF
&& (mytm.TM_GMTOFF <=
- (SMALLEST(INT_FAST32_MAX, LONG_MAX)
+ (min(INT_FAST32_MAX, LONG_MAX)
+ yourtm.TM_GMTOFF)))
: (yourtm.TM_GMTOFF <= SECSPERDAY
- && ((BIGGEST(INT_FAST32_MIN, LONG_MIN)
+ && ((max(INT_FAST32_MIN, LONG_MIN)
+ yourtm.TM_GMTOFF)
<= mytm.TM_GMTOFF)))) {
/* MYTM matches YOURTM except with the wrong UT offset.
@@ -2294,7 +2324,7 @@
#if NETBSD_INSPIRED
time_t
-mktime_z(struct state *sp, struct tm *tmp)
+mktime_z(struct state *restrict sp, struct tm *restrict tmp)
{
return mktime_tzname(sp, tmp, false);
}
@@ -2324,8 +2354,7 @@
return t;
}
-#ifdef STD_INSPIRED
-
+#if STD_INSPIRED
time_t
timelocal(struct tm *tmp)
{
@@ -2333,13 +2362,9 @@
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
-
-time_t
-timegm(struct tm *tmp)
-{
- return timeoff(tmp, 0);
-}
-
+#else
+static
+#endif
time_t
timeoff(struct tm *tmp, long offset)
{
@@ -2349,7 +2374,18 @@
return time1(tmp, gmtsub, gmtptr, offset);
}
-#endif /* defined STD_INSPIRED */
+time_t
+timegm(struct tm *tmp)
+{
+ time_t t;
+ struct tm tmcpy;
+ mktmcpy(&tmcpy, tmp);
+ tmcpy.tm_wday = -1;
+ t = timeoff(&tmcpy, 0);
+ if (0 <= tmcpy.tm_wday)
+ *tmp = tmcpy;
+ return t;
+}
static int_fast32_t
leapcorr(struct state const *sp, time_t t)
@@ -2370,7 +2406,7 @@
** XXX--is the below the right way to conditionalize??
*/
-#ifdef STD_INSPIRED
+#if STD_INSPIRED
/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
NETBSD_INSPIRED is defined, and are private otherwise. */
@@ -2455,7 +2491,7 @@
return t;
}
-#endif /* defined STD_INSPIRED */
+#endif /* STD_INSPIRED */
#if TZ_TIME_T
diff --git a/libc/tzcode/private.h b/libc/tzcode/private.h
index 4c03324..838ab2b 100644
--- a/libc/tzcode/private.h
+++ b/libc/tzcode/private.h
@@ -17,6 +17,36 @@
** Thank you!
*/
+/* PORT_TO_C89 means the code should work even if the underlying
+ compiler and library support only C89. SUPPORT_C89 means the
+ tzcode library should support C89 callers in addition to the usual
+ support for C99-and-later callers. These macros are obsolescent,
+ and the plan is to remove them along with any code needed only when
+ they are nonzero. */
+#ifndef PORT_TO_C89
+# define PORT_TO_C89 0
+#endif
+#ifndef SUPPORT_C89
+# define SUPPORT_C89 0
+#endif
+
+#ifndef __STDC_VERSION__
+# define __STDC_VERSION__ 0
+#endif
+
+/* Define true, false and bool if they don't work out of the box. */
+#if PORT_TO_C89 && __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#elif __STDC_VERSION__ < 202311
+# include <stdbool.h>
+#endif
+
+#if __STDC_VERSION__ < 202311
+# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
+#endif
+
/*
** zdump has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides.
@@ -36,79 +66,86 @@
*/
#ifndef HAVE_DECL_ASCTIME_R
-#define HAVE_DECL_ASCTIME_R 1
+# define HAVE_DECL_ASCTIME_R 1
#endif
-#if !defined HAVE_GENERIC && defined __has_extension
+#if !defined HAVE__GENERIC && defined __has_extension
# if __has_extension(c_generic_selections)
-# define HAVE_GENERIC 1
+# define HAVE__GENERIC 1
# else
-# define HAVE_GENERIC 0
+# define HAVE__GENERIC 0
# endif
#endif
/* _Generic is buggy in pre-4.9 GCC. */
-#if !defined HAVE_GENERIC && defined __GNUC__
-# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
+#if !defined HAVE__GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
+# define HAVE__GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
#endif
-#ifndef HAVE_GENERIC
-# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
+#ifndef HAVE__GENERIC
+# define HAVE__GENERIC (201112 <= __STDC_VERSION__)
#endif
+#if !defined HAVE_GETTEXT && defined __has_include
+# if __has_include(<libintl.h>)
+# define HAVE_GETTEXT true
+# endif
+#endif
#ifndef HAVE_GETTEXT
-#define HAVE_GETTEXT 0
-#endif /* !defined HAVE_GETTEXT */
+# define HAVE_GETTEXT false
+#endif
#ifndef HAVE_INCOMPATIBLE_CTIME_R
-#define HAVE_INCOMPATIBLE_CTIME_R 0
+# define HAVE_INCOMPATIBLE_CTIME_R 0
#endif
#ifndef HAVE_LINK
-#define HAVE_LINK 1
+# define HAVE_LINK 1
#endif /* !defined HAVE_LINK */
#ifndef HAVE_MALLOC_ERRNO
-#define HAVE_MALLOC_ERRNO 1
+# define HAVE_MALLOC_ERRNO 1
#endif
#ifndef HAVE_POSIX_DECLS
-#define HAVE_POSIX_DECLS 1
+# define HAVE_POSIX_DECLS 1
#endif
-#ifndef HAVE_STDBOOL_H
-#define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__)
+#ifndef HAVE_SETENV
+# define HAVE_SETENV 1
#endif
#ifndef HAVE_STRDUP
-#define HAVE_STRDUP 1
-#endif
-
-#ifndef HAVE_STRTOLL
-#define HAVE_STRTOLL 1
+# define HAVE_STRDUP 1
#endif
#ifndef HAVE_SYMLINK
-#define HAVE_SYMLINK 1
+# define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
+#if !defined HAVE_SYS_STAT_H && defined __has_include
+# if !__has_include(<sys/stat.h>)
+# define HAVE_SYS_STAT_H false
+# endif
+#endif
#ifndef HAVE_SYS_STAT_H
-#define HAVE_SYS_STAT_H 1
-#endif /* !defined HAVE_SYS_STAT_H */
+# define HAVE_SYS_STAT_H true
+#endif
+#if !defined HAVE_UNISTD_H && defined __has_include
+# if !__has_include(<unistd.h>)
+# define HAVE_UNISTD_H false
+# endif
+#endif
#ifndef HAVE_UNISTD_H
-#define HAVE_UNISTD_H 1
-#endif /* !defined HAVE_UNISTD_H */
-
-#ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H 1
-#endif /* !defined HAVE_UTMPX_H */
+# define HAVE_UNISTD_H true
+#endif
#ifndef NETBSD_INSPIRED
# define NETBSD_INSPIRED 1
#endif
#if HAVE_INCOMPATIBLE_CTIME_R
-#define asctime_r _incompatible_asctime_r
-#define ctime_r _incompatible_ctime_r
+# define asctime_r _incompatible_asctime_r
+# define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
@@ -118,15 +155,17 @@
/* Enable strtoimax on pre-C99 Solaris 11. */
#define __EXTENSIONS__ 1
-/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW,
- enable large files on GNUish systems ... */
+/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
+ On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
+ setting _TIME_BITS to 64 does not work. The code does not
+ otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
+ use off_t or functions like 'stat' that depend on off_t. */
#ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
#endif
-/* ... and on AIX ... */
-#define _LARGE_FILES 1
-/* ... and enable large inode numbers on Mac OS X 10.5 and later. */
-#define _DARWIN_USE_64_BIT_INODE 1
+#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
+# define _TIME_BITS 64
+#endif
/*
** Nested includes
@@ -157,8 +196,11 @@
#undef tzalloc
#undef tzfree
-#include <sys/types.h> /* for time_t */
+#include <stddef.h>
#include <string.h>
+#if !PORT_TO_C89
+# include <inttypes.h>
+#endif
#include <limits.h> /* for CHAR_BIT et al. */
#include <stdlib.h>
@@ -168,6 +210,9 @@
# define EINVAL ERANGE
#endif
+#ifndef ELOOP
+# define ELOOP EINVAL
+#endif
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
@@ -182,11 +227,11 @@
#endif
#if HAVE_GETTEXT
-#include <libintl.h>
+# include <libintl.h>
#endif /* HAVE_GETTEXT */
#if HAVE_UNISTD_H
-#include <unistd.h> /* for R_OK, and other POSIX goodness */
+# include <unistd.h> /* for R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */
#ifndef HAVE_STRFTIME_L
@@ -222,24 +267,29 @@
#endif
#ifndef R_OK
-#define R_OK 4
+# define R_OK 4
#endif /* !defined R_OK */
+#if PORT_TO_C89
+
/*
** Define HAVE_STDINT_H's default value here, rather than at the
** start, since __GLIBC__ and INTMAX_MAX's values depend on
-** previously-included files. glibc 2.1 and Solaris 10 and later have
+** previously included files. glibc 2.1 and Solaris 10 and later have
** stdint.h, even with pre-C99 compilers.
*/
+#if !defined HAVE_STDINT_H && defined __has_include
+# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
+#endif
#ifndef HAVE_STDINT_H
-#define HAVE_STDINT_H \
+# define HAVE_STDINT_H \
(199901 <= __STDC_VERSION__ \
- || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
+ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
|| __CYGWIN__ || INTMAX_MAX)
#endif /* !defined HAVE_STDINT_H */
#if HAVE_STDINT_H
-#include <stdint.h>
+# include <stdint.h>
#endif /* !HAVE_STDINT_H */
#ifndef HAVE_INTTYPES_H
@@ -250,36 +300,36 @@
#endif
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
-#ifdef __LONG_LONG_MAX__
+#if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__
# ifndef LLONG_MAX
# define LLONG_MAX __LONG_LONG_MAX__
# endif
# ifndef LLONG_MIN
# define LLONG_MIN (-1 - LLONG_MAX)
# endif
+# ifndef ULLONG_MAX
+# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
+# endif
#endif
#ifndef INT_FAST64_MAX
-# ifdef LLONG_MAX
-typedef long long int_fast64_t;
-# define INT_FAST64_MIN LLONG_MIN
-# define INT_FAST64_MAX LLONG_MAX
-# else
-# if LONG_MAX >> 31 < 0xffffffff
-Please use a compiler that supports a 64-bit integer type (or wider);
-you may need to compile with "-DHAVE_STDINT_H".
-# endif
-typedef long int_fast64_t;
+# if 1 <= LONG_MAX >> 31 >> 31
+typedef long int_fast64_t;
# define INT_FAST64_MIN LONG_MIN
# define INT_FAST64_MAX LONG_MAX
+# else
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef long long int_fast64_t;
+# define INT_FAST64_MIN LLONG_MIN
+# define INT_FAST64_MAX LLONG_MAX
# endif
#endif
#ifndef PRIdFAST64
-# if INT_FAST64_MAX == LLONG_MAX
-# define PRIdFAST64 "lld"
-# else
+# if INT_FAST64_MAX == LONG_MAX
# define PRIdFAST64 "ld"
+# else
+# define PRIdFAST64 "lld"
# endif
#endif
@@ -302,6 +352,9 @@
#ifndef INTMAX_MAX
# ifdef LLONG_MAX
typedef long long intmax_t;
+# ifndef HAVE_STRTOLL
+# define HAVE_STRTOLL true
+# endif
# if HAVE_STRTOLL
# define strtoimax strtoll
# endif
@@ -325,70 +378,183 @@
# endif
#endif
+#ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t))
+#endif
+
#ifndef UINT_FAST32_MAX
typedef unsigned long uint_fast32_t;
#endif
#ifndef UINT_FAST64_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
-typedef unsigned long long uint_fast64_t;
+# if 3 <= ULONG_MAX >> 31 >> 31
+typedef unsigned long uint_fast64_t;
+# define UINT_FAST64_MAX ULONG_MAX
# else
-# if ULONG_MAX >> 31 >> 1 < 0xffffffff
-Please use a compiler that supports a 64-bit integer type (or wider);
-you may need to compile with "-DHAVE_STDINT_H".
-# endif
-typedef unsigned long uint_fast64_t;
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef unsigned long long uint_fast64_t;
+# define UINT_FAST64_MAX ULLONG_MAX
# endif
#endif
#ifndef UINTMAX_MAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
typedef unsigned long long uintmax_t;
+# define UINTMAX_MAX ULLONG_MAX
# else
typedef unsigned long uintmax_t;
+# define UINTMAX_MAX ULONG_MAX
# endif
#endif
#ifndef PRIuMAX
-# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
+# ifdef ULLONG_MAX
# define PRIuMAX "llu"
# else
# define PRIuMAX "lu"
# endif
#endif
-#ifndef INT32_MAX
-#define INT32_MAX 0x7fffffff
-#endif /* !defined INT32_MAX */
-#ifndef INT32_MIN
-#define INT32_MIN (-1 - INT32_MAX)
-#endif /* !defined INT32_MIN */
-
#ifndef SIZE_MAX
-#define SIZE_MAX ((size_t) -1)
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#endif /* PORT_TO_C89 */
+
+/* The maximum size of any created object, as a signed integer.
+ Although the C standard does not outright prohibit larger objects,
+ behavior is undefined if the result of pointer subtraction does not
+ fit into ptrdiff_t, and the code assumes in several places that
+ pointer subtraction works. As a practical matter it's OK to not
+ support objects larger than this. */
+#define INDEX_MAX ((ptrdiff_t) min(PTRDIFF_MAX, SIZE_MAX))
+
+/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like
+ hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
+#if !defined HAVE_STDCKDINT_H && defined __has_include
+# if __has_include(<stdckdint.h>)
+# define HAVE_STDCKDINT_H true
+# endif
+#endif
+#ifdef HAVE_STDCKDINT_H
+# if HAVE_STDCKDINT_H
+# include <stdckdint.h>
+# endif
+#elif defined __EDG__
+/* Do nothing, to work around EDG bug <https://bugs.gnu.org/53256>. */
+#elif defined __has_builtin
+# if __has_builtin(__builtin_add_overflow)
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_sub_overflow)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_mul_overflow)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+# endif
+#elif 7 <= __GNUC__
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
#endif
#if 3 <= __GNUC__
-# define ATTRIBUTE_CONST __attribute__((const))
-# define ATTRIBUTE_MALLOC __attribute__((__malloc__))
-# define ATTRIBUTE_PURE __attribute__((__pure__))
-# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
+# define ATTRIBUTE_MALLOC __attribute__((malloc))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
#else
-# define ATTRIBUTE_CONST /* empty */
# define ATTRIBUTE_MALLOC /* empty */
-# define ATTRIBUTE_PURE /* empty */
# define ATTRIBUTE_FORMAT(spec) /* empty */
#endif
-#if !defined _Noreturn && __STDC_VERSION__ < 201112
-# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
-# define _Noreturn __attribute__((__noreturn__))
+#if (defined __has_c_attribute \
+ && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
+# define HAVE___HAS_C_ATTRIBUTE true
+#else
+# define HAVE___HAS_C_ATTRIBUTE false
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(deprecated)
+# define ATTRIBUTE_DEPRECATED [[deprecated]]
+# endif
+#endif
+#ifndef ATTRIBUTE_DEPRECATED
+# if 3 < __GNUC__ + (2 <= __GNUC_MINOR__)
+# define ATTRIBUTE_DEPRECATED __attribute__((deprecated))
# else
-# define _Noreturn
+# define ATTRIBUTE_DEPRECATED /* empty */
# endif
#endif
-#if __STDC_VERSION__ < 199901 && !defined restrict
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(fallthrough)
+# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
+# endif
+#endif
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if 7 <= __GNUC__
+# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
+# else
+# define ATTRIBUTE_FALLTHROUGH ((void) 0)
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(maybe_unused)
+# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
+# endif
+#endif
+#ifndef ATTRIBUTE_MAYBE_UNUSED
+# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
+# else
+# define ATTRIBUTE_MAYBE_UNUSED /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(noreturn)
+# define ATTRIBUTE_NORETURN [[noreturn]]
+# endif
+#endif
+#ifndef ATTRIBUTE_NORETURN
+# if 201112 <= __STDC_VERSION__
+# define ATTRIBUTE_NORETURN _Noreturn
+# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
+# else
+# define ATTRIBUTE_NORETURN /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(reproducible)
+# define ATTRIBUTE_REPRODUCIBLE [[reproducible]]
+# endif
+#endif
+#ifndef ATTRIBUTE_REPRODUCIBLE
+# if 3 <= __GNUC__
+# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure))
+# else
+# define ATTRIBUTE_REPRODUCIBLE /* empty */
+# endif
+#endif
+
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(unsequenced)
+# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+# endif
+#endif
+#ifndef ATTRIBUTE_UNSEQUENCED
+# if 3 <= __GNUC__
+# define ATTRIBUTE_UNSEQUENCED __attribute__((const))
+# else
+# define ATTRIBUTE_UNSEQUENCED /* empty */
+# endif
+#endif
+
+#if (__STDC_VERSION__ < 199901 && !defined restrict \
+ && (PORT_TO_C89 || defined _MSC_VER))
# define restrict /* empty */
#endif
@@ -505,11 +671,16 @@
# define altzone tz_altzone
# endif
-char *asctime(struct tm const *);
+# if __STDC_VERSION__ < 202311
+# define DEPRECATED_IN_C23 /* empty */
+# else
+# define DEPRECATED_IN_C23 ATTRIBUTE_DEPRECATED
+# endif
+DEPRECATED_IN_C23 char *asctime(struct tm const *);
char *asctime_r(struct tm const *restrict, char *restrict);
-char *ctime(time_t const *);
+DEPRECATED_IN_C23 char *ctime(time_t const *);
char *ctime_r(time_t const *, char *);
-double difftime(time_t, time_t) ATTRIBUTE_CONST;
+ATTRIBUTE_UNSEQUENCED double difftime(time_t, time_t);
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
@@ -522,9 +693,24 @@
struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *);
time_t time(time_t *);
+time_t timegm(struct tm *);
void tzset(void);
#endif
+#ifndef HAVE_DECL_TIMEGM
+# if (202311 <= __STDC_VERSION__ \
+ || defined __GLIBC__ || defined __tm_zone /* musl */ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# define HAVE_DECL_TIMEGM true
+# else
+# define HAVE_DECL_TIMEGM false
+# endif
+#endif
+#if !HAVE_DECL_TIMEGM && !defined timegm
+time_t timegm(struct tm *);
+#endif
+
#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
extern char *asctime_r(struct tm const *restrict, char *restrict);
#endif
@@ -557,13 +743,13 @@
** declarations if time_tz is defined.
*/
-#ifdef STD_INSPIRED
+#ifndef STD_INSPIRED
+# define STD_INSPIRED 0
+#endif
+#if STD_INSPIRED
# if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long);
# endif
-# if TZ_TIME_T || !defined timegm
-time_t timegm(struct tm *);
-# endif
# if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *);
# endif
@@ -581,6 +767,7 @@
/* Infer TM_ZONE on systems where this information is known, but suppress
guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
#if (defined __GLIBC__ \
+ || defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
@@ -606,12 +793,12 @@
time_t mktime_z(timezone_t restrict, struct tm *restrict);
timezone_t tzalloc(char const *);
void tzfree(timezone_t);
-# ifdef STD_INSPIRED
+# if STD_INSPIRED
# if TZ_TIME_T || !defined posix2time_z
-time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+ATTRIBUTE_REPRODUCIBLE time_t posix2time_z(timezone_t, time_t);
# endif
# if TZ_TIME_T || !defined time2posix_z
-time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+ATTRIBUTE_REPRODUCIBLE time_t time2posix_z(timezone_t, time_t);
# endif
# endif
#endif
@@ -620,18 +807,15 @@
** Finally, some convenience items.
*/
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#else
-# define true 1
-# define false 0
-# define bool int
-#endif
-
-#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
+#define TYPE_BIT(type) (CHAR_BIT * (ptrdiff_t) sizeof(type))
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+/* Minimum and maximum of two values. Use lower case to avoid
+ naming clashes with standard include files. */
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
/* Max and min values of the integer type T, of which only the bottom
B bits are used, and where the highest-order used bit is considered
to be a sign bit if T is signed. */
@@ -651,7 +835,7 @@
This implementation assumes no padding if time_t is signed and
either the compiler lacks support for _Generic or time_t is not one
of the standard signed integer types. */
-#if HAVE_GENERIC
+#if HAVE__GENERIC
# define TIME_T_MIN \
_Generic((time_t) 0, \
signed char: SCHAR_MIN, short: SHRT_MIN, \
@@ -664,10 +848,23 @@
int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
default: TIME_T_MAX_NO_PADDING) \
: (time_t) -1)
+enum { SIGNED_PADDING_CHECK_NEEDED
+ = _Generic((time_t) 0,
+ signed char: false, short: false,
+ int: false, long: false, long long: false,
+ default: true) };
#else
# define TIME_T_MIN TIME_T_MIN_NO_PADDING
# define TIME_T_MAX TIME_T_MAX_NO_PADDING
+enum { SIGNED_PADDING_CHECK_NEEDED = true };
#endif
+/* Try to check the padding assumptions. Although TIME_T_MAX and the
+ following check can both have undefined behavior on oddball
+ platforms due to shifts exceeding widths of signed integers, these
+ platforms' compilers are likely to diagnose these issues in integer
+ constant expressions, so it shouldn't hurt to check statically. */
+static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
+ || TIME_T_MAX >> (TYPE_BIT(time_t) - 2) == 1);
/*
** 302 / 1000 is log10(2.0) rounded up.
@@ -689,21 +886,30 @@
# define INITIALIZE(x)
#endif
+/* Whether memory access must strictly follow the C standard.
+ If 0, it's OK to read uninitialized storage so long as the value is
+ not relied upon. Defining it to 0 lets mktime access parts of
+ struct tm that might be uninitialized, as a heuristic when the
+ standard doesn't say what to return and when tm_gmtoff can help
+ mktime likely infer a better value. */
#ifndef UNINIT_TRAP
# define UNINIT_TRAP 0
#endif
#ifdef DEBUG
-# define UNREACHABLE() abort()
-#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
-# define UNREACHABLE() __builtin_unreachable()
-#elif defined __has_builtin
-# if __has_builtin(__builtin_unreachable)
-# define UNREACHABLE() __builtin_unreachable()
+# undef unreachable
+# define unreachable() abort()
+#elif !defined unreachable
+# ifdef __has_builtin
+# if __has_builtin(__builtin_unreachable)
+# define unreachable() __builtin_unreachable()
+# endif
+# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
+# define unreachable() __builtin_unreachable()
# endif
-#endif
-#ifndef UNREACHABLE
-# define UNREACHABLE() ((void) 0)
+# ifndef unreachable
+# define unreachable() ((void) 0)
+# endif
#endif
/*
@@ -725,53 +931,61 @@
#if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r
#undef ctime_r
-char *asctime_r(struct tm const *, char *);
+char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime_r(time_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Handy macros that are independent of tzfile implementation. */
-#define SECSPERMIN 60
-#define MINSPERHOUR 60
-#define HOURSPERDAY 24
-#define DAYSPERWEEK 7
-#define DAYSPERNYEAR 365
-#define DAYSPERLYEAR 366
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
-#define MONSPERYEAR 12
+enum {
+ SECSPERMIN = 60,
+ MINSPERHOUR = 60,
+ SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
+ HOURSPERDAY = 24,
+ DAYSPERWEEK = 7,
+ DAYSPERNYEAR = 365,
+ DAYSPERLYEAR = DAYSPERNYEAR + 1,
+ MONSPERYEAR = 12,
+ YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
+};
-#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+
#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-#define TM_TUESDAY 2
-#define TM_WEDNESDAY 3
-#define TM_THURSDAY 4
-#define TM_FRIDAY 5
-#define TM_SATURDAY 6
+enum {
+ TM_SUNDAY,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
-#define TM_JANUARY 0
-#define TM_FEBRUARY 1
-#define TM_MARCH 2
-#define TM_APRIL 3
-#define TM_MAY 4
-#define TM_JUNE 5
-#define TM_JULY 6
-#define TM_AUGUST 7
-#define TM_SEPTEMBER 8
-#define TM_OCTOBER 9
-#define TM_NOVEMBER 10
-#define TM_DECEMBER 11
+enum {
+ TM_JANUARY,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
-#define TM_YEAR_BASE 1900
-#define TM_WDAY_BASE TM_MONDAY
-
-#define EPOCH_YEAR 1970
-#define EPOCH_WDAY TM_THURSDAY
+enum {
+ TM_YEAR_BASE = 1900,
+ TM_WDAY_BASE = TM_MONDAY,
+ EPOCH_YEAR = 1970,
+ EPOCH_WDAY = TM_THURSDAY
+};
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index d04c5ba..4cde556 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -71,8 +71,6 @@
const char * date_fmt;
};
-#define Locale (&C_time_locale)
-
static const struct lc_time_T C_time_locale = {
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -128,13 +126,14 @@
static char * _yconv(int, int, bool, bool, char *, const char *, int);
#ifndef YEAR_2000_NAME
-#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
#if HAVE_STRFTIME_L
size_t
-strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
- locale_t locale)
+strftime_l(char *restrict s, size_t maxsize, char const *restrict format,
+ struct tm const *restrict t,
+ ATTRIBUTE_MAYBE_UNUSED locale_t locale)
{
/* Just call strftime, as only the C locale is supported. */
return strftime(s, maxsize, format, t);
@@ -144,7 +143,8 @@
#define FORCE_LOWER_CASE 0x100 /* Android extension. */
size_t
-strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
+strftime(char *restrict s, size_t maxsize, char const *restrict format,
+ struct tm const *restrict t)
{
char * p;
int saved_errno = errno;
@@ -217,6 +217,8 @@
_fmt(const char *format, const struct tm *t, char *pt,
const char *ptlim, enum warn *warnp)
{
+ struct lc_time_T const *Locale = &C_time_locale;
+
for ( ; *format; ++format) {
if (*format == '%') {
int modifier = 0;
@@ -378,12 +380,21 @@
char buf[INT_STRLEN_MAXIMUM(time64_t) + 1] __attribute__((__uninitialized__));
time64_t mkt;
- tm = *t;
+ tm.tm_sec = t->tm_sec;
+ tm.tm_min = t->tm_min;
+ tm.tm_hour = t->tm_hour;
+ tm.tm_mday = t->tm_mday;
+ tm.tm_mon = t->tm_mon;
+ tm.tm_year = t->tm_year;
+ tm.tm_isdst = t->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ tm.TM_GMTOFF = t->TM_GMTOFF;
+#endif
mkt = mktime64(&tm);
- /* There is no portable, definitive
- test for whether whether mktime
- succeeded, so treat (time_t) -1 as
- the success that it might be. */
+ /* If mktime fails, %s expands to the
+ value of (time_t) -1 as a failure
+ marker; this is better in practice
+ than strftime failing. */
if (TYPE_SIGNED(time64_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
@@ -607,19 +618,19 @@
# endif
negative = diff < 0;
if (diff == 0) {
-#ifdef TM_ZONE
+# ifdef TM_ZONE
// Android-changed: do not use TM_ZONE as it is as it may be null.
{
const char* zone = _safe_tm_zone(t);
negative = zone[0] == '-';
}
-#else
+# else
negative = t->tm_isdst < 0;
-# if HAVE_TZNAME
+# if HAVE_TZNAME
if (tzname[t->tm_isdst != 0][0] == '-')
negative = true;
+# endif
# endif
-#endif
}
if (negative) {
sign = "-";
@@ -748,7 +759,7 @@
register int lead;
register int trail;
-#define DIVISOR 100
+ int DIVISOR = 100;
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
diff --git a/libc/tzcode/tzfile.h b/libc/tzcode/tzfile.h
index c5f9967..7ac8b51 100644
--- a/libc/tzcode/tzfile.h
+++ b/libc/tzcode/tzfile.h
@@ -22,15 +22,15 @@
*/
#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+# define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
+# define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
+# define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
@@ -103,21 +103,25 @@
*/
#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 2000
+/* This must be at least 242 for Europe/London with 'zic -b fat'. */
+# define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
-/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */
+# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+/* This must be at least 40 for America/Anchorage. */
+# define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+/* This must be at least 27 for leap seconds from 1972 through mid-2023.
+ There's a plan to discontinue leap seconds by 2035. */
+# define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#define SECSPERMIN 60
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 4cdec44..fde3dfc 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -244,7 +244,7 @@
genrule {
name: "libdl.arm.map",
- out: ["libdl.arm.map"],
+ out: ["libdl.arm.map.txt"],
srcs: ["libdl.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm $(in) $(out)",
@@ -252,7 +252,7 @@
genrule {
name: "libdl.arm64.map",
- out: ["libdl.arm64.map"],
+ out: ["libdl.arm64.map.txt"],
srcs: ["libdl.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm64 $(in) $(out)",
@@ -260,7 +260,7 @@
genrule {
name: "libdl.riscv64.map",
- out: ["libdl.riscv64.map"],
+ out: ["libdl.riscv64.map.txt"],
srcs: ["libdl.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) riscv64 $(in) $(out)",
@@ -268,7 +268,7 @@
genrule {
name: "libdl.x86.map",
- out: ["libdl.x86.map"],
+ out: ["libdl.x86.map.txt"],
srcs: ["libdl.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86 $(in) $(out)",
@@ -276,7 +276,7 @@
genrule {
name: "libdl.x86_64.map",
- out: ["libdl.x86_64.map"],
+ out: ["libdl.x86_64.map.txt"],
srcs: ["libdl.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86_64 $(in) $(out)",
diff --git a/libm/Android.bp b/libm/Android.bp
index 4c34fb6..13fbf9a 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -473,7 +473,7 @@
genrule {
name: "libm.arm.map",
- out: ["libm.arm.map"],
+ out: ["libm.arm.map.txt"],
srcs: ["libm.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm $(in) $(out)",
@@ -481,7 +481,7 @@
genrule {
name: "libm.arm64.map",
- out: ["libm.arm64.map"],
+ out: ["libm.arm64.map.txt"],
srcs: ["libm.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) arm64 $(in) $(out)",
@@ -489,7 +489,7 @@
genrule {
name: "libm.riscv64.map",
- out: ["libm.riscv64.map"],
+ out: ["libm.riscv64.map.txt"],
srcs: ["libm.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) riscv64 $(in) $(out)",
@@ -497,7 +497,7 @@
genrule {
name: "libm.x86.map",
- out: ["libm.x86.map"],
+ out: ["libm.x86.map.txt"],
srcs: ["libm.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86 $(in) $(out)",
@@ -505,7 +505,7 @@
genrule {
name: "libm.x86_64.map",
- out: ["libm.x86_64.map"],
+ out: ["libm.x86_64.map.txt"],
srcs: ["libm.map.txt"],
tools: ["generate-version-script"],
cmd: "$(location generate-version-script) x86_64 $(in) $(out)",
diff --git a/linker/Android.bp b/linker/Android.bp
index 83077c6..020bd7d 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -374,6 +374,11 @@
],
symlinks: ["linker_asan"],
+ arch: {
+ arm64: {
+ symlinks: ["linker_hwasan"],
+ },
+ },
multilib: {
lib64: {
suffix: "64",
diff --git a/linker/linker.cpp b/linker/linker.cpp
index c5a822a..17b574f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/vfs.h>
@@ -133,6 +134,36 @@
nullptr
};
+#if defined(__aarch64__)
+static const char* const kHwasanSystemLibDir = "/system/lib64/hwasan";
+static const char* const kHwasanOdmLibDir = "/odm/lib64/hwasan";
+static const char* const kHwasanVendorLibDir = "/vendor/lib64/hwasan";
+
+// HWASan is only supported on aarch64.
+static const char* const kHwsanDefaultLdPaths[] = {
+ kHwasanSystemLibDir,
+ kSystemLibDir,
+ kHwasanOdmLibDir,
+ kOdmLibDir,
+ kHwasanVendorLibDir,
+ kVendorLibDir,
+ nullptr
+};
+
+// Is HWASAN enabled?
+static bool g_is_hwasan = false;
+#else
+static const char* const kHwsanDefaultLdPaths[] = {
+ kSystemLibDir,
+ kOdmLibDir,
+ kVendorLibDir,
+ nullptr
+};
+
+// Never any HWASan. Help the compiler remove the code we don't need.
+constexpr bool g_is_hwasan = false;
+#endif
+
// Is ASAN enabled?
static bool g_is_asan = false;
@@ -2134,26 +2165,46 @@
}
// End Workaround for dlopen(/system/lib/<soname>) when .so is in /apex.
- std::string asan_name_holder;
+ std::string translated_name_holder;
+ assert(!g_is_hwasan || !g_is_asan);
const char* translated_name = name;
if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
char original_path[PATH_MAX];
if (realpath(name, original_path) != nullptr) {
- asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
- if (file_exists(asan_name_holder.c_str())) {
+ translated_name_holder = std::string(kAsanLibDirPrefix) + original_path;
+ if (file_exists(translated_name_holder.c_str())) {
soinfo* si = nullptr;
if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
- asan_name_holder.c_str());
+ translated_name_holder.c_str());
} else {
PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
- translated_name = asan_name_holder.c_str();
+ translated_name = translated_name_holder.c_str();
+ }
+ }
+ }
+ } else if (g_is_hwasan && translated_name != nullptr && translated_name[0] == '/') {
+ char original_path[PATH_MAX];
+ if (realpath(name, original_path) != nullptr) {
+ // Keep this the same as CreateHwasanPath in system/linkerconfig/modules/namespace.cc.
+ std::string path(original_path);
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos || slash != path.size() - 1) {
+ translated_name_holder = path.substr(0, slash) + "/hwasan" + path.substr(slash);
+ }
+ if (!translated_name_holder.empty() && file_exists(translated_name_holder.c_str())) {
+ soinfo* si = nullptr;
+ if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
+ PRINT("linker_hwasan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
+ translated_name_holder.c_str());
+ } else {
+ PRINT("linker_hwasan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ translated_name = translated_name_holder.c_str();
}
}
}
}
-
ProtectedDataGuard guard;
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();
@@ -3335,9 +3386,10 @@
return true;
}
-static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) {
+static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan, bool is_hwasan) {
g_default_namespace.set_isolated(false);
- auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
+ auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : (
+ is_hwasan ? kHwsanDefaultLdPaths : kDefaultLdPaths);
char real_path[PATH_MAX];
std::vector<std::string> ld_default_paths;
@@ -3441,6 +3493,7 @@
return kLdConfigFilePath;
}
+
std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
g_default_namespace.set_name("(default)");
@@ -3454,6 +3507,16 @@
(strcmp(bname, "linker_asan") == 0 ||
strcmp(bname, "linker_asan64") == 0);
+#if defined(__aarch64__)
+ // HWASan is only supported on AArch64.
+ // The AT_SECURE restriction is because this is a debug feature that does
+ // not need to work on secure binaries, it doesn't hurt to disallow the
+ // environment variable for them, as it impacts the program execution.
+ char* hwasan_env = getenv("LD_HWASAN");
+ g_is_hwasan = (bname != nullptr &&
+ strcmp(bname, "linker_hwasan64") == 0) ||
+ (hwasan_env != nullptr && !getauxval(AT_SECURE) && strcmp(hwasan_env, "1") == 0);
+#endif
const Config* config = nullptr;
{
@@ -3461,7 +3524,7 @@
INFO("[ Reading linker config \"%s\" ]", ld_config_file_path.c_str());
ScopedTrace trace(("linker config " + ld_config_file_path).c_str());
std::string error_msg;
- if (!Config::read_binary_config(ld_config_file_path.c_str(), executable_path, g_is_asan,
+ if (!Config::read_binary_config(ld_config_file_path.c_str(), executable_path, g_is_asan, g_is_hwasan,
&config, &error_msg)) {
if (!error_msg.empty()) {
DL_WARN("Warning: couldn't read '%s' for '%s' (using default configuration instead): %s",
@@ -3472,7 +3535,7 @@
}
if (config == nullptr) {
- return init_default_namespace_no_config(g_is_asan);
+ return init_default_namespace_no_config(g_is_asan, g_is_hwasan);
}
const auto& namespace_configs = config->namespace_configs();
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 1771e87..ad40c50 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -463,6 +463,7 @@
bool Config::read_binary_config(const char* ld_config_file_path,
const char* binary_realpath,
bool is_asan,
+ bool is_hwasan,
const Config** config,
std::string* error_msg) {
g_config.clear();
@@ -579,6 +580,8 @@
// these are affected by is_asan flag
if (is_asan) {
property_name_prefix += ".asan";
+ } else if (is_hwasan) {
+ property_name_prefix += ".hwasan";
}
// search paths are resolved (canonicalized). This is required mainly for
diff --git a/linker/linker_config.h b/linker/linker_config.h
index fe23ec1..09fea45 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -166,6 +166,7 @@
static bool read_binary_config(const char* ld_config_file_path,
const char* binary_realpath,
bool is_asan,
+ bool is_hwasan,
const Config** config,
std::string* error_msg);
diff --git a/linker/linker_config_test.cpp b/linker/linker_config_test.cpp
index acdf641..7e947f3 100644
--- a/linker/linker_config_test.cpp
+++ b/linker/linker_config_test.cpp
@@ -40,6 +40,7 @@
#include <android-base/file.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <vector>
#if defined(__LP64__)
#define ARCH_SUFFIX "64"
@@ -64,6 +65,10 @@
"namespace.default.asan.search.paths = /data\n"
"namespace.default.asan.search.paths += /vendor/${LIB}\n"
"namespace.default.asan.permitted.paths = /data:/vendor\n"
+ "namespace.default.hwasan.search.paths = /vendor/${LIB}/hwasan\n"
+ "namespace.default.hwasan.search.paths += /vendor/${LIB}\n"
+ "namespace.default.hwasan.permitted.paths = /vendor/${LIB}/hwasan\n"
+ "namespace.default.hwasan.permitted.paths += /vendor/${LIB}\n"
"namespace.default.links = system\n"
"namespace.default.links += vndk\n"
// irregular whitespaces are added intentionally for testing purpose
@@ -77,11 +82,17 @@
"namespace.system.permitted.paths = /system/${LIB}\n"
"namespace.system.asan.search.paths = /data:/system/${LIB}\n"
"namespace.system.asan.permitted.paths = /data:/system\n"
+ "namespace.system.hwasan.search.paths = /system/${LIB}/hwasan\n"
+ "namespace.system.hwasan.search.paths += /system/${LIB}\n"
+ "namespace.system.hwasan.permitted.paths = /system/${LIB}/hwasan\n"
+ "namespace.system.hwasan.permitted.paths += /system/${LIB}\n"
"namespace.vndk.isolated = tr\n"
"namespace.vndk.isolated += ue\n" // should be ignored and return as 'false'.
"namespace.vndk.search.paths = /system/${LIB}/vndk\n"
"namespace.vndk.asan.search.paths = /data\n"
"namespace.vndk.asan.search.paths += /system/${LIB}/vndk\n"
+ "namespace.vndk.hwasan.search.paths = /system/${LIB}/vndk/hwasan\n"
+ "namespace.vndk.hwasan.search.paths += /system/${LIB}/vndk\n"
"namespace.vndk.links = default\n"
"namespace.vndk.link.default.allow_all_shared_libs = true\n"
"namespace.vndk.link.vndk_in_system.allow_all_shared_libs = true\n"
@@ -107,26 +118,50 @@
return resolved_paths;
}
-static void run_linker_config_smoke_test(bool is_asan) {
- const std::vector<std::string> kExpectedDefaultSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/vendor/lib" ARCH_SUFFIX }) :
- std::vector<std::string>({ "/vendor/lib" ARCH_SUFFIX }));
+enum class SmokeTestType {
+ None,
+ Asan,
+ Hwasan,
+};
- const std::vector<std::string> kExpectedDefaultPermittedPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/vendor" }) :
- std::vector<std::string>({ "/vendor/lib" ARCH_SUFFIX }));
+static void run_linker_config_smoke_test(SmokeTestType type) {
+ std::vector<std::string> expected_default_search_path;
+ std::vector<std::string> expected_default_permitted_path;
+ std::vector<std::string> expected_system_search_path;
+ std::vector<std::string> expected_system_permitted_path;
+ std::vector<std::string> expected_vndk_search_path;
- const std::vector<std::string> kExpectedSystemSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system/lib" ARCH_SUFFIX }) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX }));
+ switch (type) {
+ case SmokeTestType::None:
+ expected_default_search_path = { "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/vendor/lib" ARCH_SUFFIX };
+ expected_system_search_path = { "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/system/lib" ARCH_SUFFIX };
+ expected_vndk_search_path = { "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ case SmokeTestType::Asan:
+ expected_default_search_path = { "/data", "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/data", "/vendor" };
+ expected_system_search_path = { "/data", "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/data", "/system" };
+ expected_vndk_search_path = { "/data", "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ case SmokeTestType::Hwasan:
+ expected_default_search_path = { "/vendor/lib" ARCH_SUFFIX "/hwasan", "/vendor/lib" ARCH_SUFFIX };
+ expected_default_permitted_path = { "/vendor/lib" ARCH_SUFFIX "/hwasan", "/vendor/lib" ARCH_SUFFIX };
+ expected_system_search_path = { "/system/lib" ARCH_SUFFIX "/hwasan" , "/system/lib" ARCH_SUFFIX };
+ expected_system_permitted_path = { "/system/lib" ARCH_SUFFIX "/hwasan", "/system/lib" ARCH_SUFFIX };
+ expected_vndk_search_path = { "/system/lib" ARCH_SUFFIX "/vndk/hwasan", "/system/lib" ARCH_SUFFIX "/vndk" };
+ break;
+ }
- const std::vector<std::string> kExpectedSystemPermittedPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system" }) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX }));
-
- const std::vector<std::string> kExpectedVndkSearchPath =
- resolve_paths(is_asan ? std::vector<std::string>({ "/data", "/system/lib" ARCH_SUFFIX "/vndk"}) :
- std::vector<std::string>({ "/system/lib" ARCH_SUFFIX "/vndk"}));
+ expected_default_search_path = resolve_paths(expected_default_search_path);
+ // expected_default_permitted_path is skipped on purpose, permitted paths
+ // do not get resolved in linker_config.cpp
+ expected_system_search_path = resolve_paths(expected_system_search_path);
+ // expected_system_permitted_path is skipped on purpose, permitted paths
+ // do not get resolved in linker_config.cpp
+ expected_vndk_search_path = resolve_paths(expected_vndk_search_path);
TemporaryFile tmp_file;
close(tmp_file.fd);
@@ -149,7 +184,8 @@
std::string error_msg;
ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
- is_asan,
+ type == SmokeTestType::Asan,
+ type == SmokeTestType::Hwasan,
&config,
&error_msg)) << error_msg;
ASSERT_TRUE(config != nullptr);
@@ -162,8 +198,8 @@
ASSERT_TRUE(default_ns_config->isolated());
ASSERT_FALSE(default_ns_config->visible());
- ASSERT_EQ(kExpectedDefaultSearchPath, default_ns_config->search_paths());
- ASSERT_EQ(kExpectedDefaultPermittedPath, default_ns_config->permitted_paths());
+ ASSERT_EQ(expected_default_search_path, default_ns_config->search_paths());
+ ASSERT_EQ(expected_default_permitted_path, default_ns_config->permitted_paths());
const auto& default_ns_links = default_ns_config->links();
ASSERT_EQ(2U, default_ns_links.size());
@@ -202,14 +238,14 @@
ASSERT_TRUE(ns_system->isolated());
ASSERT_TRUE(ns_system->visible());
- ASSERT_EQ(kExpectedSystemSearchPath, ns_system->search_paths());
- ASSERT_EQ(kExpectedSystemPermittedPath, ns_system->permitted_paths());
+ ASSERT_EQ(expected_system_search_path, ns_system->search_paths());
+ ASSERT_EQ(expected_system_permitted_path, ns_system->permitted_paths());
ASSERT_TRUE(ns_vndk != nullptr) << "vndk namespace was not found";
ASSERT_FALSE(ns_vndk->isolated()); // malformed bool property
ASSERT_FALSE(ns_vndk->visible()); // undefined bool property
- ASSERT_EQ(kExpectedVndkSearchPath, ns_vndk->search_paths());
+ ASSERT_EQ(expected_vndk_search_path, ns_vndk->search_paths());
const auto& ns_vndk_links = ns_vndk->links();
ASSERT_EQ(1U, ns_vndk_links.size());
@@ -223,11 +259,15 @@
}
TEST(linker_config, smoke) {
- run_linker_config_smoke_test(false);
+ run_linker_config_smoke_test(SmokeTestType::None);
}
TEST(linker_config, asan_smoke) {
- run_linker_config_smoke_test(true);
+ run_linker_config_smoke_test(SmokeTestType::Asan);
+}
+
+TEST(linker_config, hwasan_smoke) {
+ run_linker_config_smoke_test(SmokeTestType::Hwasan);
}
TEST(linker_config, ns_link_shared_libs_invalid_settings) {
@@ -259,6 +299,7 @@
ASSERT_FALSE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
false,
+ false,
&config,
&error_msg));
ASSERT_TRUE(config == nullptr);
@@ -304,6 +345,7 @@
ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
executable_path.c_str(),
false,
+ false,
&config,
&error_msg)) << error_msg;
diff --git a/tests/Android.bp b/tests/Android.bp
index 1949079..281e29d 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -503,6 +503,7 @@
"sys_vfs_test.cpp",
"sys_wait_test.cpp",
"sys_xattr_test.cpp",
+ "syslog_test.cpp",
"system_properties_test.cpp",
"system_properties_test2.cpp",
"termios_test.cpp",
@@ -541,6 +542,10 @@
// unsupported relocation type 37
"ifunc_test.cpp",
+
+ // musl #defines utmp to utmpx, causing a collision with
+ // utmpx_test.cpp
+ "utmp_test.cpp",
],
},
},
@@ -1112,6 +1117,30 @@
}
cc_test {
+ name: "hwasan_test",
+ enabled: false,
+ // This does not use bionic_tests_defaults because it is not supported on
+ // host.
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ sanitize: {
+ hwaddress: true,
+ },
+ srcs: [
+ "hwasan_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ data_libs: ["libtest_simple_hwasan", "libtest_simple_hwasan_nohwasan"],
+ header_libs: ["bionic_libc_platform_headers"],
+ test_suites: ["device-tests"],
+}
+
+cc_test {
name: "bionic-stress-tests",
defaults: [
"bionic_tests_defaults",
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 3f70279..b68ee7b 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -971,9 +971,15 @@
}
#define ALTERNATE_PATH_TO_SYSTEM_LIB "/system/lib64/" ABI_STRING "/"
+#if __has_feature(hwaddress_sanitizer)
+#define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "hwasan/libc.so"
+#define PATH_TO_BOOTSTRAP_LIBC PATH_TO_SYSTEM_LIB "bootstrap/hwasan/libc.so"
+#define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "hwasan/libc.so"
+#else
#define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "libc.so"
#define PATH_TO_BOOTSTRAP_LIBC PATH_TO_SYSTEM_LIB "bootstrap/libc.so"
#define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "libc.so"
+#endif
TEST(dlfcn, dladdr_libc) {
#if defined(__GLIBC__)
@@ -981,24 +987,25 @@
#endif
Dl_info info;
- void* addr = reinterpret_cast<void*>(puts); // well-known libc function
+ void* addr = reinterpret_cast<void*>(puts); // An arbitrary libc function.
ASSERT_TRUE(dladdr(addr, &info) != 0);
- char libc_realpath[PATH_MAX];
-
// Check if libc is in canonical path or in alternate path.
+ const char* expected_path;
if (strncmp(ALTERNATE_PATH_TO_SYSTEM_LIB,
info.dli_fname,
sizeof(ALTERNATE_PATH_TO_SYSTEM_LIB) - 1) == 0) {
// Platform with emulated architecture. Symlink on ARC++.
- ASSERT_TRUE(realpath(ALTERNATE_PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = ALTERNATE_PATH_TO_LIBC;
} else if (strncmp(PATH_TO_BOOTSTRAP_LIBC, info.dli_fname,
sizeof(PATH_TO_BOOTSTRAP_LIBC) - 1) == 0) {
- ASSERT_TRUE(realpath(PATH_TO_BOOTSTRAP_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_BOOTSTRAP_LIBC;
} else {
// /system/lib is symlink when this test is executed on host.
- ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
+ expected_path = PATH_TO_LIBC;
}
+ char libc_realpath[PATH_MAX];
+ ASSERT_TRUE(realpath(expected_path, libc_realpath) != nullptr) << strerror(errno);
ASSERT_STREQ(libc_realpath, info.dli_fname);
// TODO: add check for dfi_fbase
diff --git a/tests/fortify_test.cpp b/tests/fortify_test.cpp
index 4abee67..cc3080d 100644
--- a/tests/fortify_test.cpp
+++ b/tests/fortify_test.cpp
@@ -670,6 +670,10 @@
ASSERT_FORTIFY(readlinkat(AT_FDCWD, "/dev/null", buf, ct));
}
+TEST(TEST_NAME, snprintf_nullptr_valid) {
+ ASSERT_EQ(10, snprintf(nullptr, 0, "0123456789"));
+}
+
extern "C" char* __strncat_chk(char*, const char*, size_t, size_t);
extern "C" char* __strcat_chk(char*, const char*, size_t);
diff --git a/tests/headers/posix/README.md b/tests/headers/posix/README.md
new file mode 100644
index 0000000..e7c171a
--- /dev/null
+++ b/tests/headers/posix/README.md
@@ -0,0 +1,8 @@
+# POSIX header tests
+
+These compile-time tests check that each POSIX header contains _at
+least_ what POSIX says. Every POSIX header file gets a corresponding
+`.c` file in this directory. Every constant, macro, type, struct field,
+and function in the header gets a corresponding assertion in the file.
+
+See `header_checks.h` for the implementation of the assertions.
diff --git a/tests/hwasan_test.cpp b/tests/hwasan_test.cpp
new file mode 100644
index 0000000..e32534e
--- /dev/null
+++ b/tests/hwasan_test.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 <dlfcn.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/silent_death_test.h>
+#include <android-base/test_utils.h>
+
+using HwasanDeathTest = SilentDeathTest;
+
+TEST_F(HwasanDeathTest, UseAfterFree) {
+ EXPECT_DEATH(
+ {
+ void* m = malloc(1);
+ volatile char* x = const_cast<volatile char*>(reinterpret_cast<char*>(m));
+ *x = 1;
+ free(m);
+ *x = 2;
+ },
+ "use-after-free");
+}
+
+TEST_F(HwasanDeathTest, OutOfBounds) {
+ EXPECT_DEATH(
+ {
+ void* m = malloc(1);
+ volatile char* x = const_cast<volatile char*>(reinterpret_cast<char*>(m));
+ x[1] = 1;
+ },
+ "buffer-overflow");
+}
+
+// Check whether dlopen of /foo/bar.so checks /foo/hwasan/bar.so first.
+TEST(HwasanTest, DlopenAbsolutePath) {
+ std::string path = android::base::GetExecutableDirectory() + "/libtest_simple_hwasan.so";
+ ASSERT_EQ(0, access(path.c_str(), F_OK)); // Verify test setup.
+ std::string hwasan_path =
+ android::base::GetExecutableDirectory() + "/hwasan/libtest_simple_hwasan.so";
+ ASSERT_EQ(0, access(hwasan_path.c_str(), F_OK)); // Verify test setup.
+
+ void* handle = dlopen(path.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+ uint32_t* compiled_with_hwasan =
+ reinterpret_cast<uint32_t*>(dlsym(handle, "dlopen_testlib_compiled_with_hwasan"));
+ EXPECT_TRUE(*compiled_with_hwasan);
+ dlclose(handle);
+}
+
+TEST(HwasanTest, IsRunningWithHWasan) {
+ EXPECT_TRUE(running_with_hwasan());
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 8ae2257..a2fbe55 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -243,6 +243,38 @@
}
// -----------------------------------------------------------------------------
+// Libraries used by hwasan_test
+// -----------------------------------------------------------------------------
+cc_test_library {
+ name: "libtest_simple_hwasan",
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ sanitize: {
+ hwaddress: true,
+ },
+ relative_install_path: "hwasan",
+ enabled: false,
+ srcs: ["dlopen_testlib_simple_hwasan.cpp"],
+}
+
+cc_test_library {
+ // A weird name. This is the vanilla (non-HWASan) copy of the library that
+ // is used for the hwasan test.
+ name: "libtest_simple_hwasan_nohwasan",
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+ stem: "libtest_simple_hwasan",
+ enabled: false,
+ srcs: ["dlopen_testlib_simple_hwasan.cpp"],
+}
+
+// -----------------------------------------------------------------------------
// Library used by dlext direct unload on the namespace boundary tests
// -----------------------------------------------------------------------------
cc_test_library {
diff --git a/libc/bionic/timespec_get.cpp b/tests/libs/dlopen_testlib_simple_hwasan.cpp
similarity index 79%
copy from libc/bionic/timespec_get.cpp
copy to tests/libs/dlopen_testlib_simple_hwasan.cpp
index 7fc2182..ddf8a31 100644
--- a/libc/bionic/timespec_get.cpp
+++ b/tests/libs/dlopen_testlib_simple_hwasan.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,8 +26,14 @@
* SUCH DAMAGE.
*/
-#include <time.h>
+#include <stdint.h>
-int timespec_get(timespec* ts, int base) {
- return (base == TIME_UTC && clock_gettime(CLOCK_REALTIME, ts) != -1) ? base : 0;
+#if __has_feature(hwaddress_sanitizer)
+extern "C" uint32_t dlopen_testlib_compiled_with_hwasan = true;
+#else
+extern "C" uint32_t dlopen_testlib_compiled_with_hwasan = false;
+#endif
+
+extern "C" bool dlopen_testlib_simple_hwasan_func() {
+ return true;
}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 06a0f3d..aad2a4d 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -195,12 +195,12 @@
SKIP_WITH_HWASAN; // TODO(b/148982147): Re-enable when fixed.
size_t stack_size = 640 * 1024;
- std::vector<char> stack_vec(stack_size, '\xff');
- void* stack = stack_vec.data();
+ std::unique_ptr<char[]> stack(new (std::align_val_t(getpagesize())) char[stack_size]);
+ memset(stack.get(), '\xff', stack_size);
pthread_attr_t attr;
ASSERT_EQ(0, pthread_attr_init(&attr));
- ASSERT_EQ(0, pthread_attr_setstack(&attr, stack, stack_size));
+ ASSERT_EQ(0, pthread_attr_setstack(&attr, stack.get(), stack_size));
pthread_t t;
ASSERT_EQ(0, pthread_create(&t, &attr, FnWithStackFrame, nullptr));
diff --git a/tests/semaphore_test.cpp b/tests/semaphore_test.cpp
index f3f6020..6f8797f 100644
--- a/tests/semaphore_test.cpp
+++ b/tests/semaphore_test.cpp
@@ -165,8 +165,10 @@
TEST_F(semaphore_DeathTest, sem_timedwait_null_timeout) {
sem_t s;
ASSERT_EQ(0, sem_init(&s, 0, 0));
-
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
ASSERT_EXIT(sem_timedwait(&s, nullptr), testing::KilledBySignal(SIGSEGV), "");
+#pragma clang diagnostic pop
}
TEST(semaphore, sem_getvalue) {
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index fb6bce9..800732f 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -36,6 +36,7 @@
#include <android-base/file.h>
#include <android-base/silent_death_test.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
@@ -140,6 +141,22 @@
fclose(fp);
}
+TEST(STDIO_TEST, tmpfile_TMPDIR) {
+ TemporaryDir td;
+ setenv("TMPDIR", td.path, 1);
+
+ FILE* fp = tmpfile();
+ ASSERT_TRUE(fp != nullptr);
+
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fileno(fp));
+ char path[PATH_MAX];
+ ASSERT_GT(readlink(fd_path.c_str(), path, sizeof(path)), 0);
+ // $TMPDIR influenced where our temporary file ended up?
+ ASSERT_TRUE(android::base::StartsWith(path, td.path)) << path;
+ // And we used O_TMPFILE, right?
+ ASSERT_TRUE(android::base::EndsWith(path, " (deleted)")) << path;
+}
+
TEST(STDIO_TEST, dprintf) {
TemporaryFile tf;
@@ -2984,59 +3001,153 @@
}
TEST(STDIO_TEST, snprintf_b) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIb8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08b>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIb8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIb16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIb16 ">", s));
+ EXPECT_STREQ("<0b1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0b10101010101010101010101010101010>", buf);
+
+ // clang doesn't like "%lb" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0b1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, snprintf_B) {
+#if defined(__BIONIC__)
char buf[BUFSIZ];
- EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%" PRIB8 ">", b));
EXPECT_STREQ("<101>", buf);
- EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08B>", 5));
+ EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08" PRIB8 ">", b));
EXPECT_STREQ("<00000101>", buf);
- EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, snprintf(buf, sizeof(buf), "<%" PRIB16 ">", s));
+ EXPECT_STREQ("<1010101010101010>", buf);
+ EXPECT_EQ(20, snprintf(buf, sizeof(buf), "<%#" PRIB16 ">", s));
+ EXPECT_STREQ("<0B1010101010101010>", buf);
+
+ EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
- EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_STREQ("<0B10101010101010101010101010101010>", buf);
- EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#B>", 0));
+
+ // clang doesn't like "%lB" (https://github.com/llvm/llvm-project/issues/62247)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+ EXPECT_EQ(66, snprintf(buf, sizeof(buf), "<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<1010101010101010101010101010101010101010101010101010101010101010>", buf);
+ EXPECT_EQ(68, snprintf(buf, sizeof(buf), "<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_STREQ("<0B1010101010101010101010101010101010101010101010101010101010101010>", buf);
+#pragma clang diagnostic pop
+
+ EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
EXPECT_STREQ("<0>", buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_b) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%b>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08b>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIb8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%b>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIb16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#b>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIb32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0b10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIb64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0b1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#b>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %b in glibc";
+#endif
}
TEST(STDIO_TEST, swprintf_B) {
+#if defined(__BIONIC__)
wchar_t buf[BUFSIZ];
- EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%B>", 5));
+
+ uint8_t b = 5;
+ EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<101>"), buf);
- EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08B>", 5));
+ EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08" PRIB8 ">", b));
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
- EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%B>", 0xaaaaaaaa));
+
+ uint16_t s = 0xaaaa;
+ EXPECT_EQ(18, swprintf(buf, sizeof(buf), L"<%" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<1010101010101010>"), buf);
+ EXPECT_EQ(20, swprintf(buf, sizeof(buf), L"<%#" PRIB16 ">", s));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010>"), buf);
+
+ EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
- EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#B>", 0xaaaaaaaa));
+ EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#" PRIB32 ">", 0xaaaaaaaa));
EXPECT_EQ(std::wstring(L"<0B10101010101010101010101010101010>"), buf);
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat" // clang doesn't like "%lb"
+ EXPECT_EQ(66, swprintf(buf, sizeof(buf), L"<%" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ EXPECT_EQ(68, swprintf(buf, sizeof(buf), L"<%#" PRIB64 ">", 0xaaaaaaaa'aaaaaaaa));
+ EXPECT_EQ(std::wstring(L"<0B1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+#pragma clang diagnostic pop
+
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#B>", 0));
EXPECT_EQ(std::wstring(L"<0>"), buf);
+#else
+ GTEST_SKIP() << "no %B in glibc";
+#endif
}
TEST(STDIO_TEST, scanf_i_decimal) {
@@ -3164,3 +3275,312 @@
EXPECT_EQ(0, i);
EXPECT_EQ('b', ch);
}
+
+TEST(STDIO_TEST, snprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ char buf[BUFSIZ];
+ int8_t a = 0b101;
+ snprintf(buf, sizeof(buf), "<%w8b>", a);
+ EXPECT_STREQ("<101>", buf);
+ int8_t b1 = 0xFF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b1);
+ EXPECT_STREQ("<-1>", buf);
+ int8_t b2 = 0x1FF;
+ snprintf(buf, sizeof(buf), "<%w8d>", b2);
+ EXPECT_STREQ("<-1>", buf);
+ int16_t c = 0xFFFF;
+ snprintf(buf, sizeof(buf), "<%w16i>", c);
+ EXPECT_STREQ("<-1>", buf);
+ int32_t d = 021;
+ snprintf(buf, sizeof(buf), "<%w32o>", d);
+ EXPECT_STREQ("<21>", buf);
+ uint32_t e = -1;
+ snprintf(buf, sizeof(buf), "<%w32u>", e);
+ EXPECT_STREQ("<4294967295>", buf);
+ int64_t f = 0x3b;
+ snprintf(buf, sizeof(buf), "<%w64x>", f);
+ EXPECT_STREQ("<3b>", buf);
+ snprintf(buf, sizeof(buf), "<%w64X>", f);
+ EXPECT_STREQ("<3B>", buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ char buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ snprintf(buf, sizeof(buf), "<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_STREQ("<10101010101010101010101010101010 --- 3333333344444444>", buf);
+ snprintf(buf, sizeof(buf), "<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_STREQ(
+ "<1010101010101010101010101010101010101010101010101010101010101010 --- 1111111122222222 --- "
+ "3333333344444444>",
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(snprintf(buf, sizeof(buf), "%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ wchar_t buf[BUFSIZ];
+ int8_t a = 0b101;
+ swprintf(buf, sizeof(buf), L"<%w8b>", a);
+ EXPECT_EQ(std::wstring(L"<101>"), buf);
+ int8_t b1 = 0xFF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b1);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int8_t b2 = 0x1FF;
+ swprintf(buf, sizeof(buf), L"<%w8d>", b2);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int16_t c = 0xFFFF;
+ swprintf(buf, sizeof(buf), L"<%w16i>", c);
+ EXPECT_EQ(std::wstring(L"<-1>"), buf);
+ int32_t d = 021;
+ swprintf(buf, sizeof(buf), L"<%w32o>", d);
+ EXPECT_EQ(std::wstring(L"<21>"), buf);
+ uint32_t e = -1;
+ swprintf(buf, sizeof(buf), L"<%w32u>", e);
+ EXPECT_EQ(std::wstring(L"<4294967295>"), buf);
+ int64_t f = 0x3b;
+ swprintf(buf, sizeof(buf), L"<%w64x>", f);
+ EXPECT_EQ(std::wstring(L"<3b>"), buf);
+ swprintf(buf, sizeof(buf), L"<%w64X>", f);
+ EXPECT_EQ(std::wstring(L"<3B>"), buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_w_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 0xaaaaaaaa;
+ int64_t b = 0x11111111'22222222;
+ int64_t c = 0x33333333'44444444;
+ int64_t d = 0xaaaaaaaa'aaaaaaaa;
+ swprintf(buf, sizeof(buf), L"<%2$w32b --- %1$w64x>", c, a);
+ EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010 --- 3333333344444444>"), buf);
+ swprintf(buf, sizeof(buf), L"<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d);
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010 --- "
+ L"1111111122222222 --- 3333333344444444>"),
+ buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_invalid_w_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ wchar_t buf[BUFSIZ];
+ int32_t a = 100;
+ EXPECT_DEATH(swprintf(buf, sizeof(buf), L"%w20d", &a), "%w20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_wf_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+#pragma clang diagnostic ignored "-Wformat"
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int_fast8_t a = 0b101;
+ snprintf(buf, sizeof(buf), "<%wf8b>", a);
+ EXPECT_STREQ("<101>", buf);
+ int_fast8_t b = 0x12341234'12341234;
+ snprintf(buf, sizeof(buf), "<%wf8x>", b);
+ EXPECT_STREQ("<34>", buf);
+ uint_fast16_t c = 0x11111111'22222222;
+#if defined(__LP64__)
+ snprintf(buf, sizeof(buf), "<%wf16x>", c);
+ EXPECT_STREQ("<1111111122222222>", buf);
+#else
+ snprintf(buf, sizeof(buf), "<%wf16x>", c);
+ EXPECT_STREQ("<22222222>", buf);
+#endif
+ int_fast32_t d = 0x33333333'44444444;
+#if defined(__LP64__)
+ snprintf(buf, sizeof(buf), "<%wf32x>", d);
+ EXPECT_STREQ("<3333333344444444>", buf);
+#else
+ snprintf(buf, sizeof(buf), "<%wf32x>", d);
+ EXPECT_STREQ("<44444444>", buf);
+#endif
+ int_fast64_t e = 0xaaaaaaaa'aaaaaaaa;
+ snprintf(buf, sizeof(buf), "<%wf64x>", e);
+ EXPECT_STREQ("<aaaaaaaaaaaaaaaa>", buf);
+ snprintf(buf, sizeof(buf), "<%wf64X>", e);
+ EXPECT_STREQ("<AAAAAAAAAAAAAAAA>", buf);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %wf in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_wf_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+#pragma clang diagnostic ignored "-Wformat"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int_fast16_t a = 0x11111111'22222222;
+ int_fast32_t b = 0x33333333'44444444;
+ int_fast32_t c = 0xaaaaaaaa'aaaaaaaa;
+#if defined(__LP64__)
+ snprintf(buf, sizeof(buf), "<%2$wf32x --- %1$wf32b>", c, b);
+ EXPECT_STREQ(
+ "<3333333344444444 --- 1010101010101010101010101010101010101010101010101010101010101010>",
+ buf);
+ snprintf(buf, sizeof(buf), "<%3$wf32b --- %1$wf16x --- %2$wf32x>", a, b, c);
+ EXPECT_STREQ(
+ "<1010101010101010101010101010101010101010101010101010101010101010 --- 1111111122222222 --- "
+ "3333333344444444>",
+ buf);
+#else
+ snprintf(buf, sizeof(buf), "<%2$wf32x --- %1$wf32b>", c, b);
+ EXPECT_STREQ("<44444444 --- 10101010101010101010101010101010>", buf);
+ snprintf(buf, sizeof(buf), "<%3$wf32b --- %1$wf16x --- %2$wf32x>", a, b, c);
+ EXPECT_STREQ("<10101010101010101010101010101010 --- 22222222 --- 44444444>", buf);
+#endif
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, snprintf_invalid_wf_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat"
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ char buf[BUFSIZ];
+ int_fast32_t a = 100;
+ EXPECT_DEATH(snprintf(buf, sizeof(buf), "%wf20d", &a), "%wf20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_wf_base) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wconstant-conversion"
+ wchar_t buf[BUFSIZ];
+ int_fast8_t a = 0b101;
+ swprintf(buf, sizeof(buf), L"<%wf8b>", a);
+ EXPECT_EQ(std::wstring(L"<101>"), buf);
+ int_fast8_t b = 0x12341234'12341234;
+ swprintf(buf, sizeof(buf), L"<%wf8x>", b);
+ EXPECT_EQ(std::wstring(L"<34>"), buf);
+ uint_fast16_t c = 0x11111111'22222222;
+#if defined(__LP64__)
+ swprintf(buf, sizeof(buf), L"<%wf16x>", c);
+ EXPECT_EQ(std::wstring(L"<1111111122222222>"), buf);
+#else
+ swprintf(buf, sizeof(buf), L"<%wf16x>", c);
+ EXPECT_EQ(std::wstring(L"<22222222>"), buf);
+#endif
+ int_fast32_t d = 0x33333333'44444444;
+#if defined(__LP64__)
+ swprintf(buf, sizeof(buf), L"<%wf32x>", d);
+ EXPECT_EQ(std::wstring(L"<3333333344444444>"), buf);
+#else
+ swprintf(buf, sizeof(buf), L"<%wf32x>", d);
+ EXPECT_EQ(std::wstring(L"<44444444>"), buf);
+#endif
+ int_fast64_t e = 0xaaaaaaaa'aaaaaaaa;
+ swprintf(buf, sizeof(buf), L"<%wf64x>", e);
+ EXPECT_EQ(std::wstring(L"<aaaaaaaaaaaaaaaa>"), buf);
+ swprintf(buf, sizeof(buf), L"<%wf64X>", e);
+ EXPECT_EQ(std::wstring(L"<AAAAAAAAAAAAAAAA>"), buf);
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_wf_arguments_reordering) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+ wchar_t buf[BUFSIZ];
+ int_fast16_t a = 0x11111111'22222222;
+ int_fast32_t b = 0x33333333'44444444;
+ int_fast32_t c = 0xaaaaaaaa'aaaaaaaa;
+#if defined(__LP64__)
+ swprintf(buf, sizeof(buf), L"<%2$wf32x --- %1$wf32b>", c, b);
+ EXPECT_EQ(std::wstring(L"<3333333344444444 --- "
+ L"1010101010101010101010101010101010101010101010101010101010101010>"),
+ buf);
+ swprintf(buf, sizeof(buf), L"<%3$wf32b --- %1$wf16x --- %2$wf32x>", a, b, c);
+ EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010 --- "
+ L"1111111122222222 --- 3333333344444444>"),
+ buf);
+#else
+ swprintf(buf, sizeof(buf), L"<%2$wf32x --- %1$wf32b>", c, b);
+ EXPECT_EQ(std::wstring(L"<44444444 --- 10101010101010101010101010101010>"), buf);
+ swprintf(buf, sizeof(buf), L"<%3$wf32b --- %1$wf16x --- %2$wf32x>", a, b, c);
+ EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010 --- 22222222 --- 44444444>"), buf);
+#endif
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
+
+TEST(STDIO_TEST, swprintf_invalid_wf_width) {
+#if defined(__BIONIC__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+ wchar_t buf[BUFSIZ];
+ int_fast32_t a = 100;
+ EXPECT_DEATH(swprintf(buf, sizeof(buf), L"%wf20d", &a), "%wf20 is unsupported");
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "no %w in glibc";
+#endif
+}
\ No newline at end of file
diff --git a/tests/sys_msg_test.cpp b/tests/sys_msg_test.cpp
index 200f654..da45087 100644
--- a/tests/sys_msg_test.cpp
+++ b/tests/sys_msg_test.cpp
@@ -74,9 +74,12 @@
}
TEST(sys_msg, msgctl_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, msgctl(-1, IPC_STAT, nullptr));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_msg, msgget_failure) {
@@ -86,9 +89,12 @@
}
TEST(sys_msg, msgrcv_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, msgrcv(-1, nullptr, 0, 0, 0));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_msg, msgsnd_failure) {
diff --git a/tests/sys_random_test.cpp b/tests/sys_random_test.cpp
index 2e2665b..e0cbf78 100644
--- a/tests/sys_random_test.cpp
+++ b/tests/sys_random_test.cpp
@@ -48,6 +48,8 @@
}
TEST(sys_random, getentropy_EFAULT) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
#if defined(HAVE_SYS_RANDOM)
errno = 0;
ASSERT_EQ(-1, getentropy(nullptr, 1));
@@ -55,6 +57,7 @@
#else
GTEST_SKIP() << "<sys/random.h> not available";
#endif
+#pragma clang diagnostic pop
}
TEST(sys_random, getentropy_EIO) {
@@ -84,6 +87,8 @@
}
TEST(sys_random, getrandom_EFAULT) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
#if defined(HAVE_SYS_RANDOM)
errno = 0;
ASSERT_EQ(-1, getrandom(nullptr, 256, 0));
@@ -91,6 +96,7 @@
#else
GTEST_SKIP() << "<sys/random.h> not available";
#endif
+#pragma clang diagnostic pop
}
TEST(sys_random, getrandom_EINVAL) {
diff --git a/tests/sys_sem_test.cpp b/tests/sys_sem_test.cpp
index b98926b..4bac92f 100644
--- a/tests/sys_sem_test.cpp
+++ b/tests/sys_sem_test.cpp
@@ -87,15 +87,21 @@
}
TEST(sys_sem, semop_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, semop(-1, nullptr, 0));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_sem, semtimedop_failure) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
errno = 0;
ASSERT_EQ(-1, semtimedop(-1, nullptr, 0, nullptr));
ASSERT_TRUE(errno == EINVAL || errno == ENOSYS);
+#pragma clang diagnostic pop
}
TEST(sys_sem, union_semun) {
diff --git a/tests/sys_wait_test.cpp b/tests/sys_wait_test.cpp
index c006972..200fabe 100644
--- a/tests/sys_wait_test.cpp
+++ b/tests/sys_wait_test.cpp
@@ -42,3 +42,22 @@
ASSERT_EQ(66, si.si_status);
ASSERT_EQ(CLD_EXITED, si.si_code);
}
+
+// https://github.com/android/ndk/issues/1878
+TEST(sys_wait, macros) {
+#if defined(__GLIBC__)
+ // glibc before 2016 requires an lvalue.
+#else
+ ASSERT_FALSE(WIFEXITED(0x7f));
+ ASSERT_TRUE(WIFSTOPPED(0x7f));
+ ASSERT_FALSE(WIFCONTINUED(0x7f));
+
+ ASSERT_TRUE(WIFEXITED(0x80));
+ ASSERT_FALSE(WIFSTOPPED(0x80));
+ ASSERT_FALSE(WIFCONTINUED(0x80));
+
+ ASSERT_FALSE(WIFEXITED(0xffff));
+ ASSERT_FALSE(WIFSTOPPED(0xffff));
+ ASSERT_TRUE(WIFCONTINUED(0xffff));
+#endif
+}
diff --git a/tests/syslog_test.cpp b/tests/syslog_test.cpp
new file mode 100644
index 0000000..3ec3337
--- /dev/null
+++ b/tests/syslog_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 <syslog.h>
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include "utils.h"
+
+TEST(syslog, syslog_percent_m) {
+ ExecTestHelper eth;
+ eth.Run(
+ [&]() {
+ openlog("foo", LOG_PERROR, LOG_AUTH);
+ errno = EINVAL;
+ syslog(LOG_ERR, "a b c: %m");
+ closelog();
+ exit(0);
+ },
+ 0, "foo: a b c: Invalid argument\n");
+}
+
+TEST(syslog, syslog_empty) {
+ ExecTestHelper eth;
+ eth.Run(
+ [&]() {
+ openlog("foo", LOG_PERROR, LOG_AUTH);
+ errno = EINVAL;
+ syslog(LOG_ERR, "");
+ closelog();
+ exit(0);
+ },
+ 0, "foo: \n");
+}
+
+TEST(syslog, syslog_truncation) {
+ ExecTestHelper eth;
+ eth.Run(
+ [&]() {
+ openlog("bar", LOG_PERROR, LOG_AUTH);
+ char too_long[2048] = {};
+ memset(too_long, 'x', sizeof(too_long) - 1);
+ syslog(LOG_ERR, "%s", too_long);
+ closelog();
+ exit(0);
+ },
+ 0, "bar: x{1023}\n");
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index f0ad937..d16600c 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -181,10 +181,25 @@
ASSERT_NE(static_cast<time_t>(-1), mktime(&t));
ASSERT_EQ(0, errno);
- // This will overflow for LP32 or LP64.
+ // This will overflow for LP32.
t.tm_year = INT_MAX;
errno = 0;
+#if !defined(__LP64__)
+ ASSERT_EQ(static_cast<time_t>(-1), mktime(&t));
+ ASSERT_EQ(EOVERFLOW, errno);
+#else
+ ASSERT_EQ(static_cast<time_t>(67768036166016000U), mktime(&t));
+ ASSERT_EQ(0, errno);
+#endif
+
+ // This will overflow for LP32 or LP64.
+ // tm_year is int, this t struct points to INT_MAX + 1 no matter what TZ is.
+ t.tm_year = INT_MAX;
+ t.tm_mon = 11;
+ t.tm_mday = 45;
+
+ errno = 0;
ASSERT_EQ(static_cast<time_t>(-1), mktime(&t));
ASSERT_EQ(EOVERFLOW, errno);
}
@@ -1254,8 +1269,39 @@
TEST(time, timespec_get) {
#if __BIONIC__
timespec ts = {};
- ASSERT_EQ(0, timespec_get(&ts, 123));
ASSERT_EQ(TIME_UTC, timespec_get(&ts, TIME_UTC));
+ ASSERT_EQ(TIME_MONOTONIC, timespec_get(&ts, TIME_MONOTONIC));
+ ASSERT_EQ(TIME_ACTIVE, timespec_get(&ts, TIME_ACTIVE));
+ ASSERT_EQ(TIME_THREAD_ACTIVE, timespec_get(&ts, TIME_THREAD_ACTIVE));
+#else
+ GTEST_SKIP() << "glibc doesn't have timespec_get until 2.21";
+#endif
+}
+
+TEST(time, timespec_get_invalid) {
+#if __BIONIC__
+ timespec ts = {};
+ ASSERT_EQ(0, timespec_get(&ts, 123));
+#else
+ GTEST_SKIP() << "glibc doesn't have timespec_get until 2.21";
+#endif
+}
+
+TEST(time, timespec_getres) {
+#if __BIONIC__
+ timespec ts = {};
+ ASSERT_EQ(TIME_UTC, timespec_getres(&ts, TIME_UTC));
+ ASSERT_EQ(1, ts.tv_nsec);
+ ASSERT_EQ(0, ts.tv_sec);
+#else
+ GTEST_SKIP() << "glibc doesn't have timespec_get until 2.21";
+#endif
+}
+
+TEST(time, timespec_getres_invalid) {
+#if __BIONIC__
+ timespec ts = {};
+ ASSERT_EQ(0, timespec_getres(&ts, 123));
#else
GTEST_SKIP() << "glibc doesn't have timespec_get until 2.21";
#endif
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 16d4348..8716810 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -714,7 +714,8 @@
#if defined(__BIONIC__)
wchar_t* p;
size_t size;
-
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
// Invalid buffer.
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(nullptr, &size));
@@ -724,6 +725,7 @@
errno = 0;
ASSERT_EQ(nullptr, open_wmemstream(&p, nullptr));
ASSERT_EQ(EINVAL, errno);
+#pragma clang diagnostic pop
#else
GTEST_SKIP() << "This test is bionic-specific";
#endif