aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/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 /kernel/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 'kernel/kallsyms.c')
-rw-r--r--kernel/kallsyms.c42
1 files changed, 33 insertions, 9 deletions
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