/* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
Copyright (C) 1999-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _DL_CACHE_H
#define _DL_CACHE_H
#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
#endif
#ifndef _dl_cache_check_flags
# define _dl_cache_check_flags(flags) \
((flags) == 1 || (flags) == _DL_CACHE_DEFAULT_ID)
#endif
#ifndef LD_SO_CACHE
# define LD_SO_CACHE SYSCONFDIR "/ld.so.cache"
#endif
#ifndef add_system_dir
# define add_system_dir(dir) add_dir (dir)
#endif
#define CACHEMAGIC "ld.so-1.7.0"
/* libc5 and glibc 2.0/2.1 use the same format. For glibc 2.2 another
format has been added in a compatible way:
The beginning of the string table is used for the new table:
old_magic
nlibs
libs[0]
...
libs[nlibs-1]
pad, new magic needs to be aligned
- this is string[0] for the old format
new magic - this is string[0] for the new format
newnlibs
...
newlibs[0]
...
newlibs[newnlibs-1]
string 1
string 2
...
*/
struct file_entry
{
int32_t flags; /* This is 1 for an ELF library. */
uint32_t key, value; /* String table indices. */
};
struct cache_file
{
char magic[sizeof CACHEMAGIC - 1];
unsigned int nlibs;
struct file_entry libs[0];
};
#define CACHEMAGIC_NEW "glibc-ld.so.cache"
#define CACHE_VERSION "1.1"
#define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
struct file_entry_new
{
union
{
/* Fields shared with struct file_entry. */
struct file_entry entry;
/* Also expose these fields directly. */
struct
{
int32_t flags; /* This is 1 for an ELF library. */
uint32_t key, value; /* String table indices. */
};
};
uint32_t osversion; /* Required OS version. */
uint64_t hwcap; /* Hwcap entry. */
};
/* See flags member of struct cache_file_new below. */
enum
{
/* No endianness information available. An old ldconfig version
without endianness support wrote the file. */
cache_file_new_flags_endian_unset = 0,
/* Cache is invalid and should be ignored. */
cache_file_new_flags_endian_invalid = 1,
/* Cache format is little endian. */
cache_file_new_flags_endian_little = 2,
/* Cache format is big endian. */
cache_file_new_flags_endian_big = 3,
/* Bit mask to extract the cache_file_new_flags_endian_*
values. */
cache_file_new_flags_endian_mask = 3,
/* Expected value of the endian bits in the flags member for the
current architecture. */
cache_file_new_flags_endian_current
= (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
? cache_file_new_flags_endian_little
: cache_file_new_flags_endian_big),
};
struct cache_file_new
{
char magic[sizeof CACHEMAGIC_NEW - 1];
char version[sizeof CACHE_VERSION - 1];
uint32_t nlibs; /* Number of entries. */
uint32_t len_strings; /* Size of string table. */
/* flags & cache_file_new_flags_endian_mask is one of the values
cache_file_new_flags_endian_unset, cache_file_new_flags_endian_invalid,
cache_file_new_flags_endian_little, cache_file_new_flags_endian_big.
The remaining bits are unused and should be generated as zero and
ignored by readers. */
uint8_t flags;
uint8_t padding_unsed[3]; /* Not used, 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. */
};
_Static_assert (sizeof (struct cache_file_new) == 48,
"size of struct cache_file_new");
/* Returns false if *CACHE has the wrong endianness for this
architecture, and true if the endianness matches (or is
unknown). */
static inline bool
cache_file_new_matches_endian (const struct cache_file_new *cache)
{
/* A zero value for cache->flags means that no endianness
information is available. */
return cache->flags == 0
|| ((cache->flags & cache_file_new_flags_endian_big)
== cache_file_new_flags_endian_current);
}
/* 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) \
& (~(__alignof__ (struct cache_file_new) - 1)))
extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden;
#endif /* _DL_CACHE_H */