]>
Commit | Line | Data |
---|---|---|
c41baa79 | 1 | /* Support for reading /etc/ld.so.cache files written by Linux ldconfig. |
6d7e8eda | 2 | Copyright (C) 1999-2023 Free Software Foundation, Inc. |
c41baa79 UD |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
c41baa79 UD |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
c41baa79 | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 16 | License along with the GNU C Library; if not, see |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
c41baa79 | 18 | |
84ba719b FW |
19 | #ifndef _DL_CACHE_H |
20 | #define _DL_CACHE_H | |
21 | ||
22 | #include <endian.h> | |
23 | #include <stdbool.h> | |
dfb3f101 | 24 | #include <stddef.h> |
a8fd59b0 | 25 | #include <stdint.h> |
dfb3f101 | 26 | #include <string.h> |
a8fd59b0 | 27 | |
e25054c4 AJ |
28 | #ifndef _DL_CACHE_DEFAULT_ID |
29 | # define _DL_CACHE_DEFAULT_ID 3 | |
30 | #endif | |
c41baa79 | 31 | |
e25054c4 AJ |
32 | #ifndef _dl_cache_check_flags |
33 | # define _dl_cache_check_flags(flags) \ | |
c41baa79 | 34 | ((flags) == 1 || (flags) == _DL_CACHE_DEFAULT_ID) |
e25054c4 | 35 | #endif |
45eca4d1 UD |
36 | |
37 | #ifndef LD_SO_CACHE | |
8ca91b36 | 38 | # define LD_SO_CACHE SYSCONFDIR "/ld.so.cache" |
45eca4d1 UD |
39 | #endif |
40 | ||
bd89c0b5 UD |
41 | #ifndef add_system_dir |
42 | # define add_system_dir(dir) add_dir (dir) | |
43 | #endif | |
44 | ||
45eca4d1 UD |
45 | #define CACHEMAGIC "ld.so-1.7.0" |
46 | ||
47 | /* libc5 and glibc 2.0/2.1 use the same format. For glibc 2.2 another | |
48 | format has been added in a compatible way: | |
49 | The beginning of the string table is used for the new table: | |
e25054c4 | 50 | old_magic |
45eca4d1 UD |
51 | nlibs |
52 | libs[0] | |
53 | ... | |
54 | libs[nlibs-1] | |
e25054c4 AJ |
55 | pad, new magic needs to be aligned |
56 | - this is string[0] for the old format | |
57 | new magic - this is string[0] for the new format | |
45eca4d1 UD |
58 | newnlibs |
59 | ... | |
60 | newlibs[0] | |
61 | ... | |
62 | newlibs[newnlibs-1] | |
63 | string 1 | |
64 | string 2 | |
65 | ... | |
66 | */ | |
67 | struct file_entry | |
68 | { | |
de1a9197 FW |
69 | int32_t flags; /* This is 1 for an ELF library. */ |
70 | uint32_t key, value; /* String table indices. */ | |
45eca4d1 UD |
71 | }; |
72 | ||
73 | struct cache_file | |
74 | { | |
75 | char magic[sizeof CACHEMAGIC - 1]; | |
76 | unsigned int nlibs; | |
77 | struct file_entry libs[0]; | |
78 | }; | |
79 | ||
80 | #define CACHEMAGIC_NEW "glibc-ld.so.cache" | |
a8fd59b0 | 81 | #define CACHE_VERSION "1.1" |
ea029468 | 82 | #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION |
45eca4d1 UD |
83 | |
84 | ||
85 | struct file_entry_new | |
86 | { | |
de1a9197 FW |
87 | union |
88 | { | |
89 | /* Fields shared with struct file_entry. */ | |
90 | struct file_entry entry; | |
91 | /* Also expose these fields directly. */ | |
92 | struct | |
93 | { | |
94 | int32_t flags; /* This is 1 for an ELF library. */ | |
95 | uint32_t key, value; /* String table indices. */ | |
96 | }; | |
97 | }; | |
c628c229 | 98 | uint32_t osversion_unused; /* Required OS version (unused). */ |
a8fd59b0 | 99 | uint64_t hwcap; /* Hwcap entry. */ |
45eca4d1 UD |
100 | }; |
101 | ||
b44ac4f4 FW |
102 | /* This bit in the hwcap field of struct file_entry_new indicates that |
103 | the lower 32 bits contain an index into the | |
104 | cache_extension_tag_glibc_hwcaps section. Older glibc versions do | |
105 | not know about this HWCAP bit, so they will ignore these | |
106 | entries. */ | |
107 | #define DL_CACHE_HWCAP_EXTENSION (1ULL << 62) | |
108 | ||
efbbd9c3 L |
109 | /* The number of the ISA level bits in the upper 32 bits of the hwcap |
110 | field. */ | |
111 | #define DL_CACHE_HWCAP_ISA_LEVEL_COUNT 10 | |
112 | ||
113 | /* The mask of the ISA level bits in the hwcap field. */ | |
114 | #define DL_CACHE_HWCAP_ISA_LEVEL_MASK \ | |
115 | ((1 << DL_CACHE_HWCAP_ISA_LEVEL_COUNT) -1) | |
116 | ||
b44ac4f4 FW |
117 | /* Return true if the ENTRY->hwcap value indicates that |
118 | DL_CACHE_HWCAP_EXTENSION is used. */ | |
119 | static inline bool | |
120 | dl_cache_hwcap_extension (struct file_entry_new *entry) | |
121 | { | |
efbbd9c3 L |
122 | /* This is an hwcap extension if only the DL_CACHE_HWCAP_EXTENSION bit |
123 | is set, ignoring the lower 32 bits as well as the ISA level bits in | |
124 | the upper 32 bits. */ | |
125 | return (((entry->hwcap >> 32) & ~DL_CACHE_HWCAP_ISA_LEVEL_MASK) | |
126 | == (DL_CACHE_HWCAP_EXTENSION >> 32)); | |
b44ac4f4 FW |
127 | } |
128 | ||
84ba719b FW |
129 | /* See flags member of struct cache_file_new below. */ |
130 | enum | |
131 | { | |
132 | /* No endianness information available. An old ldconfig version | |
133 | without endianness support wrote the file. */ | |
134 | cache_file_new_flags_endian_unset = 0, | |
135 | ||
136 | /* Cache is invalid and should be ignored. */ | |
137 | cache_file_new_flags_endian_invalid = 1, | |
138 | ||
139 | /* Cache format is little endian. */ | |
140 | cache_file_new_flags_endian_little = 2, | |
141 | ||
142 | /* Cache format is big endian. */ | |
143 | cache_file_new_flags_endian_big = 3, | |
144 | ||
145 | /* Bit mask to extract the cache_file_new_flags_endian_* | |
146 | values. */ | |
147 | cache_file_new_flags_endian_mask = 3, | |
148 | ||
149 | /* Expected value of the endian bits in the flags member for the | |
150 | current architecture. */ | |
151 | cache_file_new_flags_endian_current | |
152 | = (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
153 | ? cache_file_new_flags_endian_little | |
154 | : cache_file_new_flags_endian_big), | |
155 | }; | |
156 | ||
45eca4d1 UD |
157 | struct cache_file_new |
158 | { | |
159 | char magic[sizeof CACHEMAGIC_NEW - 1]; | |
160 | char version[sizeof CACHE_VERSION - 1]; | |
a8fd59b0 AJ |
161 | uint32_t nlibs; /* Number of entries. */ |
162 | uint32_t len_strings; /* Size of string table. */ | |
84ba719b FW |
163 | |
164 | /* flags & cache_file_new_flags_endian_mask is one of the values | |
165 | cache_file_new_flags_endian_unset, cache_file_new_flags_endian_invalid, | |
166 | cache_file_new_flags_endian_little, cache_file_new_flags_endian_big. | |
167 | ||
168 | The remaining bits are unused and should be generated as zero and | |
169 | ignored by readers. */ | |
170 | uint8_t flags; | |
171 | ||
172 | uint8_t padding_unsed[3]; /* Not used, for future extensions. */ | |
173 | ||
dfb3f101 FW |
174 | /* File offset of the extension directory. See struct |
175 | cache_extension below. Must be a multiple of four. */ | |
176 | uint32_t extension_offset; | |
177 | ||
178 | uint32_t unused[3]; /* Leave space for future extensions | |
a8fd59b0 | 179 | and align to 8 byte boundary. */ |
45eca4d1 | 180 | struct file_entry_new libs[0]; /* Entries describing libraries. */ |
a8fd59b0 | 181 | /* After this the string table of size len_strings is found. */ |
45eca4d1 | 182 | }; |
84ba719b FW |
183 | _Static_assert (sizeof (struct cache_file_new) == 48, |
184 | "size of struct cache_file_new"); | |
185 | ||
186 | /* Returns false if *CACHE has the wrong endianness for this | |
187 | architecture, and true if the endianness matches (or is | |
188 | unknown). */ | |
189 | static inline bool | |
190 | cache_file_new_matches_endian (const struct cache_file_new *cache) | |
191 | { | |
192 | /* A zero value for cache->flags means that no endianness | |
193 | information is available. */ | |
194 | return cache->flags == 0 | |
195 | || ((cache->flags & cache_file_new_flags_endian_big) | |
196 | == cache_file_new_flags_endian_current); | |
197 | } | |
198 | ||
45eca4d1 | 199 | |
dfb3f101 FW |
200 | /* Randomly chosen magic value, which allows for additional |
201 | consistency verification. */ | |
202 | enum { cache_extension_magic = (uint32_t) -358342284 }; | |
203 | ||
204 | /* Tag values for different kinds of extension sections. Similar to | |
205 | SHT_* constants. */ | |
206 | enum cache_extension_tag | |
207 | { | |
208 | /* Array of bytes containing the glibc version that generated this | |
209 | cache file. */ | |
210 | cache_extension_tag_generator, | |
211 | ||
b44ac4f4 FW |
212 | /* glibc-hwcaps subdirectory information. An array of uint32_t |
213 | values, which are indices into the string table. The strings | |
214 | are sorted lexicographically (according to strcmp). The extra | |
215 | level of indirection (instead of using string table indices | |
216 | directly) allows the dynamic loader to compute the preference | |
217 | order of the hwcaps names more efficiently. | |
218 | ||
219 | For this section, 4-byte alignment is required, and the section | |
220 | size must be a multiple of 4. */ | |
221 | cache_extension_tag_glibc_hwcaps, | |
222 | ||
dfb3f101 FW |
223 | /* Total number of known cache extension tags. */ |
224 | cache_extension_count | |
225 | }; | |
226 | ||
227 | /* Element in the array following struct cache_extension. Similar to | |
228 | an ELF section header. */ | |
229 | struct cache_extension_section | |
230 | { | |
231 | /* Type of the extension section. A enum cache_extension_tag value. */ | |
232 | uint32_t tag; | |
233 | ||
234 | /* Extension-specific flags. Currently generated as zero. */ | |
235 | uint32_t flags; | |
236 | ||
237 | /* Offset from the start of the file for the data in this extension | |
238 | section. Specific extensions can have alignment constraints. */ | |
239 | uint32_t offset; | |
240 | ||
241 | /* Length in bytes of the extension data. Specific extensions may | |
242 | have size requirements. */ | |
243 | uint32_t size; | |
244 | }; | |
245 | ||
246 | /* The extension directory in the cache. An array of struct | |
247 | cache_extension_section entries. */ | |
248 | struct cache_extension | |
249 | { | |
250 | uint32_t magic; /* Always cache_extension_magic. */ | |
251 | uint32_t count; /* Number of following entries. */ | |
252 | ||
253 | /* count section descriptors of type struct cache_extension_section | |
254 | follow. */ | |
255 | struct cache_extension_section sections[]; | |
256 | }; | |
257 | ||
258 | /* A relocated version of struct cache_extension_section. */ | |
259 | struct cache_extension_loaded | |
260 | { | |
261 | /* Address and size of this extension section. base is NULL if the | |
262 | section is missing from the file. */ | |
263 | const void *base; | |
264 | size_t size; | |
265 | ||
266 | /* Flags from struct cache_extension_section. */ | |
267 | uint32_t flags; | |
268 | }; | |
269 | ||
270 | /* All supported extension sections, relocated. Filled in by | |
271 | cache_extension_load below. */ | |
272 | struct cache_extension_all_loaded | |
273 | { | |
274 | struct cache_extension_loaded sections[cache_extension_count]; | |
275 | }; | |
276 | ||
b44ac4f4 FW |
277 | /* Performs basic data validation based on section tag, and removes |
278 | the sections which are invalid. */ | |
279 | static void | |
280 | cache_extension_verify (struct cache_extension_all_loaded *loaded) | |
281 | { | |
282 | { | |
283 | /* Section must not be empty, it must be aligned at 4 bytes, and | |
284 | the size must be a multiple of 4. */ | |
285 | struct cache_extension_loaded *hwcaps | |
286 | = &loaded->sections[cache_extension_tag_glibc_hwcaps]; | |
287 | if (hwcaps->size == 0 | |
288 | || ((uintptr_t) hwcaps->base % 4) != 0 | |
289 | || (hwcaps->size % 4) != 0) | |
290 | { | |
291 | hwcaps->base = NULL; | |
292 | hwcaps->size = 0; | |
293 | hwcaps->flags = 0; | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
dfb3f101 FW |
298 | static bool __attribute__ ((unused)) |
299 | cache_extension_load (const struct cache_file_new *cache, | |
300 | const void *file_base, size_t file_size, | |
301 | struct cache_extension_all_loaded *loaded) | |
302 | { | |
303 | memset (loaded, 0, sizeof (*loaded)); | |
304 | if (cache->extension_offset == 0) | |
305 | /* No extensions present. This is not a format error. */ | |
306 | return true; | |
307 | if ((cache->extension_offset % 4) != 0) | |
308 | /* Extension offset is misaligned. */ | |
309 | return false; | |
310 | size_t size_tmp; | |
311 | if (__builtin_add_overflow (cache->extension_offset, | |
312 | sizeof (struct cache_extension), &size_tmp) | |
313 | || size_tmp > file_size) | |
314 | /* Extension extends beyond the end of the file. */ | |
315 | return false; | |
316 | const struct cache_extension *ext = file_base + cache->extension_offset; | |
317 | if (ext->magic != cache_extension_magic) | |
318 | return false; | |
319 | if (__builtin_mul_overflow (ext->count, | |
320 | sizeof (struct cache_extension_section), | |
321 | &size_tmp) | |
322 | || __builtin_add_overflow (cache->extension_offset | |
323 | + sizeof (struct cache_extension), size_tmp, | |
324 | &size_tmp) | |
325 | || size_tmp > file_size) | |
326 | /* Extension array extends beyond the end of the file. */ | |
327 | return false; | |
328 | for (uint32_t i = 0; i < ext->count; ++i) | |
329 | { | |
330 | if (__builtin_add_overflow (ext->sections[i].offset, | |
331 | ext->sections[i].size, &size_tmp) | |
332 | || size_tmp > file_size) | |
333 | /* Extension data extends beyond the end of the file. */ | |
334 | return false; | |
335 | ||
336 | uint32_t tag = ext->sections[i].tag; | |
337 | if (tag >= cache_extension_count) | |
338 | /* Tag is out of range and unrecognized. */ | |
339 | continue; | |
340 | loaded->sections[tag].base = file_base + ext->sections[i].offset; | |
341 | loaded->sections[tag].size = ext->sections[i].size; | |
342 | loaded->sections[tag].flags = ext->sections[i].flags; | |
343 | } | |
b44ac4f4 | 344 | cache_extension_verify (loaded); |
dfb3f101 FW |
345 | return true; |
346 | } | |
347 | ||
e25054c4 AJ |
348 | /* Used to align cache_file_new. */ |
349 | #define ALIGN_CACHE(addr) \ | |
350 | (((addr) + __alignof__ (struct cache_file_new) -1) \ | |
351 | & (~(__alignof__ (struct cache_file_new) - 1))) | |
352 | ||
17e00cc6 | 353 | extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden; |
84ba719b FW |
354 | |
355 | #endif /* _DL_CACHE_H */ |