[PATCH 09/11] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing
Florian Weimer
fweimer@redhat.com
Tue Dec 1 20:45:47 GMT 2020
* Adhemerval Zanella via Libc-alpha:
> On 09/11/2020 15:41, Florian Weimer via Libc-alpha wrote:
>> This recognizes the DL_CACHE_HWCAP_EXTENSION flag and picks up
>> the supported cache entry with the highest priority.
>
> Maybe add a brief description of what DL_CACHE_HWCAP_EXTENSION aims to
> do?
It's documented in sysdeps/generic/dl-cache.h, added in the previous
commit:
/* 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 the priority of the cache_extension_tag_glibc_hwcaps section
>> + entry at INDEX. Zero means do not use. Otherwise, lower values
>> + indicate greater preference. */
>> +static uint32_t __attribute__ ((noinline, noclone))
>> +glibc_hwcaps_priority (uint32_t index)
>
> I have a feeling I have asked it before, but why does it need noclone/noinline
> here?
I don't recall such a discussion. It's a leftover from previous
debugging efforts.
>> +{
>> + /* Using a zero-length array as an indicator that nothing has been
>> + loaded is not a problem: It does not lead to repeated
>> + initialization attempts because caches without an extension
>> + section are processed without calling this function (unless the
>> + file is corrupted). */
>> + if (glibc_hwcaps_priorities_length == 0)
>
> Maybe an early exit here? It allow move the code one identation left
> and makes it more readable.
I've moved this into a separate initialization function.
>> + while (left < left_end && right < right_end)
>> + {
>> + uint32_t string_table_index = *left;
>> + if (string_table_index < cachesize)
>> + {
>> + const char *left_name
>> + = (const char *) cache + string_table_index;
>> + uint32_t left_name_length = strlen (left_name);
>> + uint32_t to_compare;
>> + if (left_name_length < right->name_length)
>> + to_compare = left_name_length;
>> + else
>> + to_compare = right->name_length;
>> + int cmp = memcmp (left_name, right->name, to_compare);
>> + if (cmp == 0)
>> + {
>> + if (left_name_length < right->name_length)
>> + cmp = -1;
>> + else if (left_name_length > right->name_length)
>> + cmp = 1;
>> + }
>> + if (cmp == 0)
>> + {
>> + *result = right->priority;
>> + ++result;
>> + ++left;
>> + ++right;
>> + }
>
> Maybe add as the 'else' within the 'cmp == 0' below?
>
>> + else if (cmp < 0)
>> + {
>> + *result = 0;
>> + ++result;
>> + ++left;
>> + }
>> + else
>> + ++right;
You mean like this?
if (cmp == 0)
{
*result = right->priority;
++result;
++left;
}
if (cmp < 0)
{
*result = 0;
++result;
++left;
}
if (cmp >= 0)
++right;
I don't think that's an improvement.
>> +struct dl_hwcaps_priority *_dl_hwcaps_priorities;
>> +uint32_t _dl_hwcaps_priorities_length;
>> +
>> +/* Allocate _dl_hwcaps_priorities and fill it with data. */
>> +static void
>> +compute_priorities (size_t total_count, const char *prepend,
>> + int32_t bitmask, const char *mask)
>
> I think bitmask should be a 'uint32' here.
Right, fixed.
>> +int
>> +main (int argc, char **argv)
>> +{
>> + /* Run ldconfig to populate the cache. */
>> + {
>> + char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir);
>> + if (system (command) != 0)
>> + return 1;
>> + 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", support_objdir_root);
>> + execv (path, argv);
>> + printf ("error: execv of %s failed: %m\n", path);
>> + return 1;
>> +}
>
> Ok, it should be simple enough to require use libsupport fork/timeout
> check.
Yes, we run ldconfig without timeout elsewhere, and tst-glibc-hwcaps has
its own timeout check.
> Should we test the case of shared memory removal, for instance:
>
> 1. Remove second override, without ldconfig
> 2. Remove third override, run ldconfig
> 3. Add both second and third back, run ldconfig, remove third
> 4. Keep second, run ldconfig
I added this:
+ /* Remove the second override again, without running ldconfig.
+ Ideally, this would revert to implementation 2. However, in the
+ current implementation, the cache returns exactly one file name
+ which does not exist after unlinking, so the dlopen fails. */
+ xunlink ("/glibc-test/lib/glibc-hwcaps/prepend3/" SONAME);
+ TEST_VERIFY (dlopen (SONAME, RTLD_NOW) == NULL);
+ run_ldconfig ();
+ system("/sbin/ldconfig -p");
+ {
+ /* After running ldconfig, the second implementation is available
+ once more. */
+ void *handle = xdlopen (SONAME, RTLD_NOW);
+ int (*marker1) (void) = xdlsym (handle, "marker1");
+ TEST_COMPARE (marker1 (), 2);
+ xdlclose (handle);
+ }
I think I know what I have to do to fix this in elf/dl-load.c.
I feel this is a pre-existing issue. It also applies to the legacy
hwcaps subdirectories. This only happens for ld.so.cache bringing in
libraries from a non-searched directory, otherwise the LD_LIBRARY_PATH
processing will find an implementation if it exists. I'd like to fix it
in a follow-up patch.
Thanks,
Florian
--
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill
More information about the Libc-alpha
mailing list