Linux 6.9-rc1
[linux-2.6-microblaze.git] / kernel / kallsyms.c
index 79a8583..18edd57 100644 (file)
 #include <linux/bsearch.h>
 #include <linux/btf_ids.h>
 
-/*
- * These will be re-linked against their real values
- * during the second link stage.
- */
-extern const unsigned long kallsyms_addresses[] __weak;
-extern const int kallsyms_offsets[] __weak;
-extern const u8 kallsyms_names[] __weak;
-
-/*
- * Tell the compiler that the count isn't in the small data section if the arch
- * has one (eg: FRV).
- */
-extern const unsigned int kallsyms_num_syms
-__section(".rodata") __attribute__((weak));
-
-extern const unsigned long kallsyms_relative_base
-__section(".rodata") __attribute__((weak));
-
-extern const char kallsyms_token_table[] __weak;
-extern const u16 kallsyms_token_index[] __weak;
-
-extern const unsigned int kallsyms_markers[] __weak;
+#include "kallsyms_internal.h"
 
 /*
  * Expand a compressed symbol data into the resulting uncompressed string,
@@ -71,12 +50,20 @@ static unsigned int kallsyms_expand_symbol(unsigned int off,
        data = &kallsyms_names[off];
        len = *data;
        data++;
+       off++;
+
+       /* If MSB is 1, it is a "big" symbol, so needs an additional byte. */
+       if ((len & 0x80) != 0) {
+               len = (len & 0x7F) | (*data << 7);
+               data++;
+               off++;
+       }
 
        /*
         * Update the offset to return the offset for the next symbol on
         * the compressed stream.
         */
-       off += len + 1;
+       off += len;
 
        /*
         * For every byte on the compressed symbol data, copy the table
@@ -129,7 +116,7 @@ static char kallsyms_get_symbol_type(unsigned int off)
 static unsigned int get_symbol_offset(unsigned long pos)
 {
        const u8 *name;
-       int i;
+       int i, len;
 
        /*
         * Use the closest marker we have. We have markers every 256 positions,
@@ -143,13 +130,23 @@ static unsigned int get_symbol_offset(unsigned long pos)
         * so we just need to add the len to the current pointer for every
         * symbol we wish to skip.
         */
-       for (i = 0; i < (pos & 0xFF); i++)
-               name = name + (*name) + 1;
+       for (i = 0; i < (pos & 0xFF); i++) {
+               len = *name;
+
+               /*
+                * If MSB is 1, it is a "big" symbol, so we need to look into
+                * the next byte (and skip it, too).
+                */
+               if ((len & 0x80) != 0)
+                       len = ((len & 0x7F) | (name[1] << 7)) + 1;
+
+               name = name + len + 1;
+       }
 
        return name - kallsyms_names;
 }
 
-static unsigned long kallsyms_sym_address(int idx)
+unsigned long kallsyms_sym_address(int idx)
 {
        if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
                return kallsyms_addresses[idx];
@@ -166,67 +163,118 @@ static unsigned long kallsyms_sym_address(int idx)
        return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
 }
 
-static bool cleanup_symbol_name(char *s)
+static void cleanup_symbol_name(char *s)
 {
        char *res;
 
        if (!IS_ENABLED(CONFIG_LTO_CLANG))
-               return false;
+               return;
 
        /*
         * LLVM appends various suffixes for local functions and variables that
         * must be promoted to global scope as part of LTO.  This can break
         * hooking of static functions with kprobes. '.' is not a valid
-        * character in an identifier in C. Suffixes observed:
+        * character in an identifier in C. Suffixes only in LLVM LTO observed:
         * - foo.llvm.[0-9a-f]+
-        * - foo.[0-9a-f]+
-        * - foo.[0-9a-f]+.cfi_jt
         */
-       res = strchr(s, '.');
-       if (res) {
+       res = strstr(s, ".llvm.");
+       if (res)
                *res = '\0';
-               return true;
-       }
 
-       if (!IS_ENABLED(CONFIG_CFI_CLANG) ||
-           !IS_ENABLED(CONFIG_LTO_CLANG_THIN) ||
-           CONFIG_CLANG_VERSION >= 130000)
-               return false;
+       return;
+}
 
-       /*
-        * Prior to LLVM 13, the following suffixes were observed when thinLTO
-        * and CFI are both enabled:
-        * - foo$[0-9]+
+static int compare_symbol_name(const char *name, char *namebuf)
+{
+       /* The kallsyms_seqs_of_names is sorted based on names after
+        * cleanup_symbol_name() (see scripts/kallsyms.c) if clang lto is enabled.
+        * To ensure correct bisection in kallsyms_lookup_names(), do
+        * cleanup_symbol_name(namebuf) before comparing name and namebuf.
         */
-       res = strrchr(s, '$');
-       if (res) {
-               *res = '\0';
-               return true;
+       cleanup_symbol_name(namebuf);
+       return strcmp(name, namebuf);
+}
+
+static unsigned int get_symbol_seq(int index)
+{
+       unsigned int i, seq = 0;
+
+       for (i = 0; i < 3; i++)
+               seq = (seq << 8) | kallsyms_seqs_of_names[3 * index + i];
+
+       return seq;
+}
+
+static int kallsyms_lookup_names(const char *name,
+                                unsigned int *start,
+                                unsigned int *end)
+{
+       int ret;
+       int low, mid, high;
+       unsigned int seq, off;
+       char namebuf[KSYM_NAME_LEN];
+
+       low = 0;
+       high = kallsyms_num_syms - 1;
+
+       while (low <= high) {
+               mid = low + (high - low) / 2;
+               seq = get_symbol_seq(mid);
+               off = get_symbol_offset(seq);
+               kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+               ret = compare_symbol_name(name, namebuf);
+               if (ret > 0)
+                       low = mid + 1;
+               else if (ret < 0)
+                       high = mid - 1;
+               else
+                       break;
+       }
+
+       if (low > high)
+               return -ESRCH;
+
+       low = mid;
+       while (low) {
+               seq = get_symbol_seq(low - 1);
+               off = get_symbol_offset(seq);
+               kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+               if (compare_symbol_name(name, namebuf))
+                       break;
+               low--;
+       }
+       *start = low;
+
+       if (end) {
+               high = mid;
+               while (high < kallsyms_num_syms - 1) {
+                       seq = get_symbol_seq(high + 1);
+                       off = get_symbol_offset(seq);
+                       kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
+                       if (compare_symbol_name(name, namebuf))
+                               break;
+                       high++;
+               }
+               *end = high;
        }
 
-       return false;
+       return 0;
 }
 
 /* Lookup the address for this symbol. Returns 0 if not found. */
 unsigned long kallsyms_lookup_name(const char *name)
 {
-       char namebuf[KSYM_NAME_LEN];
-       unsigned long i;
-       unsigned int off;
+       int ret;
+       unsigned int i;
 
        /* Skip the search for empty string. */
        if (!*name)
                return 0;
 
-       for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
-               off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
-
-               if (strcmp(namebuf, name) == 0)
-                       return kallsyms_sym_address(i);
+       ret = kallsyms_lookup_names(name, &i, NULL);
+       if (!ret)
+               return kallsyms_sym_address(get_symbol_seq(i));
 
-               if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
-                       return kallsyms_sym_address(i);
-       }
        return module_kallsyms_lookup_name(name);
 }
 
@@ -234,8 +282,7 @@ unsigned long kallsyms_lookup_name(const char *name)
  * Iterate over all symbols in vmlinux.  For symbols from modules use
  * module_kallsyms_on_each_symbol instead.
  */
-int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
-                                     unsigned long),
+int kallsyms_on_each_symbol(int (*fn)(void *, const char *, unsigned long),
                            void *data)
 {
        char namebuf[KSYM_NAME_LEN];
@@ -245,7 +292,7 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
 
        for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
                off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
-               ret = fn(data, namebuf, NULL, kallsyms_sym_address(i));
+               ret = fn(data, namebuf, kallsyms_sym_address(i));
                if (ret != 0)
                        return ret;
                cond_resched();
@@ -253,6 +300,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
        return 0;
 }
 
+int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long),
+                                 const char *name, void *data)
+{
+       int ret;
+       unsigned int i, start, end;
+
+       ret = kallsyms_lookup_names(name, &start, &end);
+       if (ret)
+               return 0;
+
+       for (i = start; !ret && i <= end; i++) {
+               ret = fn(data, kallsyms_sym_address(get_symbol_seq(i)));
+               cond_resched();
+       }
+
+       return ret;
+}
+
 static unsigned long get_symbol_pos(unsigned long addr,
                                    unsigned long *symbolsize,
                                    unsigned long *offset)
@@ -413,34 +478,6 @@ found:
        return 0;
 }
 
-int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
-                       unsigned long *offset, char *modname, char *name)
-{
-       int res;
-
-       name[0] = '\0';
-       name[KSYM_NAME_LEN - 1] = '\0';
-
-       if (is_ksym_addr(addr)) {
-               unsigned long pos;
-
-               pos = get_symbol_pos(addr, size, offset);
-               /* Grab name */
-               kallsyms_expand_symbol(get_symbol_offset(pos),
-                                      name, KSYM_NAME_LEN);
-               modname[0] = '\0';
-               goto found;
-       }
-       /* See if it's in a module. */
-       res = lookup_module_symbol_attrs(addr, size, offset, modname, name);
-       if (res)
-               return res;
-
-found:
-       cleanup_symbol_name(name);
-       return 0;
-}
-
 /* Look up a kernel symbol and return it in a text buffer. */
 static int __sprint_symbol(char *buffer, unsigned long address,
                           int symbol_offset, int add_offset, int add_buildid)
@@ -575,7 +612,6 @@ int sprint_backtrace_build_id(char *buffer, unsigned long address)
 /* To avoid using get_symbol_offset for every symbol, we carry prefix along. */
 struct kallsym_iter {
        loff_t pos;
-       loff_t pos_arch_end;
        loff_t pos_mod_end;
        loff_t pos_ftrace_mod_end;
        loff_t pos_bpf_end;
@@ -588,29 +624,9 @@ struct kallsym_iter {
        int show_value;
 };
 
-int __weak arch_get_kallsym(unsigned int symnum, unsigned long *value,
-                           char *type, char *name)
-{
-       return -EINVAL;
-}
-
-static int get_ksymbol_arch(struct kallsym_iter *iter)
-{
-       int ret = arch_get_kallsym(iter->pos - kallsyms_num_syms,
-                                  &iter->value, &iter->type,
-                                  iter->name);
-
-       if (ret < 0) {
-               iter->pos_arch_end = iter->pos;
-               return 0;
-       }
-
-       return 1;
-}
-
 static int get_ksymbol_mod(struct kallsym_iter *iter)
 {
-       int ret = module_get_kallsym(iter->pos - iter->pos_arch_end,
+       int ret = module_get_kallsym(iter->pos - kallsyms_num_syms,
                                     &iter->value, &iter->type,
                                     iter->name, iter->module_name,
                                     &iter->exported);
@@ -645,7 +661,7 @@ static int get_ksymbol_bpf(struct kallsym_iter *iter)
 {
        int ret;
 
-       strlcpy(iter->module_name, "bpf", MODULE_NAME_LEN);
+       strscpy(iter->module_name, "bpf", MODULE_NAME_LEN);
        iter->exported = 0;
        ret = bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end,
                              &iter->value, &iter->type,
@@ -665,7 +681,7 @@ static int get_ksymbol_bpf(struct kallsym_iter *iter)
  */
 static int get_ksymbol_kprobe(struct kallsym_iter *iter)
 {
-       strlcpy(iter->module_name, "__builtin__kprobes", MODULE_NAME_LEN);
+       strscpy(iter->module_name, "__builtin__kprobes", MODULE_NAME_LEN);
        iter->exported = 0;
        return kprobe_get_kallsym(iter->pos - iter->pos_bpf_end,
                                  &iter->value, &iter->type,
@@ -693,7 +709,6 @@ static void reset_iter(struct kallsym_iter *iter, loff_t new_pos)
        iter->nameoff = get_symbol_offset(new_pos);
        iter->pos = new_pos;
        if (new_pos == 0) {
-               iter->pos_arch_end = 0;
                iter->pos_mod_end = 0;
                iter->pos_ftrace_mod_end = 0;
                iter->pos_bpf_end = 0;
@@ -709,10 +724,6 @@ static int update_iter_mod(struct kallsym_iter *iter, loff_t pos)
 {
        iter->pos = pos;
 
-       if ((!iter->pos_arch_end || iter->pos_arch_end > pos) &&
-           get_ksymbol_arch(iter))
-               return 1;
-
        if ((!iter->pos_mod_end || iter->pos_mod_end > pos) &&
            get_ksymbol_mod(iter))
                return 1;
@@ -890,41 +901,6 @@ late_initcall(bpf_ksym_iter_register);
 
 #endif /* CONFIG_BPF_SYSCALL */
 
-static inline int kallsyms_for_perf(void)
-{
-#ifdef CONFIG_PERF_EVENTS
-       extern int sysctl_perf_event_paranoid;
-       if (sysctl_perf_event_paranoid <= 1)
-               return 1;
-#endif
-       return 0;
-}
-
-/*
- * We show kallsyms information even to normal users if we've enabled
- * kernel profiling and are explicitly not paranoid (so kptr_restrict
- * is clear, and sysctl_perf_event_paranoid isn't set).
- *
- * Otherwise, require CAP_SYSLOG (assuming kptr_restrict isn't set to
- * block even that).
- */
-bool kallsyms_show_value(const struct cred *cred)
-{
-       switch (kptr_restrict) {
-       case 0:
-               if (kallsyms_for_perf())
-                       return true;
-               fallthrough;
-       case 1:
-               if (security_capable(cred, &init_user_ns, CAP_SYSLOG,
-                                    CAP_OPT_NOAUDIT) == 0)
-                       return true;
-               fallthrough;
-       default:
-               return false;
-       }
-}
-
 static int kallsyms_open(struct inode *inode, struct file *file)
 {
        /*