Merge "__riscv_hwprobe: don't try to set errno." into main
diff --git a/libc/SECCOMP_ALLOWLIST_COMMON.TXT b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
index 67b662e..aba8303 100644
--- a/libc/SECCOMP_ALLOWLIST_COMMON.TXT
+++ b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
@@ -17,6 +17,9 @@
int rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t*) all
int restart_syscall() all
+# The public API doesn't set errno, so we call this via inline assembler.
+int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
+
# vfork is used by bionic (and java.lang.ProcessBuilder) on some
# architectures. (The others use clone(2) directly instead.)
pid_t vfork() arm,x86,x86_64
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index f4f9e1b..e8dde7c 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -357,7 +357,6 @@
# riscv64-specific
int __riscv_flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
-int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
# x86-specific
int __set_thread_area:set_thread_area(void*) x86
diff --git a/libc/bionic/vdso.cpp b/libc/bionic/vdso.cpp
index e834ec7..d0f01d0 100644
--- a/libc/bionic/vdso.cpp
+++ b/libc/bionic/vdso.cpp
@@ -24,13 +24,13 @@
#include <sys/cdefs.h>
#include <sys/hwprobe.h>
#include <sys/time.h>
+#include <syscall.h>
#include <time.h>
#include <unistd.h>
extern "C" int __clock_gettime(int, struct timespec*);
extern "C" int __clock_getres(int, struct timespec*);
extern "C" int __gettimeofday(struct timeval*, struct timezone*);
-extern "C" int riscv_hwprobe(struct riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned);
static inline int vdso_return(int result) {
if (__predict_true(result == 0)) return 0;
@@ -88,9 +88,21 @@
auto vdso_riscv_hwprobe =
reinterpret_cast<decltype(&__riscv_hwprobe)>(__libc_globals->vdso[VDSO_RISCV_HWPROBE].fn);
if (__predict_true(vdso_riscv_hwprobe)) {
- return vdso_return(vdso_riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags));
+ return -vdso_riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags);
}
- return riscv_hwprobe(pairs, pair_count, cpu_count, cpus, flags);
+ // Inline the syscall directly in case someone's calling it from an
+ // ifunc resolver where we won't be able to set errno on failure.
+ // (Rather than our usual trick of letting the python-generated
+ // wrapper set errno but saving/restoring errno in cases where the API
+ // is to return an error value rather than setting errno.)
+ register long a0 __asm__("a0") = reinterpret_cast<long>(pairs);
+ register long a1 __asm__("a1") = pair_count;
+ register long a2 __asm__("a2") = cpu_count;
+ register long a3 __asm__("a3") = reinterpret_cast<long>(cpus);
+ register long a4 __asm__("a4") = flags;
+ register long a7 __asm__("a7") = __NR_riscv_hwprobe;
+ __asm__ volatile("ecall" : "=r"(a0) : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a7));
+ return -a0;
}
#endif
diff --git a/libc/include/sys/hwprobe.h b/libc/include/sys/hwprobe.h
index f2fd98d..b1a8400 100644
--- a/libc/include/sys/hwprobe.h
+++ b/libc/include/sys/hwprobe.h
@@ -49,7 +49,7 @@
*
* A `__cpu_count` of 0 and null `__cpus` means "all online cpus".
*
- * Returns 0 on success and returns -1 and sets `errno` on failure.
+ * Returns 0 on success and returns an error number on failure.
*/
int __riscv_hwprobe(struct riscv_hwprobe* _Nonnull __pairs, size_t __pair_count, size_t __cpu_count, unsigned long* _Nullable __cpus, unsigned __flags);
diff --git a/tests/sys_hwprobe_test.cpp b/tests/sys_hwprobe_test.cpp
index a4b47c7..3c8dce2 100644
--- a/tests/sys_hwprobe_test.cpp
+++ b/tests/sys_hwprobe_test.cpp
@@ -83,3 +83,12 @@
GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
#endif
}
+
+TEST(sys_hwprobe, __riscv_hwprobe_fail) {
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
+ riscv_hwprobe probes[] = {};
+ ASSERT_EQ(EINVAL, __riscv_hwprobe(probes, 0, 0, nullptr, ~0));
+#else
+ GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}