diff options
Diffstat (limited to 'scripts/recordmcount.c')
| -rw-r--r-- | scripts/recordmcount.c | 321 | 
1 files changed, 157 insertions, 164 deletions
| diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 8387a9bc064a..612268eabef4 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -27,7 +27,6 @@  #include <getopt.h>  #include <elf.h>  #include <fcntl.h> -#include <setjmp.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -43,56 +42,37 @@ static int fd_map;	/* File descriptor for file being modified. */  static int mmap_failed; /* Boolean flag. */  static char gpfx;	/* prefix for global symbol name (sometimes '_') */  static struct stat sb;	/* Remember .st_size, etc. */ -static jmp_buf jmpenv;	/* setjmp/longjmp per-file error escape */  static const char *altmcount;	/* alternate mcount symbol name */  static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */  static void *file_map;	/* pointer of the mapped file */  static void *file_end;	/* pointer to the end of the mapped file */  static int file_updated; /* flag to state file was changed */  static void *file_ptr;	/* current file pointer location */ +  static void *file_append; /* added to the end of the file */  static size_t file_append_size; /* how much is added to end of file */ -/* setjmp() return values */ -enum { -	SJ_SETJMP = 0,  /* hardwired first return */ -	SJ_FAIL, -	SJ_SUCCEED -}; -  /* Per-file resource cleanup when multiple files. */ -static void -cleanup(void) +static void file_append_cleanup(void)  { -	if (!mmap_failed) -		munmap(file_map, sb.st_size); -	else -		free(file_map); -	file_map = NULL;  	free(file_append);  	file_append = NULL;  	file_append_size = 0;  	file_updated = 0;  } -static void __attribute__((noreturn)) -fail_file(void) +static void mmap_cleanup(void)  { -	cleanup(); -	longjmp(jmpenv, SJ_FAIL); -} - -static void __attribute__((noreturn)) -succeed_file(void) -{ -	cleanup(); -	longjmp(jmpenv, SJ_SUCCEED); +	if (!mmap_failed) +		munmap(file_map, sb.st_size); +	else +		free(file_map); +	file_map = NULL;  } -/* ulseek, uread, ...:  Check return value for errors. */ +/* ulseek, uwrite, ...:  Check return value for errors. */ -static off_t -ulseek(int const fd, off_t const offset, int const whence) +static off_t ulseek(off_t const offset, int const whence)  {  	switch (whence) {  	case SEEK_SET: @@ -107,24 +87,12 @@ ulseek(int const fd, off_t const offset, int const whence)  	}  	if (file_ptr < file_map) {  		fprintf(stderr, "lseek: seek before file\n"); -		fail_file(); +		return -1;  	}  	return file_ptr - file_map;  } -static size_t -uread(int const fd, void *const buf, size_t const count) -{ -	size_t const n = read(fd, buf, count); -	if (n != count) { -		perror("read"); -		fail_file(); -	} -	return n; -} - -static size_t -uwrite(int const fd, void const *const buf, size_t const count) +static ssize_t uwrite(void const *const buf, size_t const count)  {  	size_t cnt = count;  	off_t idx = 0; @@ -140,7 +108,9 @@ uwrite(int const fd, void const *const buf, size_t const count)  		}  		if (!file_append) {  			perror("write"); -			fail_file(); +			file_append_cleanup(); +			mmap_cleanup(); +			return -1;  		}  		if (file_ptr < file_end) {  			cnt = file_end - file_ptr; @@ -160,17 +130,81 @@ uwrite(int const fd, void const *const buf, size_t const count)  	return count;  } -static void * -umalloc(size_t size) +static void * umalloc(size_t size)  {  	void *const addr = malloc(size);  	if (addr == 0) {  		fprintf(stderr, "malloc failed: %zu bytes\n", size); -		fail_file(); +		file_append_cleanup(); +		mmap_cleanup(); +		return NULL;  	}  	return addr;  } +/* + * Get the whole file as a programming convenience in order to avoid + * malloc+lseek+read+free of many pieces.  If successful, then mmap + * avoids copying unused pieces; else just read the whole file. + * Open for both read and write; new info will be appended to the file. + * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr + * do not propagate to the file until an explicit overwrite at the last. + * This preserves most aspects of consistency (all except .st_size) + * for simultaneous readers of the file while we are appending to it. + * However, multiple writers still are bad.  We choose not to use + * locking because it is expensive and the use case of kernel build + * makes multiple writers unlikely. + */ +static void *mmap_file(char const *fname) +{ +	/* Avoid problems if early cleanup() */ +	fd_map = -1; +	mmap_failed = 1; +	file_map = NULL; +	file_ptr = NULL; +	file_updated = 0; +	sb.st_size = 0; + +	fd_map = open(fname, O_RDONLY); +	if (fd_map < 0) { +		perror(fname); +		return NULL; +	} +	if (fstat(fd_map, &sb) < 0) { +		perror(fname); +		goto out; +	} +	if (!S_ISREG(sb.st_mode)) { +		fprintf(stderr, "not a regular file: %s\n", fname); +		goto out; +	} +	file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, +			fd_map, 0); +	if (file_map == MAP_FAILED) { +		mmap_failed = 1; +		file_map = umalloc(sb.st_size); +		if (!file_map) { +			perror(fname); +			goto out; +		} +		if (read(fd_map, file_map, sb.st_size) != sb.st_size) { +			perror(fname); +			free(file_map); +			file_map = NULL; +			goto out; +		} +	} else +		mmap_failed = 0; +out: +	close(fd_map); +	fd_map = -1; + +	file_end = file_map + sb.st_size; + +	return file_map; +} + +  static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };  static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };  static unsigned char *ideal_nop; @@ -194,8 +228,10 @@ static int make_nop_x86(void *map, size_t const offset)  		return -1;  	/* convert to nop */ -	ulseek(fd_map, offset - 1, SEEK_SET); -	uwrite(fd_map, ideal_nop, 5); +	if (ulseek(offset - 1, SEEK_SET) < 0) +		return -1; +	if (uwrite(ideal_nop, 5) < 0) +		return -1;  	return 0;  } @@ -243,10 +279,12 @@ static int make_nop_arm(void *map, size_t const offset)  		return -1;  	/* Convert to nop */ -	ulseek(fd_map, off, SEEK_SET); +	if (ulseek(off, SEEK_SET) < 0) +		return -1;  	do { -		uwrite(fd_map, ideal_nop, nop_size); +		if (uwrite(ideal_nop, nop_size) < 0) +			return -1;  	} while (--cnt > 0);  	return 0; @@ -263,57 +301,20 @@ static int make_nop_arm64(void *map, size_t const offset)  		return -1;  	/* Convert to nop */ -	ulseek(fd_map, offset, SEEK_SET); -	uwrite(fd_map, ideal_nop, 4); +	if (ulseek(offset, SEEK_SET) < 0) +		return -1; +	if (uwrite(ideal_nop, 4) < 0) +		return -1;  	return 0;  } -/* - * Get the whole file as a programming convenience in order to avoid - * malloc+lseek+read+free of many pieces.  If successful, then mmap - * avoids copying unused pieces; else just read the whole file. - * Open for both read and write; new info will be appended to the file. - * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr - * do not propagate to the file until an explicit overwrite at the last. - * This preserves most aspects of consistency (all except .st_size) - * for simultaneous readers of the file while we are appending to it. - * However, multiple writers still are bad.  We choose not to use - * locking because it is expensive and the use case of kernel build - * makes multiple writers unlikely. - */ -static void *mmap_file(char const *fname) -{ -	fd_map = open(fname, O_RDONLY); -	if (fd_map < 0 || fstat(fd_map, &sb) < 0) { -		perror(fname); -		fail_file(); -	} -	if (!S_ISREG(sb.st_mode)) { -		fprintf(stderr, "not a regular file: %s\n", fname); -		fail_file(); -	} -	file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, -			fd_map, 0); -	mmap_failed = 0; -	if (file_map == MAP_FAILED) { -		mmap_failed = 1; -		file_map = umalloc(sb.st_size); -		uread(fd_map, file_map, sb.st_size); -	} -	close(fd_map); - -	file_end = file_map + sb.st_size; - -	return file_map; -} - -static void write_file(const char *fname) +static int write_file(const char *fname)  {  	char tmp_file[strlen(fname) + 4];  	size_t n;  	if (!file_updated) -		return; +		return 0;  	sprintf(tmp_file, "%s.rc", fname); @@ -325,25 +326,28 @@ static void write_file(const char *fname)  	fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);  	if (fd_map < 0) {  		perror(fname); -		fail_file(); +		return -1;  	}  	n = write(fd_map, file_map, sb.st_size);  	if (n != sb.st_size) {  		perror("write"); -		fail_file(); +		close(fd_map); +		return -1;  	}  	if (file_append_size) {  		n = write(fd_map, file_append, file_append_size);  		if (n != file_append_size) {  			perror("write"); -			fail_file(); +			close(fd_map); +			return -1;  		}  	}  	close(fd_map);  	if (rename(tmp_file, fname) < 0) {  		perror(fname); -		fail_file(); +		return -1;  	} +	return 0;  }  /* w8rev, w8nat, ...: Handle endianness. */ @@ -394,8 +398,7 @@ static uint32_t (*w)(uint32_t);  static uint32_t (*w2)(uint16_t);  /* Names of the sections that could contain calls to mcount. */ -static int -is_mcounted_section_name(char const *const txtname) +static int is_mcounted_section_name(char const *const txtname)  {  	return strncmp(".text",          txtname, 5) == 0 ||  		strcmp(".init.text",     txtname) == 0 || @@ -405,10 +408,11 @@ is_mcounted_section_name(char const *const txtname)  		strcmp(".irqentry.text", txtname) == 0 ||  		strcmp(".softirqentry.text", txtname) == 0 ||  		strcmp(".kprobes.text", txtname) == 0 || -		strcmp(".cpuidle.text", txtname) == 0 || -		strcmp(".text.unlikely", txtname) == 0; +		strcmp(".cpuidle.text", txtname) == 0;  } +static char const *already_has_rel_mcount = "success"; /* our work here is done! */ +  /* 32 bit and 64 bit are very similar */  #include "recordmcount.h"  #define RECORD_MCOUNT_64 @@ -447,11 +451,15 @@ static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)  	}).r_info;  } -static void -do_file(char const *const fname) +static int do_file(char const *const fname)  { -	Elf32_Ehdr *const ehdr = mmap_file(fname);  	unsigned int reltype = 0; +	Elf32_Ehdr *ehdr; +	int rc = -1; + +	ehdr = mmap_file(fname); +	if (!ehdr) +		goto out;  	w = w4nat;  	w2 = w2nat; @@ -461,8 +469,7 @@ do_file(char const *const fname)  	default:  		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",  			ehdr->e_ident[EI_DATA], fname); -		fail_file(); -		break; +		goto out;  	case ELFDATA2LSB:  		if (*(unsigned char const *)&endian != 1) {  			/* main() is big endian, file.o is little endian. */ @@ -490,52 +497,54 @@ do_file(char const *const fname)  		push_bl_mcount_thumb = push_bl_mcount_thumb_be;  		break;  	}  /* end switch */ -	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 -	||  w2(ehdr->e_type) != ET_REL -	||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) { +	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 || +	    w2(ehdr->e_type) != ET_REL || +	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {  		fprintf(stderr, "unrecognized ET_REL file %s\n", fname); -		fail_file(); +		goto out;  	} -	gpfx = 0; +	gpfx = '_';  	switch (w2(ehdr->e_machine)) {  	default:  		fprintf(stderr, "unrecognized e_machine %u %s\n",  			w2(ehdr->e_machine), fname); -		fail_file(); -		break; +		goto out;  	case EM_386:  		reltype = R_386_32;  		rel_type_nop = R_386_NONE;  		make_nop = make_nop_x86;  		ideal_nop = ideal_nop5_x86_32;  		mcount_adjust_32 = -1; +		gpfx = 0; +		break; +	case EM_ARM: +		reltype = R_ARM_ABS32; +		altmcount = "__gnu_mcount_nc"; +		make_nop = make_nop_arm; +		rel_type_nop = R_ARM_NONE; +		gpfx = 0;  		break; -	case EM_ARM:	 reltype = R_ARM_ABS32; -			 altmcount = "__gnu_mcount_nc"; -			 make_nop = make_nop_arm; -			 rel_type_nop = R_ARM_NONE; -			 break;  	case EM_AARCH64: -			reltype = R_AARCH64_ABS64; -			make_nop = make_nop_arm64; -			rel_type_nop = R_AARCH64_NONE; -			ideal_nop = ideal_nop4_arm64; -			gpfx = '_'; -			break; -	case EM_IA_64:	 reltype = R_IA64_IMM64;   gpfx = '_'; break; -	case EM_MIPS:	 /* reltype: e_class    */ gpfx = '_'; break; -	case EM_PPC:	 reltype = R_PPC_ADDR32;   gpfx = '_'; break; -	case EM_PPC64:	 reltype = R_PPC64_ADDR64; gpfx = '_'; break; -	case EM_S390:    /* reltype: e_class    */ gpfx = '_'; break; -	case EM_SH:	 reltype = R_SH_DIR32;                 break; -	case EM_SPARCV9: reltype = R_SPARC_64;     gpfx = '_'; break; +		reltype = R_AARCH64_ABS64; +		make_nop = make_nop_arm64; +		rel_type_nop = R_AARCH64_NONE; +		ideal_nop = ideal_nop4_arm64; +		break; +	case EM_IA_64:	reltype = R_IA64_IMM64; break; +	case EM_MIPS:	/* reltype: e_class    */ break; +	case EM_PPC:	reltype = R_PPC_ADDR32; break; +	case EM_PPC64:	reltype = R_PPC64_ADDR64; break; +	case EM_S390:	/* reltype: e_class    */ break; +	case EM_SH:	reltype = R_SH_DIR32; gpfx = 0; break; +	case EM_SPARCV9: reltype = R_SPARC_64; break;  	case EM_X86_64:  		make_nop = make_nop_x86;  		ideal_nop = ideal_nop5_x86_64;  		reltype = R_X86_64_64;  		rel_type_nop = R_X86_64_NONE;  		mcount_adjust_64 = -1; +		gpfx = 0;  		break;  	}  /* end switch */ @@ -543,20 +552,20 @@ do_file(char const *const fname)  	default:  		fprintf(stderr, "unrecognized ELF class %d %s\n",  			ehdr->e_ident[EI_CLASS], fname); -		fail_file(); -		break; +		goto out;  	case ELFCLASS32:  		if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)  		||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {  			fprintf(stderr,  				"unrecognized ET_REL file: %s\n", fname); -			fail_file(); +			goto out;  		}  		if (w2(ehdr->e_machine) == EM_MIPS) {  			reltype = R_MIPS_32;  			is_fake_mcount32 = MIPS32_is_fake_mcount;  		} -		do32(ehdr, fname, reltype); +		if (do32(ehdr, fname, reltype) < 0) +			goto out;  		break;  	case ELFCLASS64: {  		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr; @@ -564,7 +573,7 @@ do_file(char const *const fname)  		||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {  			fprintf(stderr,  				"unrecognized ET_REL file: %s\n", fname); -			fail_file(); +			goto out;  		}  		if (w2(ghdr->e_machine) == EM_S390) {  			reltype = R_390_64; @@ -576,17 +585,20 @@ do_file(char const *const fname)  			Elf64_r_info = MIPS64_r_info;  			is_fake_mcount64 = MIPS64_is_fake_mcount;  		} -		do64(ghdr, fname, reltype); +		if (do64(ghdr, fname, reltype) < 0) +			goto out;  		break;  	}  	}  /* end switch */ -	write_file(fname); -	cleanup(); +	rc = write_file(fname); +out: +	file_append_cleanup(); +	mmap_cleanup(); +	return rc;  } -int -main(int argc, char *argv[]) +int main(int argc, char *argv[])  {  	const char ftrace[] = "/ftrace.o";  	int ftrace_size = sizeof(ftrace) - 1; @@ -613,7 +625,6 @@ main(int argc, char *argv[])  	/* Process each file in turn, allowing deep failure. */  	for (i = optind; i < argc; i++) {  		char *file = argv[i]; -		int const sjval = setjmp(jmpenv);  		int len;  		/* @@ -626,28 +637,10 @@ main(int argc, char *argv[])  		    strcmp(file + (len - ftrace_size), ftrace) == 0)  			continue; -		switch (sjval) { -		default: -			fprintf(stderr, "internal error: %s\n", file); -			exit(1); -			break; -		case SJ_SETJMP:    /* normal sequence */ -			/* Avoid problems if early cleanup() */ -			fd_map = -1; -			mmap_failed = 1; -			file_map = NULL; -			file_ptr = NULL; -			file_updated = 0; -			do_file(file); -			break; -		case SJ_FAIL:    /* error in do_file or below */ +		if (do_file(file)) {  			fprintf(stderr, "%s: failed\n", file);  			++n_error; -			break; -		case SJ_SUCCEED:    /* premature success */ -			/* do nothing */ -			break; -		}  /* end switch */ +		}  	}  	return !!n_error;  } | 
