| // 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; | 
 | /* Print debugging information about die_map changes */ | 
 | int dump_die_map; | 
 | /* Print out type strings (i.e. type_map) */ | 
 | int dump_types; | 
 | /* Print out expanded type strings used for symbol versions */ | 
 | int dump_versions; | 
 | /* Support kABI stability features */ | 
 | int stable; | 
 | /* Write a symtypes file */ | 
 | int symtypes; | 
 | static const char *symtypes_file; | 
 |  | 
 | 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" | 
 | 	      "      --dump-die-map   Print debugging information about die_map changes\n" | 
 | 	      "      --dump-types     Dump type strings\n" | 
 | 	      "      --dump-versions  Dump expanded type strings used for symbol versions\n" | 
 | 	      "  -s, --stable         Support kABI stability features\n" | 
 | 	      "  -T, --symtypes file  Write a symtypes file\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; | 
 | 	FILE *symfile = arg; | 
 | 	int res; | 
 |  | 
 | 	debug("%s", name); | 
 | 	dbg = dwfl_module_getdwarf(mod, &dwbias); | 
 |  | 
 | 	/* | 
 | 	 * Look for exported symbols in each CU, follow the DIE tree, and add | 
 | 	 * the entries to die_map. | 
 | 	 */ | 
 | 	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); | 
 |  | 
 | 	/* | 
 | 	 * Use die_map to expand type strings, write them to `symfile`, and | 
 | 	 * calculate symbol versions. | 
 | 	 */ | 
 | 	generate_symtypes_and_versions(symfile); | 
 | 	die_map_free(); | 
 |  | 
 | 	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) | 
 | { | 
 | 	FILE *symfile = NULL; | 
 | 	unsigned int n; | 
 | 	int opt; | 
 |  | 
 | 	static const struct option opts[] = { | 
 | 		{ "debug", 0, NULL, 'd' }, | 
 | 		{ "dump-dies", 0, &dump_dies, 1 }, | 
 | 		{ "dump-die-map", 0, &dump_die_map, 1 }, | 
 | 		{ "dump-types", 0, &dump_types, 1 }, | 
 | 		{ "dump-versions", 0, &dump_versions, 1 }, | 
 | 		{ "stable", 0, NULL, 's' }, | 
 | 		{ "symtypes", 1, NULL, 'T' }, | 
 | 		{ "help", 0, NULL, 'h' }, | 
 | 		{ 0, 0, NULL, 0 } | 
 | 	}; | 
 |  | 
 | 	while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) { | 
 | 		switch (opt) { | 
 | 		case 0: | 
 | 			break; | 
 | 		case 'd': | 
 | 			debug = 1; | 
 | 			break; | 
 | 		case 's': | 
 | 			stable = 1; | 
 | 			break; | 
 | 		case 'T': | 
 | 			symtypes = 1; | 
 | 			symtypes_file = optarg; | 
 | 			break; | 
 | 		case 'h': | 
 | 			usage(); | 
 | 			return 0; | 
 | 		default: | 
 | 			usage(); | 
 | 			return 1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (dump_die_map) | 
 | 		dump_dies = 1; | 
 |  | 
 | 	if (optind >= argc) { | 
 | 		usage(); | 
 | 		error("no input files?"); | 
 | 	} | 
 |  | 
 | 	symbol_read_exports(stdin); | 
 |  | 
 | 	if (symtypes_file) { | 
 | 		symfile = fopen(symtypes_file, "w"); | 
 | 		if (!symfile) | 
 | 			error("fopen failed for '%s': %s", symtypes_file, | 
 | 			      strerror(errno)); | 
 | 	} | 
 |  | 
 | 	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)); | 
 |  | 
 | 		symbol_read_symtab(fd); | 
 | 		kabi_read_rules(fd); | 
 |  | 
 | 		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, symfile, 0)) | 
 | 			error("dwfl_getmodules failed for '%s'", argv[n]); | 
 |  | 
 | 		dwfl_end(dwfl); | 
 | 		kabi_free(); | 
 | 	} | 
 |  | 
 | 	if (symfile) | 
 | 		check(fclose(symfile)); | 
 |  | 
 | 	symbol_print_versions(); | 
 | 	symbol_free(); | 
 |  | 
 | 	return 0; | 
 | } |