From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-qk1-x742.google.com (mail-qk1-x742.google.com [IPv6:2607:f8b0:4864:20::742]) by sourceware.org (Postfix) with ESMTPS id 9DA7A3857C6C for ; Tue, 27 Oct 2020 17:28:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 9DA7A3857C6C Received: by mail-qk1-x742.google.com with SMTP id r7so1948404qkf.3 for ; Tue, 27 Oct 2020 10:28:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:references:from:autocrypt:subject:message-id :date:user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=bjky+agXg+WXFYpk3d9euOQJVqT2ZmASjBb6JOaI8vg=; b=mHOqi6pjfX37SEhkAjuFIjqDCHEwV4fyzlMzxA6agocZt54XQIv/LiVgg7aoL5FXtJ BRA8mOH/MiDBIjGuRjUITh+Bg8+AF/Xuu3jXQIBv5WNHxuyaTLQt+Gpl2g1mnBDBaJfF KSoTVqF40j6gl4HP26ne2wrBkZsnv4gUTw4AKVP+7BooLA5qQjebh/uJvzaONshBeADI 3Qj0ICddxAhoJ/Dba2ecYQLwCENk8JzkO/yTQzKRXLHTvjkObeH+pMj/oTe4uZW4HWHk 6NuXT0inGW62Bp9Is7b7JoB8un5mWPfIgSup3vcUFX+hUeR2KdbxE0hKoNUAeOHO0LQH gMkA== X-Gm-Message-State: AOAM532JXDcf8eSbJ2PaEmPvtYyyBl0ZK4y9MZlne9XeJTBnpFZHCrrK DkuhIoviyG7XGjWaEQ2AuDhdhFTE29c7nw== X-Google-Smtp-Source: ABdhPJwOH2Hw7bMfYrN9Ul3Z2JJcb8xoGfdHN7odNcEs4MHmG4R5O36XgzUABxxcSoAmcuvpAcuc3g== X-Received: by 2002:a37:9d0c:: with SMTP id g12mr3243370qke.264.1603819708478; Tue, 27 Oct 2020 10:28:28 -0700 (PDT) Received: from [192.168.1.4] ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id 184sm1131504qkl.34.2020.10.27.10.28.27 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 27 Oct 2020 10:28:27 -0700 (PDT) To: libc-alpha@sourceware.org, Florian Weimer References: From: Adhemerval Zanella Autocrypt: addr=adhemerval.zanella@linaro.org; prefer-encrypt=mutual; keydata= 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 Subject: Re: [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig Message-ID: Date: Tue, 27 Oct 2020 14:28:25 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-15.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 27 Oct 2020 17:28:33 -0000 On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote: > 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. Looks goods in general, some comments below. > --- > elf/cache.c | 258 ++++++++++++++++++++++++++++++++----- > elf/ldconfig.c | 153 +++++++++++++++++++--- > sysdeps/generic/dl-cache.h | 51 +++++++- > sysdeps/generic/ldconfig.h | 18 ++- > 4 files changed, 426 insertions(+), 54 deletions(-) > > diff --git a/elf/cache.c b/elf/cache.c > index eda3da98a7..7ce4ca9870 100644 > --- a/elf/cache.c > +++ b/elf/cache.c > @@ -40,6 +40,105 @@ > /* Used to store library names, paths, and other strings. */ > 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; > +}; > + Ok. > +const char * > +glibc_hwcaps_subdirectory_name (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_intern (&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; > +} > + Ok. > +/* 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 (left->name->string, right->name->string); > +} > + Maybe: 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; > +} > + Ok. > +/* 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); Do we need this assert? I think it would make sense if hwcaps is modified concurrently, which does not seem the case. > + } > + > + 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); > +} > + Ok. > 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. */ > }; > Ok. > @@ -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) > { Ok. > @@ -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; > +} > + Ok. > /* 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 ltength 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) Ok. > @@ -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. */ Ok. > @@ -311,8 +442,22 @@ 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 (e1->hwcaps->name->string, e2->hwcaps->name->string); Maybe: 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; Ok. > @@ -337,30 +482,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_count * sizeof (uint32_t); Maybe 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; > - } Ok, although maybe you could refactor the 'elf: Add extension mechanism to ld.so.cache' to avoid add such code. > + /* 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); Would it make more sense to reference the index directly using the enumeration instead or add an assert to check if the index is within the expected size? > > - 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)); So here we could just use cache_extension_count instead of 'xid' (with the advantage that we certify at compile-time that only know cache_extension_count will be written on the file). > + 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 +553,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; Ok. > @@ -474,7 +656,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 Ok. > @@ -554,7 +740,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 */ Ok. > @@ -588,8 +774,9 @@ 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)); > > @@ -597,11 +784,11 @@ add_to_cache (const char *path, const char *lib, int flags, > { > /* Use a small, on-stack buffer in most cases. */ > char buf[200]; > - int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib); > + int ret = snprintf (buf, sizeof (buf), "%s/%s", path, filename); > if (ret < 0 || ret >= sizeof (buf) - 1) > { > 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_intern (&strings, p); > free (p); Ok. > @@ -610,13 +797,20 @@ add_to_cache (const char *path, const char *lib, int flags, > path_interned = stringtable_intern (&strings, buf); > } > > - new_entry->lib = stringtable_intern (&strings, lib); > + new_entry->lib = stringtable_intern (&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) Ok. > diff --git a/elf/ldconfig.c b/elf/ldconfig.c > index 3768267bac..3136601de7 100644 > --- a/elf/ldconfig.c > +++ b/elf/ldconfig.c > @@ -16,6 +16,7 @@ > along with this program; if not, see . */ > > #define PROCINFO_CLASS static > +#include > #include > #include > #include > @@ -41,6 +42,7 @@ > > #include > #include > +#include > > #include > > @@ -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; > }; > Ok. > @@ -339,17 +345,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; > @@ -369,6 +378,7 @@ add_single_dir (struct dir_entry *entry, int verbose) > ptr->flag = entry->flag; > free (entry->path); > free (entry); > + added = false; > break; > } > prev = ptr; > @@ -379,6 +389,73 @@ add_single_dir (struct dir_entry *entry, int verbose) > dir_entries = entry; > else if (ptr == NULL) > prev->next = entry; > + return added; > +} > + Ok. > +/* 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. */ Ok. > @@ -387,6 +464,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); > @@ -444,7 +522,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) Ok. > @@ -696,15 +776,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; Ok. > @@ -746,13 +838,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); Ok. > @@ -838,7 +932,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 is_dir is an 'int', maybe make it a boolean? > @@ -1029,13 +1126,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. */ Ok. > diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h > index fec209509d..66b0312ac1 100644 > --- a/sysdeps/generic/dl-cache.h > +++ b/sysdeps/generic/dl-cache.h > @@ -81,7 +81,6 @@ struct cache_file > #define CACHE_VERSION "1.1" > #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION > > - Spurious line removal. > struct file_entry_new > { > union > @@ -99,6 +98,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 > { Ok. > @@ -161,6 +177,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 > }; Ok. > @@ -215,6 +242,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; > + } > + } > +} > + Ok. > static bool __attribute__ ((unused)) > cache_extension_load (const struct cache_file_new *cache, > const void *file_base, size_t file_size, > @@ -261,6 +309,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; > } > Ok. > diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h > index b64aab0064..30a76481aa 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 > + (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); > > Ok.