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