__riscv_hwprobe: don't try to set errno.

When used in an ifunc resolver, errno@plt won't be available. This is
the API the rivos folks contributing to glibc are leaning towards, for
the same reason. Hit by the berberis folks because they don't implement
the syscall so they were trying to set errno to ENOSYS.

Tested by looking at the generated assembler, and also disabling the
vdso (since on actual systems, this will go via the vdso).

Test: treehugger
Change-Id: Ie2779110f141f20efe97cb892fbdefd808b5339b
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