diff options
Diffstat (limited to 'scripts/recordmcount.c')
| -rw-r--r-- | scripts/recordmcount.c | 196 | 
1 files changed, 139 insertions, 57 deletions
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index f2f32eee2c5..650ecc83d7d 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -24,6 +24,7 @@  #include <sys/types.h>  #include <sys/mman.h>  #include <sys/stat.h> +#include <getopt.h>  #include <elf.h>  #include <fcntl.h>  #include <setjmp.h> @@ -32,12 +33,26 @@  #include <string.h>  #include <unistd.h> +#ifndef EM_METAG +/* Remove this when these make it to the standard system elf.h. */ +#define EM_METAG      174 +#define R_METAG_ADDR32                   2 +#define R_METAG_NONE                     3 +#endif + +#ifndef EM_AARCH64 +#define EM_AARCH64	183 +#define R_AARCH64_ABS64	257 +#endif +  static int fd_map;	/* File descriptor for file being modified. */  static int mmap_failed; /* Boolean flag. */  static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */  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 */  /* setjmp() return values */  enum { @@ -77,7 +92,7 @@ static off_t  ulseek(int const fd, off_t const offset, int const whence)  {  	off_t const w = lseek(fd, offset, whence); -	if ((off_t)-1 == w) { +	if (w == (off_t)-1) {  		perror("lseek");  		fail_file();  	} @@ -110,13 +125,41 @@ static void *  umalloc(size_t size)  {  	void *const addr = malloc(size); -	if (0 == addr) { +	if (addr == 0) {  		fprintf(stderr, "malloc failed: %zu bytes\n", size);  		fail_file();  	}  	return addr;  } +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; + +static char rel_type_nop; + +static int (*make_nop)(void *map, size_t const offset); + +static int make_nop_x86(void *map, size_t const offset) +{ +	uint32_t *ptr; +	unsigned char *op; + +	/* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */ +	ptr = map + offset; +	if (*ptr != 0) +		return -1; + +	op = map + offset - 1; +	if (*op != 0xe8) +		return -1; + +	/* convert to nop */ +	ulseek(fd_map, offset - 1, SEEK_SET); +	uwrite(fd_map, ideal_nop, 5); +	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 @@ -135,7 +178,7 @@ static void *mmap_file(char const *fname)  	void *addr;  	fd_map = open(fname, O_RDWR); -	if (0 > fd_map || 0 > fstat(fd_map, &sb)) { +	if (fd_map < 0 || fstat(fd_map, &sb) < 0) {  		perror(fname);  		fail_file();  	} @@ -146,7 +189,7 @@ static void *mmap_file(char const *fname)  	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,  		    fd_map, 0);  	mmap_failed = 0; -	if (MAP_FAILED == addr) { +	if (addr == MAP_FAILED) {  		mmap_failed = 1;  		addr = umalloc(sb.st_size);  		uread(fd_map, addr, sb.st_size); @@ -205,11 +248,13 @@ static uint32_t (*w2)(uint16_t);  static int  is_mcounted_section_name(char const *const txtname)  { -	return 0 == strcmp(".text",          txtname) || -		0 == strcmp(".sched.text",    txtname) || -		0 == strcmp(".spinlock.text", txtname) || -		0 == strcmp(".irqentry.text", txtname) || -		0 == strcmp(".text.unlikely", txtname); +	return strcmp(".text",           txtname) == 0 || +		strcmp(".ref.text",      txtname) == 0 || +		strcmp(".sched.text",    txtname) == 0 || +		strcmp(".spinlock.text", txtname) == 0 || +		strcmp(".irqentry.text", txtname) == 0 || +		strcmp(".kprobes.text", txtname) == 0 || +		strcmp(".text.unlikely", txtname) == 0;  }  /* 32 bit and 64 bit are very similar */ @@ -262,112 +307,151 @@ do_file(char const *const fname)  	w8 = w8nat;  	switch (ehdr->e_ident[EI_DATA]) {  		static unsigned int const endian = 1; -	default: { +	default:  		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",  			ehdr->e_ident[EI_DATA], fname);  		fail_file(); -	} break; -	case ELFDATA2LSB: { -		if (1 != *(unsigned char const *)&endian) { +		break; +	case ELFDATA2LSB: +		if (*(unsigned char const *)&endian != 1) {  			/* main() is big endian, file.o is little endian. */  			w = w4rev;  			w2 = w2rev;  			w8 = w8rev;  		} -	} break; -	case ELFDATA2MSB: { -		if (0 != *(unsigned char const *)&endian) { +		break; +	case ELFDATA2MSB: +		if (*(unsigned char const *)&endian != 0) {  			/* main() is little endian, file.o is big endian. */  			w = w4rev;  			w2 = w2rev;  			w8 = w8rev;  		} -	} break; +		break;  	}  /* end switch */ -	if (0 != memcmp(ELFMAG, ehdr->e_ident, SELFMAG) -	||  ET_REL != w2(ehdr->e_type) -	||  EV_CURRENT != ehdr->e_ident[EI_VERSION]) { +	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();  	}  	gpfx = 0;  	switch (w2(ehdr->e_machine)) { -	default: { +	default:  		fprintf(stderr, "unrecognized e_machine %d %s\n",  			w2(ehdr->e_machine), fname);  		fail_file(); -	} break; -	case EM_386:	 reltype = R_386_32;                   break; -	case EM_ARM:	 reltype = R_ARM_ABS32;                break; +		break; +	case EM_386: +		reltype = R_386_32; +		make_nop = make_nop_x86; +		ideal_nop = ideal_nop5_x86_32; +		mcount_adjust_32 = -1; +		break; +	case EM_ARM:	 reltype = R_ARM_ABS32; +			 altmcount = "__gnu_mcount_nc"; +			 break; +	case EM_AARCH64: +			 reltype = R_AARCH64_ABS64; gpfx = '_'; break;  	case EM_IA_64:	 reltype = R_IA64_IMM64;   gpfx = '_'; break; +	case EM_METAG:	 reltype = R_METAG_ADDR32; +			 altmcount = "_mcount_wrapper"; +			 rel_type_nop = R_METAG_NONE; +			 /* We happen to have the same requirement as MIPS */ +			 is_fake_mcount32 = MIPS32_is_fake_mcount; +			 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; -	case EM_X86_64:	 reltype = R_X86_64_64;                break; +	case EM_X86_64: +		make_nop = make_nop_x86; +		ideal_nop = ideal_nop5_x86_64; +		reltype = R_X86_64_64; +		mcount_adjust_64 = -1; +		break;  	}  /* end switch */  	switch (ehdr->e_ident[EI_CLASS]) { -	default: { +	default:  		fprintf(stderr, "unrecognized ELF class %d %s\n",  			ehdr->e_ident[EI_CLASS], fname);  		fail_file(); -	} break; -	case ELFCLASS32: { -		if (sizeof(Elf32_Ehdr) != w2(ehdr->e_ehsize) -		||  sizeof(Elf32_Shdr) != w2(ehdr->e_shentsize)) { +		break; +	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();  		} -		if (EM_S390 == w2(ehdr->e_machine)) +		if (w2(ehdr->e_machine) == EM_S390) {  			reltype = R_390_32; -		if (EM_MIPS == w2(ehdr->e_machine)) { +			mcount_adjust_32 = -4; +		} +		if (w2(ehdr->e_machine) == EM_MIPS) {  			reltype = R_MIPS_32;  			is_fake_mcount32 = MIPS32_is_fake_mcount;  		}  		do32(ehdr, fname, reltype); -	} break; +		break;  	case ELFCLASS64: {  		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr; -		if (sizeof(Elf64_Ehdr) != w2(ghdr->e_ehsize) -		||  sizeof(Elf64_Shdr) != w2(ghdr->e_shentsize)) { +		if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr) +		||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {  			fprintf(stderr,  				"unrecognized ET_REL file: %s\n", fname);  			fail_file();  		} -		if (EM_S390 == w2(ghdr->e_machine)) +		if (w2(ghdr->e_machine) == EM_S390) {  			reltype = R_390_64; -		if (EM_MIPS == w2(ghdr->e_machine)) { +			mcount_adjust_64 = -8; +		} +		if (w2(ghdr->e_machine) == EM_MIPS) {  			reltype = R_MIPS_64;  			Elf64_r_sym = MIPS64_r_sym;  			Elf64_r_info = MIPS64_r_info;  			is_fake_mcount64 = MIPS64_is_fake_mcount;  		}  		do64(ghdr, fname, reltype); -	} break; +		break; +	}  	}  /* end switch */  	cleanup();  }  int -main(int argc, char const *argv[]) +main(int argc, char *argv[])  { -	const char ftrace[] = "kernel/trace/ftrace.o"; +	const char ftrace[] = "/ftrace.o";  	int ftrace_size = sizeof(ftrace) - 1;  	int n_error = 0;  /* gcc-4.3.0 false positive complaint */ +	int c; +	int i; + +	while ((c = getopt(argc, argv, "w")) >= 0) { +		switch (c) { +		case 'w': +			warn_on_notrace_sect = 1; +			break; +		default: +			fprintf(stderr, "usage: recordmcount [-w] file.o...\n"); +			return 0; +		} +	} -	if (argc <= 1) { -		fprintf(stderr, "usage: recordmcount file.o...\n"); +	if ((argc - optind) < 1) { +		fprintf(stderr, "usage: recordmcount [-w] file.o...\n");  		return 0;  	}  	/* Process each file in turn, allowing deep failure. */ -	for (--argc, ++argv; 0 < argc; --argc, ++argv) { +	for (i = optind; i < argc; i++) { +		char *file = argv[i];  		int const sjval = setjmp(jmpenv);  		int len; @@ -376,32 +460,30 @@ main(int argc, char const *argv[])  		 * function but does not call it. Since ftrace.o should  		 * not be traced anyway, we just skip it.  		 */ -		len = strlen(argv[0]); +		len = strlen(file);  		if (len >= ftrace_size && -		    strcmp(argv[0] + (len - ftrace_size), ftrace) == 0) +		    strcmp(file + (len - ftrace_size), ftrace) == 0)  			continue;  		switch (sjval) { -		default: { -			fprintf(stderr, "internal error: %s\n", argv[0]); +		default: +			fprintf(stderr, "internal error: %s\n", file);  			exit(1); -		} break; -		case SJ_SETJMP: {  /* normal sequence */ +			break; +		case SJ_SETJMP:    /* normal sequence */  			/* Avoid problems if early cleanup() */  			fd_map = -1;  			ehdr_curr = NULL;  			mmap_failed = 1; -			do_file(argv[0]); -		} break; -		case SJ_FAIL: {  /* error in do_file or below */ +			do_file(file); +			break; +		case SJ_FAIL:    /* error in do_file or below */  			++n_error; -		} break; -		case SJ_SUCCEED: {  /* premature success */ +			break; +		case SJ_SUCCEED:    /* premature success */  			/* do nothing */ -		} break; +			break;  		}  /* end switch */  	}  	return !!n_error;  } - -  | 
