diff options
author | Florian Weimer <fweimer@redhat.com> | 2020-12-04 09:13:43 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2020-12-04 09:16:41 +0100 |
commit | b44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd (patch) | |
tree | 39c5fa6ec9ab24d282c75bb31e15fca0f584e27d | |
parent | 73b6e50a22dea9ae6144beaaa675d2ac62c281ca (diff) | |
download | glibc-b44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd.tar.gz glibc-b44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd.tar.xz glibc-b44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd.zip |
elf: Process glibc-hwcaps subdirectories in ldconfig
Libraries from these subdirectories are added to the cache with a special hwcap bit DL_CACHE_HWCAP_EXTENSION, so that they are ignored by older dynamic loaders. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
-rw-r--r-- | elf/cache.c | 257 | ||||
-rw-r--r-- | elf/ldconfig.c | 155 | ||||
-rw-r--r-- | sysdeps/generic/dl-cache.h | 50 | ||||
-rw-r--r-- | sysdeps/generic/ldconfig.h | 18 |
4 files changed, 427 insertions, 53 deletions
diff --git a/elf/cache.c b/elf/cache.c index 5a6ee20e86..b03c5319f8 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -40,6 +40,105 @@ /* Used to store library names, paths, and other strings. */ static struct stringtable strings; +/* Keeping track of "glibc-hwcaps" subdirectories. During cache + construction, a linear search by name is performed to deduplicate + entries. */ +struct glibc_hwcaps_subdirectory +{ + struct glibc_hwcaps_subdirectory *next; + + /* Interned string with the subdirectory name. */ + struct stringtable_entry *name; + + /* Array index in the cache_extension_tag_glibc_hwcaps section in + the stored cached file. This is computed after all the + subdirectories have been processed, so that subdirectory names in + the extension section can be sorted. */ + uint32_t section_index; + + /* True if the subdirectory is actually used for anything. */ + bool used; +}; + +const char * +glibc_hwcaps_subdirectory_name (const struct glibc_hwcaps_subdirectory *dir) +{ + return dir->name->string; +} + +/* Linked list of known hwcaps subdirecty names. */ +static struct glibc_hwcaps_subdirectory *hwcaps; + +struct glibc_hwcaps_subdirectory * +new_glibc_hwcaps_subdirectory (const char *name) +{ + struct stringtable_entry *name_interned = stringtable_add (&strings, name); + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->name == name_interned) + return p; + struct glibc_hwcaps_subdirectory *p = xmalloc (sizeof (*p)); + p->next = hwcaps; + p->name = name_interned; + p->section_index = 0; + p->used = false; + hwcaps = p; + return p; +} + +/* Helper for sorting struct glibc_hwcaps_subdirectory elements by + name. */ +static int +assign_glibc_hwcaps_indices_compare (const void *l, const void *r) +{ + const struct glibc_hwcaps_subdirectory *left + = *(struct glibc_hwcaps_subdirectory **)l; + const struct glibc_hwcaps_subdirectory *right + = *(struct glibc_hwcaps_subdirectory **)r; + return strcmp (glibc_hwcaps_subdirectory_name (left), + glibc_hwcaps_subdirectory_name (right)); +} + +/* Count the number of hwcaps subdirectories which are actually + used. */ +static size_t +glibc_hwcaps_count (void) +{ + size_t count = 0; + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + ++count; + return count; +} + +/* Compute the section_index fields for all */ +static void +assign_glibc_hwcaps_indices (void) +{ + /* Convert the linked list into an array, so that we can use qsort. + Only copy the subdirectories which are actually used. */ + size_t count = glibc_hwcaps_count (); + struct glibc_hwcaps_subdirectory **array + = xmalloc (sizeof (*array) * count); + { + size_t i = 0; + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + { + array[i] = p; + ++i; + } + assert (i == count); + } + + qsort (array, count, sizeof (*array), assign_glibc_hwcaps_indices_compare); + + /* Assign the array indices. */ + for (size_t i = 0; i < count; ++i) + array[i]->section_index = i; + + free (array); +} + struct cache_entry { struct stringtable_entry *lib; /* Library name. */ @@ -48,6 +147,10 @@ struct cache_entry unsigned int osversion; /* Required OS version. */ uint64_t hwcap; /* Important hardware capabilities. */ int bits_hwcap; /* Number of bits set in hwcap. */ + + /* glibc-hwcaps subdirectory. If not NULL, hwcap must be zero. */ + struct glibc_hwcaps_subdirectory *hwcaps; + struct cache_entry *next; /* Next entry in list. */ }; @@ -60,7 +163,7 @@ static const char *flag_descr[] = /* Print a single entry. */ static void print_entry (const char *lib, int flag, unsigned int osversion, - uint64_t hwcap, const char *key) + uint64_t hwcap, const char *hwcap_string, const char *key) { printf ("\t%s (", lib); switch (flag & FLAG_TYPE_MASK) @@ -132,7 +235,9 @@ print_entry (const char *lib, int flag, unsigned int osversion, printf (",%d", flag & FLAG_REQUIRED_MASK); break; } - if (hwcap != 0) + if (hwcap_string != NULL) + printf (", hwcap: \"%s\"", hwcap_string); + else if (hwcap != 0) printf (", hwcap: %#.16" PRIx64, hwcap); if (osversion != 0) { @@ -158,6 +263,29 @@ print_entry (const char *lib, int flag, unsigned int osversion, printf (") => %s\n", key); } +/* Returns the string with the name of the glibcs-hwcaps subdirectory + associated with ENTRY->hwcap. file_base must be the base address + for string table indices. */ +static const char * +glibc_hwcaps_string (struct cache_extension_all_loaded *ext, + const void *file_base, size_t file_size, + struct file_entry_new *entry) +{ + const uint32_t *hwcaps_array + = ext->sections[cache_extension_tag_glibc_hwcaps].base; + if (dl_cache_hwcap_extension (entry) && hwcaps_array != NULL) + { + uint32_t index = (uint32_t) entry->hwcap; + if (index < ext->sections[cache_extension_tag_glibc_hwcaps].size / 4) + { + uint32_t string_table_index = hwcaps_array[index]; + if (string_table_index < file_size) + return file_base + string_table_index; + } + } + return NULL; +} + /* Print an error and exit if the new-file cache is internally inconsistent. */ static void @@ -167,9 +295,7 @@ check_new_cache (struct cache_file_new *cache) error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); } -/* Print the extension information at the cache at start address - FILE_BASE, of length FILE_SIZE bytes. The new-format cache header - is at CACHE, and the file name for diagnostics is CACHE_NAME. */ +/* Print the extension information in *EXT. */ static void print_extensions (struct cache_extension_all_loaded *ext) { @@ -266,7 +392,7 @@ print_cache (const char *cache_name) /* Print everything. */ for (unsigned int i = 0; i < cache->nlibs; i++) print_entry (cache_data + cache->libs[i].key, - cache->libs[i].flags, 0, 0, + cache->libs[i].flags, 0, 0, NULL, cache_data + cache->libs[i].value); } else if (format == 1) @@ -281,11 +407,16 @@ print_cache (const char *cache_name) /* Print everything. */ for (unsigned int i = 0; i < cache_new->nlibs; i++) - print_entry (cache_data + cache_new->libs[i].key, - cache_new->libs[i].flags, - cache_new->libs[i].osversion, - cache_new->libs[i].hwcap, - cache_data + cache_new->libs[i].value); + { + const char *hwcaps_string + = glibc_hwcaps_string (&ext, cache, cache_size, + &cache_new->libs[i]); + print_entry (cache_data + cache_new->libs[i].key, + cache_new->libs[i].flags, + cache_new->libs[i].osversion, + cache_new->libs[i].hwcap, hwcaps_string, + cache_data + cache_new->libs[i].value); + } print_extensions (&ext); } /* Cleanup. */ @@ -311,8 +442,23 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2) return 1; else if (e1->flags > e2->flags) return -1; + /* Keep the glibc-hwcaps extension entries before the regular + entries, and sort them by their names. search_cache in + dl-cache.c stops searching once the first non-extension entry + is found, so the extension entries need to come first. */ + else if (e1->hwcaps != NULL && e2->hwcaps == NULL) + return -1; + else if (e1->hwcaps == NULL && e2->hwcaps != NULL) + return 1; + else if (e1->hwcaps != NULL && e2->hwcaps != NULL) + { + res = strcmp (glibc_hwcaps_subdirectory_name (e1->hwcaps), + glibc_hwcaps_subdirectory_name (e2->hwcaps)); + if (res != 0) + return res; + } /* Sort by most specific hwcap. */ - else if (e2->bits_hwcap > e1->bits_hwcap) + if (e2->bits_hwcap > e1->bits_hwcap) return 1; else if (e2->bits_hwcap < e1->bits_hwcap) return -1; @@ -337,30 +483,65 @@ enum * sizeof (struct cache_extension_section))) }; -/* Write the cache extensions to FD. The extension directory is - assumed to be located at CACHE_EXTENSION_OFFSET. */ +/* Write the cache extensions to FD. The string table is shifted by + STRING_TABLE_OFFSET. The extension directory is assumed to be + located at CACHE_EXTENSION_OFFSET. assign_glibc_hwcaps_indices + must have been called. */ static void -write_extensions (int fd, uint32_t cache_extension_offset) +write_extensions (int fd, uint32_t str_offset, + uint32_t cache_extension_offset) { assert ((cache_extension_offset % 4) == 0); + /* The length and contents of the glibc-hwcaps section. */ + uint32_t hwcaps_count = glibc_hwcaps_count (); + uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size; + uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t); + uint32_t *hwcaps_array = xmalloc (hwcaps_size); + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + hwcaps_array[p->section_index] = str_offset + p->name->offset; + + /* This is the offset of the generator string. */ + uint32_t generator_offset = hwcaps_offset; + if (hwcaps_count == 0) + /* There is no section for the hwcaps subdirectories. */ + generator_offset -= sizeof (struct cache_extension_section); + else + /* The string table indices for the hwcaps subdirectories shift + the generator string backwards. */ + generator_offset += hwcaps_size; + struct cache_extension *ext = xmalloc (cache_extension_size); ext->magic = cache_extension_magic; - ext->count = cache_extension_count; - for (int i = 0; i < cache_extension_count; ++i) - { - ext->sections[i].tag = i; - ext->sections[i].flags = 0; - } + /* Extension index current being filled. */ + size_t xid = 0; const char *generator = "ldconfig " PKGVERSION RELEASE " release version " VERSION; - ext->sections[cache_extension_tag_generator].offset - = cache_extension_offset + cache_extension_size; - ext->sections[cache_extension_tag_generator].size = strlen (generator); + ext->sections[xid].tag = cache_extension_tag_generator; + ext->sections[xid].flags = 0; + ext->sections[xid].offset = generator_offset; + ext->sections[xid].size = strlen (generator); + + if (hwcaps_count > 0) + { + ++xid; + ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps; + ext->sections[xid].flags = 0; + ext->sections[xid].offset = hwcaps_offset; + ext->sections[xid].size = hwcaps_size; + } + + ++xid; + ext->count = xid; + assert (xid <= cache_extension_count); - if (write (fd, ext, cache_extension_size) != cache_extension_size + size_t ext_size = (offsetof (struct cache_extension, sections) + + xid * sizeof (struct cache_extension_section)); + if (write (fd, ext, ext_size) != ext_size + || write (fd, hwcaps_array, hwcaps_size) != hwcaps_size || write (fd, generator, strlen (generator)) != strlen (generator)) error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); @@ -373,6 +554,8 @@ save_cache (const char *cache_name) { /* The cache entries are sorted already, save them in this order. */ + assign_glibc_hwcaps_indices (); + struct cache_entry *entry; /* Number of cache entries. */ int cache_entry_count = 0; @@ -474,7 +657,11 @@ save_cache (const char *cache_name) struct. */ file_entries_new->libs[idx_new].flags = entry->flags; file_entries_new->libs[idx_new].osversion = entry->osversion; - file_entries_new->libs[idx_new].hwcap = entry->hwcap; + if (entry->hwcaps == NULL) + 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; file_entries_new->libs[idx_new].key = str_offset + entry->lib->offset; file_entries_new->libs[idx_new].value @@ -554,7 +741,7 @@ save_cache (const char *cache_name) /* Align file position to 4. */ off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET); assert ((unsigned long long int) (extension_offset - old_offset) < 4); - write_extensions (fd, extension_offset); + write_extensions (fd, str_offset, extension_offset); } /* Make sure user can always read cache file */ @@ -588,27 +775,35 @@ save_cache (const char *cache_name) /* Add one library to the cache. */ void -add_to_cache (const char *path, const char *lib, int flags, - unsigned int osversion, uint64_t hwcap) +add_to_cache (const char *path, const char *filename, const char *soname, + int flags, unsigned int osversion, uint64_t hwcap, + struct glibc_hwcaps_subdirectory *hwcaps) { struct cache_entry *new_entry = xmalloc (sizeof (*new_entry)); struct stringtable_entry *path_interned; { char *p; - if (asprintf (&p, "%s/%s", path, lib) < 0) + if (asprintf (&p, "%s/%s", path, filename) < 0) error (EXIT_FAILURE, errno, _("Could not create library path")); path_interned = stringtable_add (&strings, p); free (p); } - new_entry->lib = stringtable_add (&strings, lib); + new_entry->lib = stringtable_add (&strings, soname); new_entry->path = path_interned; new_entry->flags = flags; new_entry->osversion = osversion; new_entry->hwcap = hwcap; + new_entry->hwcaps = hwcaps; new_entry->bits_hwcap = 0; + if (hwcaps != NULL) + { + assert (hwcap == 0); + hwcaps->used = true; + } + /* Count the number of bits set in the masked value. */ for (size_t i = 0; (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i) diff --git a/elf/ldconfig.c b/elf/ldconfig.c index 006198fe59..10927a8c7f 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -16,6 +16,7 @@ along with this program; if not, see <https://www.gnu.org/licenses/>. */ #define PROCINFO_CLASS static +#include <assert.h> #include <alloca.h> #include <argp.h> #include <dirent.h> @@ -41,6 +42,7 @@ #include <ldconfig.h> #include <dl-cache.h> +#include <dl-hwcaps.h> #include <dl-procinfo.h> @@ -85,6 +87,10 @@ struct dir_entry dev_t dev; const char *from_file; int from_line; + + /* Non-NULL for subdirectories under a glibc-hwcaps subdirectory. */ + struct glibc_hwcaps_subdirectory *hwcaps; + struct dir_entry *next; }; @@ -338,17 +344,20 @@ new_sub_entry (const struct dir_entry *entry, const char *path, new_entry->from_line = entry->from_line; new_entry->path = xstrdup (path); new_entry->flag = entry->flag; + new_entry->hwcaps = NULL; new_entry->next = NULL; new_entry->ino = st->st_ino; new_entry->dev = st->st_dev; return new_entry; } -/* Add a single directory entry. */ -static void +/* Add a single directory entry. Return true if the directory is + actually added (because it is not a duplicate). */ +static bool add_single_dir (struct dir_entry *entry, int verbose) { struct dir_entry *ptr, *prev; + bool added = true; ptr = dir_entries; prev = ptr; @@ -368,6 +377,7 @@ add_single_dir (struct dir_entry *entry, int verbose) ptr->flag = entry->flag; free (entry->path); free (entry); + added = false; break; } prev = ptr; @@ -378,6 +388,73 @@ add_single_dir (struct dir_entry *entry, int verbose) dir_entries = entry; else if (ptr == NULL) prev->next = entry; + return added; +} + +/* Check if PATH contains a "glibc-hwcaps" subdirectory. If so, queue + its subdirectories for glibc-hwcaps processing. */ +static void +add_glibc_hwcaps_subdirectories (struct dir_entry *entry, const char *path) +{ + /* glibc-hwcaps subdirectories do not nest. */ + assert (entry->hwcaps == NULL); + + char *glibc_hwcaps; + if (asprintf (&glibc_hwcaps, "%s/" GLIBC_HWCAPS_SUBDIRECTORY, path) < 0) + error (EXIT_FAILURE, errno, _("Could not form glibc-hwcaps path")); + + DIR *dir = opendir (glibc_hwcaps); + if (dir != NULL) + { + while (true) + { + errno = 0; + struct dirent64 *e = readdir64 (dir); + if (e == NULL) + { + if (errno == 0) + break; + else + error (EXIT_FAILURE, errno, _("Listing directory %s"), path); + } + + /* Ignore hidden subdirectories, including "." and "..", and + regular files. File names containing a ':' cannot be + looked up by the dynamic loader, so skip those as + well. */ + if (e->d_name[0] == '.' || e->d_type == DT_REG + || strchr (e->d_name, ':') != NULL) + continue; + + /* See if this entry eventually resolves to a directory. */ + struct stat64 st; + if (fstatat64 (dirfd (dir), e->d_name, &st, 0) < 0) + /* Ignore unreadable entries. */ + continue; + + if (S_ISDIR (st.st_mode)) + { + /* This is a directory, so it needs to be scanned for + libraries, associated with the hwcaps implied by the + subdirectory name. */ + char *new_path; + if (asprintf (&new_path, "%s/" GLIBC_HWCAPS_SUBDIRECTORY "/%s", + /* Use non-canonicalized path here. */ + entry->path, e->d_name) < 0) + error (EXIT_FAILURE, errno, + _("Could not form glibc-hwcaps path")); + struct dir_entry *new_entry = new_sub_entry (entry, new_path, + &st); + free (new_path); + new_entry->hwcaps = new_glibc_hwcaps_subdirectory (e->d_name); + add_single_dir (new_entry, 0); + } + } + + closedir (dir); + } + + free (glibc_hwcaps); } /* Add one directory to the list of directories to process. */ @@ -386,6 +463,7 @@ add_dir_1 (const char *line, const char *from_file, int from_line) { unsigned int i; struct dir_entry *entry = xmalloc (sizeof (struct dir_entry)); + entry->hwcaps = NULL; entry->next = NULL; entry->from_file = strdup (from_file); @@ -443,7 +521,9 @@ add_dir_1 (const char *line, const char *from_file, int from_line) entry->ino = stat_buf.st_ino; entry->dev = stat_buf.st_dev; - add_single_dir (entry, 1); + if (add_single_dir (entry, 1)) + /* Add glibc-hwcaps subdirectories if present. */ + add_glibc_hwcaps_subdirectories (entry, path); } if (opt_chroot) @@ -695,15 +775,27 @@ struct dlib_entry static void search_dir (const struct dir_entry *entry) { - uint64_t hwcap = path_hwcap (entry->path); - if (opt_verbose) + uint64_t hwcap; + if (entry->hwcaps == NULL) { - if (hwcap != 0) - printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap); - else - printf ("%s:", entry->path); - printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line); + hwcap = path_hwcap (entry->path); + if (opt_verbose) + { + if (hwcap != 0) + printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap); + else + printf ("%s:", entry->path); + } } + else + { + hwcap = 0; + if (opt_verbose) + printf ("%s: (hwcap: \"%s\")", entry->path, + glibc_hwcaps_subdirectory_name (entry->hwcaps)); + } + if (opt_verbose) + printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line); char *dir_name; char *real_file_name; @@ -745,13 +837,15 @@ search_dir (const struct dir_entry *entry) && direntry->d_type != DT_DIR) continue; /* Does this file look like a shared library or is it a hwcap - subdirectory? The dynamic linker is also considered as + subdirectory (if not already processing a glibc-hwcaps + subdirectory)? The dynamic linker is also considered as shared library. */ if (((strncmp (direntry->d_name, "lib", 3) != 0 && strncmp (direntry->d_name, "ld-", 3) != 0) || strstr (direntry->d_name, ".so") == NULL) && (direntry->d_type == DT_REG - || !is_hwcap_platform (direntry->d_name))) + || (entry->hwcaps == NULL + && !is_hwcap_platform (direntry->d_name)))) continue; size_t len = strlen (direntry->d_name); @@ -799,7 +893,7 @@ search_dir (const struct dir_entry *entry) } struct stat64 stat_buf; - int is_dir; + bool is_dir; int is_link = S_ISLNK (lstat_buf.st_mode); if (is_link) { @@ -837,7 +931,10 @@ search_dir (const struct dir_entry *entry) else is_dir = S_ISDIR (lstat_buf.st_mode); - if (is_dir && is_hwcap_platform (direntry->d_name)) + /* No descending into subdirectories if this directory is a + glibc-hwcaps subdirectory (which are not recursive). */ + if (entry->hwcaps == NULL + && is_dir && is_hwcap_platform (direntry->d_name)) { if (!is_link && direntry->d_type != DT_UNKNOWN @@ -1028,13 +1125,31 @@ search_dir (const struct dir_entry *entry) struct dlib_entry *dlib_ptr; for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next) { - /* Don't create links to links. */ - if (dlib_ptr->is_link == 0) - create_links (dir_name, entry->path, dlib_ptr->name, - dlib_ptr->soname); + /* The cached file name is the soname for non-glibc-hwcaps + subdirectories (relying on symbolic links; this helps with + library updates that change the file name), and the actual + file for glibc-hwcaps subdirectories. */ + const char *filename; + if (entry->hwcaps == NULL) + { + /* Don't create links to links. */ + if (dlib_ptr->is_link == 0) + create_links (dir_name, entry->path, dlib_ptr->name, + dlib_ptr->soname); + filename = dlib_ptr->soname; + } + else + { + /* Do not create links in glibc-hwcaps subdirectories, but + still log the cache addition. */ + if (opt_verbose) + printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name); + filename = dlib_ptr->name; + } if (opt_build_cache) - add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag, - dlib_ptr->osversion, hwcap); + add_to_cache (entry->path, filename, dlib_ptr->soname, + dlib_ptr->flag, dlib_ptr->osversion, + hwcap, entry->hwcaps); } /* Free all resources. */ diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index 393cdc5754..72a66b45ee 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -99,6 +99,23 @@ struct file_entry_new uint64_t hwcap; /* Hwcap entry. */ }; +/* This bit in the hwcap field of struct file_entry_new indicates that + the lower 32 bits contain an index into the + cache_extension_tag_glibc_hwcaps section. Older glibc versions do + not know about this HWCAP bit, so they will ignore these + entries. */ +#define DL_CACHE_HWCAP_EXTENSION (1ULL << 62) + +/* 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); +} + /* See flags member of struct cache_file_new below. */ enum { @@ -182,6 +199,17 @@ enum cache_extension_tag cache file. */ cache_extension_tag_generator, + /* glibc-hwcaps subdirectory information. An array of uint32_t + values, which are indices into the string table. The strings + are sorted lexicographically (according to strcmp). The extra + level of indirection (instead of using string table indices + directly) allows the dynamic loader to compute the preference + order of the hwcaps names more efficiently. + + For this section, 4-byte alignment is required, and the section + size must be a multiple of 4. */ + cache_extension_tag_glibc_hwcaps, + /* Total number of known cache extension tags. */ cache_extension_count }; @@ -236,6 +264,27 @@ struct cache_extension_all_loaded struct cache_extension_loaded sections[cache_extension_count]; }; +/* Performs basic data validation based on section tag, and removes + the sections which are invalid. */ +static void +cache_extension_verify (struct cache_extension_all_loaded *loaded) +{ + { + /* Section must not be empty, it must be aligned at 4 bytes, and + the size must be a multiple of 4. */ + struct cache_extension_loaded *hwcaps + = &loaded->sections[cache_extension_tag_glibc_hwcaps]; + if (hwcaps->size == 0 + || ((uintptr_t) hwcaps->base % 4) != 0 + || (hwcaps->size % 4) != 0) + { + hwcaps->base = NULL; + hwcaps->size = 0; + hwcaps->flags = 0; + } + } +} + static bool __attribute__ ((unused)) cache_extension_load (const struct cache_file_new *cache, const void *file_base, size_t file_size, @@ -282,6 +331,7 @@ cache_extension_load (const struct cache_file_new *cache, loaded->sections[tag].size = ext->sections[i].size; loaded->sections[tag].flags = ext->sections[i].flags; } + cache_extension_verify (loaded); return true; } diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h index cfec9d4668..1ad1528890 100644 --- a/sysdeps/generic/ldconfig.h +++ b/sysdeps/generic/ldconfig.h @@ -57,8 +57,22 @@ extern void init_cache (void); extern void save_cache (const char *cache_name); -extern void add_to_cache (const char *path, const char *lib, int flags, - unsigned int osversion, uint64_t hwcap); +struct glibc_hwcaps_subdirectory; + +/* Return a struct describing the subdirectory for NAME. Reuse an + existing struct if it exists. */ +struct glibc_hwcaps_subdirectory *new_glibc_hwcaps_subdirectory + (const char *name); + +/* Returns the name that was specified when + add_glibc_hwcaps_subdirectory was called. */ +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, + struct glibc_hwcaps_subdirectory *); extern void init_aux_cache (void); |