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