1861 lines · 44.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later#define _GNU_SOURCE /* memmem() */#include <subcmd/parse-options.h>#include <stdlib.h>#include <string.h>#include <libgen.h>#include <stdio.h>#include <ctype.h> #include <objtool/objtool.h>#include <objtool/warn.h>#include <objtool/arch.h>#include <objtool/klp.h>#include <objtool/util.h>#include <arch/special.h> #include <linux/align.h>#include <linux/objtool_types.h>#include <linux/livepatch_external.h>#include <linux/stringify.h>#include <linux/string.h>#include <linux/jhash.h> #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) struct elfs {	struct elf *orig, *patched, *out;	const char *modname;}; struct export {	struct hlist_node hash;	char *mod, *sym;}; static const char * const klp_diff_usage[] = {	"objtool klp diff [<options>] <in1.o> <in2.o> <out.o>",	NULL,}; static const struct option klp_diff_options[] = {	OPT_GROUP("Options:"),	OPT_BOOLEAN('d', "debug", &debug, "enable debug output"),	OPT_END(),}; static DEFINE_HASHTABLE(exports, 15); static inline u32 str_hash(const char *str){	return jhash(str, strlen(str), 0);} static char *escape_str(const char *orig){	size_t len = 0;	const char *a;	char *b, *new; 	for (a = orig; *a; a++) {		switch (*a) {		case '\001': len += 5; break;		case '\n':		case '\t':   len += 2; break;		default: len++;		}	} 	new = malloc(len + 1);	if (!new)		return NULL; 	for (a = orig, b = new; *a; a++) {		switch (*a) {		case '\001': memcpy(b, "<SOH>", 5); b += 5; break;		case '\n': *b++ = '\\'; *b++ = 'n'; break;		case '\t': *b++ = '\\'; *b++ = 't'; break;		default:   *b++ = *a;		}	} 	*b = '\0';	return new;} static int read_exports(void){	const char *symvers = "Module.symvers";	char line[1024], *path = NULL;	unsigned int line_num = 1;	FILE *file; 	file = fopen(symvers, "r");	if (!file) {		path = top_level_dir(symvers);		if (!path) {			ERROR("can't open '%s', \"objtool diff\" should be run from the kernel tree", symvers);			return -1;		} 		file = fopen(path, "r");		if (!file) {			ERROR_GLIBC("fopen");			return -1;		}	} 	while (fgets(line, 1024, file)) {		char *sym, *mod, *type;		struct export *export; 		sym = strchr(line, '\t');		if (!sym) {			ERROR("malformed Module.symvers (sym) at line %d", line_num);			return -1;		} 		*sym++ = '\0'; 		mod = strchr(sym, '\t');		if (!mod) {			ERROR("malformed Module.symvers (mod) at line %d", line_num);			return -1;		} 		*mod++ = '\0'; 		type = strchr(mod, '\t');		if (!type) {			ERROR("malformed Module.symvers (type) at line %d", line_num);			return -1;		} 		*type++ = '\0'; 		if (*sym == '\0' || *mod == '\0') {			ERROR("malformed Module.symvers at line %d", line_num);			return -1;		} 		export = calloc(1, sizeof(*export));		if (!export) {			ERROR_GLIBC("calloc");			return -1;		} 		export->mod = strdup(mod);		if (!export->mod) {			ERROR_GLIBC("strdup");			return -1;		} 		export->sym = strdup(sym);		if (!export->sym) {			ERROR_GLIBC("strdup");			return -1;		} 		hash_add(exports, &export->hash, str_hash(sym));	} 	free(path);	fclose(file); 	return 0;} static int read_sym_checksums(struct elf *elf){	struct section *sec; 	sec = find_section_by_name(elf, ".discard.sym_checksum");	if (!sec) {		ERROR("'%s' missing .discard.sym_checksum section, file not processed by 'objtool --checksum'?",		      elf->name);		return -1;	} 	if (!sec->rsec) {		ERROR("missing reloc section for .discard.sym_checksum");		return -1;	} 	if (sec_size(sec) % sizeof(struct sym_checksum)) {		ERROR("struct sym_checksum size mismatch");		return -1;	} 	for (int i = 0; i < sec_size(sec) / sizeof(struct sym_checksum); i++) {		struct sym_checksum *sym_checksum;		struct reloc *reloc;		struct symbol *sym; 		sym_checksum = (struct sym_checksum *)sec->data->d_buf + i; 		reloc = find_reloc_by_dest(elf, sec, i * sizeof(*sym_checksum));		if (!reloc) {			ERROR("can't find reloc for sym_checksum[%d]", i);			return -1;		} 		sym = reloc->sym; 		if (is_sec_sym(sym)) {			ERROR("not sure how to handle section %s", sym->name);			return -1;		} 		if (is_func_sym(sym))			sym->csum.checksum = sym_checksum->checksum;	} 	return 0;} static struct symbol *first_file_symbol(struct elf *elf){	struct symbol *sym; 	for_each_sym(elf, sym) {		if (is_file_sym(sym))			return sym;	} 	return NULL;} static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym){	for_each_sym_continue(elf, sym) {		if (is_file_sym(sym))			return sym;	} 	return NULL;} /* * Certain static local variables should never be correlated.  They will be * used in place rather than referencing the originals. */static bool is_uncorrelated_static_local(struct symbol *sym){	static const char * const vars[] = {		"__already_done.",		"__func__.",		"__key.",		"__warned.",		"_entry.",		"_entry_ptr.",		"_rs.",		"descriptor.",		"CSWTCH.",	}; 	if (!is_object_sym(sym) || !is_local_sym(sym))		return false; 	if (!strcmp(sym->sec->name, ".data.once"))		return true; 	for (int i = 0; i < ARRAY_SIZE(vars); i++) {		if (strstarts(sym->name, vars[i]))			return true;	} 	return false;} /* * Clang emits several useless .Ltmp_* code labels. */static bool is_clang_tmp_label(struct symbol *sym){	return is_notype_sym(sym) &&	       is_text_sec(sym->sec) &&	       strstarts(sym->name, ".Ltmp") &&	       isdigit(sym->name[5]);} static bool is_special_section(struct section *sec){	static const char * const specials[] = {		".altinstructions",		".smp_locks",		"__bug_table",		"__ex_table",		"__jump_table",		"__mcount_loc", 		/*		 * Extract .static_call_sites here to inherit non-module		 * preferential treatment.  The later static call processing		 * during klp module build will be skipped when it sees this		 * section already exists.		 */		".static_call_sites",	}; 	static const char * const non_special_discards[] = {		".discard.addressable",		".discard.sym_checksum",	}; 	if (is_text_sec(sec))		return false; 	for (int i = 0; i < ARRAY_SIZE(specials); i++) {		if (!strcmp(sec->name, specials[i]))			return true;	} 	/* Most .discard data sections are special */	for (int i = 0; i < ARRAY_SIZE(non_special_discards); i++) {		if (!strcmp(sec->name, non_special_discards[i]))			return false;	} 	return strstarts(sec->name, ".discard.");} /* * These sections are referenced by special sections but aren't considered * special sections themselves. */static bool is_special_section_aux(struct section *sec){	static const char * const specials_aux[] = {		".altinstr_replacement",		".altinstr_aux",	}; 	for (int i = 0; i < ARRAY_SIZE(specials_aux); i++) {		if (!strcmp(sec->name, specials_aux[i]))			return true;	} 	return false;} /* * These symbols should never be correlated, so their local patched versions * are used instead of linking to the originals. */static bool dont_correlate(struct symbol *sym){	return is_file_sym(sym) ||	       is_null_sym(sym) ||	       is_sec_sym(sym) ||	       is_prefix_func(sym) ||	       is_uncorrelated_static_local(sym) ||	       is_clang_tmp_label(sym) ||	       is_string_sec(sym->sec) ||	       is_special_section(sym->sec) ||	       is_special_section_aux(sym->sec) ||	       strstarts(sym->name, "__initcall__");} struct process_demangled_name_data {	struct symbol *ret;	int count;}; static void process_demangled_name(struct symbol *sym, void *d){	struct process_demangled_name_data *data = d; 	if (sym->twin)		return; 	data->count++;	data->ret = sym;} /* * When there is no full name match, try match demangled_name. This would * match original foo.llvm.123 to patched foo.llvm.456. * * Note that, in very rare cases, it is possible to have multiple * foo.llvm.<hash> in the same kernel. When this happens, report error and * fail the diff. */static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym,						struct symbol **out_sym){	struct process_demangled_name_data data = {}; 	iterate_global_symbol_by_demangled_name(elf, sym->demangled_name,						process_demangled_name,						&data);	if (data.count > 1) {		ERROR("Multiple (%d) correlation candidates for %s", data.count, sym->name);		return -1;	}	*out_sym = data.ret;	return 0;} /* * For each symbol in the original kernel, find its corresponding "twin" in the * patched kernel. */static int correlate_symbols(struct elfs *e){	struct symbol *file1_sym, *file2_sym;	struct symbol *sym1, *sym2; 	file1_sym = first_file_symbol(e->orig);	file2_sym = first_file_symbol(e->patched); 	/*	 * Correlate any locals before the first FILE symbol.  This has been	 * seen when LTO inexplicably strips the initramfs_data.o FILE symbol	 * due to the file only containing data and no code.	 */	for_each_sym(e->orig, sym1) {		if (sym1 == file1_sym || !is_local_sym(sym1))			break; 		if (dont_correlate(sym1))			continue; 		for_each_sym(e->patched, sym2) {			if (sym2 == file2_sym || !is_local_sym(sym2))				break; 			if (sym2->twin || dont_correlate(sym2))				continue; 			if (strcmp(sym1->demangled_name, sym2->demangled_name))				continue; 			sym1->twin = sym2;			sym2->twin = sym1;			break;		}	} 	/* Correlate locals after the first FILE symbol */	for (; ; file1_sym = next_file_symbol(e->orig, file1_sym),		 file2_sym = next_file_symbol(e->patched, file2_sym)) { 		if (!file1_sym && file2_sym) {			ERROR("FILE symbol mismatch: NULL != %s", file2_sym->name);			return -1;		} 		if (file1_sym && !file2_sym) {			ERROR("FILE symbol mismatch: %s != NULL", file1_sym->name);			return -1;		} 		if (!file1_sym)			break; 		if (strcmp(file1_sym->name, file2_sym->name)) {			ERROR("FILE symbol mismatch: %s != %s", file1_sym->name, file2_sym->name);			return -1;		} 		file1_sym->twin = file2_sym;		file2_sym->twin = file1_sym; 		sym1 = file1_sym; 		for_each_sym_continue(e->orig, sym1) {			if (is_file_sym(sym1) || !is_local_sym(sym1))				break; 			if (dont_correlate(sym1))				continue; 			sym2 = file2_sym;			for_each_sym_continue(e->patched, sym2) {				if (is_file_sym(sym2) || !is_local_sym(sym2))					break; 				if (sym2->twin || dont_correlate(sym2))					continue; 				if (strcmp(sym1->demangled_name, sym2->demangled_name))					continue; 				sym1->twin = sym2;				sym2->twin = sym1;				break;			}		}	} 	/* Correlate globals */	for_each_sym(e->orig, sym1) {		if (sym1->bind == STB_LOCAL)			continue; 		sym2 = find_global_symbol_by_name(e->patched, sym1->name);		if (sym2 && !sym2->twin) {			sym1->twin = sym2;			sym2->twin = sym1;		}

Lines 1–500 of 1861.