diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/cache.c | 39 | ||||
-rw-r--r-- | elf/dl-cache.c | 4 | ||||
-rw-r--r-- | elf/ldconfig.c | 18 | ||||
-rw-r--r-- | elf/readelflib.c | 81 | ||||
-rw-r--r-- | elf/readlib.c | 7 | ||||
-rw-r--r-- | elf/tst-glibc-hwcaps-2-cache.c | 45 | ||||
-rw-r--r-- | elf/tst-glibc-hwcaps-2-cache.root/etc/ld.so.conf | 2 | ||||
-rw-r--r-- | elf/tst-glibc-hwcaps-2-cache.root/postclean.req | 0 | ||||
-rw-r--r-- | elf/tst-glibc-hwcaps-2-cache.script | 8 |
9 files changed, 186 insertions, 18 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 |