1010 lines · 23.5 KB · C
RawDownload
		count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc);		/* gcc may use relocs to save the addresses, but clang does not. */		if (!count) {			count = fill_addrs(vals, size, start_loc);			sort_reloc = 0;		}	} else		count = fill_addrs(vals, size, start_loc); 	if (count < 0) {		e_msg = m_err;		goto out;	} 	if (count != size / long_size) {		snprintf(m_err, ERRSTR_MAXSZ, "Expected %u mcount elements but found %u\n",			(int)(size / long_size), count);		e_msg = m_err;		goto out;	} 	/* zero out any locations not found by function list */	if (function_list_size) {		for (void *ptr = vals; ptr < vals + size; ptr += long_size) {			uint64_t key; 			key = long_size == 4 ? *(uint32_t *)ptr : *(uint64_t *)ptr;			if (!find_func(key)) {				if (long_size == 4)					*(uint32_t *)ptr = 0;				else					*(uint64_t *)ptr = 0;			}		}	} 	compare_values = long_size == 4 ? compare_values_32 : compare_values_64; 	qsort(vals, count, long_size, compare_values); 	if (sort_reloc)		replace_relocs(vals, size, ehdr, emloc->start_mcount_loc);	else		replace_addrs(vals, size, start_loc); out:	free(vals); 	pthread_exit(e_msg);} /* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec,			   const char *strtab){	Elf_Sym *sym, *end_sym;	int symentsize = shdr_entsize(symtab_sec);	int found = 0; 	sym = (void *)emloc->ehdr + shdr_offset(symtab_sec);	end_sym = (void *)sym + shdr_size(symtab_sec); 	while (sym < end_sym) {		if (!strcmp(strtab + sym_name(sym), "__start_mcount_loc")) {			emloc->start_mcount_loc = sym_value(sym);			if (++found == 2)				break;		} else if (!strcmp(strtab + sym_name(sym), "__stop_mcount_loc")) {			emloc->stop_mcount_loc = sym_value(sym);			if (++found == 2)				break;		}		sym = (void *)sym + symentsize;	} 	if (!emloc->start_mcount_loc) {		fprintf(stderr, "get start_mcount_loc error!");		return;	} 	if (!emloc->stop_mcount_loc) {		fprintf(stderr, "get stop_mcount_loc error!");		return;	}}#else /* MCOUNT_SORT_ENABLED */static inline int parse_symbols(const char *fname) { return 0; }#endif static int do_sort(Elf_Ehdr *ehdr,		   char const *const fname,		   table_sort_t custom_sort){	int rc = -1;	Elf_Shdr *shdr_start;	Elf_Shdr *strtab_sec = NULL;	Elf_Shdr *symtab_sec = NULL;	Elf_Shdr *extab_sec = NULL;	Elf_Shdr *string_sec;	Elf_Sym *sym;	const Elf_Sym *symtab;	Elf32_Word *symtab_shndx = NULL;	Elf_Sym *sort_needed_sym = NULL;	Elf_Shdr *sort_needed_sec;	uint32_t *sort_needed_loc;	void *sym_start;	void *sym_end;	const char *secstrings;	const char *strtab;	char *extab_image;	int sort_need_index;	int symentsize;	int shentsize;	int idx;	int i;	unsigned int shnum;	unsigned int shstrndx;#ifdef MCOUNT_SORT_ENABLED	struct elf_mcount_loc mstruct = {0};#endif#ifdef UNWINDER_ORC_ENABLED	unsigned int orc_ip_size = 0;	unsigned int orc_size = 0;	unsigned int orc_num_entries = 0;#endif 	shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));	shentsize = ehdr_shentsize(ehdr); 	shstrndx = ehdr_shstrndx(ehdr);	if (shstrndx == SHN_XINDEX)		shstrndx = shdr_link(shdr_start);	string_sec = get_index(shdr_start, shentsize, shstrndx);	secstrings = (const char *)ehdr + shdr_offset(string_sec); 	shnum = ehdr_shnum(ehdr);	if (shnum == SHN_UNDEF)		shnum = shdr_size(shdr_start); 	for (i = 0; i < shnum; i++) {		Elf_Shdr *shdr = get_index(shdr_start, shentsize, i); 		idx = shdr_name(shdr);		if (!strcmp(secstrings + idx, "__ex_table"))			extab_sec = shdr;		if (!strcmp(secstrings + idx, ".symtab"))			symtab_sec = shdr;		if (!strcmp(secstrings + idx, ".strtab"))			strtab_sec = shdr; 		if (shdr_type(shdr) == SHT_SYMTAB_SHNDX)			symtab_shndx = (Elf32_Word *)((const char *)ehdr +						      shdr_offset(shdr)); #ifdef MCOUNT_SORT_ENABLED		/* locate the .init.data section in vmlinux */		if (!strcmp(secstrings + idx, ".init.data"))			mstruct.init_data_sec = shdr;#endif #ifdef UNWINDER_ORC_ENABLED		/* locate the ORC unwind tables */		if (!strcmp(secstrings + idx, ".orc_unwind_ip")) {			orc_ip_size = shdr_size(shdr);			g_orc_ip_table = (int *)((void *)ehdr +						   shdr_offset(shdr));		}		if (!strcmp(secstrings + idx, ".orc_unwind")) {			orc_size = shdr_size(shdr);			g_orc_table = (struct orc_entry *)((void *)ehdr +							     shdr_offset(shdr));		}#endif	} /* for loop */ #ifdef UNWINDER_ORC_ENABLED	if (!g_orc_ip_table || !g_orc_table) {		fprintf(stderr,			"incomplete ORC unwind tables in file: %s\n", fname);		goto out;	} 	orc_num_entries = orc_ip_size / sizeof(int);	if (orc_ip_size % sizeof(int) != 0 ||	    orc_size % sizeof(struct orc_entry) != 0 ||	    orc_num_entries != orc_size / sizeof(struct orc_entry)) {		fprintf(stderr,			"inconsistent ORC unwind table entries in file: %s\n",			fname);		goto out;	} 	/* create thread to sort ORC unwind tables concurrently */	if (pthread_create(&orc_sort_thread, NULL,			   sort_orctable, &orc_ip_size)) {		fprintf(stderr,			"pthread_create orc_sort_thread failed '%s': %s\n",			strerror(errno), fname);		goto out;	}#endif	if (!extab_sec) {		fprintf(stderr,	"no __ex_table in file: %s\n", fname);		goto out;	} 	if (!symtab_sec) {		fprintf(stderr,	"no .symtab in file: %s\n", fname);		goto out;	} 	if (!strtab_sec) {		fprintf(stderr,	"no .strtab in file: %s\n", fname);		goto out;	} 	extab_image = (void *)ehdr + shdr_offset(extab_sec);	strtab = (const char *)ehdr + shdr_offset(strtab_sec);	symtab = (const Elf_Sym *)((const char *)ehdr + shdr_offset(symtab_sec)); #ifdef MCOUNT_SORT_ENABLED	mstruct.ehdr = ehdr;	get_mcount_loc(&mstruct, symtab_sec, strtab); 	if (!mstruct.init_data_sec || !mstruct.start_mcount_loc || !mstruct.stop_mcount_loc) {		fprintf(stderr,			"incomplete mcount's sort in file: %s\n",			fname);		goto out;	} 	/* create thread to sort mcount_loc concurrently */	if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) {		fprintf(stderr,			"pthread_create mcount_sort_thread failed '%s': %s\n",			strerror(errno), fname);		goto out;	}#endif 	if (custom_sort) {		custom_sort(extab_image, shdr_size(extab_sec));	} else {		int num_entries = shdr_size(extab_sec) / extable_ent_size;		qsort(extab_image, num_entries,		      extable_ent_size, compare_extable);	} 	/* find the flag main_extable_sort_needed */	sym_start = (void *)ehdr + shdr_offset(symtab_sec);	sym_end = sym_start + shdr_size(symtab_sec);	symentsize = shdr_entsize(symtab_sec); 	for (sym = sym_start; (void *)sym + symentsize < sym_end;	     sym = (void *)sym + symentsize) {		if (sym_type(sym) != STT_OBJECT)			continue;		if (!strcmp(strtab + sym_name(sym),			    "main_extable_sort_needed")) {			sort_needed_sym = sym;			break;		}	} 	if (!sort_needed_sym) {		fprintf(stderr,			"no main_extable_sort_needed symbol in file: %s\n",			fname);		goto out;	} 	sort_need_index = get_secindex(sym_shndx(sym),				       ((void *)sort_needed_sym - (void *)symtab) / symentsize,				       symtab_shndx);	sort_needed_sec = get_index(shdr_start, shentsize, sort_need_index);	sort_needed_loc = (void *)ehdr +		shdr_offset(sort_needed_sec) +		sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec); 	/* extable has been sorted, clear the flag */	elf_parser.w(0, sort_needed_loc);	rc = 0; out:#ifdef UNWINDER_ORC_ENABLED	if (orc_sort_thread) {		void *retval = NULL;		/* wait for ORC tables sort done */		rc = pthread_join(orc_sort_thread, &retval);		if (rc) {			fprintf(stderr,				"pthread_join failed '%s': %s\n",				strerror(errno), fname);		} else if (retval) {			rc = -1;			fprintf(stderr,				"failed to sort ORC tables '%s': %s\n",				(char *)retval, fname);		}	}#endif #ifdef MCOUNT_SORT_ENABLED	if (mcount_sort_thread) {		void *retval = NULL;		/* wait for mcount sort done */		rc = pthread_join(mcount_sort_thread, &retval);		if (rc) {			fprintf(stderr,				"pthread_join failed '%s': %s\n",				strerror(errno), fname);		} else if (retval) {			rc = -1;			fprintf(stderr,				"failed to sort mcount '%s': %s\n",				(char *)retval, fname);		}	}#endif	return rc;} static int compare_relative_table(const void *a, const void *b){	int32_t av = (int32_t)elf_parser.r(a);	int32_t bv = (int32_t)elf_parser.r(b); 	if (av < bv)		return -1;	if (av > bv)		return 1;	return 0;} static void sort_relative_table(char *extab_image, int image_size){	int i = 0; 	/*	 * Do the same thing the runtime sort does, first normalize to	 * being relative to the start of the section.	 */	while (i < image_size) {		uint32_t *loc = (uint32_t *)(extab_image + i);		elf_parser.w(elf_parser.r(loc) + i, loc);		i += 4;	} 	qsort(extab_image, image_size / 8, 8, compare_relative_table); 	/* Now denormalize. */	i = 0;	while (i < image_size) {		uint32_t *loc = (uint32_t *)(extab_image + i);		elf_parser.w(elf_parser.r(loc) - i, loc);		i += 4;	}} static void sort_relative_table_with_data(char *extab_image, int image_size){	int i = 0; 	while (i < image_size) {		uint32_t *loc = (uint32_t *)(extab_image + i); 		elf_parser.w(elf_parser.r(loc) + i, loc);		elf_parser.w(elf_parser.r(loc + 1) + i + 4, loc + 1);		/* Don't touch the fixup type or data */ 		i += sizeof(uint32_t) * 3;	} 	qsort(extab_image, image_size / 12, 12, compare_relative_table); 	i = 0;	while (i < image_size) {		uint32_t *loc = (uint32_t *)(extab_image + i); 		elf_parser.w(elf_parser.r(loc) - i, loc);		elf_parser.w(elf_parser.r(loc + 1) - (i + 4), loc + 1);		/* Don't touch the fixup type or data */ 		i += sizeof(uint32_t) * 3;	}} static int do_file(char const *const fname, void *addr){	Elf_Ehdr *ehdr = addr;	table_sort_t custom_sort = NULL; 	switch (elf_map_machine(ehdr)) {#ifdef MCOUNT_SORT_ENABLED	case EM_AARCH64:		/* arm64 also needs RELA-based weak-function fixups. */		sort_reloc = true;		rela_type = 0x403;		/* fallthrough */	case EM_RISCV:		/* arm64 and RISC-V place patchable entries before the function. */		before_func = 8;#else	case EM_AARCH64:	case EM_RISCV:#endif		/* fallthrough */	case EM_386:	case EM_LOONGARCH:	case EM_S390:	case EM_X86_64:		custom_sort = sort_relative_table_with_data;		break;	case EM_PARISC:	case EM_PPC:	case EM_PPC64:		custom_sort = sort_relative_table;		break;	case EM_ARCOMPACT:	case EM_ARCV2:	case EM_ARM:	case EM_MICROBLAZE:	case EM_MIPS:	case EM_XTENSA:		break;	default:		fprintf(stderr, "unrecognized e_machine %d %s\n",			elf_parser.r2(&ehdr->e32.e_machine), fname);		return -1;	} 	switch (elf_map_long_size(addr)) {	case 4:		compare_extable	= compare_extable_32,		long_size		= 4;		extable_ent_size	= 8; 		if (elf_parser.r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||		    elf_parser.r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {			fprintf(stderr,				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);			return -1;		} 		break;	case 8:		compare_extable	= compare_extable_64,		long_size		= 8;		extable_ent_size	= 16; 		if (elf_parser.r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||		    elf_parser.r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {			fprintf(stderr,				"unrecognized ET_EXEC/ET_DYN file: %s\n",				fname);			return -1;		} 		break;	default:		fprintf(stderr, "unrecognized ELF class %d %s\n",			ehdr->e32.e_ident[EI_CLASS], fname);		return -1;	} 	return do_sort(ehdr, fname, custom_sort);} int main(int argc, char *argv[]){	int i, n_error = 0;  /* gcc-4.3.0 false positive complaint */	size_t size = 0;	void *addr = NULL;	int c; 	while ((c = getopt(argc, argv, "s:")) >= 0) {		switch (c) {		case 's':			if (parse_symbols(optarg) < 0) {				fprintf(stderr, "Could not parse %s\n", optarg);				return -1;			}			break;		default:			fprintf(stderr, "usage: sorttable [-s nm-file] vmlinux...\n");			return 0;		}	} 	if ((argc - optind) < 1) {		fprintf(stderr, "usage: sorttable vmlinux...\n");		return 0;	} 	/* Process each file in turn, allowing deep failure. */	for (i = optind; i < argc; i++) {		addr = elf_map(argv[i], &size, (1 << ET_EXEC) | (1 << ET_DYN));		if (!addr) {			++n_error;			continue;

Lines 501–1000 of 1010. Previous lines