public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
To: "H.J. Lu" <hjl.tools@gmail.com>
Cc: Florian Weimer <fweimer@redhat.com>,
	"H.J. Lu via Libc-alpha" <libc-alpha@sourceware.org>
Subject: Re: V2 [PATCH] ldconfig/x86: Store ISA level in cache and aux cache
Date: Wed, 13 Jan 2021 10:47:16 -0300	[thread overview]
Message-ID: <4472cf12-c178-8317-afcc-d90287b33a98@linaro.org> (raw)
In-Reply-To: <CAMe9rOrBF8=pJn-VEQC9_cS_uzithBmo_ggeCFfAKnFfpjAfHw@mail.gmail.com>



On 12/01/2021 20:32, H.J. Lu wrote:
> On Tue, Jan 12, 2021 at 8:25 AM Adhemerval Zanella
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 09/12/2020 00:27, H.J. Lu via Libc-alpha wrote:
>>> On Tue, Dec 8, 2020 at 7:31 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>>> @@ -850,6 +859,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;
>>> @@ -863,7 +873,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.   */
>>>  };
>>>
>>
>> I think it would be good to have a _Static_assert with the
>> expected aux_cache_entry size.
> 
> What would that be?  aux_cache_entry_id size is increased
> by 4 bytes with my patch.

My understanding is aux_cache_entry_id keeps the same and you are using
an unused field on *aux_cache_file_entry* to add the isa_level.  Their
sizes should kept the same (since isa_level is the same size of the
previous pad).

But the _Static_assert might indeed unrequired, the load_aux_cache already
check if the aux_cache_file_entry does match the file size and uses an
empty auxiliary cache otherwise (init_aux_cache).

elf/cache.c

1008   if (aux_cache == MAP_FAILED
1009       || aux_cache_size < sizeof (struct aux_cache_file)
1010       || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1)
1011       || aux_cache_size != (sizeof (struct aux_cache_file)
1012                             + aux_cache->nlibs * sizeof (struct aux_cache_file_entry)
1013                             + aux_cache->len_strings))
1014     { 
1015       close (fd);
1016       init_aux_cache ();
1017       return;
1018     }


>>>  /* 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);
>>> +  /* If DL_CACHE_HWCAP_EXTENSION is set, but other bits, except for the
>>> +     ISA level bits, as well, this is a different kind of extension.  */
>>> +  return (((entry->hwcap >> 32) & ~DL_CACHE_HWCAP_ISA_LEVEL_MASK)
>>> +       == (DL_CACHE_HWCAP_EXTENSION >> 32));
>>>  }
>>>
>>>  /* See flags member of struct cache_file_new below.  */
>>
>> The comment sounds confusing.
> 
> I changed it to
> 
>   /* 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.  */

Thanks.

---
> From 40bacb7470f0c8d8eabd4d3b24057566d45ca5dd Mon Sep 17 00:00:00 2001
> From: "H.J. Lu" <hjl.tools@gmail.com>
> Date: Sat, 5 Dec 2020 07:01:58 -0800
> Subject: [PATCH] ldconfig/x86: Store ISA level in cache and aux cache
> 
> Store ISA level in the portion of the unused upper 32 bits of the hwcaps
> field in cache and the unused pad field in aux cache.  ISA level is stored
> and checked only for shared objects in glibc-hwcaps subdirectories.  The
> shared objects in the default directories aren't checked since there are
> no fallbacks for these shared objects.
> 
> Tested on x86-64-v2, x86-64-v3 and x86-64-v4 machines with
> --disable-hardcoded-path-in-tests and --enable-hardcoded-path-in-tests.

LGTM, thanks.  

Please update the release page once you commit it.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/cache.c                                   | 39 +++++++--
>  elf/dl-cache.c                                |  4 +
>  elf/ldconfig.c                                | 18 ++--
>  elf/readelflib.c                              | 81 +++++++++++++++++-
>  elf/readlib.c                                 |  7 +-
>  elf/tst-glibc-hwcaps-2-cache.c                | 45 ++++++++++
>  .../etc/ld.so.conf                            |  2 +
>  .../postclean.req                             |  0
>  elf/tst-glibc-hwcaps-2-cache.script           |  8 ++
>  sysdeps/generic/dl-cache.h                    | 16 +++-
>  sysdeps/generic/dl-isa-level.h                | 25 ++++++
>  sysdeps/generic/elf-read-prop.h               | 34 ++++++++
>  sysdeps/generic/ldconfig.h                    | 22 +++--
>  sysdeps/unix/sysv/linux/arm/readelflib.c      | 22 ++---
>  sysdeps/unix/sysv/linux/ia64/readelflib.c     | 22 ++---
>  sysdeps/unix/sysv/linux/mips/readelflib.c     | 22 ++---
>  sysdeps/unix/sysv/linux/powerpc/readelflib.c  | 22 ++---
>  sysdeps/unix/sysv/linux/riscv/readelflib.c    | 22 ++---
>  sysdeps/unix/sysv/linux/s390/readelflib.c     | 22 ++---
>  sysdeps/unix/sysv/linux/sparc/readelflib.c    | 22 ++---
>  sysdeps/unix/sysv/linux/x86/elf-read-prop.h   | 60 +++++++++++++
>  sysdeps/unix/sysv/linux/x86/readelflib.c      | 23 ++---
>  sysdeps/unix/sysv/linux/x86_64/Makefile       | 51 +++++++++++
>  .../sysv/linux/x86_64/tst-glibc-hwcaps-2.c    | 84 +++++++++++++++++++
>  .../linux/x86_64/x86-64-isa-level-VALUE.c     |  4 +
>  sysdeps/x86/dl-isa-level.h                    | 31 +++++++
>  26 files changed, 598 insertions(+), 110 deletions(-)
>  create mode 100644 elf/tst-glibc-hwcaps-2-cache.c
>  create mode 100644 elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf
>  create mode 100644 elf/tst-glibc-hwcaps-2-cache.root/postclean.req
>  create mode 100644 elf/tst-glibc-hwcaps-2-cache.script
>  create mode 100644 sysdeps/generic/dl-isa-level.h
>  create mode 100644 sysdeps/generic/elf-read-prop.h
>  create mode 100644 sysdeps/unix/sysv/linux/x86/elf-read-prop.h
>  create mode 100644 sysdeps/unix/sysv/linux/x86_64/tst-glibc-hwcaps-2.c
>  create mode 100644 sysdeps/unix/sysv/linux/x86_64/x86-64-isa-level-VALUE.c
>  create mode 100644 sysdeps/x86/dl-isa-level.h
> 
> 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

Ok.

> @@ -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.  */

Ok.

> 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

Ok.

> 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.  */

Ok.

> 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

Ok.

> 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:

Ok.

> 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;
> +}

Ok.

> 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
> 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

Ok.

> 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.  */

Ok.

> 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;
> +}

Ok.

> 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

Ok.

> 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.  */

Ok.

> 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;

Ok.

> 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;

Ok.

> 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)
>  	{

Ok.

> 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;

Ok.

> 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;
>      }
>  

Ok.

> 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;

Ok.

> 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;

Ok.

> 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

Ok.

> 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;

Ok.

> 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

Ok.

> 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>

Ok.

> 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;
> +}
> -- 
> 2.29.2

Ok.

  reply	other threads:[~2021-01-13 13:47 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-06 14:49 V5 [PATCH 0/2] x86: Support GNU_PROPERTY_X86_ISA_1_NEEDED marker [BZ #26717] H.J. Lu
2020-12-06 14:49 ` V5 [PATCH 1/2] x86: Support GNU_PROPERTY_X86_ISA_1_V[234] " H.J. Lu
2021-01-06 12:00   ` Adhemerval Zanella
2021-01-06 17:11     ` V6 " H.J. Lu
2021-01-07 20:09       ` Adhemerval Zanella
2021-01-07 20:58         ` H.J. Lu
2021-01-18 11:17           ` Florian Weimer
2021-01-18 13:49             ` H.J. Lu
2021-01-18 15:15               ` Florian Weimer
2020-12-06 14:49 ` V5 [PATCH 2/2] ldconfig/x86: Add ISA level check to glibc-hwcaps H.J. Lu
2020-12-07 10:00   ` Florian Weimer
2020-12-08 13:25     ` H.J. Lu
2020-12-08 13:31       ` Florian Weimer
2020-12-08 14:10         ` H.J. Lu
2020-12-08 14:18           ` Florian Weimer
2020-12-08 14:23             ` H.J. Lu
2020-12-08 15:15               ` Florian Weimer
2020-12-08 15:31                 ` H.J. Lu
2020-12-09  3:27                   ` [PATCH] ldconfig/x86: Store ISA level in cache and aux cache H.J. Lu
2021-01-12 16:25                     ` Adhemerval Zanella
2021-01-12 23:32                       ` V2 " H.J. Lu
2021-01-13 13:47                         ` Adhemerval Zanella [this message]
2021-01-13 14:12                           ` H.J. Lu
2021-01-28 20:20                     ` Florian Weimer
2021-01-28 20:43                       ` H.J. Lu
2021-01-29  8:56                         ` Florian Weimer
2021-01-29 12:44                           ` H.J. Lu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4472cf12-c178-8317-afcc-d90287b33a98@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=fweimer@redhat.com \
    --cc=hjl.tools@gmail.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).