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(dso) != DSO_SPACE__USER) { 85 *map_p = find_module_map(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(dso)); 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(dso__short_name(dso), dso__short_name(text_dso))) 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); 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(dso) != DSO_SPACE__USER) 194 ret = process_subdivided_dso(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); 208 if (ret != TEST_OK) 209 return ret; 210 211 get_test_dso_filename(filename, sizeof(filename)); 212 213 ret = test_file(&ti, filename); 214 215 exit_test_info(&ti); 216 217 return ret; 218 } 219 220 DEFINE_SUITE("Symbols", symbols); 221