aboutsummaryrefslogtreecommitdiff
path: root/elf/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/cache.c')
-rw-r--r--elf/cache.c277
1 files changed, 175 insertions, 102 deletions
diff --git a/elf/cache.c b/elf/cache.c
index cc75704f54..fbbc426268 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -17,9 +17,6 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#define _GNU_SOURCE 1
-
-#include <assert.h>
#include <errno.h>
#include <error.h>
#include <dirent.h>
@@ -34,32 +31,18 @@
#include <sys/types.h>
#include "ldconfig.h"
-
-#define CACHEMAGIC "ld.so-1.7.0"
+#include "dl-cache.h"
struct cache_entry
{
- char *lib;
- char *path;
- int flags;
- struct cache_entry *next;
+ char *lib; /* Library name. */
+ char *path; /* Path to find library. */
+ int flags; /* Flags to indicate kind of library. */
+ unsigned long int hwcap; /* Important hardware capabilities. */
+ int bits_hwcap; /* Number of bits set in hwcap. */
+ struct cache_entry *next; /* Next entry in list. */
};
-struct file_entry
-{
- int flags; /* This is 1 for an ELF library. */
- unsigned int key, value; /* String table indices. */
-};
-
-
-struct cache_file
-{
- char magic[sizeof CACHEMAGIC - 1];
- unsigned int nlibs;
- struct file_entry libs[0];
-};
-
-
/* List of all cache entries. */
static struct cache_entry *entries;
@@ -69,7 +52,7 @@ static const char *flag_descr[] =
/* Print a single entry. */
static void
-print_entry (const char *lib, int flag, const char *key)
+print_entry (const char *lib, int flag, unsigned long int hwcap, const char *key)
{
printf ("\t%s (", lib);
switch (flag & FLAG_TYPE_MASK)
@@ -93,14 +76,17 @@ print_entry (const char *lib, int flag, const char *key)
case 0:
break;
default:
- fprintf (stdout, ",%d", flag & FLAG_REQUIRED_MASK);
+ printf (",%d", flag & FLAG_REQUIRED_MASK);
break;
}
+ if (hwcap != 0)
+ printf (", hwcap: 0x%lx", hwcap);
printf (") => %s\n", key);
}
-/* Print the whole cache file. */
+/* Print the whole cache file, if a file contains the new cache format
+ hidden in the old one, print the contents of the new format. */
void
print_cache (const char *cache_name)
{
@@ -109,7 +95,9 @@ print_cache (const char *cache_name)
int fd;
unsigned int i;
struct cache_file *cache;
+ struct cache_file_new *cache_new = NULL;
const char *cache_data;
+ int format = 0;
fd = open (cache_name, O_RDONLY);
if (fd < 0)
@@ -128,20 +116,63 @@ print_cache (const char *cache_name)
error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
cache_size = st.st_size;
- if (cache_size < sizeof (struct cache_file)
- || memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
- return;
- /* This is where the strings start. */
- cache_data = (const char *) &cache->libs[cache->nlibs];
+ if (cache_size < sizeof (struct cache_file))
+ error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+
+ if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
+ {
+ /* This can only be the new format without the old one. */
+ cache_new = (struct cache_file_new *) cache;
+
+ if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+ || memcmp (cache_new->version, CACHE_VERSION,
+ sizeof CACHE_VERSION - 1))
+ error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+ format = 1;
+ /* This is where the strings start. */
+ cache_data = (const char *) cache;
+ }
+ else
+ {
+ /* This is where the strings start. */
+ cache_data = (const char *) &cache->libs[cache->nlibs];
+
+ /* Check for a new cache embedded in the old format. */
+ if (cache_size >
+ (sizeof (struct cache_file)
+ + cache->nlibs * sizeof (struct file_entry)
+ + sizeof (struct cache_file_new)))
+ {
+ cache_new = (struct cache_file_new *) cache_data;
- printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+ if (!memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
+ && !memcmp (cache_new->version, CACHE_VERSION,
+ sizeof CACHE_VERSION - 1))
+ format = 1;
+ }
+ }
- /* Print everything. */
- for (i = 0; i < cache->nlibs; i++)
- print_entry (cache_data + cache->libs[i].key,
- cache->libs[i].flags,
- cache_data + cache->libs[i].value);
+ if (format == 0)
+ {
+ printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
+ /* Print everything. */
+ for (i = 0; i < cache->nlibs; i++)
+ print_entry (cache_data + cache->libs[i].key,
+ cache->libs[i].flags, 0,
+ cache_data + cache->libs[i].value);
+ }
+ else if (format == 1)
+ {
+ printf (_("%d libs found in cache `%s'\n"), cache_new->nlibs, cache_name);
+
+ /* Print everything. */
+ for (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].hwcap,
+ cache_data + cache_new->libs[i].value);
+ }
/* Cleanup. */
munmap (cache, cache_size);
close (fd);
@@ -155,45 +186,6 @@ init_cache (void)
}
-/* Helper function which must match the one in the dynamic linker, so that
- we rely on the same sort order. */
-int
-cache_libcmp (const char *p1, const char *p2)
-{
- while (*p1 != '\0')
- {
- if (*p1 >= '0' && *p1 <= '9')
- {
- if (*p2 >= '0' && *p2 <= '9')
- {
- /* Must compare this numerically. */
- int val1;
- int val2;
-
- val1 = *p1++ - '0';
- val2 = *p2++ - '0';
- while (*p1 >= '0' && *p1 <= '9')
- val1 = val1 * 10 + *p1++ - '0';
- while (*p2 >= '0' && *p2 <= '9')
- val2 = val2 * 10 + *p2++ - '0';
- if (val1 != val2)
- return val1 - val2;
- }
- else
- return 1;
- }
- else if (*p2 >= '0' && *p2 <= '9')
- return -1;
- else if (*p1 != *p2)
- return *p1 - *p2;
- else
- {
- ++p1;
- ++p2;
- }
- }
- return *p1 - *p2;
-}
static
int compare (const struct cache_entry *e1, const struct cache_entry *e2)
@@ -201,80 +193,142 @@ int compare (const struct cache_entry *e1, const struct cache_entry *e2)
int res;
/* We need to swap entries here to get the correct sort order. */
- res = cache_libcmp (e2->lib, e1->lib);
+ res = _dl_cache_libcmp (e2->lib, e1->lib);
if (res == 0)
{
if (e1->flags < e2->flags)
return 1;
else if (e1->flags > e2->flags)
return -1;
+ /* Sort by most specific hwcap. */
+ else if (e2->bits_hwcap > e1->bits_hwcap)
+ return 1;
+ else if (e2->bits_hwcap < e1->bits_hwcap)
+ return -1;
+ else if (e2->hwcap > e1->hwcap)
+ return 1;
+ else if (e2->hwcap < e1->hwcap)
+ return -1;
}
return res;
}
-
/* Save the contents of the cache. */
void
save_cache (const char *cache_name)
{
struct cache_entry *entry;
- int i, fd;
+ int fd, idx_old, idx_new;
size_t total_strlen, len;
char *strings, *str, *temp_name;
- struct cache_file *file_entries;
- size_t file_entries_size;
+ struct cache_file *file_entries = NULL;
+ struct cache_file_new *file_entries_new = NULL;
+ size_t file_entries_size = 0;
+ size_t file_entries_new_size = 0;
unsigned int str_offset;
/* Number of cache entries. */
int cache_entry_count = 0;
+ /* Number of normal cache entries. */
+ int cache_entry_old_count = 0;
/* The cache entries are sorted already, save them in this order. */
/* Count the length of all strings. */
+ /* The old format doesn't contain hwcap entries and doesn't contain
+ libraries in subdirectories with hwcaps entries. Count therefore
+ also all entries with hwcap == 0. */
total_strlen = 0;
for (entry = entries; entry != NULL; entry = entry->next)
{
/* Account the final NULs. */
total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
++cache_entry_count;
+ if (entry->hwcap == 0)
+ ++cache_entry_old_count;
}
/* Create the on disk cache structure. */
/* First an array for all strings. */
- strings = (char *)xmalloc (total_strlen + 1);
+ strings = (char *)xmalloc (total_strlen);
- /* And the list of all entries. */
- file_entries_size = sizeof (struct cache_file)
- + cache_entry_count * sizeof (struct file_entry);
- file_entries = (struct cache_file *) xmalloc (file_entries_size);
+ if (opt_format != 2)
+ {
+ /* And the list of all entries in the old format. */
+ file_entries_size = sizeof (struct cache_file)
+ + cache_entry_old_count * sizeof (struct file_entry);
+ file_entries = (struct cache_file *) xmalloc (file_entries_size);
+
+ /* Fill in the header. */
+ memset (file_entries, 0, sizeof (struct cache_file));
+ memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
- /* Fill in the header. */
- memset (file_entries, 0, sizeof (struct cache_file));
- memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
+ file_entries->nlibs = cache_entry_old_count;
+ }
- file_entries->nlibs = cache_entry_count;
+ if (opt_format != 0)
+ {
+ /* And the list of all entries in the new format. */
+ file_entries_new_size = sizeof (struct cache_file_new)
+ + cache_entry_count * sizeof (struct file_entry_new);
+ file_entries_new = (struct cache_file_new *) xmalloc (file_entries_new_size);
+
+ /* Fill in the header. */
+ memset (file_entries_new, 0, sizeof (struct cache_file_new));
+ memcpy (file_entries_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1);
+ memcpy (file_entries_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1);
+
+ file_entries_new->nlibs = cache_entry_count;
+ file_entries_new->len_strings = total_strlen;
+ }
+
+ /* If we have both formats, we hide the new format in the strings
+ table, we have to adjust all string indices for this so that
+ old libc5/glibc 2 dynamic linkers just ignore them. */
+ if (opt_format == 1)
+ str_offset = file_entries_new_size;
+ else
+ str_offset = 0;
- str_offset = 0;
str = strings;
- for (i = 0, entry = entries; entry != NULL; entry = entry->next, ++i)
+ for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
+ entry = entry->next, ++idx_new)
{
- file_entries->libs[i].flags = entry->flags;
/* First the library. */
- /* XXX: Actually we can optimize here and remove duplicates. */
- file_entries->libs[i].key = str_offset;
+ if (opt_format != 2)
+ {
+ file_entries->libs[idx_old].flags = entry->flags;
+ /* XXX: Actually we can optimize here and remove duplicates. */
+ file_entries->libs[idx_old].key = str_offset;
+ }
+ if (opt_format != 0)
+ {
+ /* We could subtract file_entries_new_size from str_offset -
+ not doing so makes the code easier, the string table
+ always begins at the beginning of the the new cache
+ struct. */
+ file_entries_new->libs[idx_new].flags = entry->flags;
+ file_entries_new->libs[idx_new].hwcap = entry->hwcap;
+ file_entries_new->libs[idx_new].key = str_offset;
+ }
len = strlen (entry->lib);
str = stpcpy (str, entry->lib);
/* Account the final NUL. */
++str;
str_offset += len + 1;
/* Then the path. */
- file_entries->libs[i].value = str_offset;
+ if (opt_format != 2)
+ file_entries->libs[idx_old].value = str_offset;
+ if (opt_format != 0)
+ file_entries_new->libs[idx_new].value = str_offset;
len = strlen (entry->path);
str = stpcpy (str, entry->path);
/* Account the final NUL. */
++str;
str_offset += len + 1;
+ /* Ignore entries with hwcap for old format. */
+ if (entry->hwcap == 0)
+ ++idx_old;
}
- assert (str_offset == total_strlen);
/* Write out the cache. */
@@ -293,8 +347,17 @@ save_cache (const char *cache_name)
temp_name);
/* Write contents. */
- if (write (fd, file_entries, file_entries_size) != (ssize_t)file_entries_size)
- error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ if (opt_format != 2)
+ {
+ if (write (fd, file_entries, file_entries_size) != (ssize_t)file_entries_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ }
+ if (opt_format != 0)
+ {
+ if (write (fd, file_entries_new, file_entries_new_size)
+ != (ssize_t)file_entries_new_size)
+ error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
+ }
if (write (fd, strings, total_strlen) != (ssize_t)total_strlen)
error (EXIT_FAILURE, errno, _("Writing of cache data failed."));
@@ -325,13 +388,15 @@ save_cache (const char *cache_name)
}
}
+
/* Add one library to the cache. */
void
-add_to_cache (const char *path, const char *lib, int flags)
+add_to_cache (const char *path, const char *lib, int flags,
+ unsigned long int hwcap)
{
struct cache_entry *new_entry, *ptr, *prev;
char *full_path;
- int len;
+ int len, i;
new_entry = (struct cache_entry *) xmalloc (sizeof (struct cache_entry));
@@ -343,6 +408,14 @@ add_to_cache (const char *path, const char *lib, int flags)
new_entry->lib = xstrdup (lib);
new_entry->path = full_path;
new_entry->flags = flags;
+ new_entry->hwcap = hwcap;
+ new_entry->bits_hwcap = 0;
+
+ /* Count the number of bits set in the masked value. */
+ for (i = 0; (~((1UL << i) - 1) & hwcap) != 0; ++i)
+ if ((hwcap & (1UL << i)) != 0)
+ ++new_entry->bits_hwcap;
+
/* Keep the list sorted - search for right place to insert. */
ptr = entries;