Merge "riscv64: increase jmp_buf size." into main
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
index 410eb78..3cfb8b5 100644
--- a/libc/bionic/bionic_call_ifunc_resolver.cpp
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -58,12 +58,12 @@
   }
   return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
 #elif defined(__riscv)
-  // This argument and its value is just a placeholder for now,
-  // but it means that if we do pass something in future (such as
-  // getauxval() and/or hwprobe key/value pairs), callees will be able to
-  // recognize what they're being given.
-  typedef ElfW(Addr) (*ifunc_resolver_t)(void*);
-  return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(nullptr);
+  // The pointer argument is currently unused, but reserved for future
+  // expansion. If we pass nullptr from the beginning, it'll be easier
+  // to recognize if/when we pass actual data (and matches glibc).
+  typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, void*);
+  static uint64_t hwcap = getauxval(AT_HWCAP);
+  return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap, nullptr);
 #else
   typedef ElfW(Addr) (*ifunc_resolver_t)(void);
   return reinterpret_cast<ifunc_resolver_t>(resolver_addr)();
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 7efbf6d..3b9e6a4 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -38,6 +38,8 @@
 #define __hwasan_thread_exit()
 #endif
 
+#include "platform/bionic/page.h"
+
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_lock.h"
 #include "private/bionic_tls.h"
@@ -236,7 +238,7 @@
 // On LP64, we could use more but there's no obvious advantage to doing
 // so, and the various media processes use RLIMIT_AS as a way to limit
 // the amount of allocation they'll do.
-#define PTHREAD_GUARD_SIZE PAGE_SIZE
+#define PTHREAD_GUARD_SIZE max_page_size()
 
 // SIGSTKSZ (8KiB) is not big enough.
 // An snprintf to a stack buffer of size PATH_MAX consumes ~7KiB of stack.
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index e92aada..8a20670 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -44,7 +44,6 @@
 #include "linker_utils.h"
 
 #include "private/KernelArgumentBlock.h"
-#include "private/ScopedPthreadMutexLocker.h"
 #include "private/bionic_call_ifunc_resolver.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
@@ -499,11 +498,6 @@
 
   if (!get_cfi_shadow()->InitialLinkDone(solist)) __linker_cannot_link(g_argv[0]);
 
-  // A constructor could spawn a thread that calls into the loader, so as soon
-  // as we've called a constructor, we need to hold the lock while accessing
-  // global loader state.
-  ScopedPthreadMutexLocker locker(&g_dl_mutex);
-
   si->call_pre_init_constructors();
   si->call_constructors();
 
@@ -696,6 +690,13 @@
  * function, or other GOT reference will generate a segfault.
  */
 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
+  // Unlock the loader mutex immediately before transferring to the executable's
+  // entry point. This must happen after destructors are called in this function
+  // (e.g. ~soinfo), so declare this variable very early.
+  struct DlMutexUnlocker {
+    ~DlMutexUnlocker() { pthread_mutex_unlock(&g_dl_mutex); }
+  } unlocker;
+
   // Initialize TLS early so system calls and errno work.
   KernelArgumentBlock args(raw_args);
   bionic_tcb temp_tcb __attribute__((uninitialized));
@@ -758,6 +759,11 @@
   // Initialize the linker's static libc's globals
   __libc_init_globals();
 
+  // A constructor could spawn a thread that calls into the loader, so as soon
+  // as we've called a constructor, we need to hold the lock until transferring
+  // to the entry point.
+  pthread_mutex_lock(&g_dl_mutex);
+
   // Initialize the linker's own global variables
   tmp_linker_so.call_constructors();
 
diff --git a/tests/ifunc_test.cpp b/tests/ifunc_test.cpp
index e3c437e..1fdbf1a 100644
--- a/tests/ifunc_test.cpp
+++ b/tests/ifunc_test.cpp
@@ -60,6 +60,26 @@
   return ret42;
 }
 
+#elif defined(__riscv)
+
+#include <sys/hwprobe.h>
+
+static uint64_t g_hwcap;
+
+static riscv_hwprobe g_hwprobes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0}};
+
+extern "C" fn_ptr_t hwcap_resolver(uint64_t hwcap, void* null) {
+  // Check hwcap like arm32/arm64.
+  g_hwcap = hwcap;
+
+  // For now, the pointer argument is reserved for future expansion.
+  if (null != NULL) abort();
+
+  // Ensure that __riscv_hwprobe() can be called from an ifunc.
+  if (__riscv_hwprobe(g_hwprobes, 1, 0, nullptr, 0) != 0) return nullptr;
+  return ret42;
+}
+
 #else
 
 extern "C" fn_ptr_t hwcap_resolver() {
@@ -81,6 +101,12 @@
   EXPECT_EQ(getauxval(AT_HWCAP2), g_arg._hwcap2);
 #elif defined(__arm__)
   EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap);
+#elif defined(__riscv)
+  EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap);
+
+  riscv_hwprobe probes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0}};
+  ASSERT_EQ(0, __riscv_hwprobe(probes, 1, 0, nullptr, 0));
+  EXPECT_EQ(probes[0].value, g_hwprobes[0].value);
 #endif
 }
 
diff --git a/tests/sys_hwprobe_test.cpp b/tests/sys_hwprobe_test.cpp
index 15028ff..a4b47c7 100644
--- a/tests/sys_hwprobe_test.cpp
+++ b/tests/sys_hwprobe_test.cpp
@@ -30,10 +30,11 @@
 
 #if __has_include(<sys/hwprobe.h>)
 #include <sys/hwprobe.h>
+#include <sys/syscall.h>
 #endif
 
 TEST(sys_hwprobe, __riscv_hwprobe) {
-#if defined(__riscv) && __has_include(<sys/cachectl.h>)
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
   riscv_hwprobe probes[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
                             {.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
   ASSERT_EQ(0, __riscv_hwprobe(probes, 2, 0, nullptr, 0));
@@ -60,3 +61,25 @@
   GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
 #endif
 }
+
+TEST(sys_hwprobe, __riscv_hwprobe_syscall_vdso) {
+#if defined(__riscv) && __has_include(<sys/hwprobe.h>)
+  riscv_hwprobe probes_vdso[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
+                                 {.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
+  ASSERT_EQ(0, __riscv_hwprobe(probes_vdso, 2, 0, nullptr, 0));
+
+  riscv_hwprobe probes_syscall[] = {{.key = RISCV_HWPROBE_KEY_IMA_EXT_0},
+                                    {.key = RISCV_HWPROBE_KEY_CPUPERF_0}};
+  ASSERT_EQ(0, syscall(SYS_riscv_hwprobe, probes_syscall, 2, 0, nullptr, 0));
+
+  // Check we got the same answers from the vdso and the syscall.
+  EXPECT_EQ(RISCV_HWPROBE_KEY_IMA_EXT_0, probes_syscall[0].key);
+  EXPECT_EQ(probes_vdso[0].key, probes_syscall[0].key);
+  EXPECT_EQ(probes_vdso[0].value, probes_syscall[0].value);
+  EXPECT_EQ(RISCV_HWPROBE_KEY_CPUPERF_0, probes_syscall[1].key);
+  EXPECT_EQ(probes_vdso[1].key, probes_syscall[1].key);
+  EXPECT_EQ(probes_vdso[1].value, probes_syscall[1].value);
+#else
+  GTEST_SKIP() << "__riscv_hwprobe requires riscv64";
+#endif
+}