mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	tools: Add gendwarfksyms
Add a basic DWARF parser, which uses libdw to traverse the debugging information in an object file and looks for functions and variables. In follow-up patches, this will be expanded to produce symbol versions for CONFIG_MODVERSIONS from DWARF. Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Reviewed-by: Petr Pavlu <petr.pavlu@suse.com> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
This commit is contained in:
		
							parent
							
								
									a56fece7f3
								
							
						
					
					
						commit
						f28568841a
					
				
					 9 changed files with 513 additions and 0 deletions
				
			
		|  | @ -9550,6 +9550,13 @@ W:	https://linuxtv.org | ||||||
| T:	git git://linuxtv.org/media.git | T:	git git://linuxtv.org/media.git | ||||||
| F:	drivers/media/radio/radio-gemtek* | F:	drivers/media/radio/radio-gemtek* | ||||||
| 
 | 
 | ||||||
|  | GENDWARFKSYMS | ||||||
|  | M:	Sami Tolvanen <samitolvanen@google.com> | ||||||
|  | L:	linux-modules@vger.kernel.org | ||||||
|  | L:	linux-kbuild@vger.kernel.org | ||||||
|  | S:	Maintained | ||||||
|  | F:	scripts/gendwarfksyms/ | ||||||
|  | 
 | ||||||
| GENERIC ARCHITECTURE TOPOLOGY | GENERIC ARCHITECTURE TOPOLOGY | ||||||
| M:	Sudeep Holla <sudeep.holla@arm.com> | M:	Sudeep Holla <sudeep.holla@arm.com> | ||||||
| L:	linux-kernel@vger.kernel.org | L:	linux-kernel@vger.kernel.org | ||||||
|  |  | ||||||
|  | @ -169,6 +169,14 @@ config MODVERSIONS | ||||||
| 	  make them incompatible with the kernel you are running.  If | 	  make them incompatible with the kernel you are running.  If | ||||||
| 	  unsure, say N. | 	  unsure, say N. | ||||||
| 
 | 
 | ||||||
|  | config GENDWARFKSYMS | ||||||
|  | 	bool "gendwarfksyms (from debugging information)" | ||||||
|  | 	depends on DEBUG_INFO | ||||||
|  | 	# Requires full debugging information, split DWARF not supported. | ||||||
|  | 	depends on !DEBUG_INFO_REDUCED && !DEBUG_INFO_SPLIT | ||||||
|  | 	# Requires ELF object files. | ||||||
|  | 	depends on !LTO | ||||||
|  | 
 | ||||||
| config ASM_MODVERSIONS | config ASM_MODVERSIONS | ||||||
| 	bool | 	bool | ||||||
| 	default HAVE_ASM_MODVERSIONS && MODVERSIONS | 	default HAVE_ASM_MODVERSIONS && MODVERSIONS | ||||||
|  |  | ||||||
|  | @ -54,6 +54,7 @@ targets += module.lds | ||||||
| 
 | 
 | ||||||
| subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins | subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins | ||||||
| subdir-$(CONFIG_MODVERSIONS) += genksyms | subdir-$(CONFIG_MODVERSIONS) += genksyms | ||||||
|  | subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms | ||||||
| subdir-$(CONFIG_SECURITY_SELINUX) += selinux | subdir-$(CONFIG_SECURITY_SELINUX) += selinux | ||||||
| subdir-$(CONFIG_SECURITY_IPE) += ipe | subdir-$(CONFIG_SECURITY_IPE) += ipe | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								scripts/gendwarfksyms/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								scripts/gendwarfksyms/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | /gendwarfksyms | ||||||
							
								
								
									
										8
									
								
								scripts/gendwarfksyms/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								scripts/gendwarfksyms/Makefile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | # SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | hostprogs-always-y += gendwarfksyms | ||||||
|  | 
 | ||||||
|  | gendwarfksyms-objs += gendwarfksyms.o | ||||||
|  | gendwarfksyms-objs += dwarf.o | ||||||
|  | gendwarfksyms-objs += symbols.o | ||||||
|  | 
 | ||||||
|  | HOSTLDLIBS_gendwarfksyms := -ldw -lelf | ||||||
							
								
								
									
										166
									
								
								scripts/gendwarfksyms/dwarf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								scripts/gendwarfksyms/dwarf.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2024 Google LLC | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "gendwarfksyms.h" | ||||||
|  | 
 | ||||||
|  | static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value) | ||||||
|  | { | ||||||
|  | 	Dwarf_Attribute da; | ||||||
|  | 
 | ||||||
|  | 	/* dwarf_formref_die returns a pointer instead of an error value. */ | ||||||
|  | 	return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define DEFINE_GET_STRING_ATTR(attr)                         \ | ||||||
|  | 	static const char *get_##attr##_attr(Dwarf_Die *die) \ | ||||||
|  | 	{                                                    \ | ||||||
|  | 		Dwarf_Attribute da;                          \ | ||||||
|  | 		if (dwarf_attr(die, DW_AT_##attr, &da))      \ | ||||||
|  | 			return dwarf_formstring(&da);        \ | ||||||
|  | 		return NULL;                                 \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | DEFINE_GET_STRING_ATTR(name) | ||||||
|  | DEFINE_GET_STRING_ATTR(linkage_name) | ||||||
|  | 
 | ||||||
|  | static const char *get_symbol_name(Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	const char *name; | ||||||
|  | 
 | ||||||
|  | 	/* rustc uses DW_AT_linkage_name for exported symbols */ | ||||||
|  | 	name = get_linkage_name_attr(die); | ||||||
|  | 	if (!name) | ||||||
|  | 		name = get_name_attr(die); | ||||||
|  | 
 | ||||||
|  | 	return name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool match_export_symbol(struct state *state, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	Dwarf_Die *source = die; | ||||||
|  | 	Dwarf_Die origin; | ||||||
|  | 
 | ||||||
|  | 	/* If the DIE has an abstract origin, use it for type information. */ | ||||||
|  | 	if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin)) | ||||||
|  | 		source = &origin; | ||||||
|  | 
 | ||||||
|  | 	state->sym = symbol_get(get_symbol_name(die)); | ||||||
|  | 
 | ||||||
|  | 	/* Look up using the origin name if there are no matches. */ | ||||||
|  | 	if (!state->sym && source != die) | ||||||
|  | 		state->sym = symbol_get(get_symbol_name(source)); | ||||||
|  | 
 | ||||||
|  | 	state->die = *source; | ||||||
|  | 	return !!state->sym; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Type string processing | ||||||
|  |  */ | ||||||
|  | static void process(const char *s) | ||||||
|  | { | ||||||
|  | 	s = s ?: "<null>"; | ||||||
|  | 
 | ||||||
|  | 	if (dump_dies) | ||||||
|  | 		fputs(s, stderr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool match_all(Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int process_die_container(struct state *state, Dwarf_Die *die, | ||||||
|  | 			  die_callback_t func, die_match_callback_t match) | ||||||
|  | { | ||||||
|  | 	Dwarf_Die current; | ||||||
|  | 	int res; | ||||||
|  | 
 | ||||||
|  | 	res = checkp(dwarf_child(die, ¤t)); | ||||||
|  | 	while (!res) { | ||||||
|  | 		if (match(¤t)) { | ||||||
|  | 			/* <0 = error, 0 = continue, >0 = stop */ | ||||||
|  | 			res = checkp(func(state, ¤t)); | ||||||
|  | 			if (res) | ||||||
|  | 				return res; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		res = checkp(dwarf_siblingof(¤t, ¤t)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Exported symbol processing | ||||||
|  |  */ | ||||||
|  | static void process_symbol(struct state *state, Dwarf_Die *die, | ||||||
|  | 			   die_callback_t process_func) | ||||||
|  | { | ||||||
|  | 	debug("%s", state->sym->name); | ||||||
|  | 	check(process_func(state, die)); | ||||||
|  | 	if (dump_dies) | ||||||
|  | 		fputs("\n", stderr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __process_subprogram(struct state *state, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	process("subprogram"); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void process_subprogram(struct state *state, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	process_symbol(state, die, __process_subprogram); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int __process_variable(struct state *state, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	process("variable "); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void process_variable(struct state *state, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	process_symbol(state, die, __process_variable); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int process_exported_symbols(struct state *unused, Dwarf_Die *die) | ||||||
|  | { | ||||||
|  | 	int tag = dwarf_tag(die); | ||||||
|  | 
 | ||||||
|  | 	switch (tag) { | ||||||
|  | 	/* Possible containers of exported symbols */ | ||||||
|  | 	case DW_TAG_namespace: | ||||||
|  | 	case DW_TAG_class_type: | ||||||
|  | 	case DW_TAG_structure_type: | ||||||
|  | 		return check(process_die_container( | ||||||
|  | 			NULL, die, process_exported_symbols, match_all)); | ||||||
|  | 
 | ||||||
|  | 	/* Possible exported symbols */ | ||||||
|  | 	case DW_TAG_subprogram: | ||||||
|  | 	case DW_TAG_variable: { | ||||||
|  | 		struct state state; | ||||||
|  | 
 | ||||||
|  | 		if (!match_export_symbol(&state, die)) | ||||||
|  | 			return 0; | ||||||
|  | 
 | ||||||
|  | 		if (tag == DW_TAG_subprogram) | ||||||
|  | 			process_subprogram(&state, &state.die); | ||||||
|  | 		else | ||||||
|  | 			process_variable(&state, &state.die); | ||||||
|  | 
 | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	default: | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void process_cu(Dwarf_Die *cudie) | ||||||
|  | { | ||||||
|  | 	check(process_die_container(NULL, cudie, process_exported_symbols, | ||||||
|  | 				    match_all)); | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								scripts/gendwarfksyms/gendwarfksyms.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								scripts/gendwarfksyms/gendwarfksyms.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,128 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2024 Google LLC | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <getopt.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include "gendwarfksyms.h" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Options | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* Print debugging information to stderr */ | ||||||
|  | int debug; | ||||||
|  | /* Dump DIE contents */ | ||||||
|  | int dump_dies; | ||||||
|  | 
 | ||||||
|  | static void usage(void) | ||||||
|  | { | ||||||
|  | 	fputs("Usage: gendwarfksyms [options] elf-object-file ... < symbol-list\n\n" | ||||||
|  | 	      "Options:\n" | ||||||
|  | 	      "  -d, --debug          Print debugging information\n" | ||||||
|  | 	      "      --dump-dies      Dump DWARF DIE contents\n" | ||||||
|  | 	      "  -h, --help           Print this message\n" | ||||||
|  | 	      "\n", | ||||||
|  | 	      stderr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int process_module(Dwfl_Module *mod, void **userdata, const char *name, | ||||||
|  | 			  Dwarf_Addr base, void *arg) | ||||||
|  | { | ||||||
|  | 	Dwarf_Addr dwbias; | ||||||
|  | 	Dwarf_Die cudie; | ||||||
|  | 	Dwarf_CU *cu = NULL; | ||||||
|  | 	Dwarf *dbg; | ||||||
|  | 	int res; | ||||||
|  | 
 | ||||||
|  | 	debug("%s", name); | ||||||
|  | 	dbg = dwfl_module_getdwarf(mod, &dwbias); | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL); | ||||||
|  | 		if (res < 0) | ||||||
|  | 			error("dwarf_get_units failed: no debugging information?"); | ||||||
|  | 		if (res == 1) | ||||||
|  | 			break; /* No more units */ | ||||||
|  | 
 | ||||||
|  | 		process_cu(&cudie); | ||||||
|  | 	} while (cu); | ||||||
|  | 
 | ||||||
|  | 	return DWARF_CB_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const Dwfl_Callbacks callbacks = { | ||||||
|  | 	.section_address = dwfl_offline_section_address, | ||||||
|  | 	.find_debuginfo = dwfl_standard_find_debuginfo, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	unsigned int n; | ||||||
|  | 	int opt; | ||||||
|  | 
 | ||||||
|  | 	static const struct option opts[] = { | ||||||
|  | 		{ "debug", 0, NULL, 'd' }, | ||||||
|  | 		{ "dump-dies", 0, &dump_dies, 1 }, | ||||||
|  | 		{ "help", 0, NULL, 'h' }, | ||||||
|  | 		{ 0, 0, NULL, 0 } | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	while ((opt = getopt_long(argc, argv, "dh", opts, NULL)) != EOF) { | ||||||
|  | 		switch (opt) { | ||||||
|  | 		case 0: | ||||||
|  | 			break; | ||||||
|  | 		case 'd': | ||||||
|  | 			debug = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'h': | ||||||
|  | 			usage(); | ||||||
|  | 			return 0; | ||||||
|  | 		default: | ||||||
|  | 			usage(); | ||||||
|  | 			return 1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optind >= argc) { | ||||||
|  | 		usage(); | ||||||
|  | 		error("no input files?"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	symbol_read_exports(stdin); | ||||||
|  | 
 | ||||||
|  | 	for (n = optind; n < argc; n++) { | ||||||
|  | 		Dwfl *dwfl; | ||||||
|  | 		int fd; | ||||||
|  | 
 | ||||||
|  | 		fd = open(argv[n], O_RDONLY); | ||||||
|  | 		if (fd == -1) | ||||||
|  | 			error("open failed for '%s': %s", argv[n], | ||||||
|  | 			      strerror(errno)); | ||||||
|  | 
 | ||||||
|  | 		dwfl = dwfl_begin(&callbacks); | ||||||
|  | 		if (!dwfl) | ||||||
|  | 			error("dwfl_begin failed for '%s': %s", argv[n], | ||||||
|  | 			      dwarf_errmsg(-1)); | ||||||
|  | 
 | ||||||
|  | 		if (!dwfl_report_offline(dwfl, argv[n], argv[n], fd)) | ||||||
|  | 			error("dwfl_report_offline failed for '%s': %s", | ||||||
|  | 			      argv[n], dwarf_errmsg(-1)); | ||||||
|  | 
 | ||||||
|  | 		dwfl_report_end(dwfl, NULL, NULL); | ||||||
|  | 
 | ||||||
|  | 		if (dwfl_getmodules(dwfl, &process_module, NULL, 0)) | ||||||
|  | 			error("dwfl_getmodules failed for '%s'", argv[n]); | ||||||
|  | 
 | ||||||
|  | 		dwfl_end(dwfl); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	symbol_free(); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										95
									
								
								scripts/gendwarfksyms/gendwarfksyms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								scripts/gendwarfksyms/gendwarfksyms.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2024 Google LLC | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <dwarf.h> | ||||||
|  | #include <elfutils/libdw.h> | ||||||
|  | #include <elfutils/libdwfl.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <hash.h> | ||||||
|  | #include <hashtable.h> | ||||||
|  | #include <xalloc.h> | ||||||
|  | 
 | ||||||
|  | #ifndef __GENDWARFKSYMS_H | ||||||
|  | #define __GENDWARFKSYMS_H | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Options -- in gendwarfksyms.c | ||||||
|  |  */ | ||||||
|  | extern int debug; | ||||||
|  | extern int dump_dies; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Output helpers | ||||||
|  |  */ | ||||||
|  | #define __PREFIX "gendwarfksyms: " | ||||||
|  | #define __println(prefix, format, ...)                                \ | ||||||
|  | 	fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \ | ||||||
|  | 		##__VA_ARGS__) | ||||||
|  | 
 | ||||||
|  | #define debug(format, ...)                                    \ | ||||||
|  | 	do {                                                  \ | ||||||
|  | 		if (debug)                                    \ | ||||||
|  | 			__println("", format, ##__VA_ARGS__); \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | #define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__) | ||||||
|  | #define error(format, ...)                                   \ | ||||||
|  | 	do {                                                 \ | ||||||
|  | 		__println("error: ", format, ##__VA_ARGS__); \ | ||||||
|  | 		exit(1);                                     \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Error handling helpers | ||||||
|  |  */ | ||||||
|  | #define __check(expr, test)                                     \ | ||||||
|  | 	({                                                      \ | ||||||
|  | 		int __res = expr;                               \ | ||||||
|  | 		if (test)                                       \ | ||||||
|  | 			error("`%s` failed: %d", #expr, __res); \ | ||||||
|  | 		__res;                                          \ | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | /* Error == non-zero values */ | ||||||
|  | #define check(expr) __check(expr, __res) | ||||||
|  | /* Error == negative values */ | ||||||
|  | #define checkp(expr) __check(expr, __res < 0) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * symbols.c | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | struct symbol { | ||||||
|  | 	const char *name; | ||||||
|  | 	struct hlist_node name_hash; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef void (*symbol_callback_t)(struct symbol *, void *arg); | ||||||
|  | 
 | ||||||
|  | void symbol_read_exports(FILE *file); | ||||||
|  | struct symbol *symbol_get(const char *name); | ||||||
|  | void symbol_free(void); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * dwarf.c | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | struct state { | ||||||
|  | 	struct symbol *sym; | ||||||
|  | 	Dwarf_Die die; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef int (*die_callback_t)(struct state *state, Dwarf_Die *die); | ||||||
|  | typedef bool (*die_match_callback_t)(Dwarf_Die *die); | ||||||
|  | bool match_all(Dwarf_Die *die); | ||||||
|  | 
 | ||||||
|  | int process_die_container(struct state *state, Dwarf_Die *die, | ||||||
|  | 			  die_callback_t func, die_match_callback_t match); | ||||||
|  | 
 | ||||||
|  | void process_cu(Dwarf_Die *cudie); | ||||||
|  | 
 | ||||||
|  | #endif /* __GENDWARFKSYMS_H */ | ||||||
							
								
								
									
										98
									
								
								scripts/gendwarfksyms/symbols.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								scripts/gendwarfksyms/symbols.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2024 Google LLC | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "gendwarfksyms.h" | ||||||
|  | 
 | ||||||
|  | #define SYMBOL_HASH_BITS 12 | ||||||
|  | 
 | ||||||
|  | /* name -> struct symbol */ | ||||||
|  | static HASHTABLE_DEFINE(symbol_names, 1 << SYMBOL_HASH_BITS); | ||||||
|  | 
 | ||||||
|  | static unsigned int for_each(const char *name, symbol_callback_t func, | ||||||
|  | 			     void *data) | ||||||
|  | { | ||||||
|  | 	struct hlist_node *tmp; | ||||||
|  | 	struct symbol *match; | ||||||
|  | 
 | ||||||
|  | 	if (!name || !*name) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	hash_for_each_possible_safe(symbol_names, match, tmp, name_hash, | ||||||
|  | 				    hash_str(name)) { | ||||||
|  | 		if (strcmp(match->name, name)) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		if (func) | ||||||
|  | 			func(match, data); | ||||||
|  | 
 | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool is_exported(const char *name) | ||||||
|  | { | ||||||
|  | 	return for_each(name, NULL, NULL) > 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void symbol_read_exports(FILE *file) | ||||||
|  | { | ||||||
|  | 	struct symbol *sym; | ||||||
|  | 	char *line = NULL; | ||||||
|  | 	char *name = NULL; | ||||||
|  | 	size_t size = 0; | ||||||
|  | 	int nsym = 0; | ||||||
|  | 
 | ||||||
|  | 	while (getline(&line, &size, file) > 0) { | ||||||
|  | 		if (sscanf(line, "%ms\n", &name) != 1) | ||||||
|  | 			error("malformed input line: %s", line); | ||||||
|  | 
 | ||||||
|  | 		if (is_exported(name)) { | ||||||
|  | 			/* Ignore duplicates */ | ||||||
|  | 			free(name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		sym = xcalloc(1, sizeof(struct symbol)); | ||||||
|  | 		sym->name = name; | ||||||
|  | 
 | ||||||
|  | 		hash_add(symbol_names, &sym->name_hash, hash_str(sym->name)); | ||||||
|  | 		++nsym; | ||||||
|  | 
 | ||||||
|  | 		debug("%s", sym->name); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	free(line); | ||||||
|  | 	debug("%d exported symbols", nsym); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void get_symbol(struct symbol *sym, void *arg) | ||||||
|  | { | ||||||
|  | 	struct symbol **res = arg; | ||||||
|  | 
 | ||||||
|  | 	*res = sym; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct symbol *symbol_get(const char *name) | ||||||
|  | { | ||||||
|  | 	struct symbol *sym = NULL; | ||||||
|  | 
 | ||||||
|  | 	for_each(name, get_symbol, &sym); | ||||||
|  | 	return sym; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void symbol_free(void) | ||||||
|  | { | ||||||
|  | 	struct hlist_node *tmp; | ||||||
|  | 	struct symbol *sym; | ||||||
|  | 
 | ||||||
|  | 	hash_for_each_safe(symbol_names, sym, tmp, name_hash) { | ||||||
|  | 		free((void *)sym->name); | ||||||
|  | 		free(sym); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hash_init(symbol_names); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue
	
	 Sami Tolvanen
						Sami Tolvanen