From 6630e06f9bcb361177d46c277cf8e67cbd722429 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 9 Oct 2020 06:06:56 -0700
Subject: [PATCH] x86: Support GNU_PROPERTY_X86_ISA_1_V[234] marker [BZ #26717]

GCC 11 supports -march=x86-64-v[234] to enable x86 micro-architecture ISA
levels:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97250

and -mneeded to emit GNU_PROPERTY_X86_ISA_1_NEEDED property with
GNU_PROPERTY_X86_ISA_1_V[234] marker:

https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/13

Binutils support for GNU_PROPERTY_X86_ISA_1_V[234] marker were added by

commit b0ab06937385e0ae25cebf1991787d64f439bf12
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Oct 30 06:49:57 2020 -0700

    x86: Support GNU_PROPERTY_X86_ISA_1_BASELINE marker

and

commit 32930e4edbc06bc6f10c435dbcc63131715df678
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Oct 9 05:05:57 2020 -0700

    x86: Support GNU_PROPERTY_X86_ISA_1_V[234] marker

GNU_PROPERTY_X86_ISA_1_NEEDED property in x86 ELF binaries indicate the
micro-architecture ISA level required to execute the binary.  The marker
must be added by programmers explicitly in one of 3 ways:

1. Pass -mneeded to GCC.
2. Add the marker in the linker inputs as this patch does.
3. Pass -z x86-64-v[234] to the linker.

Add GNU_PROPERTY_X86_ISA_1_BASELINE and GNU_PROPERTY_X86_ISA_1_V[234]
marker support to ld.so if binutils 2.32 or newer is used to build glibc:

1. Add GNU_PROPERTY_X86_ISA_1_BASELINE and GNU_PROPERTY_X86_ISA_1_V[234]
markers to elf.h.
2. Add GNU_PROPERTY_X86_ISA_1_BASELINE and GNU_PROPERTY_X86_ISA_1_V[234]
marker to abi-note.o based on the ISA level used to compile abi-note.o,
assuming that the same ISA level is used to compile the whole glibc.
3. Add isa_1 to cpu_features to record the supported x86 ISA level.
4. Rename _dl_process_cet_property_note to _dl_process_property_note and
add GNU_PROPERTY_X86_ISA_1_V[234] marker detection.
5. Update _rtld_main_check and _dl_open_check to check loaded objects
with the incompatible ISA level.
6. Add a testcase to verify that dlopen an x86-64-v4 shared object fails
on lesser platforms.
7. Use <get-isa-level.h> in dl-hwcaps-subdirs.c and tst-glibc-hwcaps.c.

Tested under i686, x32 and x86-64 modes on x86-64-v2, x86-64-v3 and
x86-64-v4 machines.

Marked elf/tst-isa-level-1 with x86-64-v4, ran it on x86-64-v3 machine
and got:

[hjl@gnu-cfl-2 build-x86_64-linux]$ ./elf/tst-isa-level-1
./elf/tst-isa-level-1: CPU ISA level is lower than required
[hjl@gnu-cfl-2 build-x86_64-linux]$
---
 config.h.in                                |   3 +
 elf/elf.h                                  |  35 +++----
 sysdeps/x86/Makefile                       |  30 ++++++
 sysdeps/x86/abi-note.c                     |  29 ++++++
 sysdeps/x86/configure                      |  79 ++++++++++++++
 sysdeps/x86/configure.ac                   |  66 ++++++++++++
 sysdeps/x86/cpu-features.c                 |   3 +
 sysdeps/x86/dl-cet.c                       |  12 ++-
 sysdeps/x86/dl-prop.h                      | 113 +++++++++++++++------
 sysdeps/x86/get-isa-level.h                |  66 ++++++++++++
 sysdeps/x86/include/cpu-features.h         |   2 +
 sysdeps/x86/isa-level.c                    |  97 ++++++++++++++++++
 sysdeps/x86/link_map.h                     |  18 ++--
 sysdeps/x86/tst-isa-level-1.c              | 104 +++++++++++++++++++
 sysdeps/x86/tst-isa-level-mod-1-baseline.c |   1 +
 sysdeps/x86/tst-isa-level-mod-1-v2.c       |   1 +
 sysdeps/x86/tst-isa-level-mod-1-v3.c       |   1 +
 sysdeps/x86/tst-isa-level-mod-1-v4.c       |   1 +
 sysdeps/x86/tst-isa-level-mod-1.c          |  25 +++++
 sysdeps/x86_64/dl-hwcaps-subdirs.c         |  30 ++----
 sysdeps/x86_64/tst-glibc-hwcaps.c          |  41 ++------
 21 files changed, 644 insertions(+), 113 deletions(-)
 create mode 100644 sysdeps/x86/abi-note.c
 create mode 100644 sysdeps/x86/get-isa-level.h
 create mode 100644 sysdeps/x86/isa-level.c
 create mode 100644 sysdeps/x86/tst-isa-level-1.c
 create mode 100644 sysdeps/x86/tst-isa-level-mod-1-baseline.c
 create mode 100644 sysdeps/x86/tst-isa-level-mod-1-v2.c
 create mode 100644 sysdeps/x86/tst-isa-level-mod-1-v3.c
 create mode 100644 sysdeps/x86/tst-isa-level-mod-1-v4.c
 create mode 100644 sysdeps/x86/tst-isa-level-mod-1.c

diff --git a/config.h.in b/config.h.in
index eca2d66a2c..947feeff36 100644
--- a/config.h.in
+++ b/config.h.in
@@ -269,4 +269,7 @@
 /* The default value of x86 CET control.  */
 #define DEFAULT_DL_X86_CET_CONTROL cet_elf_property
 
+/* Define if x86 ISA level should be included in shared libraries.  */
+#undef INCLUDE_X86_ISA_LEVEL
+
 #endif
diff --git a/elf/elf.h b/elf/elf.h
index 4a9ae97dfc..9ebd052c8a 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1324,31 +1324,26 @@ typedef struct
 
 /* The x86 instruction sets indicated by the corresponding bits are
    used in program.  Their support in the hardware is optional.  */
-#define GNU_PROPERTY_X86_ISA_1_USED		0xc0000000
+#define GNU_PROPERTY_X86_ISA_1_USED		0xc0010002
 /* The x86 instruction sets indicated by the corresponding bits are
    used in program and they must be supported by the hardware.   */
-#define GNU_PROPERTY_X86_ISA_1_NEEDED		0xc0000001
+#define GNU_PROPERTY_X86_ISA_1_NEEDED		0xc0008002
 /* X86 processor-specific features used in program.  */
 #define GNU_PROPERTY_X86_FEATURE_1_AND		0xc0000002
 
-#define GNU_PROPERTY_X86_ISA_1_486		(1U << 0)
-#define GNU_PROPERTY_X86_ISA_1_586		(1U << 1)
-#define GNU_PROPERTY_X86_ISA_1_686		(1U << 2)
-#define GNU_PROPERTY_X86_ISA_1_SSE		(1U << 3)
-#define GNU_PROPERTY_X86_ISA_1_SSE2		(1U << 4)
-#define GNU_PROPERTY_X86_ISA_1_SSE3		(1U << 5)
-#define GNU_PROPERTY_X86_ISA_1_SSSE3		(1U << 6)
-#define GNU_PROPERTY_X86_ISA_1_SSE4_1		(1U << 7)
-#define GNU_PROPERTY_X86_ISA_1_SSE4_2		(1U << 8)
-#define GNU_PROPERTY_X86_ISA_1_AVX		(1U << 9)
-#define GNU_PROPERTY_X86_ISA_1_AVX2		(1U << 10)
-#define GNU_PROPERTY_X86_ISA_1_AVX512F		(1U << 11)
-#define GNU_PROPERTY_X86_ISA_1_AVX512CD		(1U << 12)
-#define GNU_PROPERTY_X86_ISA_1_AVX512ER		(1U << 13)
-#define GNU_PROPERTY_X86_ISA_1_AVX512PF		(1U << 14)
-#define GNU_PROPERTY_X86_ISA_1_AVX512VL		(1U << 15)
-#define GNU_PROPERTY_X86_ISA_1_AVX512DQ		(1U << 16)
-#define GNU_PROPERTY_X86_ISA_1_AVX512BW		(1U << 17)
+/* GNU_PROPERTY_X86_ISA_1_BASELINE: CMOV, CX8 (cmpxchg8b), FPU (fld),
+   MMX, OSFXSR (fxsave), SCE (syscall), SSE and SSE2.  */
+#define GNU_PROPERTY_X86_ISA_1_BASELINE		(1U << 0)
+/* GNU_PROPERTY_X86_ISA_1_V2: GNU_PROPERTY_X86_ISA_1_BASELINE,
+   CMPXCHG16B (cmpxchg16b), LAHF-SAHF (lahf), POPCNT (popcnt), SSE3,
+   SSSE3, SSE4.1 and SSE4.2.  */
+#define GNU_PROPERTY_X86_ISA_1_V2		(1U << 1)
+/* GNU_PROPERTY_X86_ISA_1_V3: GNU_PROPERTY_X86_ISA_1_V2, AVX, AVX2, BMI1,
+   BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE.  */
+#define GNU_PROPERTY_X86_ISA_1_V3		(1U << 2)
+/* GNU_PROPERTY_X86_ISA_1_V4: GNU_PROPERTY_X86_ISA_1_V3, AVX512F,
+   AVX512BW, AVX512CD, AVX512DQ and AVX512VL.  */
+#define GNU_PROPERTY_X86_ISA_1_V4		(1U << 3)
 
 /* This indicates that all executable sections are compatible with
    IBT.  */
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 081cc72e93..c814d5a195 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -9,6 +9,36 @@ sysdep_headers += sys/platform/x86.h
 tests += tst-get-cpu-features tst-get-cpu-features-static \
 	 tst-cpu-features-cpuinfo tst-cpu-features-supports
 tests-static += tst-get-cpu-features-static
+ifeq (yes,$(enable-x86-isa-level))
+tests += tst-isa-level-1
+modules-names += tst-isa-level-mod-1-baseline \
+		 tst-isa-level-mod-1-v2 \
+		 tst-isa-level-mod-1-v3 \
+		 tst-isa-level-mod-1-v4 \
+
+# X86 ISA level baseline
+CFLAGS-tst-isa-level-mod-1-baseline.c += -DINCLUDE_X86_ISA_LEVEL \
+					 -DISA_LEVEL=0x1 \
+					 -march=x86-64
+# X86 ISA level v2
+CFLAGS-tst-isa-level-mod-1-v2.c += -DINCLUDE_X86_ISA_LEVEL \
+				   -DISA_LEVEL=0x3 \
+				   -march=x86-64
+# X86 ISA level v3
+CFLAGS-tst-isa-level-mod-1-v3.c += -DINCLUDE_X86_ISA_LEVEL \
+				   -DISA_LEVEL=0x7 \
+				   -march=x86-64
+# X86 ISA level v4
+CFLAGS-tst-isa-level-mod-1-v4.c += -DINCLUDE_X86_ISA_LEVEL \
+				   -DISA_LEVEL=0xf \
+				   -march=x86-64
+
+$(objpfx)tst-isa-level-1: $(libdl)
+$(objpfx)tst-isa-level-1.out: $(objpfx)tst-isa-level-mod-1-baseline.so \
+			      $(objpfx)tst-isa-level-mod-1-v2.so \
+			      $(objpfx)tst-isa-level-mod-1-v3.so \
+			      $(objpfx)tst-isa-level-mod-1-v4.so
+endif
 endif
 
 ifeq ($(subdir),math)
diff --git a/sysdeps/x86/abi-note.c b/sysdeps/x86/abi-note.c
new file mode 100644
index 0000000000..5140e6ab47
--- /dev/null
+++ b/sysdeps/x86/abi-note.c
@@ -0,0 +1,29 @@
+/* Special .init and .fini section support.  x86-64 version.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file.  (The Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <isa-level.c>
+#include <csu/abi-note.c>
diff --git a/sysdeps/x86/configure b/sysdeps/x86/configure
index 81cc4e80d6..5e32dc62b3 100644
--- a/sysdeps/x86/configure
+++ b/sysdeps/x86/configure
@@ -68,3 +68,82 @@ elif test $enable_cet = permissive; then
 fi
 config_vars="$config_vars
 enable-cet = $enable_cet"
+
+# Check if linker supports x86 ISA level.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker x86 ISA level support" >&5
+$as_echo_n "checking for linker x86 ISA level support... " >&6; }
+if ${libc_cv_include_x86_isa_level+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest1.S <<EOF
+#ifdef __LP64__
+# define P2ALIGN 3
+#else
+# define P2ALIGN 2
+#endif
+	.section ".note.gnu.property", "a"
+	.p2align P2ALIGN
+	.long 1f - 0f		/* name length.  */
+	.long 4f - 1f		/* data length.  */
+	/* NT_GNU_PROPERTY_TYPE_0 */
+	.long 5			/* note type.  */
+0:
+	.asciz "GNU"		/* vendor name.  */
+1:
+	.p2align P2ALIGN
+	/* GNU_PROPERTY_X86_ISA_1_NEEDED */
+	.long 0xc0008002	/* pr_type.  */
+	.long 3f - 2f		/* pr_datasz.  */
+2:
+	.long 0x1
+3:
+	.p2align P2ALIGN
+4:
+EOF
+cat > conftest2.S <<EOF
+#ifdef __LP64__
+# define P2ALIGN 3
+#else
+# define P2ALIGN 2
+#endif
+	.section ".note.gnu.property", "a"
+	.p2align P2ALIGN
+	.long 1f - 0f		/* name length.  */
+	.long 4f - 1f		/* data length.  */
+	/* NT_GNU_PROPERTY_TYPE_0 */
+	.long 5			/* note type.  */
+0:
+	.asciz "GNU"		/* vendor name.  */
+1:
+	.p2align P2ALIGN
+	/* GNU_PROPERTY_X86_ISA_1_NEEDED */
+	.long 0xc0008002	/* pr_type.  */
+	.long 3f - 2f		/* pr_datasz.  */
+2:
+	.long 0x2
+3:
+	.p2align P2ALIGN
+4:
+EOF
+libc_cv_include_x86_isa_level=no
+if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -nostartfiles -nostdlib -r -o conftest conftest1.S conftest2.S'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+  count=`LC_ALL=C $READELF -n conftest | grep NT_GNU_PROPERTY_TYPE_0 | wc -l`
+  if test "$count" = 1; then
+    libc_cv_include_x86_isa_level=yes
+  fi
+fi
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_include_x86_isa_level" >&5
+$as_echo "$libc_cv_include_x86_isa_level" >&6; }
+if test $libc_cv_include_x86_isa_level = yes; then
+  $as_echo "#define INCLUDE_X86_ISA_LEVEL 1" >>confdefs.h
+
+fi
+config_vars="$config_vars
+enable-x86-isa-level = $libc_cv_include_x86_isa_level"
diff --git a/sysdeps/x86/configure.ac b/sysdeps/x86/configure.ac
index 8f3e1191f6..f94088f377 100644
--- a/sysdeps/x86/configure.ac
+++ b/sysdeps/x86/configure.ac
@@ -43,3 +43,69 @@ elif test $enable_cet = permissive; then
   AC_DEFINE(DEFAULT_DL_X86_CET_CONTROL, cet_permissive)
 fi
 LIBC_CONFIG_VAR([enable-cet], [$enable_cet])
+
+# Check if linker supports x86 ISA level.
+AC_CACHE_CHECK([for linker x86 ISA level support],
+	       libc_cv_include_x86_isa_level, [dnl
+cat > conftest1.S <<EOF
+#ifdef __LP64__
+# define P2ALIGN 3
+#else
+# define P2ALIGN 2
+#endif
+	.section ".note.gnu.property", "a"
+	.p2align P2ALIGN
+	.long 1f - 0f		/* name length.  */
+	.long 4f - 1f		/* data length.  */
+	/* NT_GNU_PROPERTY_TYPE_0 */
+	.long 5			/* note type.  */
+0:
+	.asciz "GNU"		/* vendor name.  */
+1:
+	.p2align P2ALIGN
+	/* GNU_PROPERTY_X86_ISA_1_NEEDED */
+	.long 0xc0008002	/* pr_type.  */
+	.long 3f - 2f		/* pr_datasz.  */
+2:
+	.long 0x1
+3:
+	.p2align P2ALIGN
+4:
+EOF
+cat > conftest2.S <<EOF
+#ifdef __LP64__
+# define P2ALIGN 3
+#else
+# define P2ALIGN 2
+#endif
+	.section ".note.gnu.property", "a"
+	.p2align P2ALIGN
+	.long 1f - 0f		/* name length.  */
+	.long 4f - 1f		/* data length.  */
+	/* NT_GNU_PROPERTY_TYPE_0 */
+	.long 5			/* note type.  */
+0:
+	.asciz "GNU"		/* vendor name.  */
+1:
+	.p2align P2ALIGN
+	/* GNU_PROPERTY_X86_ISA_1_NEEDED */
+	.long 0xc0008002	/* pr_type.  */
+	.long 3f - 2f		/* pr_datasz.  */
+2:
+	.long 0x2
+3:
+	.p2align P2ALIGN
+4:
+EOF
+libc_cv_include_x86_isa_level=no
+if AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS -nostartfiles -nostdlib -r -o conftest conftest1.S conftest2.S); then
+  count=`LC_ALL=C $READELF -n conftest | grep NT_GNU_PROPERTY_TYPE_0 | wc -l`
+  if test "$count" = 1; then
+    libc_cv_include_x86_isa_level=yes
+  fi
+fi
+rm -f conftest*])
+if test $libc_cv_include_x86_isa_level = yes; then
+  AC_DEFINE(INCLUDE_X86_ISA_LEVEL)
+fi
+LIBC_CONFIG_VAR([enable-x86-isa-level], [$libc_cv_include_x86_isa_level])
diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c
index 529c586193..e7da682a2e 100644
--- a/sysdeps/x86/cpu-features.c
+++ b/sysdeps/x86/cpu-features.c
@@ -19,6 +19,7 @@
 #include <cpuid.h>
 #include <dl-hwcap.h>
 #include <libc-pointer-arith.h>
+#include <get-isa-level.h>
 #if IS_IN (libc) && !defined SHARED
 # include <assert.h>
 # include <unistd.h>
@@ -290,6 +291,8 @@ update_usable (struct cpu_features *cpu_features)
       CPU_FEATURE_SET_USABLE (cpu_features, KL);
       CPU_FEATURE_SET_USABLE (cpu_features, WIDE_KL);
     }
+
+  cpu_features->isa_1 = get_isa_level (cpu_features);
 }
 
 static void
diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c
index b4910f7634..ed7fd6d10d 100644
--- a/sysdeps/x86/dl-cet.c
+++ b/sysdeps/x86/dl-cet.c
@@ -76,10 +76,12 @@ dl_cet_check (struct link_map *m, const char *program)
 	   */
 	  enable_ibt &= (HAS_CPU_FEATURE (IBT)
 			 && (enable_ibt_type == cet_always_on
-			     || (m->l_cet & lc_ibt) != 0));
+			     || (m->l_x86_feature_1_and
+				 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0));
 	  enable_shstk &= (HAS_CPU_FEATURE (SHSTK)
 			   && (enable_shstk_type == cet_always_on
-			       || (m->l_cet & lc_shstk) != 0));
+			       || (m->l_x86_feature_1_and
+				   & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0));
 	}
 
       /* ld.so is CET-enabled by kernel.  But shared objects may not
@@ -111,7 +113,8 @@ dl_cet_check (struct link_map *m, const char *program)
 	      /* IBT is enabled only if it is enabled in executable as
 		 well as all shared objects.  */
 	      enable_ibt &= (enable_ibt_type == cet_always_on
-			     || (l->l_cet & lc_ibt) != 0);
+			     || (l->l_x86_feature_1_and
+				 & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0);
 	      if (!found_ibt_legacy && enable_ibt != ibt_enabled)
 		{
 		  found_ibt_legacy = true;
@@ -121,7 +124,8 @@ dl_cet_check (struct link_map *m, const char *program)
 	      /* SHSTK is enabled only if it is enabled in executable as
 		 well as all shared objects.  */
 	      enable_shstk &= (enable_shstk_type == cet_always_on
-			       || (l->l_cet & lc_shstk) != 0);
+			       || (l->l_x86_feature_1_and
+				   & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0);
 	      if (enable_shstk != shstk_enabled)
 		{
 		  found_shstk_legacy = true;
diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h
index ce1667dc7a..56bd020b3c 100644
--- a/sysdeps/x86/dl-prop.h
+++ b/sysdeps/x86/dl-prop.h
@@ -19,14 +19,54 @@
 #ifndef _DL_PROP_H
 #define _DL_PROP_H
 
+#include <libintl.h>
+
 extern void _dl_cet_check (struct link_map *, const char *)
     attribute_hidden;
 extern void _dl_cet_open_check (struct link_map *)
     attribute_hidden;
 
+static void
+dl_isa_level_check (struct link_map *m, const char *program)
+{
+  const struct cpu_features *cpu_features = __get_cpu_features ();
+  unsigned int i;
+  struct link_map *l;
+
+  i = m->l_searchlist.r_nlist;
+  while (i-- > 0)
+    {
+      /* Check each shared object to see if ISA level is compatible.  */
+      l = m->l_initfini[i];
+
+      /* Skip ISA level check if functions have been executed.  */
+      if (l->l_init_called)
+	continue;
+
+#ifdef SHARED
+      /* Skip ISA level check for ld.so since ld.so won't run if its ISA
+	 level is higher than CPU.  */
+      if (l == &GL(dl_rtld_map) || l->l_real == &GL(dl_rtld_map))
+	continue;
+#endif
+
+      if ((l->l_x86_isa_1_needed & cpu_features->isa_1)
+	  != l->l_x86_isa_1_needed)
+	{
+	  if (program)
+	    _dl_fatal_printf ("%s: CPU ISA level is lower than required\n",
+			      *l->l_name != '\0' ? l->l_name : program);
+	  else
+	    _dl_signal_error (0, l->l_name, "dlopen",
+			      N_("CPU ISA level is lower than required"));
+	}
+    }
+}
+
 static inline void __attribute__ ((always_inline))
 _rtld_main_check (struct link_map *m, const char *program)
 {
+  dl_isa_level_check (m, program);
 #if CET_ENABLED
   _dl_cet_check (m, program);
 #endif
@@ -35,20 +75,18 @@ _rtld_main_check (struct link_map *m, const char *program)
 static inline void __attribute__ ((always_inline))
 _dl_open_check (struct link_map *m)
 {
+  dl_isa_level_check (m, NULL);
 #if CET_ENABLED
   _dl_cet_open_check (m);
 #endif
 }
 
 static inline void __attribute__ ((unused))
-_dl_process_cet_property_note (struct link_map *l,
-			      const ElfW(Nhdr) *note,
-			      const ElfW(Addr) size,
-			      const ElfW(Addr) align)
+_dl_process_property_note (struct link_map *l, const ElfW(Nhdr) *note,
+			   const ElfW(Addr) size, const ElfW(Addr) align)
 {
-#if CET_ENABLED
   /* Skip if we have seen a NT_GNU_PROPERTY_TYPE_0 note before.  */
-  if (l->l_cet != lc_unknown)
+  if (l->l_property != lc_property_unknown)
     return;
 
   /* The NT_GNU_PROPERTY_TYPE_0 note must be aliged to 4 bytes in
@@ -59,7 +97,8 @@ _dl_process_cet_property_note (struct link_map *l,
 
   const ElfW(Addr) start = (ElfW(Addr)) note;
 
-  unsigned int feature_1 = 0;
+  unsigned int feature_1_and = 0;
+  unsigned int isa_1_needed = 0;
   unsigned int last_type = 0;
 
   while ((ElfW(Addr)) (note + 1) - start < size)
@@ -71,11 +110,11 @@ _dl_process_cet_property_note (struct link_map *l,
 	{
 	  /* Stop if we see more than one GNU property note which may
 	     be generated by the older linker.  */
-	  if (l->l_cet != lc_unknown)
+	  if (l->l_property != lc_property_unknown)
 	    return;
 
-	  /* Check CET status now.  */
-	  l->l_cet = lc_none;
+	  /* Check CET status and ISA levels now.  */
+	  l->l_property = lc_property_none;
 
 	  /* Check for invalid property.  */
 	  if (note->n_descsz < 8
@@ -101,26 +140,37 @@ _dl_process_cet_property_note (struct link_map *l,
 
 	      last_type = type;
 
-	      if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
+	      if (type == GNU_PROPERTY_X86_FEATURE_1_AND
+		  || type == GNU_PROPERTY_X86_ISA_1_NEEDED)
 		{
-		  /* The size of GNU_PROPERTY_X86_FEATURE_1_AND is 4
-		     bytes.  When seeing GNU_PROPERTY_X86_FEATURE_1_AND,
-		     we stop the search regardless if its size is correct
-		     or not.  There is no point to continue if this note
-		     is ill-formed.  */
+		  /* The sizes of types which we are searching for are
+		     4 bytes.  There is no point to continue if this
+		     note is ill-formed.  */
 		  if (datasz != 4)
 		    return;
 
-		  feature_1 = *(unsigned int *) ptr;
-
-		  /* Keep searching for the next GNU property note
-		     generated by the older linker.  */
-		  break;
+		  /* NB: Stop the scan only after seeing all types which
+		     we are searching for.  */
+		  _Static_assert ((GNU_PROPERTY_X86_ISA_1_NEEDED >
+				   GNU_PROPERTY_X86_FEATURE_1_AND),
+				  "GNU_PROPERTY_X86_ISA_1_NEEDED > "
+				  "GNU_PROPERTY_X86_FEATURE_1_AND");
+		  if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
+		    feature_1_and = *(unsigned int *) ptr;
+		  else
+		    {
+		      isa_1_needed = *(unsigned int *) ptr;
+
+		      /* Keep searching for the next GNU property note
+			 generated by the older linker.  */
+		      break;
+		    }
 		}
-	      else if (type > GNU_PROPERTY_X86_FEATURE_1_AND)
+	      else if (type > GNU_PROPERTY_X86_ISA_1_NEEDED)
 		{
-		  /* Stop since property type is in ascending order.  */
-		  return;
+		  /* Stop the scan since property type is in ascending
+		     order.  */
+		  break;
 		}
 
 	      /* Check the next property item.  */
@@ -137,18 +187,21 @@ _dl_process_cet_property_note (struct link_map *l,
     }
 
   /* We get here only if there is one or no GNU property note.  */
-  if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_IBT))
-    l->l_cet |= lc_ibt;
-  if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_SHSTK))
-    l->l_cet |= lc_shstk;
-#endif
+  if (isa_1_needed != 0 || feature_1_and != 0)
+    {
+      l->l_property = lc_property_valid;
+      l->l_x86_isa_1_needed = isa_1_needed;
+      l->l_x86_feature_1_and = feature_1_and;
+    }
+  else
+    l->l_property = lc_property_none;
 }
 
 static inline void __attribute__ ((unused))
 _dl_process_pt_note (struct link_map *l, int fd, const ElfW(Phdr) *ph)
 {
   const ElfW(Nhdr) *note = (const void *) (ph->p_vaddr + l->l_addr);
-  _dl_process_cet_property_note (l, note, ph->p_memsz, ph->p_align);
+  _dl_process_property_note (l, note, ph->p_memsz, ph->p_align);
 }
 
 static inline int __attribute__ ((always_inline))
diff --git a/sysdeps/x86/get-isa-level.h b/sysdeps/x86/get-isa-level.h
new file mode 100644
index 0000000000..a86e1e8941
--- /dev/null
+++ b/sysdeps/x86/get-isa-level.h
@@ -0,0 +1,66 @@
+/* Get x86 ISA level.
+   This file is part of the GNU C Library.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sys/platform/x86.h>
+
+/* Get GNU_PROPERTY_X86_ISA_1_BASELINE and GNU_PROPERTY_X86_ISA_1_V[234]
+   ISA level.  */
+
+static unsigned int
+get_isa_level (const struct cpu_features *cpu_features)
+{
+  unsigned int isa_level = 0;
+
+  if (CPU_FEATURE_USABLE_P (cpu_features, CMOV)
+      && CPU_FEATURE_USABLE_P (cpu_features, CX8)
+      && CPU_FEATURE_CPU_P (cpu_features, FPU)
+      && CPU_FEATURE_USABLE_P (cpu_features, FXSR)
+      && CPU_FEATURE_USABLE_P (cpu_features, MMX)
+      && CPU_FEATURE_USABLE_P (cpu_features, SSE)
+      && CPU_FEATURE_USABLE_P (cpu_features, SSE2))
+    {
+      isa_level = GNU_PROPERTY_X86_ISA_1_BASELINE;
+      if (CPU_FEATURE_USABLE_P (cpu_features, CMPXCHG16B)
+	  && CPU_FEATURE_USABLE_P (cpu_features, LAHF64_SAHF64)
+	  && CPU_FEATURE_USABLE_P (cpu_features, POPCNT)
+	  && CPU_FEATURE_USABLE_P (cpu_features, SSE3)
+	  && CPU_FEATURE_USABLE_P (cpu_features, SSSE3)
+	  && CPU_FEATURE_USABLE_P (cpu_features, SSE4_1)
+	  && CPU_FEATURE_USABLE_P (cpu_features, SSE4_2))
+	{
+	  isa_level |= GNU_PROPERTY_X86_ISA_1_V2;
+	  if (CPU_FEATURE_USABLE_P (cpu_features, AVX)
+	      && CPU_FEATURE_USABLE_P (cpu_features, AVX2)
+	      && CPU_FEATURE_USABLE_P (cpu_features, F16C)
+	      && CPU_FEATURE_USABLE_P (cpu_features, FMA)
+	      && CPU_FEATURE_USABLE_P (cpu_features, LZCNT)
+	      && CPU_FEATURE_USABLE_P (cpu_features, MOVBE))
+	    {
+	      isa_level |= GNU_PROPERTY_X86_ISA_1_V3;
+	      if (CPU_FEATURE_USABLE_P (cpu_features, AVX512F)
+		  && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)
+		  && CPU_FEATURE_USABLE_P (cpu_features, AVX512CD)
+		  && CPU_FEATURE_USABLE_P (cpu_features, AVX512DQ)
+		  && CPU_FEATURE_USABLE_P (cpu_features, AVX512VL))
+		isa_level |= GNU_PROPERTY_X86_ISA_1_V4;
+	    }
+	}
+    }
+
+  return isa_level;
+}
diff --git a/sysdeps/x86/include/cpu-features.h b/sysdeps/x86/include/cpu-features.h
index ffe300c931..99e7ee08cf 100644
--- a/sysdeps/x86/include/cpu-features.h
+++ b/sysdeps/x86/include/cpu-features.h
@@ -123,6 +123,8 @@ struct cpu_features
   struct cpu_features_basic basic;
   struct cpuid_features features[COMMON_CPUID_INDEX_MAX];
   unsigned int preferred[PREFERRED_FEATURE_INDEX_MAX];
+  /* X86 micro-architecture ISA levels.  */
+  unsigned int isa_1;
   /* The state size for XSAVEC or XSAVE.  The type must be unsigned long
      int so that we use
 
diff --git a/sysdeps/x86/isa-level.c b/sysdeps/x86/isa-level.c
new file mode 100644
index 0000000000..aaf524cb56
--- /dev/null
+++ b/sysdeps/x86/isa-level.c
@@ -0,0 +1,97 @@
+/* ELF program property for x86 ISA level.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file.  (The Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <elf.h>
+
+/* ELF program property for x86 ISA level.  */
+#ifdef INCLUDE_X86_ISA_LEVEL
+# if defined __x86_64__ || defined __FXSR__ || !defined _SOFT_FLOAT \
+     || defined  __MMX__ || defined __SSE__ || defined __SSE2__
+#  define ISA_BASELINE	GNU_PROPERTY_X86_ISA_1_BASELINE
+# else
+#  define ISA_BASELINE	0
+# endif
+
+# if defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 \
+     || (defined __x86_64__ && defined __LAHF_SAHF__) \
+     || defined __POPCNT__ || defined __SSE3__ \
+     || defined __SSSE3__ || defined __SSE4_1__ || defined __SSE4_2__
+#  define ISA_V2	GNU_PROPERTY_X86_ISA_1_V2
+# else
+#  define ISA_V2	0
+# endif
+
+# if defined __AVX__ || defined __AVX2__ || defined __F16C__ \
+     || defined __FMA__ || defined __LZCNT__ || defined __MOVBE__ \
+     || defined __XSAVE__
+#  define ISA_V3	GNU_PROPERTY_X86_ISA_1_V3
+# else
+#  define ISA_V3	0
+# endif
+
+# if defined __AVX512F__ || defined __AVX512BW__ || defined __AVX512CD__ \
+     || defined __AVX512DQ__ || defined __AVX512VL__
+#  define ISA_V4	GNU_PROPERTY_X86_ISA_1_V4
+# else
+#  define ISA_V4	0
+# endif
+
+# ifndef ISA_LEVEL
+#  define ISA_LEVEL (ISA_BASELINE | ISA_V2 | ISA_V3 | ISA_V4)
+# endif
+
+# if ISA_LEVEL
+#  ifdef __LP64__
+#   define PROPERTY_ALIGN 3
+#  else
+#   define PROPERTY_ALIGN 2
+#  endif
+
+#  define note_stringify(arg) note_stringify_1(arg)
+#  define note_stringify_1(arg) #arg
+
+asm(".pushsection \".note.gnu.property\",\"a\",@note\n"
+"	.p2align " note_stringify (PROPERTY_ALIGN)
+	/* name length.  */
+"\n	.long 1f - 0f\n"
+	/* data length.  */
+"	.long 4f - 1f\n"
+	/* note type: NT_GNU_PROPERTY_TYPE_0.  */
+"	.long " note_stringify (NT_GNU_PROPERTY_TYPE_0)
+	/* vendor name.  */
+"\n0:	.asciz \"GNU\"\n"
+"1:	.p2align " note_stringify (PROPERTY_ALIGN)
+	/* pr_type: GNU_PROPERTY_X86_ISA_1_NEEDED.  */
+"\n	.long " note_stringify (GNU_PROPERTY_X86_ISA_1_NEEDED)
+	/* pr_datasz.  */
+"\n	.long 3f - 2f\n"
+	/* GNU_PROPERTY_X86_ISA_1_V[234].  */
+"2:\n	 .long " note_stringify (ISA_LEVEL)
+"\n3:\n	.p2align " note_stringify (PROPERTY_ALIGN)
+"\n4:\n .popsection");
+# endif /* ISA_LEVEL */
+#endif /* INCLUDE_X86_ISA_LEVEL */
diff --git a/sysdeps/x86/link_map.h b/sysdeps/x86/link_map.h
index 8074739b47..4c46a25f83 100644
--- a/sysdeps/x86/link_map.h
+++ b/sysdeps/x86/link_map.h
@@ -16,12 +16,16 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-/* If this object is enabled with CET.  */
+/* if this object has GNU property.  */
 enum
   {
-    lc_unknown = 0,			 /* Unknown CET status.  */
-    lc_none = 1 << 0,			 /* Not enabled with CET.  */
-    lc_ibt = 1 << 1,			 /* Enabled with IBT.  */
-    lc_shstk = 1 << 2,			 /* Enabled with STSHK.  */
-    lc_ibt_and_shstk = lc_ibt | lc_shstk /* Enabled with both.  */
-  } l_cet:3;
+    lc_property_unknown = 0,		/* Unknown property status.  */
+    lc_property_none = 1 << 0,		/* No property.  */
+    lc_property_valid = 1 << 1		/* Has valid property.  */
+  } l_property:2;
+
+/* GNU_PROPERTY_X86_FEATURE_1_AND of this object.  */
+unsigned int l_x86_feature_1_and;
+
+/* GNU_PROPERTY_X86_ISA_1_NEEDED of this object.  */
+unsigned int l_x86_isa_1_needed;
diff --git a/sysdeps/x86/tst-isa-level-1.c b/sysdeps/x86/tst-isa-level-1.c
new file mode 100644
index 0000000000..dc31d71ad2
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-1.c
@@ -0,0 +1,104 @@
+/* Check ISA level on dlopened shared object.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <elf.h>
+#include <get-isa-level.h>
+#include <support/xdlfcn.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+static void
+do_test_1 (const char *modname, bool fail)
+{
+  int (*fp) (void);
+  void *h;
+
+  h = dlopen (modname, RTLD_LAZY);
+  if (h == NULL)
+    {
+      const char *err = dlerror ();
+      if (fail)
+	{
+	  if (strstr (err, "CPU ISA level is lower than required") == NULL)
+	    FAIL_EXIT1 ("incorrect dlopen '%s' error: %s\n", modname, err);
+
+	  return;
+	}
+
+      FAIL_EXIT1 ("cannot open '%s': %s\n", modname, err);
+    }
+
+  if (fail)
+    FAIL_EXIT1 ("dlopen '%s' should have failed\n", modname);
+
+  fp = xdlsym (h, "test");
+
+  if (fp () != 0)
+    FAIL_EXIT1 ("test () != 0\n");
+
+  dlclose (h);
+}
+
+static int
+do_test (void)
+{
+  const struct cpu_features *cpu_features
+    = __x86_get_cpu_features (COMMON_CPUID_INDEX_MAX);
+  unsigned int isa_level = get_isa_level (cpu_features);
+  bool has_isa_baseline = ((isa_level & GNU_PROPERTY_X86_ISA_1_BASELINE)
+			   == GNU_PROPERTY_X86_ISA_1_BASELINE);
+  bool has_isa_v2 = ((isa_level & GNU_PROPERTY_X86_ISA_1_V2)
+			   == GNU_PROPERTY_X86_ISA_1_V2);
+  bool has_isa_v3 = ((isa_level & GNU_PROPERTY_X86_ISA_1_V3)
+			   == GNU_PROPERTY_X86_ISA_1_V3);
+  bool has_isa_v4 = ((isa_level & GNU_PROPERTY_X86_ISA_1_V4)
+			   == GNU_PROPERTY_X86_ISA_1_V4);
+
+  if (!has_isa_baseline)
+    {
+      do_test_1 ("tst-isa-level-mod-1-baseline.so", true);
+      return EXIT_SUCCESS;
+    }
+
+  do_test_1 ("tst-isa-level-mod-1-baseline.so", false);
+
+  /* Skip on x86-64-v4 platforms since dlopen v4 module always works.  */
+  if (has_isa_v4)
+    return EXIT_UNSUPPORTED;
+
+  do_test_1 ("tst-isa-level-mod-1-v4.so", true);
+
+  /* Skip on x86-64-v3 platforms since dlopen v3 module always works.  */
+  if (has_isa_v3)
+    return EXIT_SUCCESS;
+
+  do_test_1 ("tst-isa-level-mod-1-v3.so", true);
+
+  /* Skip on x86-64-v2 platforms since dlopen v2 module always works.  */
+  if (has_isa_v2)
+    return EXIT_SUCCESS;
+
+  do_test_1 ("tst-isa-level-mod-1-v2.so", true);
+
+  return EXIT_SUCCESS;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/x86/tst-isa-level-mod-1-baseline.c b/sysdeps/x86/tst-isa-level-mod-1-baseline.c
new file mode 100644
index 0000000000..d6fe0685a4
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-mod-1-baseline.c
@@ -0,0 +1 @@
+#include "tst-isa-level-mod-1.c"
diff --git a/sysdeps/x86/tst-isa-level-mod-1-v2.c b/sysdeps/x86/tst-isa-level-mod-1-v2.c
new file mode 100644
index 0000000000..d6fe0685a4
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-mod-1-v2.c
@@ -0,0 +1 @@
+#include "tst-isa-level-mod-1.c"
diff --git a/sysdeps/x86/tst-isa-level-mod-1-v3.c b/sysdeps/x86/tst-isa-level-mod-1-v3.c
new file mode 100644
index 0000000000..d6fe0685a4
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-mod-1-v3.c
@@ -0,0 +1 @@
+#include "tst-isa-level-mod-1.c"
diff --git a/sysdeps/x86/tst-isa-level-mod-1-v4.c b/sysdeps/x86/tst-isa-level-mod-1-v4.c
new file mode 100644
index 0000000000..d6fe0685a4
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-mod-1-v4.c
@@ -0,0 +1 @@
+#include "tst-isa-level-mod-1.c"
diff --git a/sysdeps/x86/tst-isa-level-mod-1.c b/sysdeps/x86/tst-isa-level-mod-1.c
new file mode 100644
index 0000000000..40a2e1d99f
--- /dev/null
+++ b/sysdeps/x86/tst-isa-level-mod-1.c
@@ -0,0 +1,25 @@
+/* Check ISA level on dlopened shared object.
+   Copyright (C) 2019-2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <isa-level.c>
+
+int
+test (void)
+{
+  return 0;
+}
diff --git a/sysdeps/x86_64/dl-hwcaps-subdirs.c b/sysdeps/x86_64/dl-hwcaps-subdirs.c
index 0aaa635a00..e030534f1b 100644
--- a/sysdeps/x86_64/dl-hwcaps-subdirs.c
+++ b/sysdeps/x86_64/dl-hwcaps-subdirs.c
@@ -18,6 +18,7 @@
 
 #include <dl-hwcaps.h>
 #include <cpu-features.h>
+#include <get-isa-level.h>
 
 const char _dl_hwcaps_subdirs[] = "x86-64-v4:x86-64-v3:x86-64-v2";
 enum { subdirs_count = 3 }; /* Number of components in _dl_hwcaps_subdirs.  */
@@ -25,40 +26,25 @@ enum { subdirs_count = 3 }; /* Number of components in _dl_hwcaps_subdirs.  */
 uint32_t
 _dl_hwcaps_subdirs_active (void)
 {
+  const struct cpu_features *cpu_features
+    = __x86_get_cpu_features (COMMON_CPUID_INDEX_MAX);
+  unsigned int isa_level = get_isa_level (cpu_features);
   int active = 0;
 
   /* Test in reverse preference order.  */
 
   /* x86-64-v2.  */
-  if (!(CPU_FEATURE_USABLE (CMPXCHG16B)
-        && CPU_FEATURE_USABLE (LAHF64_SAHF64)
-        && CPU_FEATURE_USABLE (POPCNT)
-        && CPU_FEATURE_USABLE (SSE3)
-        && CPU_FEATURE_USABLE (SSE4_1)
-        && CPU_FEATURE_USABLE (SSE4_2)
-        && CPU_FEATURE_USABLE (SSSE3)))
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V2))
     return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
   ++active;
 
   /* x86-64-v3.  */
-  if (!(CPU_FEATURE_USABLE (AVX)
-        && CPU_FEATURE_USABLE (AVX2)
-        && CPU_FEATURE_USABLE (BMI1)
-        && CPU_FEATURE_USABLE (BMI2)
-        && CPU_FEATURE_USABLE (F16C)
-        && CPU_FEATURE_USABLE (FMA)
-        && CPU_FEATURE_USABLE (LZCNT)
-        && CPU_FEATURE_USABLE (MOVBE)
-        && CPU_FEATURE_USABLE (OSXSAVE)))
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V3))
     return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
   ++active;
 
- /* x86-64-v4.  */
-  if (!(CPU_FEATURE_USABLE (AVX512F)
-        && CPU_FEATURE_USABLE (AVX512BW)
-        && CPU_FEATURE_USABLE (AVX512CD)
-        && CPU_FEATURE_USABLE (AVX512DQ)
-        && CPU_FEATURE_USABLE (AVX512VL)))
+  /* x86-64-v4.  */
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V4))
     return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
   ++active;
 
diff --git a/sysdeps/x86_64/tst-glibc-hwcaps.c b/sysdeps/x86_64/tst-glibc-hwcaps.c
index 8bdcb91db0..37b61d3c84 100644
--- a/sysdeps/x86_64/tst-glibc-hwcaps.c
+++ b/sysdeps/x86_64/tst-glibc-hwcaps.c
@@ -19,7 +19,8 @@
 #include <stdio.h>
 #include <support/check.h>
 #include <sys/param.h>
-#include <sys/platform/x86.h>
+#include <elf.h>
+#include <get-isa-level.h>
 
 extern int marker2 (void);
 extern int marker3 (void);
@@ -31,35 +32,15 @@ compute_level (void)
 {
   const struct cpu_features *cpu_features
     = __x86_get_cpu_features (COMMON_CPUID_INDEX_MAX);
-
- if (!(CPU_FEATURE_USABLE_P (cpu_features, CMPXCHG16B)
-       && CPU_FEATURE_USABLE_P (cpu_features, LAHF64_SAHF64)
-       && CPU_FEATURE_USABLE_P (cpu_features, POPCNT)
-       && CPU_FEATURE_USABLE_P (cpu_features, MMX)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSE)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSE2)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSE3)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSSE3)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSE4_1)
-       && CPU_FEATURE_USABLE_P (cpu_features, SSE4_2)))
-   return 1;
- if (!(CPU_FEATURE_USABLE_P (cpu_features, AVX)
-       && CPU_FEATURE_USABLE_P (cpu_features, AVX2)
-       && CPU_FEATURE_USABLE_P (cpu_features, BMI1)
-       && CPU_FEATURE_USABLE_P (cpu_features, BMI2)
-       && CPU_FEATURE_USABLE_P (cpu_features, F16C)
-       && CPU_FEATURE_USABLE_P (cpu_features, FMA)
-       && CPU_FEATURE_USABLE_P (cpu_features, LZCNT)
-       && CPU_FEATURE_USABLE_P (cpu_features, MOVBE)
-       && CPU_FEATURE_USABLE_P (cpu_features, OSXSAVE)))
-   return 2;
- if (!(CPU_FEATURE_USABLE_P (cpu_features, AVX512F)
-       && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)
-       && CPU_FEATURE_USABLE_P (cpu_features, AVX512CD)
-       && CPU_FEATURE_USABLE_P (cpu_features, AVX512DQ)
-       && CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)))
-   return 3;
- return 4;
+  unsigned int isa_level = get_isa_level (cpu_features);
+
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V2))
+    return 1;
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V3))
+    return 2;
+  if (!(isa_level & GNU_PROPERTY_X86_ISA_1_V4))
+    return 3;
+  return 4;
 }
 
 static int
-- 
2.29.2

