diff mbox series

[v2] x86: Require full ISA support for x86-64 level marker [BZ #27318]

Message ID CAMe9rOpuQ_87_WCZTkje-odxvkpOez9DTq+YX1Xq5q3qz8omMQ@mail.gmail.com
State Superseded
Headers show
Series [v2] x86: Require full ISA support for x86-64 level marker [BZ #27318] | expand

Commit Message

H.J. Lu Feb. 3, 2021, 3:09 p.m. UTC
On Wed, Feb 3, 2021 at 6:14 AM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Tue, 2 Feb 2021, H.J. Lu wrote:
>
> > On Tue, Feb 2, 2021 at 3:11 PM Joseph Myers <joseph@codesourcery.com> wrote:
> > >
> > > On Tue, 2 Feb 2021, H.J. Lu via Libc-alpha wrote:
> > >
> > > > Instead, we should require full ISA support for x86-64 level marker to
> > > > detect such case:
> > > >
> > > > In file included from ../sysdeps/x86/abi-note.c:28:
> > > > ../sysdeps/x86/isa-level.c:62:5: error: #error "Invalid ISAs for x86-64 ISA level v3"
> > > >    62 | #   error "Invalid ISAs for x86-64 ISA level v3"
> > > >       |     ^~~~~
> > >
> > > When does this error occur (what conditions for compilation /
> > > configuration of glibc)?
> >
> > It happens at compile time when glibc is built with "-march=sandybridge".
>
> That's bad.  Since glibc supports execution on Sandy Bridge processors,
> compilation with -march=sandybridge should (a) work, with no special
> configure options needed and (b) produce a glibc that works on Sandy
> Bridge, with no special configure options needed.  I understand that bug
> 27318 is reporting that (b) fails at present.  We need to fix (b) without
> breaking (a).
>
> This is not specific at all to x86_64.  It applies to all architectures
> and processors supported by glibc: compiling with a compiler that defaults
> to any such processor should just work, regardless of how that processor
> relates to particular ISA levels in the glibc-hwcaps machinery.
>
> > We can add a configure option, --disable-isa-level, to unset
> > INCLUDE_X86_ISA_LEVEL.  The resulting libc.so doesn't have a marker
> > and won't run on all machines.
>
> No special configure option should be needed for (a) and (b) to hold.
> They are general principles for any processor supported by glibc, for any
> architecture.

Here is the updated patch to disable x86-64 level marker for
-march=sandybridge which enables ISAs between v2 and v3.

Comments

Florian Weimer Feb. 4, 2021, 10:38 a.m. UTC | #1
* H. J. Lu via Libc-alpha:

> Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
> marker is set on libc.so.  We couldn't set the needed ISA marker to v2
> since this libc won't run on all v2 machines.  Technically, the v3 marker
> is correct.  But the resulting libc.so won't run on Sandy Brigde, which
> is a v2 machine, even when libc is compiled with -march=sandybridge:

The v3 marker is no better than the v2 marker because it still does not
cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
much all model-based -march= options have this effect.

What's the intended effect of this change?  Only require x86-64-v3 if
all x86-64-v3 features are implied by the -march= setting?

Thanks,
Florian
H.J. Lu Feb. 4, 2021, 1:22 p.m. UTC | #2
On Thu, Feb 4, 2021 at 2:38 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu via Libc-alpha:
>
> > Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
> > marker is set on libc.so.  We couldn't set the needed ISA marker to v2
> > since this libc won't run on all v2 machines.  Technically, the v3 marker
> > is correct.  But the resulting libc.so won't run on Sandy Brigde, which
> > is a v2 machine, even when libc is compiled with -march=sandybridge:
>
> The v3 marker is no better than the v2 marker because it still does not
> cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
> much all model-based -march= options have this effect.
>
> What's the intended effect of this change?  Only require x86-64-v3 if
> all x86-64-v3 features are implied by the -march= setting?
>

Yes.
Andreas K. Huettel Feb. 4, 2021, 10:55 p.m. UTC | #3
Am Donnerstag, 4. Februar 2021, 15:22:14 EET schrieb H.J. Lu via Libc-alpha:
> On Thu, Feb 4, 2021 at 2:38 AM Florian Weimer <fweimer@redhat.com> wrote:
> > * H. J. Lu via Libc-alpha:
> > > Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
> > > marker is set on libc.so.  We couldn't set the needed ISA marker to v2
> > > since this libc won't run on all v2 machines.  Technically, the v3
> > > marker
> > > is correct.  But the resulting libc.so won't run on Sandy Brigde, which
> > 
> > > is a v2 machine, even when libc is compiled with -march=sandybridge:
> > The v3 marker is no better than the v2 marker because it still does not
> > cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
> > much all model-based -march= options have this effect.
> > 
> > What's the intended effect of this change?  Only require x86-64-v3 if
> > all x86-64-v3 features are implied by the -march= setting?
> 
> Yes.

Instead of failing the build with #error, wouldn't it make more sense to 
disable ISA markers in this case?

Also, is Sandy Bridge the only case where this happens? Given the number of 
different -march arguments (plus -march=native and single feature flags), it 
seems unlikely to me that this can be mapped to a scale as simple as  1,2,3...
H.J. Lu Feb. 4, 2021, 11:09 p.m. UTC | #4
On Thu, Feb 4, 2021 at 2:55 PM Andreas K. Hüttel <dilfridge@gentoo.org> wrote:
>
> Am Donnerstag, 4. Februar 2021, 15:22:14 EET schrieb H.J. Lu via Libc-alpha:
> > On Thu, Feb 4, 2021 at 2:38 AM Florian Weimer <fweimer@redhat.com> wrote:
> > > * H. J. Lu via Libc-alpha:
> > > > Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
> > > > marker is set on libc.so.  We couldn't set the needed ISA marker to v2
> > > > since this libc won't run on all v2 machines.  Technically, the v3
> > > > marker
> > > > is correct.  But the resulting libc.so won't run on Sandy Brigde, which
> > >
> > > > is a v2 machine, even when libc is compiled with -march=sandybridge:
> > > The v3 marker is no better than the v2 marker because it still does not
> > > cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
> > > much all model-based -march= options have this effect.
> > >
> > > What's the intended effect of this change?  Only require x86-64-v3 if
> > > all x86-64-v3 features are implied by the -march= setting?
> >
> > Yes.
>
> Instead of failing the build with #error, wouldn't it make more sense to
> disable ISA markers in this case?

It is exactly what this patch does.

> Also, is Sandy Bridge the only case where this happens? Given the number of
> different -march arguments (plus -march=native and single feature flags), it
> seems unlikely to me that this can be mapped to a scale as simple as  1,2,3...

It should work for all cases.

> --
> Andreas K. Hüttel
> dilfridge@gentoo.org
> Gentoo Linux developer
> (council, qa, toolchain, base-system, perl, libreoffice)
Florian Weimer Feb. 7, 2021, 10:27 a.m. UTC | #5
* H. J. Lu:

> On Thu, Feb 4, 2021 at 2:55 PM Andreas K. Hüttel <dilfridge@gentoo.org> wrote:
>>
>> Am Donnerstag, 4. Februar 2021, 15:22:14 EET schrieb H.J. Lu via Libc-alpha:
>> > On Thu, Feb 4, 2021 at 2:38 AM Florian Weimer <fweimer@redhat.com> wrote:
>> > > * H. J. Lu via Libc-alpha:
>> > > > Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
>> > > > marker is set on libc.so.  We couldn't set the needed ISA marker to v2
>> > > > since this libc won't run on all v2 machines.  Technically, the v3
>> > > > marker
>> > > > is correct.  But the resulting libc.so won't run on Sandy Brigde, which
>> > >
>> > > > is a v2 machine, even when libc is compiled with -march=sandybridge:
>> > > The v3 marker is no better than the v2 marker because it still does not
>> > > cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
>> > > much all model-based -march= options have this effect.
>> > >
>> > > What's the intended effect of this change?  Only require x86-64-v3 if
>> > > all x86-64-v3 features are implied by the -march= setting?
>> >
>> > Yes.
>>
>> Instead of failing the build with #error, wouldn't it make more sense to
>> disable ISA markers in this case?
>
> It is exactly what this patch does.

Is it possible to make this a little bit clearer in the preprocessor
conditionals?

I assume that this:

# 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__
#  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__
#   error "Invalid ISAs for x86-64 ISA level v2"
#  endif
#  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__
# if !defined __AVX__ || !defined __AVX2__ || !defined __F16C__ \
     || !defined __FMA__ || !defined __LZCNT__ || !defined __MOVBE__
#   error "Invalid ISAs for x86-64 ISA level v3"
#  endif
#  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__
#  if !defined __AVX512F__ || !defined __AVX512BW__ \
      || !defined __AVX512CD__ || !defined __AVX512DQ__ \
      || !defined __AVX512VL__
#   error "Invalid ISAs for x86-64 ISA level v4"
#  endif
#  define ISA_V4        GNU_PROPERTY_X86_ISA_1_V4
# else
#  define ISA_V4        0
# endif


Could be written as:

# if TEMP_ISA_LEVEL == 1 && 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__
#  undef TEMP_ISA_LEVEL
#  define  TEMP_ISA_LEVEL 2
# endif

# if TEMP_ISA_LEVEL == 2 \
     && defined __AVX__ && defined __AVX2__ && defined __F16C__ \
     && defined __FMA__ && defined __LZCNT__ && defined __MOVBE__ \
     && defined __XSAVE__
#  undef TEMP_ISA_LEVEL
#  define  TEMP_ISA_LEVEL 3
# endif

# if TEMP_ISA_LEVEL == 3 \
     && defined __AVX512F__ && defined __AVX512BW__ && defined __AVX512CD__ \
     && defined __AVX512DQ__ && defined __AVX512VL__
#  undef TEMP_ISA_LEVEL
#  define  TEMP_ISA_LEVEL 4
# endif

So TEMP_ISA_LEVEL is moved higher and higher as long as there is
support.

I'm not sure what the point of the existing construct (|| followed by ! ||)
is.

Are ISA levels really defined for 32-bit?

Thanks,
Florian
H.J. Lu Feb. 7, 2021, 3:05 p.m. UTC | #6
On Sun, Feb 7, 2021 at 2:27 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu:
>
> > On Thu, Feb 4, 2021 at 2:55 PM Andreas K. Hüttel <dilfridge@gentoo.org> wrote:
> >>
> >> Am Donnerstag, 4. Februar 2021, 15:22:14 EET schrieb H.J. Lu via Libc-alpha:
> >> > On Thu, Feb 4, 2021 at 2:38 AM Florian Weimer <fweimer@redhat.com> wrote:
> >> > > * H. J. Lu via Libc-alpha:
> >> > > > Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
> >> > > > marker is set on libc.so.  We couldn't set the needed ISA marker to v2
> >> > > > since this libc won't run on all v2 machines.  Technically, the v3
> >> > > > marker
> >> > > > is correct.  But the resulting libc.so won't run on Sandy Brigde, which
> >> > >
> >> > > > is a v2 machine, even when libc is compiled with -march=sandybridge:
> >> > > The v3 marker is no better than the v2 marker because it still does not
> >> > > cover all Sandy Bridge features implied by -march=sandybridge.  Pretty
> >> > > much all model-based -march= options have this effect.
> >> > >
> >> > > What's the intended effect of this change?  Only require x86-64-v3 if
> >> > > all x86-64-v3 features are implied by the -march= setting?
> >> >
> >> > Yes.
> >>
> >> Instead of failing the build with #error, wouldn't it make more sense to
> >> disable ISA markers in this case?
> >
> > It is exactly what this patch does.
>
> Is it possible to make this a little bit clearer in the preprocessor
> conditionals?
>
> I assume that this:
>
> # 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__
> #  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__
> #   error "Invalid ISAs for x86-64 ISA level v2"
> #  endif
> #  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__
> # if !defined __AVX__ || !defined __AVX2__ || !defined __F16C__ \
>      || !defined __FMA__ || !defined __LZCNT__ || !defined __MOVBE__
> #   error "Invalid ISAs for x86-64 ISA level v3"
> #  endif
> #  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__
> #  if !defined __AVX512F__ || !defined __AVX512BW__ \
>       || !defined __AVX512CD__ || !defined __AVX512DQ__ \
>       || !defined __AVX512VL__
> #   error "Invalid ISAs for x86-64 ISA level v4"
> #  endif
> #  define ISA_V4        GNU_PROPERTY_X86_ISA_1_V4
> # else
> #  define ISA_V4        0
> # endif
>
>
> Could be written as:
>
> # if TEMP_ISA_LEVEL == 1 && 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__
> #  undef TEMP_ISA_LEVEL
> #  define  TEMP_ISA_LEVEL 2
> # endif
>
> # if TEMP_ISA_LEVEL == 2 \
>      && defined __AVX__ && defined __AVX2__ && defined __F16C__ \
>      && defined __FMA__ && defined __LZCNT__ && defined __MOVBE__ \
>      && defined __XSAVE__
> #  undef TEMP_ISA_LEVEL
> #  define  TEMP_ISA_LEVEL 3
> # endif
>
> # if TEMP_ISA_LEVEL == 3 \
>      && defined __AVX512F__ && defined __AVX512BW__ && defined __AVX512CD__ \
>      && defined __AVX512DQ__ && defined __AVX512VL__
> #  undef TEMP_ISA_LEVEL
> #  define  TEMP_ISA_LEVEL 4
> # endif
>
> So TEMP_ISA_LEVEL is moved higher and higher as long as there is
> support.
>
> I'm not sure what the point of the existing construct (|| followed by ! ||)
> is.

It is used to check if INCLUDE_X86_ISA_LEVEL should be defined in
config.h.   INCLUDE_X86_ISA_LEVEL is defined only if GCC enables
the full ISAs of a level without any ISAs of the higher ISA levels.   Since
I want to support GCC older than GCC 11,  || is used to detect any
ISAs in a level and !|| is used to check if any ISAs are missing.

> Are ISA levels really defined for 32-bit?

32-bit can just use the same x86-64 ISA level.
Nix Feb. 8, 2021, 1:35 p.m. UTC | #7
On 4 Feb 2021, Andreas K. Hüttel via Libc-alpha outgrape:
> Also, is Sandy Bridge the only case where this happens?

Definitely not. Thank goodness I spotted this thread: I just nearly
wrecked my (32-bit) AMD Geode firewall (built with -march=geode)
installing 2.33, because it is showing up with this totally wrong
ABI-needed stamp:

Displaying notes found in: .note.gnu.property
  Owner                Data size 	Description
  GNU                  0x0000000c	NT_GNU_PROPERTY_TYPE_0
      Properties: x86 ISA needed: x86-64-v2

merely because it is built on a Broadwell CPU (Geodes are a bit too slow
to build glibc these days, and also this Geode has no compiler
installed). (My GCC is GCC 10.2 built with -march=native, but obviously
the -march=geode used at glibc compilation time usually overrides this
perfectly well and glibc works fine.)

It seems to me that this brings back the same problem we used to have
with the --enable-kernel option, where specifying a minimum kernel
version that was too high would lead to all binaries failing to execute,
only worse: if you *do* upgrade and not keep the old shared libraries
around to sln to (and don't have a statically-linked busybox shell or
something to recover), you can't recover by merely starting a suitable
kernel: you have to *physically upgrade the processor*, which is
probably impossible. (Or boot off an emergency rescue disk or something,
and recover from there). This despite the fact that a 32-bit x86 glibc
is not going to be using any of the (by definition x86-64-specific)
facilities that the ISA level is checking for, so it's not even
preventing execution for a good reason! (We don't even use SSE math on
x86-32, do we?)

Worse yet, because this is looking at the CPU ID, tests in a compilation
container are going to pass: even tests in a virtual machine will
probably pass, only to fail disastrously as soon as you install on the
real hardware.
Florian Weimer Feb. 8, 2021, 3:06 p.m. UTC | #8
* H. J. Lu via Libc-alpha:

> It is used to check if INCLUDE_X86_ISA_LEVEL should be defined in
> config.h.   INCLUDE_X86_ISA_LEVEL is defined only if GCC enables
> the full ISAs of a level without any ISAs of the higher ISA levels.   Since
> I want to support GCC older than GCC 11,  || is used to detect any
> ISAs in a level and !|| is used to check if any ISAs are missing.

But isn't && the right construct anyway?  As in, if GCC was built to
include *all* feature flags for a specific level, require that level at
run time?

>> Are ISA levels really defined for 32-bit?
>
> 32-bit can just use the same x86-64 ISA level.

At least it requires figuring out the baseline level between i386 and
x86-64-v2, probably based on what GCC's -march=x86-64 option does with
-m32.

Thanks,
Florian
H.J. Lu Feb. 8, 2021, 4:09 p.m. UTC | #9
On Mon, Feb 8, 2021 at 7:06 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * H. J. Lu via Libc-alpha:
>
> > It is used to check if INCLUDE_X86_ISA_LEVEL should be defined in
> > config.h.   INCLUDE_X86_ISA_LEVEL is defined only if GCC enables
> > the full ISAs of a level without any ISAs of the higher ISA levels.   Since
> > I want to support GCC older than GCC 11,  || is used to detect any
> > ISAs in a level and !|| is used to check if any ISAs are missing.
>
> But isn't && the right construct anyway?  As in, if GCC was built to
> include *all* feature flags for a specific level, require that level at
> run time?

isa-level.c serves 2 purposes:

1.  Add ISA level marker (|| path) when INCLUDE_X86_ISA_LEVEL is defined.
"# error" path will never be hit.
2.  Used by sysdeps/x86/configure.ac to check if INCLUDE_X86_ISA_LEVEL
should be defined.  If any ISAs (!|| path) are missing, INCLUDE_X86_ISA_LEVEL
won't be defined.

Since GCC 10 doesn't define __LAHF_SAHF__, __MOVBE__ nor __XSAVE__,
"&&" doesn't work for GCC 10.  However, I can move ISAs check to
sysdeps/x86/configure.ac and define INCLUDE_X86_ISA_LEVEL to

#define INCLUDE_X86_ISA_LEVEL GNU_PROPERTY_X86_ISA_1_XXX

> >> Are ISA levels really defined for 32-bit?
> >
> > 32-bit can just use the same x86-64 ISA level.
>
> At least it requires figuring out the baseline level between i386 and
> x86-64-v2, probably based on what GCC's -march=x86-64 option does with
> -m32.
>

Yes, we can make the minimum i386 ISAs for glibc as i386 baseline.
diff mbox series

Patch

From 7aa4117dabbd62d49f5b19ce36edd959d7bb260d Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Tue, 2 Feb 2021 13:45:58 -0800
Subject: [PATCH v2] x86: Require full ISA support for x86-64 level marker [BZ
 #27318]

Since -march=sandybridge enables ISAs in x86-64 ISA level v3, the v3
marker is set on libc.so.  We couldn't set the needed ISA marker to v2
since this libc won't run on all v2 machines.  Technically, the v3 marker
is correct.  But the resulting libc.so won't run on Sandy Brigde, which
is a v2 machine, even when libc is compiled with -march=sandybridge:

$ ./elf/ld.so ./libc.so
./libc.so: (p) CPU ISA level is lower than required: needed: 7; got: 3

Instead, we require full ISA support for x86-64 level marker and disable
x86-64 level marker for -march=sandybridge which enables ISAs between v2
and v3.
---
 sysdeps/x86/configure    |  7 ++++++-
 sysdeps/x86/configure.ac |  2 +-
 sysdeps/x86/isa-level.c  | 21 ++++++++++++++++++++-
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/sysdeps/x86/configure b/sysdeps/x86/configure
index 5e32dc62b3..5b20646843 100644
--- a/sysdeps/x86/configure
+++ b/sysdeps/x86/configure
@@ -133,7 +133,12 @@  if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -nostartfiles -nostdlib -r -o conftest c
   $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
+  if test "$count" = 1 && { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -DINCLUDE_X86_ISA_LEVEL -S -o conftest.s $srcdir/sysdeps/x86/isa-level.c'
+  { { 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
     libc_cv_include_x86_isa_level=yes
   fi
 fi
diff --git a/sysdeps/x86/configure.ac b/sysdeps/x86/configure.ac
index f94088f377..54ecd33d2c 100644
--- a/sysdeps/x86/configure.ac
+++ b/sysdeps/x86/configure.ac
@@ -100,7 +100,7 @@  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
+  if test "$count" = 1 && AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS -DINCLUDE_X86_ISA_LEVEL -S -o conftest.s $srcdir/sysdeps/x86/isa-level.c); then
     libc_cv_include_x86_isa_level=yes
   fi
 fi
diff --git a/sysdeps/x86/isa-level.c b/sysdeps/x86/isa-level.c
index aaf524cb56..7f83449061 100644
--- a/sysdeps/x86/isa-level.c
+++ b/sysdeps/x86/isa-level.c
@@ -25,12 +25,17 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#include <elf.h>
+#ifdef _LIBC
+# include <elf.h>
+#endif
 
 /* 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__
+#  if !defined __SSE__ || !defined __SSE2__
+#   error "Missing ISAs for x86-64 ISA level baseline"
+#  endif
 #  define ISA_BASELINE	GNU_PROPERTY_X86_ISA_1_BASELINE
 # else
 #  define ISA_BASELINE	0
@@ -40,6 +45,11 @@ 
      || (defined __x86_64__ && defined __LAHF_SAHF__) \
      || defined __POPCNT__ || defined __SSE3__ \
      || defined __SSSE3__ || defined __SSE4_1__ || defined __SSE4_2__
+#  if !defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 \
+     || !defined __POPCNT__ || !defined __SSE3__ \
+     || !defined __SSSE3__ || !defined __SSE4_1__ || !defined __SSE4_2__
+#   error "Missing ISAs for x86-64 ISA level v2"
+#  endif
 #  define ISA_V2	GNU_PROPERTY_X86_ISA_1_V2
 # else
 #  define ISA_V2	0
@@ -48,6 +58,10 @@ 
 # if defined __AVX__ || defined __AVX2__ || defined __F16C__ \
      || defined __FMA__ || defined __LZCNT__ || defined __MOVBE__ \
      || defined __XSAVE__
+# if !defined __AVX__ || !defined __AVX2__ || !defined __F16C__ \
+     || !defined __FMA__ || !defined __LZCNT__
+#   error "Missing ISAs for x86-64 ISA level v3"
+#  endif
 #  define ISA_V3	GNU_PROPERTY_X86_ISA_1_V3
 # else
 #  define ISA_V3	0
@@ -55,6 +69,11 @@ 
 
 # if defined __AVX512F__ || defined __AVX512BW__ || defined __AVX512CD__ \
      || defined __AVX512DQ__ || defined __AVX512VL__
+#  if !defined __AVX512F__ || !defined __AVX512BW__ \
+      || !defined __AVX512CD__ || !defined __AVX512DQ__ \
+      || !defined __AVX512VL__
+#   error "Missing ISAs for x86-64 ISA level v4"
+#  endif
 #  define ISA_V4	GNU_PROPERTY_X86_ISA_1_V4
 # else
 #  define ISA_V4	0
-- 
2.29.2