| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * sortextable.c: Sort the kernel's exception table | 
 | 3 |  * | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 4 |  * Copyright 2011 - 2012 Cavium, Inc. | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 5 |  * | 
 | 6 |  * Based on code taken from recortmcount.c which is: | 
 | 7 |  * | 
 | 8 |  * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved. | 
 | 9 |  * Licensed under the GNU General Public License, version 2 (GPLv2). | 
 | 10 |  * | 
 | 11 |  * Restructured to fit Linux format, as well as other updates: | 
 | 12 |  *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | /* | 
 | 16 |  * Strategy: alter the vmlinux file in-place. | 
 | 17 |  */ | 
 | 18 |  | 
 | 19 | #include <sys/types.h> | 
 | 20 | #include <sys/mman.h> | 
 | 21 | #include <sys/stat.h> | 
 | 22 | #include <getopt.h> | 
 | 23 | #include <elf.h> | 
 | 24 | #include <fcntl.h> | 
 | 25 | #include <setjmp.h> | 
 | 26 | #include <stdio.h> | 
 | 27 | #include <stdlib.h> | 
 | 28 | #include <string.h> | 
 | 29 | #include <unistd.h> | 
 | 30 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 31 | #include <tools/be_byteshift.h> | 
 | 32 | #include <tools/le_byteshift.h> | 
 | 33 |  | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 34 | static int fd_map;	/* File descriptor for file being modified. */ | 
 | 35 | static int mmap_failed; /* Boolean flag. */ | 
 | 36 | static void *ehdr_curr; /* current ElfXX_Ehdr *  for resource cleanup */ | 
 | 37 | static struct stat sb;	/* Remember .st_size, etc. */ | 
 | 38 | static jmp_buf jmpenv;	/* setjmp/longjmp per-file error escape */ | 
 | 39 |  | 
 | 40 | /* setjmp() return values */ | 
 | 41 | enum { | 
 | 42 | 	SJ_SETJMP = 0,  /* hardwired first return */ | 
 | 43 | 	SJ_FAIL, | 
 | 44 | 	SJ_SUCCEED | 
 | 45 | }; | 
 | 46 |  | 
 | 47 | /* Per-file resource cleanup when multiple files. */ | 
 | 48 | static void | 
 | 49 | cleanup(void) | 
 | 50 | { | 
 | 51 | 	if (!mmap_failed) | 
 | 52 | 		munmap(ehdr_curr, sb.st_size); | 
 | 53 | 	close(fd_map); | 
 | 54 | } | 
 | 55 |  | 
 | 56 | static void __attribute__((noreturn)) | 
 | 57 | fail_file(void) | 
 | 58 | { | 
 | 59 | 	cleanup(); | 
 | 60 | 	longjmp(jmpenv, SJ_FAIL); | 
 | 61 | } | 
 | 62 |  | 
 | 63 | static void __attribute__((noreturn)) | 
 | 64 | succeed_file(void) | 
 | 65 | { | 
 | 66 | 	cleanup(); | 
 | 67 | 	longjmp(jmpenv, SJ_SUCCEED); | 
 | 68 | } | 
 | 69 |  | 
 | 70 |  | 
 | 71 | /* | 
 | 72 |  * Get the whole file as a programming convenience in order to avoid | 
 | 73 |  * malloc+lseek+read+free of many pieces.  If successful, then mmap | 
 | 74 |  * avoids copying unused pieces; else just read the whole file. | 
 | 75 |  * Open for both read and write. | 
 | 76 |  */ | 
 | 77 | static void *mmap_file(char const *fname) | 
 | 78 | { | 
 | 79 | 	void *addr; | 
 | 80 |  | 
 | 81 | 	fd_map = open(fname, O_RDWR); | 
 | 82 | 	if (fd_map < 0 || fstat(fd_map, &sb) < 0) { | 
 | 83 | 		perror(fname); | 
 | 84 | 		fail_file(); | 
 | 85 | 	} | 
 | 86 | 	if (!S_ISREG(sb.st_mode)) { | 
 | 87 | 		fprintf(stderr, "not a regular file: %s\n", fname); | 
 | 88 | 		fail_file(); | 
 | 89 | 	} | 
 | 90 | 	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, | 
 | 91 | 		    fd_map, 0); | 
 | 92 | 	if (addr == MAP_FAILED) { | 
 | 93 | 		mmap_failed = 1; | 
 | 94 | 		fprintf(stderr, "Could not mmap file: %s\n", fname); | 
 | 95 | 		fail_file(); | 
 | 96 | 	} | 
 | 97 | 	return addr; | 
 | 98 | } | 
 | 99 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 100 | static uint64_t r8be(const uint64_t *x) | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 101 | { | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 102 | 	return get_unaligned_be64(x); | 
 | 103 | } | 
 | 104 | static uint32_t rbe(const uint32_t *x) | 
 | 105 | { | 
 | 106 | 	return get_unaligned_be32(x); | 
 | 107 | } | 
 | 108 | static uint16_t r2be(const uint16_t *x) | 
 | 109 | { | 
 | 110 | 	return get_unaligned_be16(x); | 
 | 111 | } | 
 | 112 | static uint64_t r8le(const uint64_t *x) | 
 | 113 | { | 
 | 114 | 	return get_unaligned_le64(x); | 
 | 115 | } | 
 | 116 | static uint32_t rle(const uint32_t *x) | 
 | 117 | { | 
 | 118 | 	return get_unaligned_le32(x); | 
 | 119 | } | 
 | 120 | static uint16_t r2le(const uint16_t *x) | 
 | 121 | { | 
 | 122 | 	return get_unaligned_le16(x); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 123 | } | 
 | 124 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 125 | static void w8be(uint64_t val, uint64_t *x) | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 126 | { | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 127 | 	put_unaligned_be64(val, x); | 
 | 128 | } | 
 | 129 | static void wbe(uint32_t val, uint32_t *x) | 
 | 130 | { | 
 | 131 | 	put_unaligned_be32(val, x); | 
 | 132 | } | 
 | 133 | static void w2be(uint16_t val, uint16_t *x) | 
 | 134 | { | 
 | 135 | 	put_unaligned_be16(val, x); | 
 | 136 | } | 
 | 137 | static void w8le(uint64_t val, uint64_t *x) | 
 | 138 | { | 
 | 139 | 	put_unaligned_le64(val, x); | 
 | 140 | } | 
 | 141 | static void wle(uint32_t val, uint32_t *x) | 
 | 142 | { | 
 | 143 | 	put_unaligned_le32(val, x); | 
 | 144 | } | 
 | 145 | static void w2le(uint16_t val, uint16_t *x) | 
 | 146 | { | 
 | 147 | 	put_unaligned_le16(val, x); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 148 | } | 
 | 149 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 150 | static uint64_t (*r8)(const uint64_t *); | 
 | 151 | static uint32_t (*r)(const uint32_t *); | 
 | 152 | static uint16_t (*r2)(const uint16_t *); | 
 | 153 | static void (*w8)(uint64_t, uint64_t *); | 
 | 154 | static void (*w)(uint32_t, uint32_t *); | 
 | 155 | static void (*w2)(uint16_t, uint16_t *); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 156 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 157 | typedef void (*table_sort_t)(char *, int); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 158 |  | 
 | 159 | /* 32 bit and 64 bit are very similar */ | 
 | 160 | #include "sortextable.h" | 
 | 161 | #define SORTEXTABLE_64 | 
 | 162 | #include "sortextable.h" | 
 | 163 |  | 
| Heiko Carstens | eb608fb | 2012-09-05 13:26:11 +0200 | [diff] [blame] | 164 | static int compare_relative_table(const void *a, const void *b) | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 165 | { | 
 | 166 | 	int32_t av = (int32_t)r(a); | 
 | 167 | 	int32_t bv = (int32_t)r(b); | 
 | 168 |  | 
 | 169 | 	if (av < bv) | 
 | 170 | 		return -1; | 
 | 171 | 	if (av > bv) | 
 | 172 | 		return 1; | 
 | 173 | 	return 0; | 
 | 174 | } | 
 | 175 |  | 
| Heiko Carstens | eb608fb | 2012-09-05 13:26:11 +0200 | [diff] [blame] | 176 | static void sort_relative_table(char *extab_image, int image_size) | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 177 | { | 
 | 178 | 	int i; | 
 | 179 |  | 
 | 180 | 	/* | 
 | 181 | 	 * Do the same thing the runtime sort does, first normalize to | 
 | 182 | 	 * being relative to the start of the section. | 
 | 183 | 	 */ | 
 | 184 | 	i = 0; | 
 | 185 | 	while (i < image_size) { | 
 | 186 | 		uint32_t *loc = (uint32_t *)(extab_image + i); | 
 | 187 | 		w(r(loc) + i, loc); | 
 | 188 | 		i += 4; | 
 | 189 | 	} | 
 | 190 |  | 
| Heiko Carstens | eb608fb | 2012-09-05 13:26:11 +0200 | [diff] [blame] | 191 | 	qsort(extab_image, image_size / 8, 8, compare_relative_table); | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 192 |  | 
 | 193 | 	/* Now denormalize. */ | 
 | 194 | 	i = 0; | 
 | 195 | 	while (i < image_size) { | 
 | 196 | 		uint32_t *loc = (uint32_t *)(extab_image + i); | 
 | 197 | 		w(r(loc) - i, loc); | 
 | 198 | 		i += 4; | 
 | 199 | 	} | 
 | 200 | } | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 201 |  | 
 | 202 | static void | 
 | 203 | do_file(char const *const fname) | 
 | 204 | { | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 205 | 	table_sort_t custom_sort; | 
 | 206 | 	Elf32_Ehdr *ehdr = mmap_file(fname); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 207 |  | 
 | 208 | 	ehdr_curr = ehdr; | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 209 | 	switch (ehdr->e_ident[EI_DATA]) { | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 210 | 	default: | 
 | 211 | 		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n", | 
 | 212 | 			ehdr->e_ident[EI_DATA], fname); | 
 | 213 | 		fail_file(); | 
 | 214 | 		break; | 
 | 215 | 	case ELFDATA2LSB: | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 216 | 		r = rle; | 
 | 217 | 		r2 = r2le; | 
 | 218 | 		r8 = r8le; | 
 | 219 | 		w = wle; | 
 | 220 | 		w2 = w2le; | 
 | 221 | 		w8 = w8le; | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 222 | 		break; | 
 | 223 | 	case ELFDATA2MSB: | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 224 | 		r = rbe; | 
 | 225 | 		r2 = r2be; | 
 | 226 | 		r8 = r8be; | 
 | 227 | 		w = wbe; | 
 | 228 | 		w2 = w2be; | 
 | 229 | 		w8 = w8be; | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 230 | 		break; | 
 | 231 | 	}  /* end switch */ | 
 | 232 | 	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 233 | 	||  r2(&ehdr->e_type) != ET_EXEC | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 234 | 	||  ehdr->e_ident[EI_VERSION] != EV_CURRENT) { | 
 | 235 | 		fprintf(stderr, "unrecognized ET_EXEC file %s\n", fname); | 
 | 236 | 		fail_file(); | 
 | 237 | 	} | 
 | 238 |  | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 239 | 	custom_sort = NULL; | 
 | 240 | 	switch (r2(&ehdr->e_machine)) { | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 241 | 	default: | 
 | 242 | 		fprintf(stderr, "unrecognized e_machine %d %s\n", | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 243 | 			r2(&ehdr->e_machine), fname); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 244 | 		fail_file(); | 
 | 245 | 		break; | 
 | 246 | 	case EM_386: | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 247 | 	case EM_X86_64: | 
| Heiko Carstens | 3193a98 | 2012-07-24 14:51:34 +0200 | [diff] [blame] | 248 | 	case EM_S390: | 
| Heiko Carstens | eb608fb | 2012-09-05 13:26:11 +0200 | [diff] [blame] | 249 | 		custom_sort = sort_relative_table; | 
 | 250 | 		break; | 
| Stephen Boyd | ee951c6 | 2012-10-29 19:19:34 +0100 | [diff] [blame] | 251 | 	case EM_ARM: | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 252 | 	case EM_MIPS: | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 253 | 		break; | 
 | 254 | 	}  /* end switch */ | 
 | 255 |  | 
 | 256 | 	switch (ehdr->e_ident[EI_CLASS]) { | 
 | 257 | 	default: | 
 | 258 | 		fprintf(stderr, "unrecognized ELF class %d %s\n", | 
 | 259 | 			ehdr->e_ident[EI_CLASS], fname); | 
 | 260 | 		fail_file(); | 
 | 261 | 		break; | 
 | 262 | 	case ELFCLASS32: | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 263 | 		if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr) | 
 | 264 | 		||  r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) { | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 265 | 			fprintf(stderr, | 
 | 266 | 				"unrecognized ET_EXEC file: %s\n", fname); | 
 | 267 | 			fail_file(); | 
 | 268 | 		} | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 269 | 		do32(ehdr, fname, custom_sort); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 270 | 		break; | 
 | 271 | 	case ELFCLASS64: { | 
 | 272 | 		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr; | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 273 | 		if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr) | 
 | 274 | 		||  r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) { | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 275 | 			fprintf(stderr, | 
 | 276 | 				"unrecognized ET_EXEC file: %s\n", fname); | 
 | 277 | 			fail_file(); | 
 | 278 | 		} | 
| David Daney | d59a168 | 2012-04-24 11:23:14 -0700 | [diff] [blame] | 279 | 		do64(ghdr, fname, custom_sort); | 
| David Daney | a79f248 | 2012-04-19 14:59:55 -0700 | [diff] [blame] | 280 | 		break; | 
 | 281 | 	} | 
 | 282 | 	}  /* end switch */ | 
 | 283 |  | 
 | 284 | 	cleanup(); | 
 | 285 | } | 
 | 286 |  | 
 | 287 | int | 
 | 288 | main(int argc, char *argv[]) | 
 | 289 | { | 
 | 290 | 	int n_error = 0;  /* gcc-4.3.0 false positive complaint */ | 
 | 291 | 	int i; | 
 | 292 |  | 
 | 293 | 	if (argc < 2) { | 
 | 294 | 		fprintf(stderr, "usage: sortextable vmlinux...\n"); | 
 | 295 | 		return 0; | 
 | 296 | 	} | 
 | 297 |  | 
 | 298 | 	/* Process each file in turn, allowing deep failure. */ | 
 | 299 | 	for (i = 1; i < argc; i++) { | 
 | 300 | 		char *file = argv[i]; | 
 | 301 | 		int const sjval = setjmp(jmpenv); | 
 | 302 |  | 
 | 303 | 		switch (sjval) { | 
 | 304 | 		default: | 
 | 305 | 			fprintf(stderr, "internal error: %s\n", file); | 
 | 306 | 			exit(1); | 
 | 307 | 			break; | 
 | 308 | 		case SJ_SETJMP:    /* normal sequence */ | 
 | 309 | 			/* Avoid problems if early cleanup() */ | 
 | 310 | 			fd_map = -1; | 
 | 311 | 			ehdr_curr = NULL; | 
 | 312 | 			mmap_failed = 1; | 
 | 313 | 			do_file(file); | 
 | 314 | 			break; | 
 | 315 | 		case SJ_FAIL:    /* error in do_file or below */ | 
 | 316 | 			++n_error; | 
 | 317 | 			break; | 
 | 318 | 		case SJ_SUCCEED:    /* premature success */ | 
 | 319 | 			/* do nothing */ | 
 | 320 | 			break; | 
 | 321 | 		}  /* end switch */ | 
 | 322 | 	} | 
 | 323 | 	return !!n_error; | 
 | 324 | } |