aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Daney <david.daney@cavium.com>2012-04-19 17:59:55 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2012-04-19 18:06:55 -0400
commita79f248b9b309ebb5f34ca6a8fd1eb9e18db5720 (patch)
treebab3fe4ac76370948b4ca62b41253c488ca96c38
parente816b57a337ea3b755de72bec38c10c864f23015 (diff)
scripts: Add sortextable to sort the kernel's exception table.
Using this build-time sort saves time booting as we don't have to burn cycles sorting the exception table. Signed-off-by: David Daney <david.daney@cavium.com> Link: http://lkml.kernel.org/r/1334872799-14589-2-git-send-email-ddaney.cavm@gmail.com Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Makefile1
-rw-r--r--scripts/sortextable.c271
-rw-r--r--scripts/sortextable.h168
4 files changed, 441 insertions, 0 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 105b21f08185..65f362d931b5 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -9,3 +9,4 @@ unifdef
9ihex2fw 9ihex2fw
10recordmcount 10recordmcount
11docproc 11docproc
12sortextable
diff --git a/scripts/Makefile b/scripts/Makefile
index df7678febf27..43e19b9fc641 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -13,6 +13,7 @@ hostprogs-$(CONFIG_LOGO) += pnmtologo
13hostprogs-$(CONFIG_VT) += conmakehash 13hostprogs-$(CONFIG_VT) += conmakehash
14hostprogs-$(CONFIG_IKCONFIG) += bin2c 14hostprogs-$(CONFIG_IKCONFIG) += bin2c
15hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount 15hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
16hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
16 17
17always := $(hostprogs-y) $(hostprogs-m) 18always := $(hostprogs-y) $(hostprogs-m)
18 19
diff --git a/scripts/sortextable.c b/scripts/sortextable.c
new file mode 100644
index 000000000000..f51f1d43da63
--- /dev/null
+++ b/scripts/sortextable.c
@@ -0,0 +1,271 @@
1/*
2 * sortextable.c: Sort the kernel's exception table
3 *
4 * Copyright 2011 Cavium, Inc.
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
31static int fd_map; /* File descriptor for file being modified. */
32static int mmap_failed; /* Boolean flag. */
33static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */
34static struct stat sb; /* Remember .st_size, etc. */
35static jmp_buf jmpenv; /* setjmp/longjmp per-file error escape */
36
37/* setjmp() return values */
38enum {
39 SJ_SETJMP = 0, /* hardwired first return */
40 SJ_FAIL,
41 SJ_SUCCEED
42};
43
44/* Per-file resource cleanup when multiple files. */
45static void
46cleanup(void)
47{
48 if (!mmap_failed)
49 munmap(ehdr_curr, sb.st_size);
50 close(fd_map);
51}
52
53static void __attribute__((noreturn))
54fail_file(void)
55{
56 cleanup();
57 longjmp(jmpenv, SJ_FAIL);
58}
59
60static void __attribute__((noreturn))
61succeed_file(void)
62{
63 cleanup();
64 longjmp(jmpenv, SJ_SUCCEED);
65}
66
67
68/*
69 * Get the whole file as a programming convenience in order to avoid
70 * malloc+lseek+read+free of many pieces. If successful, then mmap
71 * avoids copying unused pieces; else just read the whole file.
72 * Open for both read and write.
73 */
74static void *mmap_file(char const *fname)
75{
76 void *addr;
77
78 fd_map = open(fname, O_RDWR);
79 if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
80 perror(fname);
81 fail_file();
82 }
83 if (!S_ISREG(sb.st_mode)) {
84 fprintf(stderr, "not a regular file: %s\n", fname);
85 fail_file();
86 }
87 addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
88 fd_map, 0);
89 if (addr == MAP_FAILED) {
90 mmap_failed = 1;
91 fprintf(stderr, "Could not mmap file: %s\n", fname);
92 fail_file();
93 }
94 return addr;
95}
96
97/* w8rev, w8nat, ...: Handle endianness. */
98
99static uint64_t w8rev(uint64_t const x)
100{
101 return ((0xff & (x >> (0 * 8))) << (7 * 8))
102 | ((0xff & (x >> (1 * 8))) << (6 * 8))
103 | ((0xff & (x >> (2 * 8))) << (5 * 8))
104 | ((0xff & (x >> (3 * 8))) << (4 * 8))
105 | ((0xff & (x >> (4 * 8))) << (3 * 8))
106 | ((0xff & (x >> (5 * 8))) << (2 * 8))
107 | ((0xff & (x >> (6 * 8))) << (1 * 8))
108 | ((0xff & (x >> (7 * 8))) << (0 * 8));
109}
110
111static uint32_t w4rev(uint32_t const x)
112{
113 return ((0xff & (x >> (0 * 8))) << (3 * 8))
114 | ((0xff & (x >> (1 * 8))) << (2 * 8))
115 | ((0xff & (x >> (2 * 8))) << (1 * 8))
116 | ((0xff & (x >> (3 * 8))) << (0 * 8));
117}
118
119static uint32_t w2rev(uint16_t const x)
120{
121 return ((0xff & (x >> (0 * 8))) << (1 * 8))
122 | ((0xff & (x >> (1 * 8))) << (0 * 8));
123}
124
125static uint64_t w8nat(uint64_t const x)
126{
127 return x;
128}
129
130static uint32_t w4nat(uint32_t const x)
131{
132 return x;
133}
134
135static uint32_t w2nat(uint16_t const x)
136{
137 return x;
138}
139
140static uint64_t (*w8)(uint64_t);
141static uint32_t (*w)(uint32_t);
142static uint32_t (*w2)(uint16_t);
143
144
145/* 32 bit and 64 bit are very similar */
146#include "sortextable.h"
147#define SORTEXTABLE_64
148#include "sortextable.h"
149
150
151static void
152do_file(char const *const fname)
153{
154 Elf32_Ehdr *const ehdr = mmap_file(fname);
155
156 ehdr_curr = ehdr;
157 w = w4nat;
158 w2 = w2nat;
159 w8 = w8nat;
160 switch (ehdr->e_ident[EI_DATA]) {
161 static unsigned int const endian = 1;
162 default:
163 fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
164 ehdr->e_ident[EI_DATA], fname);
165 fail_file();
166 break;
167 case ELFDATA2LSB:
168 if (*(unsigned char const *)&endian != 1) {
169 /* main() is big endian, file.o is little endian. */
170 w = w4rev;
171 w2 = w2rev;
172 w8 = w8rev;
173 }
174 break;
175 case ELFDATA2MSB:
176 if (*(unsigned char const *)&endian != 0) {
177 /* main() is little endian, file.o is big endian. */
178 w = w4rev;
179 w2 = w2rev;
180 w8 = w8rev;
181 }
182 break;
183 } /* end switch */
184 if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0
185 || w2(ehdr->e_type) != ET_EXEC
186 || ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
187 fprintf(stderr, "unrecognized ET_EXEC file %s\n", fname);
188 fail_file();
189 }
190
191 switch (w2(ehdr->e_machine)) {
192 default:
193 fprintf(stderr, "unrecognized e_machine %d %s\n",
194 w2(ehdr->e_machine), fname);
195 fail_file();
196 break;
197 case EM_386:
198 case EM_MIPS:
199 case EM_X86_64:
200 break;
201 } /* end switch */
202
203 switch (ehdr->e_ident[EI_CLASS]) {
204 default:
205 fprintf(stderr, "unrecognized ELF class %d %s\n",
206 ehdr->e_ident[EI_CLASS], fname);
207 fail_file();
208 break;
209 case ELFCLASS32:
210 if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
211 || w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
212 fprintf(stderr,
213 "unrecognized ET_EXEC file: %s\n", fname);
214 fail_file();
215 }
216 do32(ehdr, fname);
217 break;
218 case ELFCLASS64: {
219 Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
220 if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
221 || w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
222 fprintf(stderr,
223 "unrecognized ET_EXEC file: %s\n", fname);
224 fail_file();
225 }
226 do64(ghdr, fname);
227 break;
228 }
229 } /* end switch */
230
231 cleanup();
232}
233
234int
235main(int argc, char *argv[])
236{
237 int n_error = 0; /* gcc-4.3.0 false positive complaint */
238 int i;
239
240 if (argc < 2) {
241 fprintf(stderr, "usage: sortextable vmlinux...\n");
242 return 0;
243 }
244
245 /* Process each file in turn, allowing deep failure. */
246 for (i = 1; i < argc; i++) {
247 char *file = argv[i];
248 int const sjval = setjmp(jmpenv);
249
250 switch (sjval) {
251 default:
252 fprintf(stderr, "internal error: %s\n", file);
253 exit(1);
254 break;
255 case SJ_SETJMP: /* normal sequence */
256 /* Avoid problems if early cleanup() */
257 fd_map = -1;
258 ehdr_curr = NULL;
259 mmap_failed = 1;
260 do_file(file);
261 break;
262 case SJ_FAIL: /* error in do_file or below */
263 ++n_error;
264 break;
265 case SJ_SUCCEED: /* premature success */
266 /* do nothing */
267 break;
268 } /* end switch */
269 }
270 return !!n_error;
271}
diff --git a/scripts/sortextable.h b/scripts/sortextable.h
new file mode 100644
index 000000000000..bb6aaf166002
--- /dev/null
+++ b/scripts/sortextable.h
@@ -0,0 +1,168 @@
1/*
2 * sortextable.h
3 *
4 * Copyright 2011 Cavium, Inc.
5 *
6 * Some of this code was taken out of recordmcount.h written by:
7 *
8 * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>. All rights reserved.
9 * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
10 *
11 *
12 * Licensed under the GNU General Public License, version 2 (GPLv2).
13 */
14
15#undef extable_ent_size
16#undef compare_extable
17#undef do_func
18#undef Elf_Addr
19#undef Elf_Ehdr
20#undef Elf_Shdr
21#undef Elf_Rel
22#undef Elf_Rela
23#undef Elf_Sym
24#undef ELF_R_SYM
25#undef Elf_r_sym
26#undef ELF_R_INFO
27#undef Elf_r_info
28#undef ELF_ST_BIND
29#undef ELF_ST_TYPE
30#undef fn_ELF_R_SYM
31#undef fn_ELF_R_INFO
32#undef uint_t
33#undef _w
34
35#ifdef SORTEXTABLE_64
36# define extable_ent_size 16
37# define compare_extable compare_extable_64
38# define do_func do64
39# define Elf_Addr Elf64_Addr
40# define Elf_Ehdr Elf64_Ehdr
41# define Elf_Shdr Elf64_Shdr
42# define Elf_Rel Elf64_Rel
43# define Elf_Rela Elf64_Rela
44# define Elf_Sym Elf64_Sym
45# define ELF_R_SYM ELF64_R_SYM
46# define Elf_r_sym Elf64_r_sym
47# define ELF_R_INFO ELF64_R_INFO
48# define Elf_r_info Elf64_r_info
49# define ELF_ST_BIND ELF64_ST_BIND
50# define ELF_ST_TYPE ELF64_ST_TYPE
51# define fn_ELF_R_SYM fn_ELF64_R_SYM
52# define fn_ELF_R_INFO fn_ELF64_R_INFO
53# define uint_t uint64_t
54# define _w w8
55#else
56# define extable_ent_size 8
57# define compare_extable compare_extable_32
58# define do_func do32
59# define Elf_Addr Elf32_Addr
60# define Elf_Ehdr Elf32_Ehdr
61# define Elf_Shdr Elf32_Shdr
62# define Elf_Rel Elf32_Rel
63# define Elf_Rela Elf32_Rela
64# define Elf_Sym Elf32_Sym
65# define ELF_R_SYM ELF32_R_SYM
66# define Elf_r_sym Elf32_r_sym
67# define ELF_R_INFO ELF32_R_INFO
68# define Elf_r_info Elf32_r_info
69# define ELF_ST_BIND ELF32_ST_BIND
70# define ELF_ST_TYPE ELF32_ST_TYPE
71# define fn_ELF_R_SYM fn_ELF32_R_SYM
72# define fn_ELF_R_INFO fn_ELF32_R_INFO
73# define uint_t uint32_t
74# define _w w
75#endif
76
77static int compare_extable(const void *a, const void *b)
78{
79 const uint_t *aa = a;
80 const uint_t *bb = b;
81
82 if (_w(*aa) < _w(*bb))
83 return -1;
84 if (_w(*aa) > _w(*bb))
85 return 1;
86 return 0;
87}
88
89static void
90do_func(Elf_Ehdr *const ehdr, char const *const fname)
91{
92 Elf_Shdr *shdr;
93 Elf_Shdr *shstrtab_sec;
94 Elf_Shdr *strtab_sec = NULL;
95 Elf_Shdr *symtab_sec = NULL;
96 Elf_Shdr *extab_sec = NULL;
97 Elf_Sym *sym;
98 Elf_Sym *sort_needed_sym;
99 Elf_Shdr *sort_needed_sec;
100 uint32_t *sort_done_location;
101 const char *secstrtab;
102 const char *strtab;
103 int i;
104 int idx;
105
106 shdr = (Elf_Shdr *)((void *)ehdr + _w(ehdr->e_shoff));
107 shstrtab_sec = shdr + w2(ehdr->e_shstrndx);
108 secstrtab = (const char *)ehdr + _w(shstrtab_sec->sh_offset);
109 for (i = 0; i < w2(ehdr->e_shnum); i++) {
110 idx = w(shdr[i].sh_name);
111 if (strcmp(secstrtab + idx, "__ex_table") == 0)
112 extab_sec = shdr + i;
113 if (strcmp(secstrtab + idx, ".symtab") == 0)
114 symtab_sec = shdr + i;
115 if (strcmp(secstrtab + idx, ".strtab") == 0)
116 strtab_sec = shdr + i;
117 }
118 if (strtab_sec == NULL) {
119 fprintf(stderr, "no .strtab in file: %s\n", fname);
120 fail_file();
121 }
122 if (symtab_sec == NULL) {
123 fprintf(stderr, "no .symtab in file: %s\n", fname);
124 fail_file();
125 }
126 if (extab_sec == NULL) {
127 fprintf(stderr, "no __ex_table in file: %s\n", fname);
128 fail_file();
129 }
130 strtab = (const char *)ehdr + _w(strtab_sec->sh_offset);
131
132 /* Sort the table in place */
133 qsort((void *)ehdr + _w(extab_sec->sh_offset),
134 (_w(extab_sec->sh_size) / extable_ent_size),
135 extable_ent_size, compare_extable);
136
137 /* find main_extable_sort_needed */
138 sort_needed_sym = NULL;
139 for (i = 0; i < _w(symtab_sec->sh_size) / sizeof(Elf_Sym); i++) {
140 sym = (void *)ehdr + _w(symtab_sec->sh_offset);
141 sym += i;
142 if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
143 continue;
144 idx = w(sym->st_name);
145 if (strcmp(strtab + idx, "main_extable_sort_needed") == 0) {
146 sort_needed_sym = sym;
147 break;
148 }
149 }
150 if (sort_needed_sym == NULL) {
151 fprintf(stderr,
152 "no main_extable_sort_needed symbol in file: %s\n",
153 fname);
154 fail_file();
155 }
156 sort_needed_sec = &shdr[w2(sort_needed_sym->st_shndx)];
157 sort_done_location = (void *)ehdr +
158 _w(sort_needed_sec->sh_offset) +
159 _w(sort_needed_sym->st_value) -
160 _w(sort_needed_sec->sh_addr);
161
162 printf("sort done marker at %lx\n",
163 (unsigned long) (_w(sort_needed_sec->sh_offset) +
164 _w(sort_needed_sym->st_value) -
165 _w(sort_needed_sec->sh_addr)));
166 /* We sorted it, clear the flag. */
167 *sort_done_location = 0;
168}