1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/compiler.h> |
3 | #include <linux/string.h> |
4 | #include <sys/mman.h> |
5 | #include <limits.h> |
6 | #include "debug.h" |
7 | #include "dso.h" |
8 | #include "machine.h" |
9 | #include "thread.h" |
10 | #include "symbol.h" |
11 | #include "map.h" |
12 | #include "util.h" |
13 | #include "tests.h" |
14 | |
15 | struct test_info { |
16 | struct machine *machine; |
17 | struct thread *thread; |
18 | }; |
19 | |
20 | static int init_test_info(struct test_info *ti) |
21 | { |
22 | ti->machine = machine__new_host(); |
23 | if (!ti->machine) { |
24 | pr_debug("machine__new_host() failed!\n" ); |
25 | return TEST_FAIL; |
26 | } |
27 | |
28 | /* Create a dummy thread */ |
29 | ti->thread = machine__findnew_thread(ti->machine, 100, 100); |
30 | if (!ti->thread) { |
31 | pr_debug("machine__findnew_thread() failed!\n" ); |
32 | return TEST_FAIL; |
33 | } |
34 | |
35 | return TEST_OK; |
36 | } |
37 | |
38 | static void exit_test_info(struct test_info *ti) |
39 | { |
40 | thread__put(ti->thread); |
41 | machine__delete(ti->machine); |
42 | } |
43 | |
44 | struct dso_map { |
45 | struct dso *dso; |
46 | struct map *map; |
47 | }; |
48 | |
49 | static int find_map_cb(struct map *map, void *d) |
50 | { |
51 | struct dso_map *data = d; |
52 | |
53 | if (map__dso(map) != data->dso) |
54 | return 0; |
55 | data->map = map; |
56 | return 1; |
57 | } |
58 | |
59 | static struct map *find_module_map(struct machine *machine, struct dso *dso) |
60 | { |
61 | struct dso_map data = { .dso = dso }; |
62 | |
63 | machine__for_each_kernel_map(machine, find_map_cb, &data); |
64 | |
65 | return data.map; |
66 | } |
67 | |
68 | static void get_test_dso_filename(char *filename, size_t max_sz) |
69 | { |
70 | if (dso_to_test) |
71 | strlcpy(filename, dso_to_test, max_sz); |
72 | else |
73 | perf_exe(filename, max_sz); |
74 | } |
75 | |
76 | static int create_map(struct test_info *ti, char *filename, struct map **map_p) |
77 | { |
78 | struct dso *dso = machine__findnew_dso(ti->machine, filename); |
79 | |
80 | /* |
81 | * If 'filename' matches a current kernel module, must use a kernel |
82 | * map. Find the one that already exists. |
83 | */ |
84 | if (dso && dso->kernel) { |
85 | *map_p = find_module_map(machine: ti->machine, dso); |
86 | dso__put(dso); |
87 | if (!*map_p) { |
88 | pr_debug("Failed to find map for current kernel module %s" , |
89 | filename); |
90 | return TEST_FAIL; |
91 | } |
92 | map__get(*map_p); |
93 | return TEST_OK; |
94 | } |
95 | |
96 | dso__put(dso); |
97 | |
98 | /* Create a dummy map at 0x100000 */ |
99 | *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL, |
100 | PROT_EXEC, 0, NULL, filename, ti->thread); |
101 | if (!*map_p) { |
102 | pr_debug("Failed to create map!" ); |
103 | return TEST_FAIL; |
104 | } |
105 | |
106 | return TEST_OK; |
107 | } |
108 | |
109 | static int test_dso(struct dso *dso) |
110 | { |
111 | struct symbol *last_sym = NULL; |
112 | struct rb_node *nd; |
113 | int ret = TEST_OK; |
114 | |
115 | /* dso__fprintf() prints all the symbols */ |
116 | if (verbose > 1) |
117 | dso__fprintf(dso, stderr); |
118 | |
119 | for (nd = rb_first_cached(&dso->symbols); nd; nd = rb_next(nd)) { |
120 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
121 | |
122 | if (sym->type != STT_FUNC && sym->type != STT_GNU_IFUNC) |
123 | continue; |
124 | |
125 | /* Check for overlapping function symbols */ |
126 | if (last_sym && sym->start < last_sym->end) { |
127 | pr_debug("Overlapping symbols:\n" ); |
128 | symbol__fprintf(last_sym, stderr); |
129 | symbol__fprintf(sym, stderr); |
130 | ret = TEST_FAIL; |
131 | } |
132 | /* Check for zero-length function symbol */ |
133 | if (sym->start == sym->end) { |
134 | pr_debug("Zero-length symbol:\n" ); |
135 | symbol__fprintf(sym, stderr); |
136 | ret = TEST_FAIL; |
137 | } |
138 | last_sym = sym; |
139 | } |
140 | |
141 | return ret; |
142 | } |
143 | |
144 | static int subdivided_dso_cb(struct dso *dso, struct machine *machine __maybe_unused, void *d) |
145 | { |
146 | struct dso *text_dso = d; |
147 | |
148 | if (dso != text_dso && strstarts(str: dso->short_name, prefix: text_dso->short_name)) |
149 | if (test_dso(dso) != TEST_OK) |
150 | return -1; |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static int process_subdivided_dso(struct machine *machine, struct dso *dso) |
156 | { |
157 | int ret; |
158 | |
159 | ret = machine__for_each_dso(machine, subdivided_dso_cb, dso); |
160 | |
161 | return ret < 0 ? TEST_FAIL : TEST_OK; |
162 | } |
163 | |
164 | static int test_file(struct test_info *ti, char *filename) |
165 | { |
166 | struct map *map = NULL; |
167 | int ret, nr; |
168 | struct dso *dso; |
169 | |
170 | pr_debug("Testing %s\n" , filename); |
171 | |
172 | ret = create_map(ti, filename, map_p: &map); |
173 | if (ret != TEST_OK) |
174 | return ret; |
175 | |
176 | dso = map__dso(map); |
177 | nr = dso__load(dso, map); |
178 | if (nr < 0) { |
179 | pr_debug("dso__load() failed!\n" ); |
180 | ret = TEST_FAIL; |
181 | goto out_put; |
182 | } |
183 | |
184 | if (nr == 0) { |
185 | pr_debug("DSO has no symbols!\n" ); |
186 | ret = TEST_SKIP; |
187 | goto out_put; |
188 | } |
189 | |
190 | ret = test_dso(dso); |
191 | |
192 | /* Module dso is split into many dsos by section */ |
193 | if (ret == TEST_OK && dso->kernel) |
194 | ret = process_subdivided_dso(machine: ti->machine, dso); |
195 | out_put: |
196 | map__put(map); |
197 | |
198 | return ret; |
199 | } |
200 | |
201 | static int test__symbols(struct test_suite *test __maybe_unused, int subtest __maybe_unused) |
202 | { |
203 | char filename[PATH_MAX]; |
204 | struct test_info ti; |
205 | int ret; |
206 | |
207 | ret = init_test_info(ti: &ti); |
208 | if (ret != TEST_OK) |
209 | return ret; |
210 | |
211 | get_test_dso_filename(filename, max_sz: sizeof(filename)); |
212 | |
213 | ret = test_file(ti: &ti, filename); |
214 | |
215 | exit_test_info(ti: &ti); |
216 | |
217 | return ret; |
218 | } |
219 | |
220 | DEFINE_SUITE("Symbols" , symbols); |
221 | |