diff --git a/libgcc/config/riscv/feature_bits.c b/libgcc/config/riscv/feature_bits.c
new file mode 100644
index 00000000000..f893055e728
--- /dev/null
+++ b/libgcc/config/riscv/feature_bits.c
@@ -0,0 +1,407 @@
+/* Helper function for function multi-versioning for RISC-V.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#define RISCV_FEATURE_BITS_LENGTH 2
+
+struct {
+  unsigned length;
+  unsigned long long features[RISCV_FEATURE_BITS_LENGTH];
+} __riscv_feature_bits __attribute__ ((visibility ("hidden"), nocommon));
+
+#define RISCV_VENDOR_FEATURE_BITS_LENGTH 1
+
+struct {
+  unsigned length;
+  unsigned long long features[RISCV_VENDOR_FEATURE_BITS_LENGTH];
+} __riscv_vendor_feature_bits __attribute__ ((visibility ("hidden"), nocommon));
+
+struct {
+  unsigned mvendorid;
+  unsigned long long marchid;
+  unsigned long long mimpid;
+} __riscv_cpu_model __attribute__ ((visibility ("hidden"), nocommon));
+
+#define A_GROUPID 0
+#define A_BITMASK (1ULL << 0)
+#define C_GROUPID 0
+#define C_BITMASK (1ULL << 2)
+#define D_GROUPID 0
+#define D_BITMASK (1ULL << 3)
+#define F_GROUPID 0
+#define F_BITMASK (1ULL << 5)
+#define I_GROUPID 0
+#define I_BITMASK (1ULL << 8)
+#define M_GROUPID 0
+#define M_BITMASK (1ULL << 12)
+#define V_GROUPID 0
+#define V_BITMASK (1ULL << 21)
+#define ZACAS_GROUPID 0
+#define ZACAS_BITMASK (1ULL << 26)
+#define ZBA_GROUPID 0
+#define ZBA_BITMASK (1ULL << 27)
+#define ZBB_GROUPID 0
+#define ZBB_BITMASK (1ULL << 28)
+#define ZBC_GROUPID 0
+#define ZBC_BITMASK (1ULL << 29)
+#define ZBKB_GROUPID 0
+#define ZBKB_BITMASK (1ULL << 30)
+#define ZBKC_GROUPID 0
+#define ZBKC_BITMASK (1ULL << 31)
+#define ZBKX_GROUPID 0
+#define ZBKX_BITMASK (1ULL << 32)
+#define ZBS_GROUPID 0
+#define ZBS_BITMASK (1ULL << 33)
+#define ZFA_GROUPID 0
+#define ZFA_BITMASK (1ULL << 34)
+#define ZFH_GROUPID 0
+#define ZFH_BITMASK (1ULL << 35)
+#define ZFHMIN_GROUPID 0
+#define ZFHMIN_BITMASK (1ULL << 36)
+#define ZICBOZ_GROUPID 0
+#define ZICBOZ_BITMASK (1ULL << 37)
+#define ZICOND_GROUPID 0
+#define ZICOND_BITMASK (1ULL << 38)
+#define ZIHINTNTL_GROUPID 0
+#define ZIHINTNTL_BITMASK (1ULL << 39)
+#define ZIHINTPAUSE_GROUPID 0
+#define ZIHINTPAUSE_BITMASK (1ULL << 40)
+#define ZKND_GROUPID 0
+#define ZKND_BITMASK (1ULL << 41)
+#define ZKNE_GROUPID 0
+#define ZKNE_BITMASK (1ULL << 42)
+#define ZKNH_GROUPID 0
+#define ZKNH_BITMASK (1ULL << 43)
+#define ZKSED_GROUPID 0
+#define ZKSED_BITMASK (1ULL << 44)
+#define ZKSH_GROUPID 0
+#define ZKSH_BITMASK (1ULL << 45)
+#define ZKT_GROUPID 0
+#define ZKT_BITMASK (1ULL << 46)
+#define ZTSO_GROUPID 0
+#define ZTSO_BITMASK (1ULL << 47)
+#define ZVBB_GROUPID 0
+#define ZVBB_BITMASK (1ULL << 48)
+#define ZVBC_GROUPID 0
+#define ZVBC_BITMASK (1ULL << 49)
+#define ZVFH_GROUPID 0
+#define ZVFH_BITMASK (1ULL << 50)
+#define ZVFHMIN_GROUPID 0
+#define ZVFHMIN_BITMASK (1ULL << 51)
+#define ZVKB_GROUPID 0
+#define ZVKB_BITMASK (1ULL << 52)
+#define ZVKG_GROUPID 0
+#define ZVKG_BITMASK (1ULL << 53)
+#define ZVKNED_GROUPID 0
+#define ZVKNED_BITMASK (1ULL << 54)
+#define ZVKNHA_GROUPID 0
+#define ZVKNHA_BITMASK (1ULL << 55)
+#define ZVKNHB_GROUPID 0
+#define ZVKNHB_BITMASK (1ULL << 56)
+#define ZVKSED_GROUPID 0
+#define ZVKSED_BITMASK (1ULL << 57)
+#define ZVKSH_GROUPID 0
+#define ZVKSH_BITMASK (1ULL << 58)
+#define ZVKT_GROUPID 0
+#define ZVKT_BITMASK (1ULL << 59)
+#define ZVE32X_GROUPID 0
+#define ZVE32X_BITMASK (1ULL << 60)
+#define ZVE32F_GROUPID 0
+#define ZVE32F_BITMASK (1ULL << 61)
+#define ZVE64X_GROUPID 0
+#define ZVE64X_BITMASK (1ULL << 62)
+#define ZVE64F_GROUPID 0
+#define ZVE64F_BITMASK (1ULL << 63)
+#define ZVE64D_GROUPID 1
+#define ZVE64D_BITMASK (1ULL << 0)
+#define ZIMOP_GROUPID 1
+#define ZIMOP_BITMASK (1ULL << 1)
+#define ZCA_GROUPID 1
+#define ZCA_BITMASK (1ULL << 2)
+#define ZCB_GROUPID 1
+#define ZCB_BITMASK (1ULL << 3)
+#define ZCD_GROUPID 1
+#define ZCD_BITMASK (1ULL << 4)
+#define ZCF_GROUPID 1
+#define ZCF_BITMASK (1ULL << 5)
+#define ZCMOP_GROUPID 1
+#define ZCMOP_BITMASK (1ULL << 6)
+#define ZAWRS_GROUPID 1
+#define ZAWRS_BITMASK (1ULL << 7)
+
+#define SET_EXT(EXT) features[EXT##_GROUPID] |= EXT##_BITMASK
+
+#ifdef __linux
+
+#define __NR_riscv_hwprobe 258
+#define RISCV_HWPROBE_KEY_MVENDORID 0
+#define RISCV_HWPROBE_KEY_MARCHID 1
+#define RISCV_HWPROBE_KEY_MIMPID 2
+#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
+#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1ULL << 0)
+#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
+#define RISCV_HWPROBE_IMA_FD (1ULL << 0)
+#define RISCV_HWPROBE_IMA_C (1ULL << 1)
+#define RISCV_HWPROBE_IMA_V (1ULL << 2)
+#define RISCV_HWPROBE_EXT_ZBA (1ULL << 3)
+#define RISCV_HWPROBE_EXT_ZBB (1ULL << 4)
+#define RISCV_HWPROBE_EXT_ZBS (1ULL << 5)
+#define RISCV_HWPROBE_EXT_ZICBOZ (1ULL << 6)
+#define RISCV_HWPROBE_EXT_ZBC (1ULL << 7)
+#define RISCV_HWPROBE_EXT_ZBKB (1ULL << 8)
+#define RISCV_HWPROBE_EXT_ZBKC (1ULL << 9)
+#define RISCV_HWPROBE_EXT_ZBKX (1ULL << 10)
+#define RISCV_HWPROBE_EXT_ZKND (1ULL << 11)
+#define RISCV_HWPROBE_EXT_ZKNE (1ULL << 12)
+#define RISCV_HWPROBE_EXT_ZKNH (1ULL << 13)
+#define RISCV_HWPROBE_EXT_ZKSED (1ULL << 14)
+#define RISCV_HWPROBE_EXT_ZKSH (1ULL << 15)
+#define RISCV_HWPROBE_EXT_ZKT (1ULL << 16)
+#define RISCV_HWPROBE_EXT_ZVBB (1ULL << 17)
+#define RISCV_HWPROBE_EXT_ZVBC (1ULL << 18)
+#define RISCV_HWPROBE_EXT_ZVKB (1ULL << 19)
+#define RISCV_HWPROBE_EXT_ZVKG (1ULL << 20)
+#define RISCV_HWPROBE_EXT_ZVKNED (1ULL << 21)
+#define RISCV_HWPROBE_EXT_ZVKNHA (1ULL << 22)
+#define RISCV_HWPROBE_EXT_ZVKNHB (1ULL << 23)
+#define RISCV_HWPROBE_EXT_ZVKSED (1ULL << 24)
+#define RISCV_HWPROBE_EXT_ZVKSH (1ULL << 25)
+#define RISCV_HWPROBE_EXT_ZVKT (1ULL << 26)
+#define RISCV_HWPROBE_EXT_ZFH (1ULL << 27)
+#define RISCV_HWPROBE_EXT_ZFHMIN (1ULL << 28)
+#define RISCV_HWPROBE_EXT_ZIHINTNTL (1ULL << 29)
+#define RISCV_HWPROBE_EXT_ZVFH (1ULL << 30)
+#define RISCV_HWPROBE_EXT_ZVFHMIN (1ULL << 31)
+#define RISCV_HWPROBE_EXT_ZFA (1ULL << 32)
+#define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33)
+#define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34)
+#define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35)
+#define RISCV_HWPROBE_EXT_ZIHINTPAUSE (1ULL << 36)
+#define RISCV_HWPROBE_EXT_ZVE32X (1ULL << 37)
+#define RISCV_HWPROBE_EXT_ZVE32F (1ULL << 38)
+#define RISCV_HWPROBE_EXT_ZVE64X (1ULL << 39)
+#define RISCV_HWPROBE_EXT_ZVE64F (1ULL << 40)
+#define RISCV_HWPROBE_EXT_ZVE64D (1ULL << 41)
+#define RISCV_HWPROBE_EXT_ZIMOP  (1ULL << 42)
+#define RISCV_HWPROBE_EXT_ZCA    (1ULL << 43)
+#define RISCV_HWPROBE_EXT_ZCB    (1ULL << 44)
+#define RISCV_HWPROBE_EXT_ZCD    (1ULL << 45)
+#define RISCV_HWPROBE_EXT_ZCF    (1ULL << 46)
+#define RISCV_HWPROBE_EXT_ZCMOP  (1ULL << 47)
+#define RISCV_HWPROBE_EXT_ZAWRS  (1ULL << 48)
+#define RISCV_HWPROBE_KEY_CPUPERF_0 5
+#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
+#define RISCV_HWPROBE_MISALIGNED_EMULATED (1ULL << 0)
+#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
+#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
+#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
+#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
+#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
+
+struct riscv_hwprobe {
+  long long key;
+  unsigned long long value;
+};
+
+static long syscall_5_args (long number, long arg1, long arg2, long arg3,
+			    long arg4, long arg5)
+{
+  register long a7 __asm__ ("a7") = number;
+  register long a0 __asm__ ("a0") = arg1;
+  register long a1 __asm__ ("a1") = arg2;
+  register long a2 __asm__ ("a2") = arg3;
+  register long a3 __asm__ ("a3") = arg4;
+  register long a4 __asm__ ("a4") = arg5;
+  __asm__ __volatile__ ("ecall\n\t"
+			: "=r"(a0)
+			: "r"(a7), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
+			: "memory");
+  return a0;
+}
+
+#define SET_FROM_HWPROBE(HWPROBE_VAR, EXT) \
+  if (HWPROBE_VAR.value & RISCV_HWPROBE_EXT_##EXT) \
+    SET_EXT (EXT)
+
+#define SET_FROM_IMA_EXT(EXT) \
+  SET_FROM_HWPROBE (hwprobe_ima_ext, EXT)
+
+static void __init_riscv_features_bits_linux ()
+{
+  struct riscv_hwprobe hwprobes[] = {
+    {RISCV_HWPROBE_KEY_MVENDORID, 0},
+    {RISCV_HWPROBE_KEY_MARCHID, 0},
+    {RISCV_HWPROBE_KEY_MIMPID, 0},
+    {RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0},
+    {RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
+  };
+
+  long res = syscall_5_args (__NR_riscv_hwprobe, (long)&hwprobes,
+			     sizeof (hwprobes) / sizeof (hwprobes[0]), 0,
+			     0, 0);
+
+  if (res)
+    return;
+
+  const struct riscv_hwprobe hwprobe_mvendorid = hwprobes[0];
+
+  __riscv_cpu_model.mvendorid = hwprobe_mvendorid.value;
+
+  const struct riscv_hwprobe hwprobe_marchid = hwprobes[1];
+
+  __riscv_cpu_model.marchid = hwprobe_marchid.value;
+
+  const struct riscv_hwprobe hwprobe_mimpid = hwprobes[2];
+
+  __riscv_cpu_model.mimpid = hwprobe_mimpid.value;
+
+  const struct riscv_hwprobe hwprobe_base_behavior = hwprobes[3];
+  unsigned long long features[RISCV_FEATURE_BITS_LENGTH];
+  int i;
+  for (i = 0; i < RISCV_FEATURE_BITS_LENGTH; ++i)
+    features[i] = 0;
+
+  if (hwprobe_base_behavior.value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA)
+    {
+      SET_EXT (I);
+      SET_EXT (M);
+      SET_EXT (A);
+    }
+
+  const struct riscv_hwprobe hwprobe_ima_ext = hwprobes[4];
+
+  /* Every time we add new extensions, we should check if previous extensions
+     imply the new extension and set the corresponding bit.  */
+
+  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_FD)
+    {
+      SET_EXT (F);
+      SET_EXT (D);
+    }
+
+  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_C)
+    {
+      SET_EXT (C);
+      SET_EXT (ZCA);
+      if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_FD)
+	{
+#if __riscv_xlen == 32
+	  SET_EXT (ZCF);
+#endif
+	  SET_EXT (ZCD);
+	}
+    }
+
+  /* Added since Linux v6.5.  */
+  if (hwprobe_ima_ext.value & RISCV_HWPROBE_IMA_V)
+    {
+      SET_EXT (V);
+      SET_EXT (ZVE32X);
+      SET_EXT (ZVE32F);
+      SET_EXT (ZVE64X);
+      SET_EXT (ZVE64F);
+      SET_EXT (ZVE64D);
+    }
+
+  SET_FROM_IMA_EXT (ZBA);
+  SET_FROM_IMA_EXT (ZBB);
+  SET_FROM_IMA_EXT (ZBS);
+  /* Added since Linux v6.7.  */
+  SET_FROM_IMA_EXT (ZICBOZ);
+  /* Added since Linux v6.8.  */
+  SET_FROM_IMA_EXT (ZBC);
+  SET_FROM_IMA_EXT (ZBKB);
+  SET_FROM_IMA_EXT (ZBKC);
+  SET_FROM_IMA_EXT (ZBKX);
+  SET_FROM_IMA_EXT (ZKND);
+  SET_FROM_IMA_EXT (ZKNE);
+  SET_FROM_IMA_EXT (ZKNH);
+  SET_FROM_IMA_EXT (ZKSED);
+  SET_FROM_IMA_EXT (ZKSH);
+  SET_FROM_IMA_EXT (ZKT);
+  SET_FROM_IMA_EXT (ZVBB);
+  SET_FROM_IMA_EXT (ZVBC);
+  SET_FROM_IMA_EXT (ZVKB);
+  SET_FROM_IMA_EXT (ZVKG);
+  SET_FROM_IMA_EXT (ZVKNED);
+  SET_FROM_IMA_EXT (ZVKNHA);
+  SET_FROM_IMA_EXT (ZVKNHB);
+  SET_FROM_IMA_EXT (ZVKSED);
+  SET_FROM_IMA_EXT (ZVKSH);
+  SET_FROM_IMA_EXT (ZVKT);
+  SET_FROM_IMA_EXT (ZFH);
+  SET_FROM_IMA_EXT (ZFHMIN);
+  SET_FROM_IMA_EXT (ZIHINTNTL);
+  SET_FROM_IMA_EXT (ZVFH);
+  SET_FROM_IMA_EXT (ZVFHMIN);
+  SET_FROM_IMA_EXT (ZFA);
+  SET_FROM_IMA_EXT (ZTSO);
+  SET_FROM_IMA_EXT (ZACAS);
+  SET_FROM_IMA_EXT (ZICOND);
+  /* Added since Linux v6.10.  */
+  SET_FROM_IMA_EXT (ZIHINTPAUSE);
+  /* Added since Linux v6.11.  */
+  SET_FROM_IMA_EXT (ZVE32X);
+  SET_FROM_IMA_EXT (ZVE32F);
+  SET_FROM_IMA_EXT (ZVE64X);
+  SET_FROM_IMA_EXT (ZVE64F);
+  SET_FROM_IMA_EXT (ZVE64D);
+  SET_FROM_IMA_EXT (ZIMOP);
+  SET_FROM_IMA_EXT (ZCA);
+  SET_FROM_IMA_EXT (ZCB);
+  SET_FROM_IMA_EXT (ZCD);
+  SET_FROM_IMA_EXT (ZCF);
+  SET_FROM_IMA_EXT (ZCMOP);
+  SET_FROM_IMA_EXT (ZAWRS);
+
+  for (i = 0; i < RISCV_FEATURE_BITS_LENGTH; ++i)
+    __riscv_feature_bits.features[i] = features[i];
+
+  __riscv_feature_bits.length = RISCV_FEATURE_BITS_LENGTH;
+
+  __riscv_vendor_feature_bits.length = 0;
+}
+#endif
+
+
+static int __init = 0;
+
+void
+__attribute__ ((constructor (101)))
+__init_riscv_feature_bits ()
+{
+  if (__init)
+    return;
+
+#ifdef __linux
+  __init_riscv_features_bits_linux ();
+#else
+  /* Unsupported, just initlizaed that into all zeros.  */
+  __riscv_feature_bits.length = 0;
+  __riscv_vendor_feature_bits.length = 0;
+  __riscv_vendor_feature_bits.vendorID = 0;
+#endif
+
+  __init = 1;
+}
diff --git a/libgcc/config/riscv/t-elf b/libgcc/config/riscv/t-elf
index 415e1fffbe7..acb5e280676 100644
--- a/libgcc/config/riscv/t-elf
+++ b/libgcc/config/riscv/t-elf
@@ -3,6 +3,7 @@ LIB2ADD += $(srcdir)/config/riscv/save-restore.S \
 	   $(srcdir)/config/riscv/multi3.c \
 	   $(srcdir)/config/riscv/div.S \
 	   $(srcdir)/config/riscv/atomic.c \
+	   $(srcdir)/config/riscv/feature_bits.c \
 
 # Avoid the full unwinder being pulled along with the division libcalls.
 LIB2_DIVMOD_EXCEPTION_FLAGS := -fasynchronous-unwind-tables
