Linux Perf
vmlinux-kallsyms.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 #include <linux/rbtree.h>
4 #include <inttypes.h>
5 #include <string.h>
6 #include "map.h"
7 #include "symbol.h"
8 #include "util.h"
9 #include "tests.h"
10 #include "debug.h"
11 #include "machine.h"
12 
13 #define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x))
14 
15 int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest __maybe_unused)
16 {
17  int err = -1;
18  struct rb_node *nd;
19  struct symbol *sym;
20  struct map *kallsyms_map, *vmlinux_map, *map;
21  struct machine kallsyms, vmlinux;
22  struct maps *maps = machine__kernel_maps(&vmlinux);
23  u64 mem_start, mem_end;
24  bool header_printed;
25 
26  /*
27  * Step 1:
28  *
29  * Init the machines that will hold kernel, modules obtained from
30  * both vmlinux + .ko files and from /proc/kallsyms split by modules.
31  */
32  machine__init(&kallsyms, "", HOST_KERNEL_ID);
33  machine__init(&vmlinux, "", HOST_KERNEL_ID);
34 
35  /*
36  * Step 2:
37  *
38  * Create the kernel maps for kallsyms and the DSO where we will then
39  * load /proc/kallsyms. Also create the modules maps from /proc/modules
40  * and find the .ko files that match them in /lib/modules/`uname -r`/.
41  */
42  if (machine__create_kernel_maps(&kallsyms) < 0) {
43  pr_debug("machine__create_kernel_maps ");
44  goto out;
45  }
46 
47  /*
48  * Step 3:
49  *
50  * Load and split /proc/kallsyms into multiple maps, one per module.
51  * Do not use kcore, as this test was designed before kcore support
52  * and has parts that only make sense if using the non-kcore code.
53  * XXX: extend it to stress the kcorre code as well, hint: the list
54  * of modules extracted from /proc/kcore, in its current form, can't
55  * be compacted against the list of modules found in the "vmlinux"
56  * code and with the one got from /proc/modules from the "kallsyms" code.
57  */
58  if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms") <= 0) {
59  pr_debug("dso__load_kallsyms ");
60  goto out;
61  }
62 
63  /*
64  * Step 4:
65  *
66  * kallsyms will be internally on demand sorted by name so that we can
67  * find the reference relocation * symbol, i.e. the symbol we will use
68  * to see if the running kernel was relocated by checking if it has the
69  * same value in the vmlinux file we load.
70  */
71  kallsyms_map = machine__kernel_map(&kallsyms);
72 
73  /*
74  * Step 5:
75  *
76  * Now repeat step 2, this time for the vmlinux file we'll auto-locate.
77  */
78  if (machine__create_kernel_maps(&vmlinux) < 0) {
79  pr_debug("machine__create_kernel_maps ");
80  goto out;
81  }
82 
83  vmlinux_map = machine__kernel_map(&vmlinux);
84 
85  /*
86  * Step 6:
87  *
88  * Locate a vmlinux file in the vmlinux path that has a buildid that
89  * matches the one of the running kernel.
90  *
91  * While doing that look if we find the ref reloc symbol, if we find it
92  * we'll have its ref_reloc_symbol.unrelocated_addr and then
93  * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
94  * to fixup the symbols.
95  */
96  if (machine__load_vmlinux_path(&vmlinux) <= 0) {
97  pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n");
98  err = TEST_SKIP;
99  goto out;
100  }
101 
102  err = 0;
103  /*
104  * Step 7:
105  *
106  * Now look at the symbols in the vmlinux DSO and check if we find all of them
107  * in the kallsyms dso. For the ones that are in both, check its names and
108  * end addresses too.
109  */
110  map__for_each_symbol(vmlinux_map, sym, nd) {
111  struct symbol *pair, *first_pair;
112 
113  sym = rb_entry(nd, struct symbol, rb_node);
114 
115  if (sym->start == sym->end)
116  continue;
117 
118  mem_start = vmlinux_map->unmap_ip(vmlinux_map, sym->start);
119  mem_end = vmlinux_map->unmap_ip(vmlinux_map, sym->end);
120 
121  first_pair = machine__find_kernel_symbol(&kallsyms, mem_start, NULL);
122  pair = first_pair;
123 
124  if (pair && UM(pair->start) == mem_start) {
125 next_pair:
126  if (arch__compare_symbol_names(sym->name, pair->name) == 0) {
127  /*
128  * kallsyms don't have the symbol end, so we
129  * set that by using the next symbol start - 1,
130  * in some cases we get this up to a page
131  * wrong, trace_kmalloc when I was developing
132  * this code was one such example, 2106 bytes
133  * off the real size. More than that and we
134  * _really_ have a problem.
135  */
136  s64 skew = mem_end - UM(pair->end);
137  if (llabs(skew) >= page_size)
138  pr_debug("WARN: %#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
139  mem_start, sym->name, mem_end,
140  UM(pair->end));
141 
142  /*
143  * Do not count this as a failure, because we
144  * could really find a case where it's not
145  * possible to get proper function end from
146  * kallsyms.
147  */
148  continue;
149  } else {
150  pair = machine__find_kernel_symbol_by_name(&kallsyms, sym->name, NULL);
151  if (pair) {
152  if (UM(pair->start) == mem_start)
153  goto next_pair;
154 
155  pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
156  mem_start, sym->name, pair->name);
157  } else {
158  pr_debug("WARN: %#" PRIx64 ": diff name v: %s k: %s\n",
159  mem_start, sym->name, first_pair->name);
160  }
161 
162  continue;
163  }
164  } else
165  pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n",
166  mem_start, sym->name);
167 
168  err = -1;
169  }
170 
171  if (verbose <= 0)
172  goto out;
173 
174  header_printed = false;
175 
176  for (map = maps__first(maps); map; map = map__next(map)) {
177  struct map *
178  /*
179  * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
180  * the kernel will have the path for the vmlinux file being used,
181  * so use the short name, less descriptive but the same ("[kernel]" in
182  * both cases.
183  */
184  pair = map_groups__find_by_name(&kallsyms.kmaps,
185  (map->dso->kernel ?
186  map->dso->short_name :
187  map->dso->name));
188  if (pair) {
189  pair->priv = 1;
190  } else {
191  if (!header_printed) {
192  pr_info("WARN: Maps only in vmlinux:\n");
193  header_printed = true;
194  }
195  map__fprintf(map, stderr);
196  }
197  }
198 
199  header_printed = false;
200 
201  for (map = maps__first(maps); map; map = map__next(map)) {
202  struct map *pair;
203 
204  mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start);
205  mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end);
206 
207  pair = map_groups__find(&kallsyms.kmaps, mem_start);
208  if (pair == NULL || pair->priv)
209  continue;
210 
211  if (pair->start == mem_start) {
212  if (!header_printed) {
213  pr_info("WARN: Maps in vmlinux with a different name in kallsyms:\n");
214  header_printed = true;
215  }
216 
217  pr_info("WARN: %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
218  map->start, map->end, map->pgoff, map->dso->name);
219  if (mem_end != pair->end)
220  pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64,
221  pair->start, pair->end, pair->pgoff);
222  pr_info(" %s\n", pair->dso->name);
223  pair->priv = 1;
224  }
225  }
226 
227  header_printed = false;
228 
229  maps = machine__kernel_maps(&kallsyms);
230 
231  for (map = maps__first(maps); map; map = map__next(map)) {
232  if (!map->priv) {
233  if (!header_printed) {
234  pr_info("WARN: Maps only in kallsyms:\n");
235  header_printed = true;
236  }
237  map__fprintf(map, stderr);
238  }
239  }
240 out:
241  machine__exit(&kallsyms);
242  machine__exit(&vmlinux);
243  return err;
244 }
struct rb_node rb_node
Definition: symbol.h:56
struct map * map_groups__find_by_name(struct map_groups *mg, const char *name)
Definition: symbol.c:1679
u64 pgoff
Definition: map.h:34
#define UM(x)
unsigned int page_size
Definition: util.c:40
int machine__create_kernel_maps(struct machine *machine)
Definition: machine.c:1321
int int err
Definition: 5sec.c:44
#define map__for_each_symbol(map, pos, n)
Definition: map.h:122
void machine__exit(struct machine *machine)
Definition: machine.c:193
int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
Definition: machine.c:65
u64 end
Definition: symbol.h:58
int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest __maybe_unused)
static struct symbol * machine__find_kernel_symbol_by_name(struct machine *machine, const char *name, struct map **mapp)
Definition: machine.h:210
u64 start
Definition: map.h:28
static struct map * machine__kernel_map(struct machine *machine)
Definition: machine.h:72
struct map * maps__first(struct maps *maps)
Definition: map.c:856
char name[0]
Definition: dso.h:197
#define pr_debug(fmt,...)
Definition: json.h:27
u64 start
Definition: symbol.h:57
size_t map__fprintf(struct map *map, FILE *fp)
Definition: map.c:398
struct map_groups kmaps
Definition: machine.h:51
struct map * map__next(struct map *map)
Definition: map.c:865
struct dso * dso
Definition: map.h:45
char name[0]
Definition: symbol.h:66
Definition: map.h:58
enum dso_kernel_type kernel
Definition: dso.h:154
static struct map * map_groups__find(struct map_groups *mg, u64 addr)
Definition: map.h:211
int machine__load_vmlinux_path(struct machine *machine)
Definition: machine.c:1113
Definition: jevents.c:228
int machine__load_kallsyms(struct machine *machine, const char *filename)
Definition: machine.c:1095
#define HOST_KERNEL_ID
Definition: machine.h:21
static int sym(yyscan_t scanner, int type, int config)
Definition: tests.h:30
static struct symbol * machine__find_kernel_symbol(struct machine *machine, u64 addr, struct map **mapp)
Definition: machine.h:203
#define pr_info(fmt,...)
Definition: json.h:24
static struct maps * machine__kernel_maps(struct machine *machine)
Definition: machine.h:81
int verbose
Definition: jevents.c:53
u64(* unmap_ip)(struct map *, u64)
Definition: map.h:43
Definition: symbol.h:55
int arch__compare_symbol_names(const char *namea, const char *nameb)
Definition: sym-handling.c:45
u64 end
Definition: map.h:29
const char * short_name
Definition: dso.h:172
u32 priv
Definition: map.h:31