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
+}