1 /* $OpenBSD: ctfconv.c,v 1.17 2018/08/08 20:15:17 mestre Exp $ */ 2 3 /* 4 * Copyright (c) 2016-2017 Martin Pieuchot 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 #include <sys/mman.h> 22 #include <sys/queue.h> 23 #include <sys/tree.h> 24 #include <sys/ctf.h> 25 26 #include <assert.h> 27 #include <elf.h> 28 #include <err.h> 29 #include <fcntl.h> 30 #include <locale.h> 31 #include <stdio.h> 32 #include <stdint.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "itype.h" 38 #include "xmalloc.h" 39 40 #ifndef nitems 41 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 42 #endif 43 44 #define DEBUG_ABBREV ".debug_abbrev" 45 #define DEBUG_INFO ".debug_info" 46 #define DEBUG_LINE ".debug_line" 47 #define DEBUG_STR ".debug_str" 48 49 __dead void usage(void); 50 int convert(const char *); 51 int generate(const char *, const char *, int); 52 int elf_convert(char *, size_t); 53 void elf_sort(void); 54 struct itype *find_symb(struct itype *, size_t); 55 void dump_type(struct itype *); 56 void dump_func(struct itype *, int *); 57 void dump_obj(struct itype *, int *); 58 59 /* elf.c */ 60 int iself(const char *, size_t); 61 int elf_getshstab(const char *, size_t, const char **, size_t *); 62 ssize_t elf_getsymtab(const char *, size_t, const char *, size_t, 63 const Elf_Sym **, size_t *, const char **, size_t *); 64 ssize_t elf_getsection(char *, size_t, const char *, const char *, 65 size_t, const char **, size_t *); 66 67 /* parse.c */ 68 void dwarf_parse(const char *, size_t, const char *, size_t); 69 70 const char *ctf_enc2name(unsigned short); 71 72 /* lists of parsed types and functions */ 73 struct itype_queue itypeq = TAILQ_HEAD_INITIALIZER(itypeq); 74 struct itype_queue ifuncq = TAILQ_HEAD_INITIALIZER(ifuncq); 75 struct itype_queue iobjq = TAILQ_HEAD_INITIALIZER(iobjq); 76 77 __dead void 78 usage(void) 79 { 80 fprintf(stderr, "usage: %s [-d] -l label -o outfile file\n", 81 getprogname()); 82 exit(1); 83 } 84 85 int 86 main(int argc, char *argv[]) 87 { 88 const char *filename, *label = NULL, *outfile = NULL; 89 int dump = 0; 90 int ch, error = 0; 91 struct itype *it; 92 93 setlocale(LC_ALL, ""); 94 95 while ((ch = getopt(argc, argv, "dl:o:")) != -1) { 96 switch (ch) { 97 case 'd': 98 dump = 1; /* ctfdump(1)-like SUNW_ctf sections */ 99 break; 100 case 'l': 101 if (label != NULL) 102 usage(); 103 label = optarg; 104 break; 105 case 'o': 106 if (outfile != NULL) 107 usage(); 108 outfile = optarg; 109 break; 110 default: 111 usage(); 112 } 113 } 114 115 argc -= optind; 116 argv += optind; 117 118 if (argc != 1) 119 usage(); 120 121 /* Either dump the sections, or write it out. */ 122 if ((dump && (outfile != NULL || label != NULL)) || 123 (!dump && (outfile == NULL || label == NULL))) 124 usage(); 125 126 filename = *argv; 127 128 if (unveil(filename, "r") == -1) 129 err(1, "unveil"); 130 131 if (outfile != NULL) { 132 if (unveil(outfile, "wc") == -1) 133 err(1, "unveil"); 134 } 135 136 if (pledge("stdio rpath wpath cpath", NULL) == -1) 137 err(1, "pledge"); 138 139 error = convert(filename); 140 if (error != 0) 141 return error; 142 143 if (outfile != NULL) { 144 if (pledge("stdio wpath cpath", NULL) == -1) 145 err(1, "pledge"); 146 147 error = generate(outfile, label, 1); 148 if (error != 0) 149 return error; 150 } 151 152 if (dump) { 153 if (pledge("stdio", NULL) == -1) 154 err(1, "pledge"); 155 156 int fidx = -1, oidx = -1; 157 158 TAILQ_FOREACH(it, &iobjq, it_symb) 159 dump_obj(it, &oidx); 160 printf("\n"); 161 162 TAILQ_FOREACH(it, &ifuncq, it_symb) 163 dump_func(it, &fidx); 164 printf("\n"); 165 166 TAILQ_FOREACH(it, &itypeq, it_next) { 167 if (it->it_flags & (ITF_FUNC|ITF_OBJ)) 168 continue; 169 170 dump_type(it); 171 } 172 173 return 0; 174 } 175 176 return 0; 177 } 178 179 int 180 convert(const char *path) 181 { 182 struct stat st; 183 int fd, error = 1; 184 char *p; 185 186 fd = open(path, O_RDONLY); 187 if (fd == -1) { 188 warn("open %s", path); 189 return 1; 190 } 191 if (fstat(fd, &st) == -1) { 192 warn("fstat %s", path); 193 close(fd); 194 return 1; 195 } 196 if ((uintmax_t)st.st_size > SIZE_MAX) { 197 warnx("file too big to fit memory"); 198 close(fd); 199 return 1; 200 } 201 202 p = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 203 if (p == MAP_FAILED) 204 err(1, "mmap"); 205 206 if (iself(p, st.st_size)) 207 error = elf_convert(p, st.st_size); 208 209 munmap(p, st.st_size); 210 close(fd); 211 212 return error; 213 } 214 215 const char *dstrbuf; 216 size_t dstrlen; 217 const char *strtab; 218 const Elf_Sym *symtab; 219 size_t strtabsz, nsymb; 220 221 int 222 elf_convert(char *p, size_t filesize) 223 { 224 const char *shstab; 225 const char *infobuf, *abbuf; 226 size_t infolen, ablen; 227 size_t shstabsz; 228 229 /* Find section header string table location and size. */ 230 if (elf_getshstab(p, filesize, &shstab, &shstabsz)) 231 return 1; 232 233 /* Find symbol table and associated string table. */ 234 if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb, 235 &strtab, &strtabsz) == -1) 236 warnx("symbol table not found"); 237 238 /* Find abbreviation location and size. */ 239 if (elf_getsection(p, filesize, DEBUG_ABBREV, shstab, shstabsz, &abbuf, 240 &ablen) == -1) { 241 warnx("%s section not found", DEBUG_ABBREV); 242 return 1; 243 } 244 245 if (elf_getsection(p, filesize, DEBUG_INFO, shstab, shstabsz, &infobuf, 246 &infolen) == -1) { 247 warnx("%s section not found", DEBUG_INFO); 248 return 1; 249 } 250 251 /* Find string table location and size. */ 252 if (elf_getsection(p, filesize, DEBUG_STR, shstab, shstabsz, &dstrbuf, 253 &dstrlen) == -1) 254 warnx("%s section not found", DEBUG_STR); 255 256 dwarf_parse(infobuf, infolen, abbuf, ablen); 257 258 /* Sort functions */ 259 elf_sort(); 260 261 return 0; 262 } 263 264 struct itype * 265 find_symb(struct itype *tmp, size_t stroff) 266 { 267 struct itype *it; 268 char *sname, *p; 269 270 if (strtab == NULL || stroff >= strtabsz) 271 return NULL; 272 273 /* 274 * Skip local suffix 275 * 276 * FIXME: only skip local copies. 277 */ 278 sname = xstrdup(strtab + stroff); 279 if ((p = strtok(sname, ".")) == NULL) { 280 free(sname); 281 return NULL; 282 } 283 284 strlcpy(tmp->it_name, p, ITNAME_MAX); 285 free(sname); 286 it = RB_FIND(isymb_tree, &isymbt, tmp); 287 288 /* Restore original name */ 289 if (it == NULL) 290 strlcpy(tmp->it_name, (strtab + stroff), ITNAME_MAX); 291 292 return it; 293 } 294 295 void 296 elf_sort(void) 297 { 298 struct itype *it, tmp; 299 size_t i; 300 301 memset(&tmp, 0, sizeof(tmp)); 302 for (i = 0; i < nsymb; i++) { 303 const Elf_Sym *st = &symtab[i]; 304 305 if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON) 306 continue; 307 308 switch (ELF_ST_TYPE(st->st_info)) { 309 case STT_FUNC: 310 tmp.it_flags = ITF_FUNC; 311 break; 312 case STT_OBJECT: 313 tmp.it_flags = ITF_OBJ; 314 break; 315 default: 316 continue; 317 } 318 319 it = find_symb(&tmp, st->st_name); 320 if (it == NULL) { 321 /* Insert 'unknown' entry to match symbol order. */ 322 it = it_dup(&tmp); 323 it->it_refp = it; 324 #ifdef DEBUG 325 warnx("symbol not found: %s", it_name(it)); 326 #endif 327 } 328 329 if (it->it_flags & ITF_INSERTED) { 330 #ifdef DEBUG 331 warnx("%s: already inserted", it_name(it)); 332 #endif 333 it = it_dup(it); 334 } 335 336 /* Save symbol index for dump. */ 337 it->it_ref = i; 338 339 it->it_flags |= ITF_INSERTED; 340 if (it->it_flags & ITF_FUNC) 341 TAILQ_INSERT_TAIL(&ifuncq, it, it_symb); 342 else 343 TAILQ_INSERT_TAIL(&iobjq, it, it_symb); 344 } 345 } 346 347 const char * 348 type_name(struct itype *it) 349 { 350 const char *name; 351 352 name = it_name(it); 353 if (name == NULL) 354 return "(anon)"; 355 356 return name; 357 } 358 359 /* Display parsed types a la ctfdump(1) */ 360 void 361 dump_type(struct itype *it) 362 { 363 struct imember *im; 364 365 #ifdef DEBUG 366 switch (it->it_type) { 367 case CTF_K_POINTER: 368 case CTF_K_TYPEDEF: 369 case CTF_K_VOLATILE: 370 case CTF_K_CONST: 371 case CTF_K_RESTRICT: 372 case CTF_K_ARRAY: 373 case CTF_K_FUNCTION: 374 if (it->it_refp == NULL) { 375 printf("unresolved: %s type=%d\n", it_name(it), 376 it->it_type); 377 return; 378 } 379 default: 380 break; 381 } 382 #endif 383 384 switch (it->it_type) { 385 case CTF_K_FLOAT: 386 case CTF_K_INTEGER: 387 printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n", 388 it->it_idx, 389 (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT", 390 it_name(it), ctf_enc2name(it->it_enc), it->it_size); 391 break; 392 case CTF_K_POINTER: 393 printf(" <%u> POINTER %s refers to %u\n", it->it_idx, 394 type_name(it), it->it_refp->it_idx); 395 break; 396 case CTF_K_TYPEDEF: 397 printf(" <%u> TYPEDEF %s refers to %u\n", 398 it->it_idx, it_name(it), it->it_refp->it_idx); 399 break; 400 case CTF_K_VOLATILE: 401 printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx, 402 type_name(it), it->it_refp->it_idx); 403 break; 404 case CTF_K_CONST: 405 printf(" <%u> CONST %s refers to %u\n", it->it_idx, 406 type_name(it), it->it_refp->it_idx); 407 break; 408 case CTF_K_RESTRICT: 409 printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx, 410 it_name(it), it->it_refp->it_idx); 411 break; 412 case CTF_K_ARRAY: 413 printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n", 414 it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx, 415 it->it_nelems); 416 printf("\n"); 417 break; 418 case CTF_K_STRUCT: 419 case CTF_K_UNION: 420 printf(" [%u] %s %s (%u bytes)\n", it->it_idx, 421 (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION", 422 type_name(it), it->it_size); 423 TAILQ_FOREACH(im, &it->it_members, im_next) { 424 printf("\t%s type=%u off=%zu\n", 425 (im_name(im) == NULL) ? "unknown" : im_name(im), 426 im->im_refp ? im->im_refp->it_idx : 0, im->im_off); 427 } 428 printf("\n"); 429 break; 430 case CTF_K_ENUM: 431 printf(" [%u] ENUM %s\n", it->it_idx, type_name(it)); 432 TAILQ_FOREACH(im, &it->it_members, im_next) { 433 printf("\t%s = %zu\n", im_name(im), im->im_ref); 434 } 435 printf("\n"); 436 break; 437 case CTF_K_FUNCTION: 438 printf(" [%u] FUNCTION (%s) returns: %u args: (", 439 it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon", 440 it->it_refp->it_idx); 441 TAILQ_FOREACH(im, &it->it_members, im_next) { 442 printf("%u%s", im->im_refp->it_idx, 443 TAILQ_NEXT(im, im_next) ? ", " : ""); 444 } 445 printf(")\n"); 446 break; 447 default: 448 assert(0 == 1); 449 } 450 } 451 452 void 453 dump_func(struct itype *it, int *idx) 454 { 455 struct imember *im; 456 457 (*idx)++; 458 459 if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0) 460 return; 461 462 printf(" [%u] FUNC (%s) returns: %u args: (", (*idx), 463 (it_name(it) != NULL) ? it_name(it) : "unknown", 464 it->it_refp->it_idx); 465 TAILQ_FOREACH(im, &it->it_members, im_next) { 466 printf("%u%s", im->im_refp->it_idx, 467 TAILQ_NEXT(im, im_next) ? ", " : ""); 468 } 469 printf(")\n"); 470 } 471 472 void 473 dump_obj(struct itype *it, int *idx) 474 { 475 int l; 476 477 (*idx)++; 478 479 l = printf(" [%u] %u", (*idx), it->it_refp->it_idx); 480 printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref); 481 } 482 483 const char * 484 ctf_enc2name(unsigned short enc) 485 { 486 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR", 487 "BOOL", "SIGNED BOOL" }; 488 static char invalid[7]; 489 490 if (enc == CTF_INT_VARARGS) 491 return "VARARGS"; 492 493 if (enc > 0 && enc < nitems(enc_name)) 494 return enc_name[enc - 1]; 495 496 snprintf(invalid, sizeof(invalid), "0x%x", enc); 497 return invalid; 498 } 499