.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * kallsyms.c: in-kernel printing of symbolic oopses and stack traces. |
---|
3 | 4 | * |
---|
.. | .. |
---|
23 | 24 | #include <linux/slab.h> |
---|
24 | 25 | #include <linux/filter.h> |
---|
25 | 26 | #include <linux/ftrace.h> |
---|
| 27 | +#include <linux/kprobes.h> |
---|
26 | 28 | #include <linux/compiler.h> |
---|
27 | 29 | |
---|
28 | 30 | /* |
---|
.. | .. |
---|
37 | 39 | * Tell the compiler that the count isn't in the small data section if the arch |
---|
38 | 40 | * has one (eg: FRV). |
---|
39 | 41 | */ |
---|
40 | | -extern const unsigned long kallsyms_num_syms |
---|
41 | | -__attribute__((weak, section(".rodata"))); |
---|
| 42 | +extern const unsigned int kallsyms_num_syms |
---|
| 43 | +__section(".rodata") __attribute__((weak)); |
---|
42 | 44 | |
---|
43 | 45 | extern const unsigned long kallsyms_relative_base |
---|
44 | | -__attribute__((weak, section(".rodata"))); |
---|
| 46 | +__section(".rodata") __attribute__((weak)); |
---|
45 | 47 | |
---|
46 | | -extern const u8 kallsyms_token_table[] __weak; |
---|
| 48 | +extern const char kallsyms_token_table[] __weak; |
---|
47 | 49 | extern const u16 kallsyms_token_index[] __weak; |
---|
48 | 50 | |
---|
49 | | -extern const unsigned long kallsyms_markers[] __weak; |
---|
| 51 | +extern const unsigned int kallsyms_markers[] __weak; |
---|
50 | 52 | |
---|
51 | 53 | /* |
---|
52 | 54 | * Expand a compressed symbol data into the resulting uncompressed string, |
---|
.. | .. |
---|
57 | 59 | char *result, size_t maxlen) |
---|
58 | 60 | { |
---|
59 | 61 | int len, skipped_first = 0; |
---|
60 | | - const u8 *tptr, *data; |
---|
| 62 | + const char *tptr; |
---|
| 63 | + const u8 *data; |
---|
61 | 64 | |
---|
62 | 65 | /* Get the compressed symbol length from the first symbol byte. */ |
---|
63 | 66 | data = &kallsyms_names[off]; |
---|
.. | .. |
---|
158 | 161 | return kallsyms_relative_base - 1 - kallsyms_offsets[idx]; |
---|
159 | 162 | } |
---|
160 | 163 | |
---|
| 164 | +#if defined(CONFIG_CFI_CLANG) && defined(CONFIG_LTO_CLANG_THIN) |
---|
| 165 | +/* |
---|
| 166 | + * LLVM appends a hash to static function names when ThinLTO and CFI are |
---|
| 167 | + * both enabled, which causes confusion and potentially breaks user space |
---|
| 168 | + * tools, so we will strip the postfix from expanded symbol names. |
---|
| 169 | + */ |
---|
| 170 | +static inline char *cleanup_symbol_name(char *s) |
---|
| 171 | +{ |
---|
| 172 | + char *res = NULL; |
---|
| 173 | + |
---|
| 174 | + res = strrchr(s, '$'); |
---|
| 175 | + if (res) |
---|
| 176 | + *res = '\0'; |
---|
| 177 | + |
---|
| 178 | + return res; |
---|
| 179 | +} |
---|
| 180 | +#else |
---|
| 181 | +static inline char *cleanup_symbol_name(char *s) { return NULL; } |
---|
| 182 | +#endif |
---|
| 183 | + |
---|
161 | 184 | /* Lookup the address for this symbol. Returns 0 if not found. */ |
---|
162 | 185 | unsigned long kallsyms_lookup_name(const char *name) |
---|
163 | 186 | { |
---|
.. | .. |
---|
169 | 192 | off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); |
---|
170 | 193 | |
---|
171 | 194 | if (strcmp(namebuf, name) == 0) |
---|
| 195 | + return kallsyms_sym_address(i); |
---|
| 196 | + |
---|
| 197 | + if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0) |
---|
172 | 198 | return kallsyms_sym_address(i); |
---|
173 | 199 | } |
---|
174 | 200 | return module_kallsyms_lookup_name(name); |
---|
.. | .. |
---|
267 | 293 | return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf) || |
---|
268 | 294 | !!__bpf_address_lookup(addr, symbolsize, offset, namebuf); |
---|
269 | 295 | } |
---|
270 | | - |
---|
271 | | -#ifdef CONFIG_CFI_CLANG |
---|
272 | | -/* |
---|
273 | | - * LLVM appends .cfi to function names when CONFIG_CFI_CLANG is enabled, |
---|
274 | | - * which causes confusion and potentially breaks user space tools, so we |
---|
275 | | - * will strip the postfix from expanded symbol names. |
---|
276 | | - */ |
---|
277 | | -static inline void cleanup_symbol_name(char *s) |
---|
278 | | -{ |
---|
279 | | - char *res; |
---|
280 | | - |
---|
281 | | -#ifdef CONFIG_THINLTO |
---|
282 | | - /* Filter out hashes from static functions */ |
---|
283 | | - res = strrchr(s, '$'); |
---|
284 | | - if (res) |
---|
285 | | - *res = '\0'; |
---|
286 | | -#endif |
---|
287 | | - res = strrchr(s, '.'); |
---|
288 | | - if (res && !strcmp(res, ".cfi")) |
---|
289 | | - *res = '\0'; |
---|
290 | | -} |
---|
291 | | -#else |
---|
292 | | -static inline void cleanup_symbol_name(char *s) {} |
---|
293 | | -#endif |
---|
294 | 296 | |
---|
295 | 297 | /* |
---|
296 | 298 | * Lookup an address |
---|
.. | .. |
---|
480 | 482 | loff_t pos_arch_end; |
---|
481 | 483 | loff_t pos_mod_end; |
---|
482 | 484 | loff_t pos_ftrace_mod_end; |
---|
| 485 | + loff_t pos_bpf_end; |
---|
483 | 486 | unsigned long value; |
---|
484 | 487 | unsigned int nameoff; /* If iterating in core kernel symbols. */ |
---|
485 | 488 | char type; |
---|
.. | .. |
---|
523 | 526 | return 1; |
---|
524 | 527 | } |
---|
525 | 528 | |
---|
| 529 | +/* |
---|
| 530 | + * ftrace_mod_get_kallsym() may also get symbols for pages allocated for ftrace |
---|
| 531 | + * purposes. In that case "__builtin__ftrace" is used as a module name, even |
---|
| 532 | + * though "__builtin__ftrace" is not a module. |
---|
| 533 | + */ |
---|
526 | 534 | static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter) |
---|
527 | 535 | { |
---|
528 | 536 | int ret = ftrace_mod_get_kallsym(iter->pos - iter->pos_mod_end, |
---|
.. | .. |
---|
539 | 547 | |
---|
540 | 548 | static int get_ksymbol_bpf(struct kallsym_iter *iter) |
---|
541 | 549 | { |
---|
542 | | - iter->module_name[0] = '\0'; |
---|
| 550 | + int ret; |
---|
| 551 | + |
---|
| 552 | + strlcpy(iter->module_name, "bpf", MODULE_NAME_LEN); |
---|
543 | 553 | iter->exported = 0; |
---|
544 | | - return bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end, |
---|
545 | | - &iter->value, &iter->type, |
---|
546 | | - iter->name) < 0 ? 0 : 1; |
---|
| 554 | + ret = bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end, |
---|
| 555 | + &iter->value, &iter->type, |
---|
| 556 | + iter->name); |
---|
| 557 | + if (ret < 0) { |
---|
| 558 | + iter->pos_bpf_end = iter->pos; |
---|
| 559 | + return 0; |
---|
| 560 | + } |
---|
| 561 | + |
---|
| 562 | + return 1; |
---|
| 563 | +} |
---|
| 564 | + |
---|
| 565 | +/* |
---|
| 566 | + * This uses "__builtin__kprobes" as a module name for symbols for pages |
---|
| 567 | + * allocated for kprobes' purposes, even though "__builtin__kprobes" is not a |
---|
| 568 | + * module. |
---|
| 569 | + */ |
---|
| 570 | +static int get_ksymbol_kprobe(struct kallsym_iter *iter) |
---|
| 571 | +{ |
---|
| 572 | + strlcpy(iter->module_name, "__builtin__kprobes", MODULE_NAME_LEN); |
---|
| 573 | + iter->exported = 0; |
---|
| 574 | + return kprobe_get_kallsym(iter->pos - iter->pos_bpf_end, |
---|
| 575 | + &iter->value, &iter->type, |
---|
| 576 | + iter->name) < 0 ? 0 : 1; |
---|
547 | 577 | } |
---|
548 | 578 | |
---|
549 | 579 | /* Returns space to next name. */ |
---|
.. | .. |
---|
570 | 600 | iter->pos_arch_end = 0; |
---|
571 | 601 | iter->pos_mod_end = 0; |
---|
572 | 602 | iter->pos_ftrace_mod_end = 0; |
---|
| 603 | + iter->pos_bpf_end = 0; |
---|
573 | 604 | } |
---|
574 | 605 | } |
---|
575 | 606 | |
---|
.. | .. |
---|
594 | 625 | get_ksymbol_ftrace_mod(iter)) |
---|
595 | 626 | return 1; |
---|
596 | 627 | |
---|
597 | | - return get_ksymbol_bpf(iter); |
---|
| 628 | + if ((!iter->pos_bpf_end || iter->pos_bpf_end > pos) && |
---|
| 629 | + get_ksymbol_bpf(iter)) |
---|
| 630 | + return 1; |
---|
| 631 | + |
---|
| 632 | + return get_ksymbol_kprobe(iter); |
---|
598 | 633 | } |
---|
599 | 634 | |
---|
600 | 635 | /* Returns false if pos at or past end of file. */ |
---|
.. | .. |
---|
693 | 728 | case 0: |
---|
694 | 729 | if (kallsyms_for_perf()) |
---|
695 | 730 | return true; |
---|
696 | | - /* fallthrough */ |
---|
| 731 | + fallthrough; |
---|
697 | 732 | case 1: |
---|
698 | 733 | if (security_capable(cred, &init_user_ns, CAP_SYSLOG, |
---|
699 | 734 | CAP_OPT_NOAUDIT) == 0) |
---|
700 | 735 | return true; |
---|
701 | | - /* fallthrough */ |
---|
| 736 | + fallthrough; |
---|
702 | 737 | default: |
---|
703 | 738 | return false; |
---|
704 | 739 | } |
---|
.. | .. |
---|
745 | 780 | } |
---|
746 | 781 | #endif /* CONFIG_KGDB_KDB */ |
---|
747 | 782 | |
---|
748 | | -static const struct file_operations kallsyms_operations = { |
---|
749 | | - .open = kallsyms_open, |
---|
750 | | - .read = seq_read, |
---|
751 | | - .llseek = seq_lseek, |
---|
752 | | - .release = seq_release_private, |
---|
| 783 | +static const struct proc_ops kallsyms_proc_ops = { |
---|
| 784 | + .proc_open = kallsyms_open, |
---|
| 785 | + .proc_read = seq_read, |
---|
| 786 | + .proc_lseek = seq_lseek, |
---|
| 787 | + .proc_release = seq_release_private, |
---|
753 | 788 | }; |
---|
754 | 789 | |
---|
755 | 790 | static int __init kallsyms_init(void) |
---|
756 | 791 | { |
---|
757 | | - proc_create("kallsyms", 0444, NULL, &kallsyms_operations); |
---|
| 792 | + proc_create("kallsyms", 0444, NULL, &kallsyms_proc_ops); |
---|
758 | 793 | return 0; |
---|
759 | 794 | } |
---|
760 | 795 | device_initcall(kallsyms_init); |
---|