xref: /linux/scripts/gendwarfksyms/types.c (revision ba6ec09911b805778a2fed6d626bfe77b011a717)
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