1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2021 Oxide Computer Company 14 */ 15 16 /* 17 * Check that a given core dump generated as part of our test framework has the 18 * sections that we'd expect. We have here the dumper binary. In that, we expect 19 * to find the following libraries and sections: 20 * 21 * a.out: symtab, ctf, .debug_* (dwarf) 22 * ld.so.1: symtab 23 * libc.so: symtab, ctf 24 * libproc.so: symtab, ctf 25 * libdumper.so: symtab, ctf, .debug_* (dwarf) 26 * 27 * Note, there will also be additional libraries and things here can change over 28 * time (e.g. deps of libproc, etc.), but we try to ignore them generally 29 * speaking if we can know enough to do so. 30 */ 31 32 #include <err.h> 33 #include <stdlib.h> 34 #include <libproc.h> 35 #include <gelf.h> 36 #include <libelf.h> 37 #include <limits.h> 38 #include <string.h> 39 #include <libgen.h> 40 41 typedef enum { 42 SECMAP_CTF, 43 SECMAP_SYMTAB, 44 SECMAP_DEBUG, 45 SECMAP_MAX 46 } secmap_type_t; 47 48 typedef struct secmap_data { 49 core_content_t sd_content; 50 const char *sd_name; 51 } secmap_data_t; 52 53 secmap_data_t secmap_data[SECMAP_MAX] = { 54 { CC_CONTENT_CTF, ".SUNW_ctf" }, 55 { CC_CONTENT_SYMTAB, ".symtab" }, 56 { CC_CONTENT_DEBUG, ".debug_" } 57 }; 58 59 typedef struct { 60 uint64_t sm_addr; 61 char sm_obj[PATH_MAX]; 62 size_t sm_nfound[SECMAP_MAX]; 63 Elf *sm_elf; 64 GElf_Ehdr sm_ehdr; 65 boolean_t sm_ctf; 66 boolean_t sm_debug; 67 boolean_t sm_symtab; 68 } secmap_t; 69 70 static secmap_t *secmaps; 71 static size_t secmap_count; 72 static core_content_t secmap_content; 73 74 static int secmap_exit = EXIT_SUCCESS; 75 76 static void 77 secmap_fail(const char *fmt, ...) 78 { 79 va_list ap; 80 81 va_start(ap, fmt); 82 vwarnx(fmt, ap); 83 va_end(ap); 84 secmap_exit = EXIT_FAILURE; 85 } 86 87 88 static void 89 check_content(core_content_t content, struct ps_prochandle *Pr) 90 { 91 secmap_content = Pcontent(Pr); 92 93 if (secmap_content == CC_CONTENT_INVALID) { 94 secmap_fail("TEST FAILED: failed to get core content"); 95 return; 96 } 97 98 if (secmap_content != content) { 99 secmap_fail("TEST FAILED: core file contains different " 100 "content than expected, found 0x%x, expected 0x%x", 101 secmap_content, content); 102 } 103 } 104 105 static secmap_t * 106 secmap_find(uint64_t addr) 107 { 108 for (size_t i = 0; i < secmap_count; i++) { 109 if (secmaps[i].sm_addr == addr) { 110 return (&secmaps[i]); 111 } 112 } 113 114 return (NULL); 115 } 116 117 static void 118 secmap_matches_content(secmap_type_t type) 119 { 120 boolean_t exist = (secmap_data[type].sd_content & secmap_content) != 0; 121 boolean_t found = B_FALSE; 122 123 /* 124 * Dumping CTF data implies that some symbol tables will exist for CTF. 125 */ 126 if (type == SECMAP_SYMTAB && (secmap_content & CC_CONTENT_CTF) != 0) { 127 exist = B_TRUE; 128 } 129 130 for (size_t i = 0; i < secmap_count; i++) { 131 if (secmaps[i].sm_nfound[type] != 0) { 132 found = B_TRUE; 133 } 134 } 135 136 if (exist != found) { 137 secmap_fail("content type mismatch for %s: expected %s, but " 138 "found %s", secmap_data[type].sd_name, 139 exist ? "some" : "none", 140 found ? "some" : "none"); 141 } 142 } 143 144 static secmap_t * 145 secmap_alloc(struct ps_prochandle *Pr, uint64_t addr) 146 { 147 int fd; 148 secmap_t *sm; 149 char *base; 150 151 sm = recallocarray(secmaps, secmap_count, secmap_count + 1, 152 sizeof (secmap_t)); 153 if (sm == NULL) { 154 err(EXIT_FAILURE, "TEST FAILED: failed to allocate memory for " 155 "secmap %zu", secmap_count + 1); 156 } 157 158 secmaps = sm; 159 sm = &secmaps[secmap_count]; 160 sm->sm_addr = addr; 161 secmap_count++; 162 163 /* 164 * We also have some tests that we don't expect to have anything here 165 * because we only include the relevant sections. Experimentally, we 166 * know that libproc needs both anon and data mappings for this to work. 167 * So if we don't have both, then we'll not warn on that. 168 */ 169 if (Pobjname(Pr, addr, sm->sm_obj, sizeof (sm->sm_obj)) == NULL) { 170 core_content_t need = CC_CONTENT_ANON | CC_CONTENT_DATA; 171 172 if ((secmap_content & need) == need) { 173 secmap_fail("TEST FAILURE: object at address 0x%lx " 174 "has no name", addr); 175 } 176 177 return (sm); 178 } 179 180 /* 181 * Since we have a name, we should be able to open this elf object and 182 * identify it as well. 183 */ 184 fd = open(sm->sm_obj, O_RDONLY); 185 if (fd < 0) { 186 err(EXIT_FAILURE, "failed to open object %s", sm->sm_obj); 187 } 188 189 sm->sm_elf = elf_begin(fd, ELF_C_READ, NULL); 190 if (sm->sm_elf == NULL) { 191 err(EXIT_FAILURE, "failed to find open elf object %s: %s", 192 sm->sm_obj, elf_errmsg(elf_errno())); 193 } 194 195 if (gelf_getehdr(sm->sm_elf, &sm->sm_ehdr) == NULL) { 196 err(EXIT_FAILURE, "failed to get ehdr for %s: %s", 197 sm->sm_obj, elf_errmsg(elf_errno())); 198 } 199 200 base = basename(sm->sm_obj); 201 if (strcmp(base, "dumper.32") == 0 || strcmp(base, "dumper.64") == 0) { 202 sm->sm_debug = sm->sm_symtab = sm->sm_ctf = B_TRUE; 203 } else if (strcmp(base, "libc.so.1") == 0) { 204 sm->sm_symtab = sm->sm_ctf = B_TRUE; 205 } else if (strcmp(base, "ld.so.1") == 0) { 206 sm->sm_symtab = B_TRUE; 207 } else if (strcmp(base, "libproc.so.1") == 0) { 208 sm->sm_symtab = sm->sm_ctf = B_TRUE; 209 } else if (strcmp(base, "libdumper.so.1") == 0) { 210 sm->sm_debug = sm->sm_symtab = sm->sm_ctf = B_TRUE; 211 } else { 212 sm->sm_symtab = B_TRUE; 213 } 214 215 return (sm); 216 } 217 218 static void 219 secmap_data_cmp(secmap_t *sm, const char *sname, Elf_Scn *scn, GElf_Shdr *shdr) 220 { 221 for (Elf_Scn *comp_scn = elf_nextscn(sm->sm_elf, NULL); 222 comp_scn != NULL; comp_scn = elf_nextscn(sm->sm_elf, comp_scn)) { 223 GElf_Shdr comp_shdr; 224 const char *comp_name; 225 Elf_Data *src_data, *comp_data; 226 227 if (gelf_getshdr(comp_scn, &comp_shdr) == NULL) { 228 secmap_fail("failed to load section header from %s " 229 "during data comparison", sm->sm_obj); 230 return; 231 } 232 233 comp_name = elf_strptr(sm->sm_elf, sm->sm_ehdr.e_shstrndx, 234 comp_shdr.sh_name); 235 if (comp_name == NULL) { 236 secmap_fail("failed to load section name from %s " 237 "with index %lu", sm->sm_obj, comp_shdr.sh_name); 238 return; 239 } 240 241 if (strcmp(comp_name, sname) != 0) 242 continue; 243 244 if (comp_shdr.sh_type != shdr->sh_type || 245 comp_shdr.sh_addralign != shdr->sh_addralign || 246 comp_shdr.sh_size != shdr->sh_size || 247 comp_shdr.sh_entsize != shdr->sh_entsize) { 248 continue; 249 } 250 251 if ((src_data = elf_getdata(scn, NULL)) == NULL) { 252 secmap_fail("failed to load section data from " 253 "source to compare to %s %s", sm->sm_obj, sname); 254 return; 255 } 256 257 if ((comp_data = elf_getdata(comp_scn, NULL)) == NULL) { 258 secmap_fail("failed to load section data from " 259 "source to compare to %s %s", sm->sm_obj, sname); 260 return; 261 } 262 263 if (comp_data->d_size != src_data->d_size) { 264 secmap_fail("data size mismatch for %s: %s, core: " 265 "%zu, file: %zu", sm->sm_obj, sname, 266 src_data->d_size, comp_data->d_size); 267 return; 268 } 269 270 if (memcmp(comp_data->d_buf, src_data->d_buf, 271 comp_data->d_size) != 0) { 272 secmap_fail("data mismatch between core and source " 273 "in %s: %s", sm->sm_obj, sname); 274 return; 275 } 276 277 return; 278 } 279 280 secmap_fail("failed to find matching section for %s in %s", 281 sname, sm->sm_obj); 282 } 283 284 static void 285 secmap_file_check(secmap_t *sm) 286 { 287 if (sm->sm_ctf && (secmap_content & CC_CONTENT_CTF) != 0 && 288 sm->sm_nfound[SECMAP_CTF] == 0) { 289 secmap_fail("expected object %s to have CTF, but it doesn't", 290 sm->sm_obj); 291 } 292 293 if (sm->sm_symtab && (secmap_content & CC_CONTENT_SYMTAB) != 0 && 294 sm->sm_nfound[SECMAP_SYMTAB] == 0) { 295 secmap_fail("expected object %s to have a symbol table, " 296 "but it doesn't", sm->sm_obj); 297 } 298 299 if (sm->sm_debug && (secmap_content & CC_CONTENT_DEBUG) != 0 && 300 sm->sm_nfound[SECMAP_DEBUG] == 0) { 301 secmap_fail("expected object %s to have debug sections, " 302 "but it doesn't", sm->sm_obj); 303 } 304 } 305 306 int 307 main(int argc, char *argv[]) 308 { 309 core_content_t content; 310 struct ps_prochandle *Pr; 311 int perr, fd; 312 Elf *elf; 313 Elf_Scn *scn; 314 GElf_Ehdr ehdr; 315 316 if (argc != 3) { 317 warnx("missing required file and core content"); 318 (void) fprintf(stderr, "Usage: secmapper file content\n"); 319 exit(EXIT_FAILURE); 320 } 321 322 if (elf_version(EV_CURRENT) == EV_NONE) { 323 errx(EXIT_FAILURE, "failed to init libelf"); 324 } 325 326 Pr = Pgrab_core(argv[1], NULL, PGRAB_RDONLY, &perr); 327 if (Pr == NULL) { 328 errx(EXIT_FAILURE, "failed to open %s: %s", argv[1], 329 Pgrab_error(perr)); 330 } 331 332 if ((fd = open(argv[1], O_RDONLY)) < 0) { 333 err(EXIT_FAILURE, "failed to open %s\n", argv[1]); 334 } 335 336 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 337 errx(EXIT_FAILURE, "failed to open elf file %s: %s", argv[1], 338 elf_errmsg(elf_errno())); 339 } 340 341 if (proc_str2content(argv[2], &content) != 0) { 342 err(EXIT_FAILURE, "failed to parse content %s", argv[2]); 343 } 344 345 if (gelf_getehdr(elf, &ehdr) == NULL) { 346 errx(EXIT_FAILURE, "failed to get edr: %s", 347 elf_errmsg(elf_errno())); 348 } 349 350 /* 351 * Before we go futher, make sure that we have the content in this file 352 * that we expect. 353 */ 354 check_content(content, Pr); 355 356 for (scn = elf_nextscn(elf, NULL); scn != NULL; 357 scn = elf_nextscn(elf, scn)) { 358 const char *sname; 359 GElf_Shdr shdr; 360 size_t index; 361 secmap_t *secmap; 362 363 index = elf_ndxscn(scn); 364 if (gelf_getshdr(scn, &shdr) == NULL) { 365 errx(EXIT_FAILURE, "failed to get section header for " 366 "shdr %zu: %s", index, elf_errmsg(elf_errno())); 367 } 368 369 /* 370 * Skip the strtab. 371 */ 372 if (shdr.sh_type == SHT_STRTAB) { 373 continue; 374 } 375 376 sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); 377 if (sname == NULL) { 378 secmap_fail("TEST FAILURE: string name missing for " 379 "shdr %zu", index); 380 continue; 381 } 382 383 /* 384 * Find or cons up a new secmap for this object. 385 */ 386 secmap = secmap_find(shdr.sh_addr); 387 if (secmap == NULL) { 388 secmap = secmap_alloc(Pr, shdr.sh_addr); 389 } 390 391 if (strcmp(sname, ".symtab") == 0) { 392 secmap->sm_nfound[SECMAP_SYMTAB]++; 393 } else if (strcmp(sname, ".SUNW_ctf") == 0) { 394 secmap->sm_nfound[SECMAP_CTF]++; 395 } else if (strncmp(sname, ".debug_", strlen(".debug_")) == 0) { 396 secmap->sm_nfound[SECMAP_DEBUG]++; 397 } else { 398 continue; 399 } 400 401 /* 402 * For one of our three primary sections, make sure that the 403 * data that is in the core file that we find in it actually 404 * matches the underlying object. That is, if the secmap 405 * actually has something here. 406 */ 407 if (secmap->sm_elf != NULL) { 408 secmap_data_cmp(secmap, sname, scn, &shdr); 409 } 410 } 411 412 /* 413 * Now that we have iterated over all of these sections, check and make 414 * sure certain things are true of them. In particular, go through some 415 * of the various types of data and make sure it exists at all or 416 * doesn't based on our core content. 417 */ 418 secmap_matches_content(SECMAP_CTF); 419 secmap_matches_content(SECMAP_SYMTAB); 420 secmap_matches_content(SECMAP_DEBUG); 421 422 /* 423 * Finally, if we have enough information to know that we've found 424 * a file that we know it should at least have a given type of data, 425 * check for it. Here, it is OK for data to be present we don't expect 426 * (assuming the core content allows it). This makes this test less 427 * prone to broader changes in the system. 428 */ 429 for (size_t i = 0; i < secmap_count; i++) { 430 secmap_file_check(&secmaps[i]); 431 } 432 433 return (secmap_exit); 434 } 435