1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2024 Google LLC
4 */
5
6 #define _GNU_SOURCE
7 #include <inttypes.h>
8 #include <stdio.h>
9 #include <zlib.h>
10
11 #include "gendwarfksyms.h"
12
13 static struct cache expansion_cache;
14
15 /*
16 * A simple linked list of shared or owned strings to avoid copying strings
17 * around when not necessary.
18 */
19 struct type_list_entry {
20 const char *str;
21 void *owned;
22 struct list_head list;
23 };
24
type_list_free(struct list_head * list)25 static void type_list_free(struct list_head *list)
26 {
27 struct type_list_entry *entry;
28 struct type_list_entry *tmp;
29
30 list_for_each_entry_safe(entry, tmp, list, list) {
31 if (entry->owned)
32 free(entry->owned);
33 free(entry);
34 }
35
36 INIT_LIST_HEAD(list);
37 }
38
type_list_append(struct list_head * list,const char * s,void * owned)39 static int type_list_append(struct list_head *list, const char *s, void *owned)
40 {
41 struct type_list_entry *entry;
42
43 if (!s)
44 return 0;
45
46 entry = xmalloc(sizeof(struct type_list_entry));
47 entry->str = s;
48 entry->owned = owned;
49 list_add_tail(&entry->list, list);
50
51 return strlen(entry->str);
52 }
53
type_list_write(struct list_head * list,FILE * file)54 static void type_list_write(struct list_head *list, FILE *file)
55 {
56 struct type_list_entry *entry;
57
58 list_for_each_entry(entry, list, list) {
59 if (entry->str)
60 checkp(fputs(entry->str, file));
61 }
62 }
63
64 /*
65 * An expanded type string in symtypes format.
66 */
67 struct type_expansion {
68 char *name;
69 size_t len;
70 struct list_head expanded;
71 struct hlist_node hash;
72 };
73
type_expansion_init(struct type_expansion * type)74 static void type_expansion_init(struct type_expansion *type)
75 {
76 type->name = NULL;
77 type->len = 0;
78 INIT_LIST_HEAD(&type->expanded);
79 }
80
type_expansion_free(struct type_expansion * type)81 static inline void type_expansion_free(struct type_expansion *type)
82 {
83 free(type->name);
84 type->name = NULL;
85 type->len = 0;
86 type_list_free(&type->expanded);
87 }
88
type_expansion_append(struct type_expansion * type,const char * s,void * owned)89 static void type_expansion_append(struct type_expansion *type, const char *s,
90 void *owned)
91 {
92 type->len += type_list_append(&type->expanded, s, owned);
93 }
94
95 /*
96 * type_map -- the longest expansions for each type.
97 *
98 * const char *name -> struct type_expansion *
99 */
100 #define TYPE_HASH_BITS 12
101 static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS);
102
type_map_get(const char * name,struct type_expansion ** res)103 static int type_map_get(const char *name, struct type_expansion **res)
104 {
105 struct type_expansion *e;
106
107 hash_for_each_possible(type_map, e, hash, hash_str(name)) {
108 if (!strcmp(name, e->name)) {
109 *res = e;
110 return 0;
111 }
112 }
113
114 return -1;
115 }
116
type_map_add(const char * name,struct type_expansion * type)117 static void type_map_add(const char *name, struct type_expansion *type)
118 {
119 struct type_expansion *e;
120
121 if (type_map_get(name, &e)) {
122 e = xmalloc(sizeof(struct type_expansion));
123 type_expansion_init(e);
124 e->name = xstrdup(name);
125
126 hash_add(type_map, &e->hash, hash_str(e->name));
127
128 if (dump_types)
129 debug("adding %s", e->name);
130 } else {
131 /* Use the longest available expansion */
132 if (type->len <= e->len)
133 return;
134
135 type_list_free(&e->expanded);
136
137 if (dump_types)
138 debug("replacing %s", e->name);
139 }
140
141 /* Take ownership of type->expanded */
142 list_replace_init(&type->expanded, &e->expanded);
143 e->len = type->len;
144
145 if (dump_types) {
146 checkp(fputs(e->name, stderr));
147 checkp(fputs(" ", stderr));
148 type_list_write(&e->expanded, stderr);
149 checkp(fputs("\n", stderr));
150 }
151 }
152
type_map_write(FILE * file)153 static void type_map_write(FILE *file)
154 {
155 struct type_expansion *e;
156 struct hlist_node *tmp;
157
158 if (!file)
159 return;
160
161 hash_for_each_safe(type_map, e, tmp, hash) {
162 checkp(fputs(e->name, file));
163 checkp(fputs(" ", file));
164 type_list_write(&e->expanded, file);
165 checkp(fputs("\n", file));
166 }
167 }
168
type_map_free(void)169 static void type_map_free(void)
170 {
171 struct type_expansion *e;
172 struct hlist_node *tmp;
173
174 hash_for_each_safe(type_map, e, tmp, hash) {
175 type_expansion_free(e);
176 free(e);
177 }
178
179 hash_init(type_map);
180 }
181
182 /*
183 * CRC for a type, with an optional fully expanded type string for
184 * debugging.
185 */
186 struct version {
187 struct type_expansion type;
188 unsigned long crc;
189 };
190
version_init(struct version * version)191 static void version_init(struct version *version)
192 {
193 version->crc = crc32(0, NULL, 0);
194 type_expansion_init(&version->type);
195 }
196
version_free(struct version * version)197 static void version_free(struct version *version)
198 {
199 type_expansion_free(&version->type);
200 }
201
version_add(struct version * version,const char * s)202 static void version_add(struct version *version, const char *s)
203 {
204 version->crc = crc32(version->crc, (void *)s, strlen(s));
205 if (dump_versions)
206 type_expansion_append(&version->type, s, NULL);
207 }
208
209 /*
210 * Type reference format: <prefix>#<name>, where prefix:
211 * s -> structure
212 * u -> union
213 * e -> enum
214 * t -> typedef
215 *
216 * Names with spaces are additionally wrapped in single quotes.
217 */
is_type_prefix(const char * s)218 static inline bool is_type_prefix(const char *s)
219 {
220 return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') &&
221 s[1] == '#';
222 }
223
get_type_prefix(int tag)224 static char get_type_prefix(int tag)
225 {
226 switch (tag) {
227 case DW_TAG_class_type:
228 case DW_TAG_structure_type:
229 return 's';
230 case DW_TAG_union_type:
231 return 'u';
232 case DW_TAG_enumeration_type:
233 return 'e';
234 case DW_TAG_typedef_type:
235 return 't';
236 default:
237 return 0;
238 }
239 }
240
get_type_name(struct die * cache)241 static char *get_type_name(struct die *cache)
242 {
243 const char *quote;
244 char prefix;
245 char *name;
246
247 if (cache->state == DIE_INCOMPLETE) {
248 warn("found incomplete cache entry: %p", cache);
249 return NULL;
250 }
251 if (cache->state == DIE_SYMBOL)
252 return NULL;
253 if (!cache->fqn || !*cache->fqn)
254 return NULL;
255
256 prefix = get_type_prefix(cache->tag);
257 if (!prefix)
258 return NULL;
259
260 /* Wrap names with spaces in single quotes */
261 quote = strstr(cache->fqn, " ") ? "'" : "";
262
263 /* <prefix>#<type_name>\0 */
264 if (asprintf(&name, "%c#%s%s%s", prefix, quote, cache->fqn, quote) < 0)
265 error("asprintf failed for '%s'", cache->fqn);
266
267 return name;
268 }
269
__calculate_version(struct version * version,struct list_head * list)270 static void __calculate_version(struct version *version, struct list_head *list)
271 {
272 struct type_list_entry *entry;
273 struct type_expansion *e;
274
275 /* Calculate a CRC over an expanded type string */
276 list_for_each_entry(entry, list, list) {
277 if (is_type_prefix(entry->str)) {
278 check(type_map_get(entry->str, &e));
279
280 /*
281 * It's sufficient to expand each type reference just
282 * once to detect changes.
283 */
284 if (cache_was_expanded(&expansion_cache, e)) {
285 version_add(version, entry->str);
286 } else {
287 cache_mark_expanded(&expansion_cache, e);
288 __calculate_version(version, &e->expanded);
289 }
290 } else {
291 version_add(version, entry->str);
292 }
293 }
294 }
295
calculate_version(struct version * version,struct list_head * list)296 static void calculate_version(struct version *version, struct list_head *list)
297 {
298 version_init(version);
299 __calculate_version(version, list);
300 cache_free(&expansion_cache);
301 }
302
303 static void __type_expand(struct die *cache, struct type_expansion *type,
304 bool recursive);
305
type_expand_child(struct die * cache,struct type_expansion * type,bool recursive)306 static void type_expand_child(struct die *cache, struct type_expansion *type,
307 bool recursive)
308 {
309 struct type_expansion child;
310 char *name;
311
312 name = get_type_name(cache);
313 if (!name) {
314 __type_expand(cache, type, recursive);
315 return;
316 }
317
318 if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) {
319 __cache_mark_expanded(&expansion_cache, cache->addr);
320 type_expansion_init(&child);
321 __type_expand(cache, &child, true);
322 type_map_add(name, &child);
323 type_expansion_free(&child);
324 }
325
326 type_expansion_append(type, name, name);
327 }
328
__type_expand(struct die * cache,struct type_expansion * type,bool recursive)329 static void __type_expand(struct die *cache, struct type_expansion *type,
330 bool recursive)
331 {
332 struct die_fragment *df;
333 struct die *child;
334
335 list_for_each_entry(df, &cache->fragments, list) {
336 switch (df->type) {
337 case FRAGMENT_STRING:
338 type_expansion_append(type, df->data.str, NULL);
339 break;
340 case FRAGMENT_DIE:
341 /* Use a complete die_map expansion if available */
342 if (__die_map_get(df->data.addr, DIE_COMPLETE,
343 &child) &&
344 __die_map_get(df->data.addr, DIE_UNEXPANDED,
345 &child))
346 error("unknown child: %" PRIxPTR,
347 df->data.addr);
348
349 type_expand_child(child, type, recursive);
350 break;
351 case FRAGMENT_LINEBREAK:
352 /*
353 * Keep whitespace in the symtypes format, but avoid
354 * repeated spaces.
355 */
356 if (list_is_last(&df->list, &cache->fragments) ||
357 list_next_entry(df, list)->type !=
358 FRAGMENT_LINEBREAK)
359 type_expansion_append(type, " ", NULL);
360 break;
361 default:
362 error("empty die_fragment in %p", cache);
363 }
364 }
365 }
366
type_expand(struct die * cache,struct type_expansion * type,bool recursive)367 static void type_expand(struct die *cache, struct type_expansion *type,
368 bool recursive)
369 {
370 type_expansion_init(type);
371 __type_expand(cache, type, recursive);
372 cache_free(&expansion_cache);
373 }
374
expand_type(struct die * cache,void * arg)375 static void expand_type(struct die *cache, void *arg)
376 {
377 struct type_expansion type;
378 char *name;
379
380 if (cache->mapped)
381 return;
382
383 cache->mapped = true;
384
385 /*
386 * Skip unexpanded die_map entries if there's a complete
387 * expansion available for this DIE.
388 */
389 if (cache->state == DIE_UNEXPANDED &&
390 !__die_map_get(cache->addr, DIE_COMPLETE, &cache)) {
391 if (cache->mapped)
392 return;
393
394 cache->mapped = true;
395 }
396
397 name = get_type_name(cache);
398 if (!name)
399 return;
400
401 debug("%s", name);
402 type_expand(cache, &type, true);
403 type_map_add(name, &type);
404
405 type_expansion_free(&type);
406 free(name);
407 }
408
expand_symbol(struct symbol * sym,void * arg)409 static void expand_symbol(struct symbol *sym, void *arg)
410 {
411 struct type_expansion type;
412 struct version version;
413 struct die *cache;
414
415 /*
416 * No need to expand again unless we want a symtypes file entry
417 * for the symbol. Note that this means `sym` has the same address
418 * as another symbol that was already processed.
419 */
420 if (!symtypes && sym->state == SYMBOL_PROCESSED)
421 return;
422
423 if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
424 return; /* We'll warn about missing CRCs later. */
425
426 type_expand(cache, &type, false);
427
428 /* If the symbol already has a version, don't calculate it again. */
429 if (sym->state != SYMBOL_PROCESSED) {
430 calculate_version(&version, &type.expanded);
431 symbol_set_crc(sym, version.crc);
432 debug("%s = %lx", sym->name, version.crc);
433
434 if (dump_versions) {
435 checkp(fputs(sym->name, stderr));
436 checkp(fputs(" ", stderr));
437 type_list_write(&version.type.expanded, stderr);
438 checkp(fputs("\n", stderr));
439 }
440
441 version_free(&version);
442 }
443
444 /* These aren't needed in type_map unless we want a symtypes file. */
445 if (symtypes)
446 type_map_add(sym->name, &type);
447
448 type_expansion_free(&type);
449 }
450
generate_symtypes_and_versions(FILE * file)451 void generate_symtypes_and_versions(FILE *file)
452 {
453 cache_init(&expansion_cache);
454
455 /*
456 * die_map processing:
457 *
458 * 1. die_map contains all types referenced in exported symbol
459 * signatures, but can contain duplicates just like the original
460 * DWARF, and some references may not be fully expanded depending
461 * on how far we processed the DIE tree for that specific symbol.
462 *
463 * For each die_map entry, find the longest available expansion,
464 * and add it to type_map.
465 */
466 die_map_for_each(expand_type, NULL);
467
468 /*
469 * 2. For each exported symbol, expand the die_map type, and use
470 * type_map expansions to calculate a symbol version from the
471 * fully expanded type string.
472 */
473 symbol_for_each(expand_symbol, NULL);
474
475 /*
476 * 3. If a symtypes file is requested, write type_map contents to
477 * the file.
478 */
479 type_map_write(file);
480 type_map_free();
481 }
482