aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2016-03-15 17:58:19 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-15 19:55:16 -0400
commit2213e9a66bb87d8344a1256b4ef568220d9587fb (patch)
tree0729c7a1170f86b2f830024f12bf6a785f3b3e9e
parent8c996940b3be9c3ac40ce558c270817e1722a95b (diff)
kallsyms: add support for relative offsets in kallsyms address table
Similar to how relative extables are implemented, it is possible to emit the kallsyms table in such a way that it contains offsets relative to some anchor point in the kernel image rather than absolute addresses. On 64-bit architectures, it cuts the size of the kallsyms address table in half, since offsets between kernel symbols can typically be expressed in 32 bits. This saves several hundreds of kilobytes of permanent .rodata on average. In addition, the kallsyms address table is no longer subject to dynamic relocation when CONFIG_RELOCATABLE is in effect, so the relocation work done after decompression now doesn't have to do relocation updates for all these values. This saves up to 24 bytes (i.e., the size of a ELF64 RELA relocation table entry) per value, which easily adds up to a couple of megabytes of uncompressed __init data on ppc64 or arm64. Even if these relocation entries typically compress well, the combined size reduction of 2.8 MB uncompressed for a ppc64_defconfig build (of which 2.4 MB is __init data) results in a ~500 KB space saving in the compressed image. Since it is useful for some architectures (like x86) to retain the ability to emit absolute values as well, this patch also adds support for capturing both absolute and relative values when KALLSYMS_ABSOLUTE_PERCPU is in effect, by emitting absolute per-cpu addresses as positive 32-bit values, and addresses relative to the lowest encountered relative symbol as negative values, which are subtracted from the runtime address of this base symbol to produce the actual address. Support for the above is enabled by default for all architectures except IA-64 and Tile-GX, whose symbols are too far apart to capture in this manner. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Guenter Roeck <linux@roeck-us.net> Reviewed-by: Kees Cook <keescook@chromium.org> Tested-by: Kees Cook <keescook@chromium.org> Cc: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Ingo Molnar <mingo@kernel.org> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Michal Marek <mmarek@suse.cz> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--init/Kconfig18
-rw-r--r--kernel/kallsyms.c42
-rw-r--r--scripts/kallsyms.c79
-rwxr-xr-xscripts/link-vmlinux.sh4
-rwxr-xr-xscripts/namespace.pl2
5 files changed, 126 insertions, 19 deletions
diff --git a/init/Kconfig b/init/Kconfig
index b17824a875fa..fd664b3ab99e 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1424,6 +1424,24 @@ config KALLSYMS_ABSOLUTE_PERCPU
1424 bool 1424 bool
1425 default X86_64 && SMP 1425 default X86_64 && SMP
1426 1426
1427config KALLSYMS_BASE_RELATIVE
1428 bool
1429 depends on KALLSYMS
1430 default !IA64 && !(TILE && 64BIT)
1431 help
1432 Instead of emitting them as absolute values in the native word size,
1433 emit the symbol references in the kallsyms table as 32-bit entries,
1434 each containing a relative value in the range [base, base + U32_MAX]
1435 or, when KALLSYMS_ABSOLUTE_PERCPU is in effect, each containing either
1436 an absolute value in the range [0, S32_MAX] or a relative value in the
1437 range [base, base + S32_MAX], where base is the lowest relative symbol
1438 address encountered in the image.
1439
1440 On 64-bit builds, this reduces the size of the address table by 50%,
1441 but more importantly, it results in entries whose values are build
1442 time constants, and no relocation pass is required at runtime to fix
1443 up the entries based on the runtime load address of the kernel.
1444
1427config PRINTK 1445config PRINTK
1428 default y 1446 default y
1429 bool "Enable support for printk" if EXPERT 1447 bool "Enable support for printk" if EXPERT
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 5c5987f10819..fafd1a3ef0da 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -38,6 +38,7 @@
38 * during the second link stage. 38 * during the second link stage.
39 */ 39 */
40extern const unsigned long kallsyms_addresses[] __weak; 40extern const unsigned long kallsyms_addresses[] __weak;
41extern const int kallsyms_offsets[] __weak;
41extern const u8 kallsyms_names[] __weak; 42extern const u8 kallsyms_names[] __weak;
42 43
43/* 44/*
@@ -47,6 +48,9 @@ extern const u8 kallsyms_names[] __weak;
47extern const unsigned long kallsyms_num_syms 48extern const unsigned long kallsyms_num_syms
48__attribute__((weak, section(".rodata"))); 49__attribute__((weak, section(".rodata")));
49 50
51extern const unsigned long kallsyms_relative_base
52__attribute__((weak, section(".rodata")));
53
50extern const u8 kallsyms_token_table[] __weak; 54extern const u8 kallsyms_token_table[] __weak;
51extern const u16 kallsyms_token_index[] __weak; 55extern const u16 kallsyms_token_index[] __weak;
52 56
@@ -176,6 +180,23 @@ static unsigned int get_symbol_offset(unsigned long pos)
176 return name - kallsyms_names; 180 return name - kallsyms_names;
177} 181}
178 182
183static unsigned long kallsyms_sym_address(int idx)
184{
185 if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
186 return kallsyms_addresses[idx];
187
188 /* values are unsigned offsets if --absolute-percpu is not in effect */
189 if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
190 return kallsyms_relative_base + (u32)kallsyms_offsets[idx];
191
192 /* ...otherwise, positive offsets are absolute values */
193 if (kallsyms_offsets[idx] >= 0)
194 return kallsyms_offsets[idx];
195
196 /* ...and negative offsets are relative to kallsyms_relative_base - 1 */
197 return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
198}
199
179/* Lookup the address for this symbol. Returns 0 if not found. */ 200/* Lookup the address for this symbol. Returns 0 if not found. */
180unsigned long kallsyms_lookup_name(const char *name) 201unsigned long kallsyms_lookup_name(const char *name)
181{ 202{
@@ -187,7 +208,7 @@ unsigned long kallsyms_lookup_name(const char *name)
187 off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); 208 off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
188 209
189 if (strcmp(namebuf, name) == 0) 210 if (strcmp(namebuf, name) == 0)
190 return kallsyms_addresses[i]; 211 return kallsyms_sym_address(i);
191 } 212 }
192 return module_kallsyms_lookup_name(name); 213 return module_kallsyms_lookup_name(name);
193} 214}
@@ -204,7 +225,7 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
204 225
205 for (i = 0, off = 0; i < kallsyms_num_syms; i++) { 226 for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
206 off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); 227 off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
207 ret = fn(data, namebuf, NULL, kallsyms_addresses[i]); 228 ret = fn(data, namebuf, NULL, kallsyms_sym_address(i));
208 if (ret != 0) 229 if (ret != 0)
209 return ret; 230 return ret;
210 } 231 }
@@ -220,7 +241,10 @@ static unsigned long get_symbol_pos(unsigned long addr,
220 unsigned long i, low, high, mid; 241 unsigned long i, low, high, mid;
221 242
222 /* This kernel should never had been booted. */ 243 /* This kernel should never had been booted. */
223 BUG_ON(!kallsyms_addresses); 244 if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
245 BUG_ON(!kallsyms_addresses);
246 else
247 BUG_ON(!kallsyms_offsets);
224 248
225 /* Do a binary search on the sorted kallsyms_addresses array. */ 249 /* Do a binary search on the sorted kallsyms_addresses array. */
226 low = 0; 250 low = 0;
@@ -228,7 +252,7 @@ static unsigned long get_symbol_pos(unsigned long addr,
228 252
229 while (high - low > 1) { 253 while (high - low > 1) {
230 mid = low + (high - low) / 2; 254 mid = low + (high - low) / 2;
231 if (kallsyms_addresses[mid] <= addr) 255 if (kallsyms_sym_address(mid) <= addr)
232 low = mid; 256 low = mid;
233 else 257 else
234 high = mid; 258 high = mid;
@@ -238,15 +262,15 @@ static unsigned long get_symbol_pos(unsigned long addr,
238 * Search for the first aliased symbol. Aliased 262 * Search for the first aliased symbol. Aliased
239 * symbols are symbols with the same address. 263 * symbols are symbols with the same address.
240 */ 264 */
241 while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low]) 265 while (low && kallsyms_sym_address(low-1) == kallsyms_sym_address(low))
242 --low; 266 --low;
243 267
244 symbol_start = kallsyms_addresses[low]; 268 symbol_start = kallsyms_sym_address(low);
245 269
246 /* Search for next non-aliased symbol. */ 270 /* Search for next non-aliased symbol. */
247 for (i = low + 1; i < kallsyms_num_syms; i++) { 271 for (i = low + 1; i < kallsyms_num_syms; i++) {
248 if (kallsyms_addresses[i] > symbol_start) { 272 if (kallsyms_sym_address(i) > symbol_start) {
249 symbol_end = kallsyms_addresses[i]; 273 symbol_end = kallsyms_sym_address(i);
250 break; 274 break;
251 } 275 }
252 } 276 }
@@ -470,7 +494,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
470 unsigned off = iter->nameoff; 494 unsigned off = iter->nameoff;
471 495
472 iter->module_name[0] = '\0'; 496 iter->module_name[0] = '\0';
473 iter->value = kallsyms_addresses[iter->pos]; 497 iter->value = kallsyms_sym_address(iter->pos);
474 498
475 iter->type = kallsyms_get_symbol_type(off); 499 iter->type = kallsyms_get_symbol_type(off);
476 500
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index d39a1eeb080e..638b143ee60f 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -22,6 +22,7 @@
22#include <stdlib.h> 22#include <stdlib.h>
23#include <string.h> 23#include <string.h>
24#include <ctype.h> 24#include <ctype.h>
25#include <limits.h>
25 26
26#ifndef ARRAY_SIZE 27#ifndef ARRAY_SIZE
27#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 28#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -43,6 +44,7 @@ struct addr_range {
43}; 44};
44 45
45static unsigned long long _text; 46static unsigned long long _text;
47static unsigned long long relative_base;
46static struct addr_range text_ranges[] = { 48static struct addr_range text_ranges[] = {
47 { "_stext", "_etext" }, 49 { "_stext", "_etext" },
48 { "_sinittext", "_einittext" }, 50 { "_sinittext", "_einittext" },
@@ -62,6 +64,7 @@ static int all_symbols = 0;
62static int absolute_percpu = 0; 64static int absolute_percpu = 0;
63static char symbol_prefix_char = '\0'; 65static char symbol_prefix_char = '\0';
64static unsigned long long kernel_start_addr = 0; 66static unsigned long long kernel_start_addr = 0;
67static int base_relative = 0;
65 68
66int token_profit[0x10000]; 69int token_profit[0x10000];
67 70
@@ -75,7 +78,7 @@ static void usage(void)
75 fprintf(stderr, "Usage: kallsyms [--all-symbols] " 78 fprintf(stderr, "Usage: kallsyms [--all-symbols] "
76 "[--symbol-prefix=<prefix char>] " 79 "[--symbol-prefix=<prefix char>] "
77 "[--page-offset=<CONFIG_PAGE_OFFSET>] " 80 "[--page-offset=<CONFIG_PAGE_OFFSET>] "
78 "< in.map > out.S\n"); 81 "[--base-relative] < in.map > out.S\n");
79 exit(1); 82 exit(1);
80} 83}
81 84
@@ -205,6 +208,8 @@ static int symbol_valid(struct sym_entry *s)
205 */ 208 */
206 static char *special_symbols[] = { 209 static char *special_symbols[] = {
207 "kallsyms_addresses", 210 "kallsyms_addresses",
211 "kallsyms_offsets",
212 "kallsyms_relative_base",
208 "kallsyms_num_syms", 213 "kallsyms_num_syms",
209 "kallsyms_names", 214 "kallsyms_names",
210 "kallsyms_markers", 215 "kallsyms_markers",
@@ -349,16 +354,48 @@ static void write_src(void)
349 354
350 printf("\t.section .rodata, \"a\"\n"); 355 printf("\t.section .rodata, \"a\"\n");
351 356
352 /* Provide proper symbols relocatability by their '_text' 357 /* Provide proper symbols relocatability by their relativeness
353 * relativeness. The symbol names cannot be used to construct 358 * to a fixed anchor point in the runtime image, either '_text'
354 * normal symbol references as the list of symbols contains 359 * for absolute address tables, in which case the linker will
355 * symbols that are declared static and are private to their 360 * emit the final addresses at build time. Otherwise, use the
356 * .o files. This prevents .tmp_kallsyms.o or any other 361 * offset relative to the lowest value encountered of all relative
357 * object from referencing them. 362 * symbols, and emit non-relocatable fixed offsets that will be fixed
363 * up at runtime.
364 *
365 * The symbol names cannot be used to construct normal symbol
366 * references as the list of symbols contains symbols that are
367 * declared static and are private to their .o files. This prevents
368 * .tmp_kallsyms.o or any other object from referencing them.
358 */ 369 */
359 output_label("kallsyms_addresses"); 370 if (!base_relative)
371 output_label("kallsyms_addresses");
372 else
373 output_label("kallsyms_offsets");
374
360 for (i = 0; i < table_cnt; i++) { 375 for (i = 0; i < table_cnt; i++) {
361 if (!symbol_absolute(&table[i])) { 376 if (base_relative) {
377 long long offset;
378 int overflow;
379
380 if (!absolute_percpu) {
381 offset = table[i].addr - relative_base;
382 overflow = (offset < 0 || offset > UINT_MAX);
383 } else if (symbol_absolute(&table[i])) {
384 offset = table[i].addr;
385 overflow = (offset < 0 || offset > INT_MAX);
386 } else {
387 offset = relative_base - table[i].addr - 1;
388 overflow = (offset < INT_MIN || offset >= 0);
389 }
390 if (overflow) {
391 fprintf(stderr, "kallsyms failure: "
392 "%s symbol value %#llx out of range in relative mode\n",
393 symbol_absolute(&table[i]) ? "absolute" : "relative",
394 table[i].addr);
395 exit(EXIT_FAILURE);
396 }
397 printf("\t.long\t%#x\n", (int)offset);
398 } else if (!symbol_absolute(&table[i])) {
362 if (_text <= table[i].addr) 399 if (_text <= table[i].addr)
363 printf("\tPTR\t_text + %#llx\n", 400 printf("\tPTR\t_text + %#llx\n",
364 table[i].addr - _text); 401 table[i].addr - _text);
@@ -371,6 +408,12 @@ static void write_src(void)
371 } 408 }
372 printf("\n"); 409 printf("\n");
373 410
411 if (base_relative) {
412 output_label("kallsyms_relative_base");
413 printf("\tPTR\t_text - %#llx\n", _text - relative_base);
414 printf("\n");
415 }
416
374 output_label("kallsyms_num_syms"); 417 output_label("kallsyms_num_syms");
375 printf("\tPTR\t%d\n", table_cnt); 418 printf("\tPTR\t%d\n", table_cnt);
376 printf("\n"); 419 printf("\n");
@@ -695,6 +738,18 @@ static void make_percpus_absolute(void)
695 } 738 }
696} 739}
697 740
741/* find the minimum non-absolute symbol address */
742static void record_relative_base(void)
743{
744 unsigned int i;
745
746 relative_base = -1ULL;
747 for (i = 0; i < table_cnt; i++)
748 if (!symbol_absolute(&table[i]) &&
749 table[i].addr < relative_base)
750 relative_base = table[i].addr;
751}
752
698int main(int argc, char **argv) 753int main(int argc, char **argv)
699{ 754{
700 if (argc >= 2) { 755 if (argc >= 2) {
@@ -713,7 +768,9 @@ int main(int argc, char **argv)
713 } else if (strncmp(argv[i], "--page-offset=", 14) == 0) { 768 } else if (strncmp(argv[i], "--page-offset=", 14) == 0) {
714 const char *p = &argv[i][14]; 769 const char *p = &argv[i][14];
715 kernel_start_addr = strtoull(p, NULL, 16); 770 kernel_start_addr = strtoull(p, NULL, 16);
716 } else 771 } else if (strcmp(argv[i], "--base-relative") == 0)
772 base_relative = 1;
773 else
717 usage(); 774 usage();
718 } 775 }
719 } else if (argc != 1) 776 } else if (argc != 1)
@@ -722,6 +779,8 @@ int main(int argc, char **argv)
722 read_map(stdin); 779 read_map(stdin);
723 if (absolute_percpu) 780 if (absolute_percpu)
724 make_percpus_absolute(); 781 make_percpus_absolute();
782 if (base_relative)
783 record_relative_base();
725 sort_symbols(); 784 sort_symbols();
726 optimize_token_table(); 785 optimize_token_table();
727 write_src(); 786 write_src();
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 7a08bf9a9576..453ede9d2f3d 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -90,6 +90,10 @@ kallsyms()
90 kallsymopt="${kallsymopt} --absolute-percpu" 90 kallsymopt="${kallsymopt} --absolute-percpu"
91 fi 91 fi
92 92
93 if [ -n "${CONFIG_KALLSYMS_BASE_RELATIVE}" ]; then
94 kallsymopt="${kallsymopt} --base-relative"
95 fi
96
93 local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \ 97 local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
94 ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}" 98 ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}"
95 99
diff --git a/scripts/namespace.pl b/scripts/namespace.pl
index a71be6b7cdec..9f3c9d47a4a5 100755
--- a/scripts/namespace.pl
+++ b/scripts/namespace.pl
@@ -117,6 +117,8 @@ my %nameexception = (
117 'kallsyms_names' => 1, 117 'kallsyms_names' => 1,
118 'kallsyms_num_syms' => 1, 118 'kallsyms_num_syms' => 1,
119 'kallsyms_addresses'=> 1, 119 'kallsyms_addresses'=> 1,
120 'kallsyms_offsets' => 1,
121 'kallsyms_relative_base'=> 1,
120 '__this_module' => 1, 122 '__this_module' => 1,
121 '_etext' => 1, 123 '_etext' => 1,
122 '_edata' => 1, 124 '_edata' => 1,