about summary refs log tree commit diff
path: root/sysdeps
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-12-04 09:13:43 +0100
committerFlorian Weimer <fweimer@redhat.com>2020-12-04 09:16:41 +0100
commitdfb3f101c5ef23adf60d389058a2b33e23303d04 (patch)
tree68f9ddebe898005e690cb8099df05b46ddc461d9 /sysdeps
parent84ba719b260551918965d0a433914de683087645 (diff)
downloadglibc-dfb3f101c5ef23adf60d389058a2b33e23303d04.tar.gz
glibc-dfb3f101c5ef23adf60d389058a2b33e23303d04.tar.xz
glibc-dfb3f101c5ef23adf60d389058a2b33e23303d04.zip
elf: Add extension mechanism to ld.so.cache
A previously unused new-format header field is used to record
the address of an extension directory.

This change adds a demo extension which records the version of
ldconfig which builds a file.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/generic/dl-cache.h123
1 files changed, 122 insertions, 1 deletions
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index 4c77cd0c1b..393cdc5754 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -21,7 +21,9 @@
 
 #include <endian.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 
 #ifndef _DL_CACHE_DEFAULT_ID
 # define _DL_CACHE_DEFAULT_ID	3
@@ -142,7 +144,11 @@ struct cache_file_new
 
   uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
 
-  uint32_t unused[4];		/* Leave space for future extensions
+  /* File offset of the extension directory.  See struct
+     cache_extension below.  Must be a multiple of four.  */
+  uint32_t extension_offset;
+
+  uint32_t unused[3];		/* Leave space for future extensions
 				   and align to 8 byte boundary.  */
   struct file_entry_new libs[0]; /* Entries describing libraries.  */
   /* After this the string table of size len_strings is found.	*/
@@ -164,6 +170,121 @@ cache_file_new_matches_endian (const struct cache_file_new *cache)
 }
 
 
+/* Randomly chosen magic value, which allows for additional
+   consistency verification.  */
+enum { cache_extension_magic = (uint32_t) -358342284 };
+
+/* Tag values for different kinds of extension sections.  Similar to
+   SHT_* constants.  */
+enum cache_extension_tag
+  {
+   /* Array of bytes containing the glibc version that generated this
+      cache file.  */
+   cache_extension_tag_generator,
+
+   /* Total number of known cache extension tags.  */
+   cache_extension_count
+  };
+
+/* Element in the array following struct cache_extension.  Similar to
+   an ELF section header.  */
+struct cache_extension_section
+{
+  /* Type of the extension section.  A enum cache_extension_tag value.  */
+  uint32_t tag;
+
+  /* Extension-specific flags.  Currently generated as zero.  */
+  uint32_t flags;
+
+  /* Offset from the start of the file for the data in this extension
+     section.  Specific extensions can have alignment constraints.  */
+  uint32_t offset;
+
+  /* Length in bytes of the extension data.  Specific extensions may
+     have size requirements.  */
+  uint32_t size;
+};
+
+/* The extension directory in the cache.  An array of struct
+   cache_extension_section entries.  */
+struct cache_extension
+{
+  uint32_t magic;		/* Always cache_extension_magic.  */
+  uint32_t count;		/* Number of following entries.  */
+
+  /* count section descriptors of type struct cache_extension_section
+     follow.  */
+  struct cache_extension_section sections[];
+};
+
+/* A relocated version of struct cache_extension_section.  */
+struct cache_extension_loaded
+{
+  /* Address and size of this extension section.  base is NULL if the
+     section is missing from the file.  */
+  const void *base;
+  size_t size;
+
+  /* Flags from struct cache_extension_section.  */
+  uint32_t flags;
+};
+
+/* All supported extension sections, relocated.  Filled in by
+   cache_extension_load below.  */
+struct cache_extension_all_loaded
+{
+  struct cache_extension_loaded sections[cache_extension_count];
+};
+
+static bool __attribute__ ((unused))
+cache_extension_load (const struct cache_file_new *cache,
+		      const void *file_base, size_t file_size,
+		      struct cache_extension_all_loaded *loaded)
+{
+  memset (loaded, 0, sizeof (*loaded));
+  if (cache->extension_offset == 0)
+    /* No extensions present.  This is not a format error.  */
+    return true;
+  if ((cache->extension_offset % 4) != 0)
+    /* Extension offset is misaligned.  */
+    return false;
+  size_t size_tmp;
+  if (__builtin_add_overflow (cache->extension_offset,
+			      sizeof (struct cache_extension), &size_tmp)
+      || size_tmp > file_size)
+    /* Extension extends beyond the end of the file.  */
+    return false;
+  const struct cache_extension *ext = file_base + cache->extension_offset;
+  if (ext->magic != cache_extension_magic)
+    return false;
+  if (__builtin_mul_overflow (ext->count,
+			      sizeof (struct cache_extension_section),
+			      &size_tmp)
+      || __builtin_add_overflow (cache->extension_offset
+				 + sizeof (struct cache_extension), size_tmp,
+				 &size_tmp)
+      || size_tmp > file_size)
+    /* Extension array extends beyond the end of the file.  */
+    return false;
+  for (uint32_t i = 0; i < ext->count; ++i)
+    {
+      if (__builtin_add_overflow (ext->sections[i].offset,
+				  ext->sections[i].size, &size_tmp)
+	  || size_tmp > file_size)
+	/* Extension data extends beyond the end of the file.  */
+	return false;
+
+      uint32_t tag = ext->sections[i].tag;
+      if (tag >= cache_extension_count)
+	/* Tag is out of range and unrecognized.  */
+	continue;
+      loaded->sections[tag].base = file_base + ext->sections[i].offset;
+      loaded->sections[tag].size = ext->sections[i].size;
+      loaded->sections[tag].flags = ext->sections[i].flags;
+    }
+  return true;
+}
+
 /* Used to align cache_file_new.  */
 #define ALIGN_CACHE(addr)				\
 (((addr) + __alignof__ (struct cache_file_new) -1)	\