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