aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/kallsyms.c
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 /scripts/kallsyms.c
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>
Diffstat (limited to 'scripts/kallsyms.c')
-rw-r--r--scripts/kallsyms.c79
1 files changed, 69 insertions, 10 deletions
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();