summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--elf/cache.c39
-rw-r--r--elf/dl-cache.c4
-rw-r--r--elf/ldconfig.c18
-rw-r--r--elf/readelflib.c81
-rw-r--r--elf/readlib.c7
-rw-r--r--elf/tst-glibc-hwcaps-2-cache.c45
-rw-r--r--elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf2
-rw-r--r--elf/tst-glibc-hwcaps-2-cache.root/postclean.req0
-rw-r--r--elf/tst-glibc-hwcaps-2-cache.script8
-rw-r--r--sysdeps/generic/dl-cache.h16
-rw-r--r--sysdeps/generic/dl-isa-level.h25
-rw-r--r--sysdeps/generic/elf-read-prop.h34
-rw-r--r--sysdeps/generic/ldconfig.h22
-rw-r--r--sysdeps/unix/sysv/linux/arm/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/ia64/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/mips/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/riscv/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/s390/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/sparc/readelflib.c22
-rw-r--r--sysdeps/unix/sysv/linux/x86/elf-read-prop.h60
-rw-r--r--sysdeps/unix/sysv/linux/x86/readelflib.c23
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/Makefile51
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/tst-glibc-hwcaps-2.c84
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c4
-rw-r--r--sysdeps/x86/dl-isa-level.h31
26 files changed, 598 insertions, 110 deletions
diff --git a/elf/cache.c b/elf/cache.c
index 11ce4ade8a..c01d302072 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -146,6 +146,7 @@ struct cache_entry
struct stringtable_entry *path; /* Path to find library. */
int flags; /* Flags to indicate kind of library. */
unsigned int osversion; /* Required OS version. */
+ unsigned int isa_level; /* Required ISA level. */
uint64_t hwcap; /* Important hardware capabilities. */
int bits_hwcap; /* Number of bits set in hwcap. */
@@ -549,6 +550,19 @@ write_extensions (int fd, uint32_t str_offset,
free (ext);
}
+/* Compute the hwcap value from ENTRY. */
+static inline uint64_t
+compute_hwcap_value (struct cache_entry *entry)
+{
+ if (entry->isa_level > DL_CACHE_HWCAP_ISA_LEVEL_MASK)
+ error (EXIT_FAILURE, 0, _("%s: ISA level is too high (%d > %d)"),
+ entry->path->string, entry->isa_level,
+ DL_CACHE_HWCAP_ISA_LEVEL_MASK);
+ return (DL_CACHE_HWCAP_EXTENSION
+ | (((uint64_t) entry->isa_level) << 32)
+ | entry->hwcaps->section_index);
+}
+
/* Save the contents of the cache. */
void
save_cache (const char *cache_name)
@@ -662,7 +676,7 @@ save_cache (const char *cache_name)
file_entries_new->libs[idx_new].hwcap = entry->hwcap;
else
file_entries_new->libs[idx_new].hwcap
- = DL_CACHE_HWCAP_EXTENSION | entry->hwcaps->section_index;
+ = compute_hwcap_value (entry);
file_entries_new->libs[idx_new].key
= str_offset + entry->lib->offset;
file_entries_new->libs[idx_new].value
@@ -777,7 +791,8 @@ save_cache (const char *cache_name)
/* Add one library to the cache. */
void
add_to_cache (const char *path, const char *filename, const char *soname,
- int flags, unsigned int osversion, uint64_t hwcap,
+ int flags, unsigned int osversion,
+ unsigned int isa_level, uint64_t hwcap,
struct glibc_hwcaps_subdirectory *hwcaps)
{
struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
@@ -795,6 +810,7 @@ add_to_cache (const char *path, const char *filename, const char *soname,
new_entry->path = path_interned;
new_entry->flags = flags;
new_entry->osversion = osversion;
+ new_entry->isa_level = isa_level;
new_entry->hwcap = hwcap;
new_entry->hwcaps = hwcaps;
new_entry->bits_hwcap = 0;
@@ -851,6 +867,7 @@ struct aux_cache_entry
struct aux_cache_entry_id id;
int flags;
unsigned int osversion;
+ unsigned int isa_level;
int used;
char *soname;
struct aux_cache_entry *next;
@@ -864,7 +881,7 @@ struct aux_cache_file_entry
int32_t flags; /* This is 1 for an ELF library. */
uint32_t soname; /* String table indice. */
uint32_t osversion; /* Required OS version. */
- int32_t pad;
+ uint32_t isa_level; /* Required ISA level. */
};
/* ldconfig maintains an auxiliary cache file that allows
@@ -915,7 +932,8 @@ init_aux_cache (void)
int
search_aux_cache (struct stat64 *stat_buf, int *flags,
- unsigned int *osversion, char **soname)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname)
{
struct aux_cache_entry_id id;
id.ino = (uint64_t) stat_buf->st_ino;
@@ -933,6 +951,7 @@ search_aux_cache (struct stat64 *stat_buf, int *flags,
{
*flags = entry->flags;
*osversion = entry->osversion;
+ *isa_level = entry->isa_level;
if (entry->soname != NULL)
*soname = xstrdup (entry->soname);
else
@@ -946,7 +965,8 @@ search_aux_cache (struct stat64 *stat_buf, int *flags,
static void
insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
- unsigned int osversion, const char *soname, int used)
+ unsigned int osversion, unsigned int isa_level,
+ const char *soname, int used)
{
size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size;
struct aux_cache_entry *entry;
@@ -962,6 +982,7 @@ insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
entry->id = *id;
entry->flags = flags;
entry->osversion = osversion;
+ entry->isa_level = isa_level;
entry->used = used;
if (soname != NULL)
entry->soname = memcpy ((char *) (entry + 1), soname, len);
@@ -973,14 +994,15 @@ insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
void
add_to_aux_cache (struct stat64 *stat_buf, int flags,
- unsigned int osversion, const char *soname)
+ unsigned int osversion, unsigned int isa_level,
+ const char *soname)
{
struct aux_cache_entry_id id;
id.ino = (uint64_t) stat_buf->st_ino;
id.ctime = (uint64_t) stat_buf->st_ctime;
id.size = (uint64_t) stat_buf->st_size;
id.dev = (uint64_t) stat_buf->st_dev;
- insert_to_aux_cache (&id, flags, osversion, soname, 1);
+ insert_to_aux_cache (&id, flags, osversion, isa_level, soname, 1);
}
/* Load auxiliary cache to search for unchanged entries. */
@@ -1026,6 +1048,7 @@ load_aux_cache (const char *aux_cache_name)
insert_to_aux_cache (&aux_cache->libs[i].id,
aux_cache->libs[i].flags,
aux_cache->libs[i].osversion,
+ aux_cache->libs[i].isa_level,
aux_cache->libs[i].soname == 0
? NULL : aux_cache_data + aux_cache->libs[i].soname,
0);
@@ -1094,7 +1117,7 @@ save_aux_cache (const char *aux_cache_name)
str_offset += len;
}
file_entries->libs[idx].osversion = entry->osversion;
- file_entries->libs[idx++].pad = 0;
+ file_entries->libs[idx++].isa_level = entry->isa_level;
}
/* Write out auxiliary cache file. */
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 935e3a60b4..32f3bef5ea 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <_itoa.h>
#include <dl-hwcaps.h>
+#include <dl-isa-level.h>
#ifndef _DL_PLATFORMS_COUNT
# define _DL_PLATFORMS_COUNT 0
@@ -284,6 +285,9 @@ search_cache (const char *string_table, uint32_t string_table_size,
#ifdef SHARED
named_hwcap = dl_cache_hwcap_extension (libnew);
+ if (named_hwcap
+ && !dl_cache_hwcap_isa_level_compatible (libnew))
+ continue;
#endif
/* The entries with named/extension hwcaps
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index bbcf8f5c5c..28ed637a29 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -655,6 +655,7 @@ manual_link (char *library)
struct stat64 stat_buf;
int flag;
unsigned int osversion;
+ unsigned int isa_level;
/* Prepare arguments for create_links call. Split library name in
directory and filename first. Since path is allocated, we've got
@@ -721,7 +722,7 @@ manual_link (char *library)
}
if (process_file (real_library, library, libname, &flag, &osversion,
- &soname, 0, &stat_buf))
+ &isa_level, &soname, 0, &stat_buf))
{
error (0, 0, _("No link created since soname could not be found for %s"),
library);
@@ -768,6 +769,7 @@ struct dlib_entry
int flag;
int is_link;
unsigned int osversion;
+ unsigned int isa_level;
struct dlib_entry *next;
};
@@ -980,17 +982,21 @@ search_dir (const struct dir_entry *entry)
library already and it's not changed. */
char *soname;
unsigned int osversion;
- if (!search_aux_cache (&lstat_buf, &flag, &osversion, &soname))
+ unsigned int isa_level;
+ if (!search_aux_cache (&lstat_buf, &flag, &osversion, &isa_level,
+ &soname))
{
if (process_file (real_name, file_name, direntry->d_name, &flag,
- &osversion, &soname, is_link, &lstat_buf))
+ &osversion, &isa_level, &soname, is_link,
+ &lstat_buf))
{
if (real_name != real_file_name)
free (real_name);
continue;
}
else if (opt_build_cache)
- add_to_aux_cache (&lstat_buf, flag, osversion, soname);
+ add_to_aux_cache (&lstat_buf, flag, osversion, isa_level,
+ soname);
}
if (soname == NULL)
@@ -1096,6 +1102,7 @@ search_dir (const struct dir_entry *entry)
dlib_ptr->name = xstrdup (direntry->d_name);
dlib_ptr->is_link = is_link;
dlib_ptr->osversion = osversion;
+ dlib_ptr->isa_level = isa_level;
}
/* Don't add this library, abort loop. */
/* Also free soname, since it's dynamically allocated. */
@@ -1112,6 +1119,7 @@ search_dir (const struct dir_entry *entry)
dlib_ptr->flag = flag;
dlib_ptr->is_link = is_link;
dlib_ptr->osversion = osversion;
+ dlib_ptr->isa_level = isa_level;
/* Add at head of list. */
dlib_ptr->next = dlibs;
dlibs = dlib_ptr;
@@ -1149,7 +1157,7 @@ search_dir (const struct dir_entry *entry)
if (opt_build_cache)
add_to_cache (entry->path, filename, dlib_ptr->soname,
dlib_ptr->flag, dlib_ptr->osversion,
- hwcap, entry->hwcaps);
+ dlib_ptr->isa_level, hwcap, entry->hwcaps);
}
/* Free all resources. */
diff --git a/elf/readelflib.c b/elf/readelflib.c
index cdea79d729..c09425a574 100644
--- a/elf/readelflib.c
+++ b/elf/readelflib.c
@@ -17,6 +17,8 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
+#include <elf-read-prop.h>
+
/* This code is a heavily simplified version of the readelf program
that's part of the current binutils development version. For architectures
which need to handle both 32bit and 64bit ELF libraries, this file is
@@ -40,8 +42,8 @@ do \
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
int i;
unsigned int j;
@@ -86,6 +88,9 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
libc5/libc6. */
*flag = FLAG_ELF;
+ /* The default ISA level is 0. */
+ *isa_level = 0;
+
dynamic_addr = 0;
dynamic_size = 0;
program_interpreter = NULL;
@@ -164,6 +169,78 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
}
break;
+ case PT_GNU_PROPERTY:
+ /* The NT_GNU_PROPERTY_TYPE_0 note must be aligned to 4 bytes
+ in 32-bit objects and to 8 bytes in 64-bit objects. Skip
+ notes with incorrect alignment. */
+ if (segment->p_align == (__ELF_NATIVE_CLASS / 8))
+ {
+ const ElfW(Nhdr) *note = (const void *) (file_contents
+ + segment->p_offset);
+ const ElfW(Addr) size = segment->p_filesz;
+ const ElfW(Addr) align = segment->p_align;
+
+ const ElfW(Addr) start = (ElfW(Addr)) (uintptr_t) note;
+ unsigned int last_type = 0;
+
+ while ((ElfW(Addr)) (uintptr_t) (note + 1) - start < size)
+ {
+ /* Find the NT_GNU_PROPERTY_TYPE_0 note. */
+ if (note->n_namesz == 4
+ && note->n_type == NT_GNU_PROPERTY_TYPE_0
+ && memcmp (note + 1, "GNU", 4) == 0)
+ {
+ /* Check for invalid property. */
+ if (note->n_descsz < 8
+ || (note->n_descsz % sizeof (ElfW(Addr))) != 0)
+ goto done;
+
+ /* Start and end of property array. */
+ unsigned char *ptr = (unsigned char *) (note + 1) + 4;
+ unsigned char *ptr_end = ptr + note->n_descsz;
+
+ do
+ {
+ unsigned int type = *(unsigned int *) ptr;
+ unsigned int datasz = *(unsigned int *) (ptr + 4);
+
+ /* Property type must be in ascending order. */
+ if (type < last_type)
+ goto done;
+
+ ptr += 8;
+ if ((ptr + datasz) > ptr_end)
+ goto done;
+
+ last_type = type;
+
+ /* Target specific property processing.
+ Return value:
+ false: Continue processing the properties.
+ true : Stop processing the properties.
+ */
+ if (read_gnu_property (isa_level, type,
+ datasz, ptr))
+ goto done;
+
+ /* Check the next property item. */
+ ptr += ALIGN_UP (datasz, sizeof (ElfW(Addr)));
+ }
+ while ((ptr_end - ptr) >= 8);
+
+ /* Only handle one NT_GNU_PROPERTY_TYPE_0. */
+ goto done;
+ }
+
+ note = ((const void *) note
+ + ELF_NOTE_NEXT_OFFSET (note->n_namesz,
+ note->n_descsz,
+ align));
+ }
+ }
+done:
+ break;
+
default:
break;
}
diff --git a/elf/readlib.c b/elf/readlib.c
index 3d52c9a980..7383c23249 100644
--- a/elf/readlib.c
+++ b/elf/readlib.c
@@ -75,7 +75,8 @@ is_gdb_python_file (const char *name)
int
process_file (const char *real_file_name, const char *file_name,
const char *lib, int *flag, unsigned int *osversion,
- char **soname, int is_link, struct stat64 *stat_buf)
+ unsigned int *isa_level, char **soname, int is_link,
+ struct stat64 *stat_buf)
{
FILE *file;
struct stat64 statbuf;
@@ -173,8 +174,8 @@ process_file (const char *real_file_name, const char *file_name,
/* Libraries have to be shared object files. */
else if (elf_header->e_type != ET_DYN)
ret = 1;
- else if (process_elf_file (file_name, lib, flag, osversion, soname,
- file_contents, statbuf.st_size))
+ else if (process_elf_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, statbuf.st_size))
ret = 1;
done:
diff --git a/elf/tst-glibc-hwcaps-2-cache.c b/elf/tst-glibc-hwcaps-2-cache.c
new file mode 100644
index 0000000000..97d9835ddb
--- /dev/null
+++ b/elf/tst-glibc-hwcaps-2-cache.c
@@ -0,0 +1,45 @@
+/* Wrapper to invoke tst-glibc-hwcaps-2 in a container to test ldconfig.
+ Copyright (C) 2021 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/>. */
+
+/* This program is just a wrapper that runs ldconfig followed by
+ tst-glibc-hwcaps-2. The actual test is provided via an
+ implementation in a sysdeps subdirectory. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <support/support.h>
+#include <support/capture_subprocess.h>
+
+int
+main (int argc, char **argv)
+{
+ /* Run ldconfig to populate the cache. */
+ char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir);
+ struct support_capture_subprocess result =
+ support_capture_subprogram (command, &((char *) { NULL }));
+ support_capture_subprocess_check (&result, "ldconfig", 0, sc_allow_none);
+ free (command);
+
+ /* Reuse tst-glibc-hwcaps. Since this code is running in a
+ container, we can launch it directly. */
+ char *path = xasprintf ("%s/elf/tst-glibc-hwcaps-2", support_objdir_root);
+ execv (path, argv);
+ printf ("error: execv of %s failed: %m\n", path);
+ return 1;
+}
diff --git a/elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf b/elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf
new file mode 100644
index 0000000000..e1e74dbda2
--- /dev/null
+++ b/elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf
@@ -0,0 +1,2 @@
+# This file was created to suppress a warning from ldconfig:
+# /sbin/ldconfig: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf: No such file or directory
diff --git a/elf/tst-glibc-hwcaps-2-cache.root/postclean.req b/elf/tst-glibc-hwcaps-2-cache.root/postclean.req
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/elf/tst-glibc-hwcaps-2-cache.root/postclean.req
diff --git a/elf/tst-glibc-hwcaps-2-cache.script b/elf/tst-glibc-hwcaps-2-cache.script
new file mode 100644
index 0000000000..8e4e9896ee
--- /dev/null
+++ b/elf/tst-glibc-hwcaps-2-cache.script
@@ -0,0 +1,8 @@
+# test-container does not support scripts in sysdeps directories, so
+# collect everything in one file.
+
+mkdirp 0770 $L/glibc-hwcaps/x86-64-v2
+mkdirp 0770 $L/glibc-hwcaps/x86-64-v3
+cp $B/elf/libx86-64-isa-level-1.so $L/libx86-64-isa-level.so
+cp $B/elf/libx86-64-isa-level-3.so $L/glibc-hwcaps/x86-64-v2/libx86-64-isa-level.so
+cp $B/elf/libx86-64-isa-level-4.so $L/glibc-hwcaps/x86-64-v3/libx86-64-isa-level.so
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index c7eca70d0c..964d50a486 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -106,14 +106,24 @@ struct file_entry_new
entries. */
#define DL_CACHE_HWCAP_EXTENSION (1ULL << 62)
+/* The number of the ISA level bits in the upper 32 bits of the hwcap
+ field. */
+#define DL_CACHE_HWCAP_ISA_LEVEL_COUNT 10
+
+/* The mask of the ISA level bits in the hwcap field. */
+#define DL_CACHE_HWCAP_ISA_LEVEL_MASK \
+ ((1 << DL_CACHE_HWCAP_ISA_LEVEL_COUNT) -1)
+
/* Return true if the ENTRY->hwcap value indicates that
DL_CACHE_HWCAP_EXTENSION is used. */
static inline bool
dl_cache_hwcap_extension (struct file_entry_new *entry)
{
- /* If DL_CACHE_HWCAP_EXTENSION is set, but other bits as well, this
- is a different kind of extension. */
- return (entry->hwcap >> 32) == (DL_CACHE_HWCAP_EXTENSION >> 32);
+ /* This is an hwcap extension if only the DL_CACHE_HWCAP_EXTENSION bit
+ is set, ignoring the lower 32 bits as well as the ISA level bits in
+ the upper 32 bits. */
+ return (((entry->hwcap >> 32) & ~DL_CACHE_HWCAP_ISA_LEVEL_MASK)
+ == (DL_CACHE_HWCAP_EXTENSION >> 32));
}
/* See flags member of struct cache_file_new below. */
diff --git a/sysdeps/generic/dl-isa-level.h b/sysdeps/generic/dl-isa-level.h
new file mode 100644
index 0000000000..6fcd319c49
--- /dev/null
+++ b/sysdeps/generic/dl-isa-level.h
@@ -0,0 +1,25 @@
+/* Support for reading ISA level in /etc/ld.so.cache files written by
+ Linux ldconfig. Generic version.
+ Copyright (C) 2021 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/>. */
+
+/* Return true if the ISA level in ENTRY is compatible with CPU. */
+static inline bool
+dl_cache_hwcap_isa_level_compatible (struct file_entry_new *entry)
+{
+ return true;
+}
diff --git a/sysdeps/generic/elf-read-prop.h b/sysdeps/generic/elf-read-prop.h
new file mode 100644
index 0000000000..98c3d2acb5
--- /dev/null
+++ b/sysdeps/generic/elf-read-prop.h
@@ -0,0 +1,34 @@
+/* Support for GNU properties in ldconfig. Generic version.
+ Copyright (C) 2021 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/>. */
+
+#ifndef _ELF_READ_PROP_H
+#define _ELF_READ_PROP_H
+
+/* Called for each property in the NT_GNU_PROPERTY_TYPE_0 note of SEGMENT.
+ Return value:
+ false: Continue processing the properties.
+ true : Stop processing the properties.
+ */
+static inline bool __attribute__ ((always_inline))
+read_gnu_property (unsigned int *isal_level, uint32_t type, uint32_t
+ datasz, void *data)
+{
+ return true;
+}
+
+#endif
diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h
index eb070fd259..3ab757077d 100644
--- a/sysdeps/generic/ldconfig.h
+++ b/sysdeps/generic/ldconfig.h
@@ -70,8 +70,9 @@ const char *glibc_hwcaps_subdirectory_name
(const struct glibc_hwcaps_subdirectory *);
extern void add_to_cache (const char *path, const char *filename,
- const char *soname,
- int flags, unsigned int osversion, uint64_t hwcap,
+ const char *soname, int flags,
+ unsigned int osversion, unsigned int isa_level,
+ uint64_t hwcap,
struct glibc_hwcaps_subdirectory *);
extern void init_aux_cache (void);
@@ -79,23 +80,28 @@ extern void init_aux_cache (void);
extern void load_aux_cache (const char *aux_cache_name);
extern int search_aux_cache (struct stat64 *stat_buf, int *flags,
- unsigned int *osversion, char **soname);
+ unsigned int *osversion,
+ unsigned int *isa_level, char **soname);
extern void add_to_aux_cache (struct stat64 *stat_buf, int flags,
- unsigned int osversion, const char *soname);
+ unsigned int osversion,
+ unsigned int isa_level, const char *soname);
extern void save_aux_cache (const char *aux_cache_name);
/* Declared in readlib.c. */
extern int process_file (const char *real_file_name, const char *file_name,
- const char *lib, int *flag, unsigned int *osversion,
- char **soname, int is_link, struct stat64 *stat_buf);
+ const char *lib, int *flag,
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, int is_link,
+ struct stat64 *stat_buf);
extern char *implicit_soname (const char *lib, int flag);
/* Declared in readelflib.c. */
-extern int process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+extern int process_elf_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Declared in chroot_canon.c. */
diff --git a/sysdeps/unix/sysv/linux/arm/readelflib.c b/sysdeps/unix/sysv/linux/arm/readelflib.c
index 2d3ecb1eef..ade2f49aeb 100644
--- a/sysdeps/unix/sysv/linux/arm/readelflib.c
+++ b/sysdeps/unix/sysv/linux/arm/readelflib.c
@@ -18,18 +18,20 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret;
@@ -38,8 +40,8 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
{
Elf32_Ehdr *elf32_header = (Elf32_Ehdr *) elf_header;
- ret = process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
if (!ret && EF_ARM_EABI_VERSION (elf32_header->e_flags) == EF_ARM_EABI_VER5)
{
@@ -57,8 +59,8 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
}
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* AArch64 libraries are always libc.so.6+. */
if (!ret)
*flag = FLAG_AARCH64_LIB64|FLAG_ELF_LIBC6;
diff --git a/sysdeps/unix/sysv/linux/ia64/readelflib.c b/sysdeps/unix/sysv/linux/ia64/readelflib.c
index 7fb3ace359..2aa32666aa 100644
--- a/sysdeps/unix/sysv/linux/ia64/readelflib.c
+++ b/sysdeps/unix/sysv/linux/ia64/readelflib.c
@@ -16,29 +16,31 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
- void *file_contents, size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret;
if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
- return process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ return process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* Intel 64bit libraries are always libc.so.6+. */
if (!ret)
*flag = FLAG_IA64_LIB64|FLAG_ELF_LIBC6;
diff --git a/sysdeps/unix/sysv/linux/mips/readelflib.c b/sysdeps/unix/sysv/linux/mips/readelflib.c
index e50bc3ce23..d0e809d893 100644
--- a/sysdeps/unix/sysv/linux/mips/readelflib.c
+++ b/sysdeps/unix/sysv/linux/mips/readelflib.c
@@ -20,18 +20,20 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
union
{
@@ -45,8 +47,8 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
elf_header.eh = file_contents;
if (elf_header.eh->e_ident [EI_CLASS] == ELFCLASS32)
{
- ret = process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
if (!ret)
{
Elf32_Word flags = elf_header.eh32->e_flags;
@@ -62,8 +64,8 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
}
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* n64 libraries are always libc.so.6+. */
if (!ret)
{
diff --git a/sysdeps/unix/sysv/linux/powerpc/readelflib.c b/sysdeps/unix/sysv/linux/powerpc/readelflib.c
index 4f9a9be254..51f8a9496a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/readelflib.c
+++ b/sysdeps/unix/sysv/linux/powerpc/readelflib.c
@@ -17,29 +17,31 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret;
if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
- return process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ return process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* PowerPC 64bit libraries are always libc.so.6+. */
if (!ret)
*flag = FLAG_POWERPC_LIB64|FLAG_ELF_LIBC6;
diff --git a/sysdeps/unix/sysv/linux/riscv/readelflib.c b/sysdeps/unix/sysv/linux/riscv/readelflib.c
index 2aca6670a8..3822d63a05 100644
--- a/sysdeps/unix/sysv/linux/riscv/readelflib.c
+++ b/sysdeps/unix/sysv/linux/riscv/readelflib.c
@@ -17,11 +17,13 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* The ELF flags supported by our current glibc port:
@@ -38,8 +40,8 @@ int process_elf64_file (const char *file_name, const char *lib, int *flag,
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
Elf32_Ehdr *elf32_header = (Elf32_Ehdr *) elf_header;
@@ -52,14 +54,14 @@ process_elf_file (const char *file_name, const char *lib, int *flag,
if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
{
- ret = process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
flags = elf32_header->e_flags;
}
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
flags = elf64_header->e_flags;
}
diff --git a/sysdeps/unix/sysv/linux/s390/readelflib.c b/sysdeps/unix/sysv/linux/s390/readelflib.c
index fc21af34d8..e190109e3d 100644
--- a/sysdeps/unix/sysv/linux/s390/readelflib.c
+++ b/sysdeps/unix/sysv/linux/s390/readelflib.c
@@ -16,29 +16,31 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret;
if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
- return process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ return process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* S/390 64bit libraries are always libc.so.6+. */
if (!ret)
*flag = FLAG_S390_LIB64|FLAG_ELF_LIBC6;
diff --git a/sysdeps/unix/sysv/linux/sparc/readelflib.c b/sysdeps/unix/sysv/linux/sparc/readelflib.c
index d54b8dfd28..bbfc81337a 100644
--- a/sysdeps/unix/sysv/linux/sparc/readelflib.c
+++ b/sysdeps/unix/sysv/linux/sparc/readelflib.c
@@ -18,29 +18,31 @@
<https://www.gnu.org/licenses/>. */
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret;
if (elf_header->e_ident [EI_CLASS] == ELFCLASS32)
- return process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ return process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
else
{
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
/* Sparc 64bit libraries are always libc.so.6+. */
if (!ret)
*flag = FLAG_SPARC_LIB64|FLAG_ELF_LIBC6;
diff --git a/sysdeps/unix/sysv/linux/x86/elf-read-prop.h b/sysdeps/unix/sysv/linux/x86/elf-read-prop.h
new file mode 100644
index 0000000000..85affa2864
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86/elf-read-prop.h
@@ -0,0 +1,60 @@
+/* Support for GNU properties in ldconfig. x86 version.
+ Copyright (C) 2021 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/>. */
+
+#ifndef _ELF_READ_PROP_H
+#define _ELF_READ_PROP_H
+
+#include <dl-cache.h>
+
+/* Called for each property in the NT_GNU_PROPERTY_TYPE_0 note of SEGMENT.
+ Return value:
+ false: Continue processing the properties.
+ true : Stop processing the properties.
+ */
+static inline bool __attribute__ ((always_inline))
+read_gnu_property (unsigned int *isal_level, uint32_t type,
+ uint32_t datasz, void *data)
+{
+ /* Property type must be in ascending order. */
+ if (type > GNU_PROPERTY_X86_ISA_1_NEEDED)
+ return true;
+
+ if (type == GNU_PROPERTY_X86_ISA_1_NEEDED)
+ {
+ if (datasz == 4)
+ {
+ /* The size of GNU_PROPERTY_X86_ISA_1_NEEDED must be 4 bytes.
+ There is no point to continue if this type is ill-formed. */
+ unsigned int isa_1_needed = *(unsigned int *) data;
+ _Static_assert (((sizeof (isa_1_needed) * 8)
+ <= (1 << DL_CACHE_HWCAP_ISA_LEVEL_COUNT)),
+ "DL_CACHE_HWCAP_ISA_LEVEL_COUNT is too small");
+ if (isa_1_needed != 0)
+ {
+ unsigned int level;
+ asm ("bsr %1, %0" : "=r" (level) : "g" (isa_1_needed));
+ *isal_level = level;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/x86/readelflib.c b/sysdeps/unix/sysv/linux/x86/readelflib.c
index a4df70e7e6..4a9bcc82cb 100644
--- a/sysdeps/unix/sysv/linux/x86/readelflib.c
+++ b/sysdeps/unix/sysv/linux/x86/readelflib.c
@@ -17,19 +17,20 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-
-int process_elf32_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf32_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
-int process_elf64_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname,
+int process_elf64_file (const char *file_name, const char *lib,
+ int *flag, unsigned int *osversion,
+ unsigned int *isa_level, char **soname,
void *file_contents, size_t file_length);
/* Returns 0 if everything is ok, != 0 in case of error. */
int
process_elf_file (const char *file_name, const char *lib, int *flag,
- unsigned int *osversion, char **soname, void *file_contents,
- size_t file_length)
+ unsigned int *osversion, unsigned int *isa_level,
+ char **soname, void *file_contents, size_t file_length)
{
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_contents;
int ret, file_flag = 0;
@@ -68,11 +69,11 @@ failed:
}
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32)
- ret = process_elf32_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf32_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
else
- ret = process_elf64_file (file_name, lib, flag, osversion, soname,
- file_contents, file_length);
+ ret = process_elf64_file (file_name, lib, flag, osversion, isa_level,
+ soname, file_contents, file_length);
if (!ret && file_flag)
*flag = file_flag;
diff --git a/sysdeps/unix/sysv/linux/x86_64/Makefile b/sysdeps/unix/sysv/linux/x86_64/Makefile
index 9b82155393..5e19202ebf 100644
--- a/sysdeps/unix/sysv/linux/x86_64/Makefile
+++ b/sysdeps/unix/sysv/linux/x86_64/Makefile
@@ -13,3 +13,54 @@ endif
ifeq ($(subdir),misc)
gen-as-const-headers += sigaltstack-offsets.sym
endif
+
+ifeq ($(subdir),elf)
+ifeq (yes,$(enable-x86-isa-level))
+tests += \
+ tst-glibc-hwcaps-2
+ifeq (no,$(build-hardcoded-path-in-tests))
+# This is an ld.so.cache test, and RPATH/RUNPATH in the executable
+# interferes with its test objectives.
+tests-container += \
+ tst-glibc-hwcaps-2-cache
+endif
+modules-names += \
+ libx86-64-isa-level-1 \
+ libx86-64-isa-level-2 \
+ libx86-64-isa-level-3 \
+ libx86-64-isa-level-4
+
+$(objpfx)tst-glibc-hwcaps-2: $(objpfx)libx86-64-isa-level.so
+
+$(objpfx)tst-glibc-hwcaps-2.out: \
+ $(objpfx)glibc-hwcaps/x86-64-v2/libx86-64-isa-level.so \
+ $(objpfx)glibc-hwcaps/x86-64-v4/libx86-64-isa-level.so \
+ $(objpfx)glibc-hwcaps/x86-64-v3/libx86-64-isa-level.so
+$(objpfx)glibc-hwcaps/x86-64-v2/libx86-64-isa-level.so: \
+ $(objpfx)libx86-64-isa-level-2.so
+ $(make-target-directory)
+ cp $< $@
+$(objpfx)glibc-hwcaps/x86-64-v3/libx86-64-isa-level.so: \
+ $(objpfx)libx86-64-isa-level-3.so
+ $(make-target-directory)
+ cp $< $@
+$(objpfx)glibc-hwcaps/x86-64-v4/libx86-64-isa-level.so: \
+ $(objpfx)libx86-64-isa-level-4.so
+ $(make-target-directory)
+ cp $< $@
+
+CFLAGS-libx86-64-isa-level-1.os += -march=x86-64
+CFLAGS-libx86-64-isa-level-2.os += -march=x86-64
+CFLAGS-libx86-64-isa-level-3.os += -march=x86-64
+CFLAGS-libx86-64-isa-level-4.os += -march=x86-64
+
+# The test modules are parameterized by preprocessor macros.
+LDFLAGS-libx86-64-isa-level-1.so += -Wl,-soname,libx86-64-isa-level.so
+LDFLAGS-libx86-64-isa-level-4.so += -Wl,-soname,libx86-64-isa-level.so
+$(objpfx)libx86-64-isa-level%.os: $(..)/sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c
+ $(compile-command.c) -DVALUE=$(lastword $(subst -, ,$*)) \
+ -DISA_LEVEL="(1 << ($(lastword $(subst -, ,$*)) - 1))"
+$(objpfx)libx86-64-isa-level.so: $(objpfx)libx86-64-isa-level-1.so
+ cp $< $@
+endif
+endif # $(subdir) == elf
diff --git a/sysdeps/unix/sysv/linux/x86_64/tst-glibc-hwcaps-2.c b/sysdeps/unix/sysv/linux/x86_64/tst-glibc-hwcaps-2.c
new file mode 100644
index 0000000000..fe91bfd224
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/tst-glibc-hwcaps-2.c
@@ -0,0 +1,84 @@
+/* Check ISA level on shared object in glibc-hwcaps subdirectories.
+ Copyright (C) 2021 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 <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <elf.h>
+#include <get-isa-level.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+extern int dso_isa_level (void);
+
+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)
+ return EXIT_FAILURE;
+
+ int level = dso_isa_level ();
+ int ret;
+ switch (level)
+ {
+ case 1:
+ case 2:
+ /* The default libx86-64-isa-level.so is used. */
+ printf ("The default shared library is used.\n");
+ if (has_isa_v3 || has_isa_v4 || !has_isa_v2)
+ ret = EXIT_FAILURE;
+ else
+ ret = EXIT_SUCCESS;
+ break;
+ case 3:
+ /* libx86-64-isa-level.so marked as x86-64 ISA level 3 needed in
+ x86-64-v2 should be ignored on lesser CPU. */
+ printf ("x86-64 ISA level 3 shared library is used.\n");
+ if (has_isa_v4 || !has_isa_v3)
+ ret = EXIT_FAILURE;
+ else
+ ret = EXIT_SUCCESS;
+ break;
+ case 4:
+ /* libx86-64-isa-level.so marked as x86-64 ISA level 4 needed in
+ x86-64-v3 should be ignored on lesser CPU. */
+ printf ("x86-64 ISA level 4 shared library is used.\n");
+ if (has_isa_v4)
+ ret = EXIT_SUCCESS;
+ else
+ ret = EXIT_FAILURE;
+ break;
+ default:
+ abort ();
+ }
+ return ret;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c b/sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c
new file mode 100644
index 0000000000..2813d627cc
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c
@@ -0,0 +1,4 @@
+#define INCLUDE_X86_ISA_LEVEL
+#define MARKER dso_isa_level
+#include <isa-level.c>
+#include <markermodMARKER-VALUE.c>
diff --git a/sysdeps/x86/dl-isa-level.h b/sysdeps/x86/dl-isa-level.h
new file mode 100644
index 0000000000..afe390c365
--- /dev/null
+++ b/sysdeps/x86/dl-isa-level.h
@@ -0,0 +1,31 @@
+/* Support for reading ISA level in /etc/ld.so.cache files written by
+ Linux ldconfig. x86 version.
+ Copyright (C) 2021 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 <sys/platform/x86.h>
+
+/* Return true if the ISA level in ENTRY is compatible with CPU. */
+static inline bool
+dl_cache_hwcap_isa_level_compatible (struct file_entry_new *entry)
+{
+ const struct cpu_features *cpu_features = __get_cpu_features ();
+ unsigned int isa_level
+ = 1 << ((entry->hwcap >> 32) & DL_CACHE_HWCAP_ISA_LEVEL_MASK);
+
+ return (isa_level & cpu_features->isa_1) == isa_level;
+}