sortextable.c 7.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4
/*
 * sortextable.c: Sort the kernel's exception table
 *
5
 * Copyright 2011 - 2012 Cavium, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Based on code taken from recortmcount.c which is:
 *
 * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
 *
 * Restructured to fit Linux format, as well as other updates:
 *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
 */

/*
 * Strategy: alter the vmlinux file in-place.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <getopt.h>
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

30 31 32
#include <tools/be_byteshift.h>
#include <tools/le_byteshift.h>

33 34 35 36
#ifndef EM_ARCOMPACT
#define EM_ARCOMPACT	93
#endif

37 38 39 40
#ifndef EM_XTENSA
#define EM_XTENSA	94
#endif

41 42 43 44
#ifndef EM_AARCH64
#define EM_AARCH64	183
#endif

45 46 47 48
#ifndef EM_MICROBLAZE
#define EM_MICROBLAZE	189
#endif

49 50 51 52
#ifndef EM_ARCV2
#define EM_ARCV2	195
#endif

53 54 55 56 57 58 59 60
static uint32_t (*r)(const uint32_t *);
static uint16_t (*r2)(const uint16_t *);
static uint64_t (*r8)(const uint64_t *);
static void (*w)(uint32_t, uint32_t *);
static void (*w2)(uint16_t, uint16_t *);
static void (*w8)(uint64_t, uint64_t *);
typedef void (*table_sort_t)(char *, int);

61 62 63 64 65 66
/*
 * 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.
 */
67
static void *mmap_file(char const *fname, size_t *size)
68
{
69 70 71
	int fd;
	struct stat sb;
	void *addr = NULL;
72

73 74
	fd = open(fname, O_RDWR);
	if (fd < 0) {
75
		perror(fname);
76 77 78 79 80
		return NULL;
	}
	if (fstat(fd, &sb) < 0) {
		perror(fname);
		goto out;
81 82 83
	}
	if (!S_ISREG(sb.st_mode)) {
		fprintf(stderr, "not a regular file: %s\n", fname);
84
		goto out;
85
	}
86

87
	addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
88 89
	if (addr == MAP_FAILED) {
		fprintf(stderr, "Could not mmap file: %s\n", fname);
90
		goto out;
91
	}
92 93 94 95 96

	*size = sb.st_size;

out:
	close(fd);
97 98 99
	return addr;
}

100
static uint32_t rbe(const uint32_t *x)
101
{
102
	return get_unaligned_be32(x);
103
}
104

105
static uint16_t r2be(const uint16_t *x)
106
{
107
	return get_unaligned_be16(x);
108
}
109 110

static uint64_t r8be(const uint64_t *x)
111
{
112
	return get_unaligned_be64(x);
113
}
114

115 116 117 118
static uint32_t rle(const uint32_t *x)
{
	return get_unaligned_le32(x);
}
119

120
static uint16_t r2le(const uint16_t *x)
121
{
122
	return get_unaligned_le16(x);
123 124
}

125
static uint64_t r8le(const uint64_t *x)
126
{
127
	return get_unaligned_le64(x);
128
}
129

130 131 132 133
static void wbe(uint32_t val, uint32_t *x)
{
	put_unaligned_be32(val, x);
}
134

135 136 137 138
static void w2be(uint16_t val, uint16_t *x)
{
	put_unaligned_be16(val, x);
}
139 140

static void w8be(uint64_t val, uint64_t *x)
141
{
142
	put_unaligned_be64(val, x);
143
}
144

145 146 147 148
static void wle(uint32_t val, uint32_t *x)
{
	put_unaligned_le32(val, x);
}
149

150
static void w2le(uint16_t val, uint16_t *x)
151
{
152
	put_unaligned_le16(val, x);
153 154
}

155 156 157 158
static void w8le(uint64_t val, uint64_t *x)
{
	put_unaligned_le64(val, x);
}
159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
/*
 * Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
 * the way to -256..-1, to avoid conflicting with real section
 * indices.
 */
#define SPECIAL(i) ((i) - (SHN_HIRESERVE + 1))

static inline int is_shndx_special(unsigned int i)
{
	return i != SHN_XINDEX && i >= SHN_LORESERVE && i <= SHN_HIRESERVE;
}

/* Accessor for sym->st_shndx, hides ugliness of "64k sections" */
static inline unsigned int get_secindex(unsigned int shndx,
					unsigned int sym_offs,
					const Elf32_Word *symtab_shndx_start)
{
	if (is_shndx_special(shndx))
		return SPECIAL(shndx);
	if (shndx != SHN_XINDEX)
		return shndx;
	return r(&symtab_shndx_start[sym_offs]);
}

184 185 186 187 188
/* 32 bit and 64 bit are very similar */
#include "sortextable.h"
#define SORTEXTABLE_64
#include "sortextable.h"

189
static int compare_relative_table(const void *a, const void *b)
190 191 192 193 194 195 196 197 198 199 200
{
	int32_t av = (int32_t)r(a);
	int32_t bv = (int32_t)r(b);

	if (av < bv)
		return -1;
	if (av > bv)
		return 1;
	return 0;
}

201
static void sort_relative_table(char *extab_image, int image_size)
202
{
203
	int i = 0;
204

205 206 207 208
	/*
	 * Do the same thing the runtime sort does, first normalize to
	 * being relative to the start of the section.
	 */
209 210 211
	while (i < image_size) {
		uint32_t *loc = (uint32_t *)(extab_image + i);
		w(r(loc) + i, loc);
212
		i += 4;
213 214
	}

215
	qsort(extab_image, image_size / 8, 8, compare_relative_table);
216

217
	/* Now denormalize. */
218 219 220 221
	i = 0;
	while (i < image_size) {
		uint32_t *loc = (uint32_t *)(extab_image + i);
		w(r(loc) - i, loc);
222
		i += 4;
223 224 225
	}
}

226
static void x86_sort_relative_table(char *extab_image, int image_size)
227
{
228
	int i = 0;
229 230 231

	while (i < image_size) {
		uint32_t *loc = (uint32_t *)(extab_image + i);
232

233
		w(r(loc) + i, loc);
234 235 236 237
		w(r(loc + 1) + i + 4, loc + 1);
		w(r(loc + 2) + i + 8, loc + 2);

		i += sizeof(uint32_t) * 3;
238 239
	}

240
	qsort(extab_image, image_size / 12, 12, compare_relative_table);
241 242 243 244

	i = 0;
	while (i < image_size) {
		uint32_t *loc = (uint32_t *)(extab_image + i);
245

246
		w(r(loc) - i, loc);
247 248 249 250
		w(r(loc + 1) - (i + 4), loc + 1);
		w(r(loc + 2) - (i + 8), loc + 2);

		i += sizeof(uint32_t) * 3;
251 252
	}
}
253

254
static int do_file(char const *const fname, void *addr)
255
{
256
	int rc = -1;
257 258
	Elf32_Ehdr *ehdr = addr;
	table_sort_t custom_sort = NULL;
259 260 261

	switch (ehdr->e_ident[EI_DATA]) {
	case ELFDATA2LSB:
262 263 264 265 266 267
		r	= rle;
		r2	= r2le;
		r8	= r8le;
		w	= wle;
		w2	= w2le;
		w8	= w8le;
268 269
		break;
	case ELFDATA2MSB:
270 271 272 273 274 275
		r	= rbe;
		r2	= r2be;
		r8	= r8be;
		w	= wbe;
		w2	= w2be;
		w8	= w8be;
276
		break;
277 278 279 280 281 282 283 284 285
	default:
		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
			ehdr->e_ident[EI_DATA], fname);
		return -1;
	}

	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
	    (r2(&ehdr->e_type) != ET_EXEC && r2(&ehdr->e_type) != ET_DYN) ||
	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
286
		fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
287
		return -1;
288 289
	}

290
	switch (r2(&ehdr->e_machine)) {
291 292
	case EM_386:
	case EM_X86_64:
293 294
		custom_sort = x86_sort_relative_table;
		break;
295
	case EM_S390:
296
	case EM_AARCH64:
297
	case EM_PARISC:
298 299
	case EM_PPC:
	case EM_PPC64:
300 301
		custom_sort = sort_relative_table;
		break;
302
	case EM_ARCOMPACT:
303
	case EM_ARCV2:
304
	case EM_ARM:
305
	case EM_MICROBLAZE:
306
	case EM_MIPS:
307
	case EM_XTENSA:
308
		break;
309 310 311 312 313
	default:
		fprintf(stderr, "unrecognized e_machine %d %s\n",
			r2(&ehdr->e_machine), fname);
		return -1;
	}
314 315 316

	switch (ehdr->e_ident[EI_CLASS]) {
	case ELFCLASS32:
317 318
		if (r2(&ehdr->e_ehsize) != sizeof(Elf32_Ehdr) ||
		    r2(&ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
319
			fprintf(stderr,
320
				"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
321
			break;
322
		}
323
		rc = do32(ehdr, fname, custom_sort);
324
		break;
325 326
	case ELFCLASS64:
		{
327
		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
328 329
		if (r2(&ghdr->e_ehsize) != sizeof(Elf64_Ehdr) ||
		    r2(&ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
330
			fprintf(stderr,
331 332
				"unrecognized ET_EXEC/ET_DYN file: %s\n",
				fname);
333
			break;
334
		}
335
		rc = do64(ghdr, fname, custom_sort);
336 337 338 339 340
		}
		break;
	default:
		fprintf(stderr, "unrecognized ELF class %d %s\n",
			ehdr->e_ident[EI_CLASS], fname);
341 342 343
		break;
	}

344
	return rc;
345 346
}

347
int main(int argc, char *argv[])
348
{
349 350 351
	int i, n_error = 0;  /* gcc-4.3.0 false positive complaint */
	size_t size = 0;
	void *addr = NULL;
352 353 354 355 356 357 358 359

	if (argc < 2) {
		fprintf(stderr, "usage: sortextable vmlinux...\n");
		return 0;
	}

	/* Process each file in turn, allowing deep failure. */
	for (i = 1; i < argc; i++) {
360 361 362 363 364
		addr = mmap_file(argv[i], &size);
		if (!addr) {
			++n_error;
			continue;
		}
365

366
		if (do_file(argv[i], addr))
367
			++n_error;
368 369

		munmap(addr, size);
370
	}
371

372 373
	return !!n_error;
}