1 /* $OpenBSD: ctfconv.c,v 1.16 2017/11/06 14:59:27 mpi 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 if (pledge("stdio rpath wpath cpath", NULL) == -1) 96 err(1, "pledge"); 97 98 while ((ch = getopt(argc, argv, "dl:o:")) != -1) { 99 switch (ch) { 100 case 'd': 101 dump = 1; /* ctfdump(1)-like SUNW_ctf sections */ 102 break; 103 case 'l': 104 if (label != NULL) 105 usage(); 106 label = optarg; 107 break; 108 case 'o': 109 if (outfile != NULL) 110 usage(); 111 outfile = optarg; 112 break; 113 default: 114 usage(); 115 } 116 } 117 118 argc -= optind; 119 argv += optind; 120 121 if (argc != 1) 122 usage(); 123 124 /* Either dump the sections, or write it out. */ 125 if ((dump && (outfile != NULL || label != NULL)) || 126 (!dump && (outfile == NULL || label == NULL))) 127 usage(); 128 129 filename = *argv; 130 error = convert(filename); 131 if (error != 0) 132 return error; 133 134 if (outfile != NULL) { 135 if (pledge("stdio wpath cpath", NULL) == -1) 136 err(1, "pledge"); 137 138 error = generate(outfile, label, 1); 139 if (error != 0) 140 return error; 141 } 142 143 if (dump) { 144 if (pledge("stdio", NULL) == -1) 145 err(1, "pledge"); 146 147 int fidx = -1, oidx = -1; 148 149 TAILQ_FOREACH(it, &iobjq, it_symb) 150 dump_obj(it, &oidx); 151 printf("\n"); 152 153 TAILQ_FOREACH(it, &ifuncq, it_symb) 154 dump_func(it, &fidx); 155 printf("\n"); 156 157 TAILQ_FOREACH(it, &itypeq, it_next) { 158 if (it->it_flags & (ITF_FUNC|ITF_OBJ)) 159 continue; 160 161 dump_type(it); 162 } 163 164 return 0; 165 } 166 167 return 0; 168 } 169 170 int 171 convert(const char *path) 172 { 173 struct stat st; 174 int fd, error = 1; 175 char *p; 176 177 fd = open(path, O_RDONLY); 178 if (fd == -1) { 179 warn("open %s", path); 180 return 1; 181 } 182 if (fstat(fd, &st) == -1) { 183 warn("fstat %s", path); 184 close(fd); 185 return 1; 186 } 187 if ((uintmax_t)st.st_size > SIZE_MAX) { 188 warnx("file too big to fit memory"); 189 close(fd); 190 return 1; 191 } 192 193 p = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 194 if (p == MAP_FAILED) 195 err(1, "mmap"); 196 197 if (iself(p, st.st_size)) 198 error = elf_convert(p, st.st_size); 199 200 munmap(p, st.st_size); 201 close(fd); 202 203 return error; 204 } 205 206 const char *dstrbuf; 207 size_t dstrlen; 208 const char *strtab; 209 const Elf_Sym *symtab; 210 size_t strtabsz, nsymb; 211 212 int 213 elf_convert(char *p, size_t filesize) 214 { 215 const char *shstab; 216 const char *infobuf, *abbuf; 217 size_t infolen, ablen; 218 size_t shstabsz; 219 220 /* Find section header string table location and size. */ 221 if (elf_getshstab(p, filesize, &shstab, &shstabsz)) 222 return 1; 223 224 /* Find symbol table and associated string table. */ 225 if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb, 226 &strtab, &strtabsz) == -1) 227 warnx("symbol table not found"); 228 229 /* Find abbreviation location and size. */ 230 if (elf_getsection(p, filesize, DEBUG_ABBREV, shstab, shstabsz, &abbuf, 231 &ablen) == -1) { 232 warnx("%s section not found", DEBUG_ABBREV); 233 return 1; 234 } 235 236 if (elf_getsection(p, filesize, DEBUG_INFO, shstab, shstabsz, &infobuf, 237 &infolen) == -1) { 238 warnx("%s section not found", DEBUG_INFO); 239 return 1; 240 } 241 242 /* Find string table location and size. */ 243 if (elf_getsection(p, filesize, DEBUG_STR, shstab, shstabsz, &dstrbuf, 244 &dstrlen) == -1) 245 warnx("%s section not found", DEBUG_STR); 246 247 dwarf_parse(infobuf, infolen, abbuf, ablen); 248 249 /* Sort functions */ 250 elf_sort(); 251 252 return 0; 253 } 254 255 struct itype * 256 find_symb(struct itype *tmp, size_t stroff) 257 { 258 struct itype *it; 259 char *sname, *p; 260 261 if (strtab == NULL || stroff >= strtabsz) 262 return NULL; 263 264 /* 265 * Skip local suffix 266 * 267 * FIXME: only skip local copies. 268 */ 269 sname = xstrdup(strtab + stroff); 270 if ((p = strtok(sname, ".")) == NULL) { 271 free(sname); 272 return NULL; 273 } 274 275 strlcpy(tmp->it_name, p, ITNAME_MAX); 276 free(sname); 277 it = RB_FIND(isymb_tree, &isymbt, tmp); 278 279 /* Restore original name */ 280 if (it == NULL) 281 strlcpy(tmp->it_name, (strtab + stroff), ITNAME_MAX); 282 283 return it; 284 } 285 286 void 287 elf_sort(void) 288 { 289 struct itype *it, tmp; 290 size_t i; 291 292 memset(&tmp, 0, sizeof(tmp)); 293 for (i = 0; i < nsymb; i++) { 294 const Elf_Sym *st = &symtab[i]; 295 296 if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON) 297 continue; 298 299 switch (ELF_ST_TYPE(st->st_info)) { 300 case STT_FUNC: 301 tmp.it_flags = ITF_FUNC; 302 break; 303 case STT_OBJECT: 304 tmp.it_flags = ITF_OBJ; 305 break; 306 default: 307 continue; 308 } 309 310 it = find_symb(&tmp, st->st_name); 311 if (it == NULL) { 312 /* Insert 'unknown' entry to match symbol order. */ 313 it = it_dup(&tmp); 314 it->it_refp = it; 315 #ifdef DEBUG 316 warnx("symbol not found: %s", it_name(it)); 317 #endif 318 } 319 320 if (it->it_flags & ITF_INSERTED) { 321 #ifdef DEBUG 322 warnx("%s: already inserted", it_name(it)); 323 #endif 324 it = it_dup(it); 325 } 326 327 /* Save symbol index for dump. */ 328 it->it_ref = i; 329 330 it->it_flags |= ITF_INSERTED; 331 if (it->it_flags & ITF_FUNC) 332 TAILQ_INSERT_TAIL(&ifuncq, it, it_symb); 333 else 334 TAILQ_INSERT_TAIL(&iobjq, it, it_symb); 335 } 336 } 337 338 const char * 339 type_name(struct itype *it) 340 { 341 const char *name; 342 343 name = it_name(it); 344 if (name == NULL) 345 return "(anon)"; 346 347 return name; 348 } 349 350 /* Display parsed types a la ctfdump(1) */ 351 void 352 dump_type(struct itype *it) 353 { 354 struct imember *im; 355 356 #ifdef DEBUG 357 switch (it->it_type) { 358 case CTF_K_POINTER: 359 case CTF_K_TYPEDEF: 360 case CTF_K_VOLATILE: 361 case CTF_K_CONST: 362 case CTF_K_RESTRICT: 363 case CTF_K_ARRAY: 364 case CTF_K_FUNCTION: 365 if (it->it_refp == NULL) { 366 printf("unresolved: %s type=%d\n", it_name(it), 367 it->it_type); 368 return; 369 } 370 default: 371 break; 372 } 373 #endif 374 375 switch (it->it_type) { 376 case CTF_K_FLOAT: 377 case CTF_K_INTEGER: 378 printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n", 379 it->it_idx, 380 (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT", 381 it_name(it), ctf_enc2name(it->it_enc), it->it_size); 382 break; 383 case CTF_K_POINTER: 384 printf(" <%u> POINTER %s refers to %u\n", it->it_idx, 385 type_name(it), it->it_refp->it_idx); 386 break; 387 case CTF_K_TYPEDEF: 388 printf(" <%u> TYPEDEF %s refers to %u\n", 389 it->it_idx, it_name(it), it->it_refp->it_idx); 390 break; 391 case CTF_K_VOLATILE: 392 printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx, 393 type_name(it), it->it_refp->it_idx); 394 break; 395 case CTF_K_CONST: 396 printf(" <%u> CONST %s refers to %u\n", it->it_idx, 397 type_name(it), it->it_refp->it_idx); 398 break; 399 case CTF_K_RESTRICT: 400 printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx, 401 it_name(it), it->it_refp->it_idx); 402 break; 403 case CTF_K_ARRAY: 404 printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n", 405 it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx, 406 it->it_nelems); 407 printf("\n"); 408 break; 409 case CTF_K_STRUCT: 410 case CTF_K_UNION: 411 printf(" [%u] %s %s (%u bytes)\n", it->it_idx, 412 (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION", 413 type_name(it), it->it_size); 414 TAILQ_FOREACH(im, &it->it_members, im_next) { 415 printf("\t%s type=%u off=%zu\n", 416 (im_name(im) == NULL) ? "unknown" : im_name(im), 417 im->im_refp ? im->im_refp->it_idx : 0, im->im_off); 418 } 419 printf("\n"); 420 break; 421 case CTF_K_ENUM: 422 printf(" [%u] ENUM %s\n", it->it_idx, type_name(it)); 423 TAILQ_FOREACH(im, &it->it_members, im_next) { 424 printf("\t%s = %zu\n", im_name(im), im->im_ref); 425 } 426 printf("\n"); 427 break; 428 case CTF_K_FUNCTION: 429 printf(" [%u] FUNCTION (%s) returns: %u args: (", 430 it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon", 431 it->it_refp->it_idx); 432 TAILQ_FOREACH(im, &it->it_members, im_next) { 433 printf("%u%s", im->im_refp->it_idx, 434 TAILQ_NEXT(im, im_next) ? ", " : ""); 435 } 436 printf(")\n"); 437 break; 438 default: 439 assert(0 == 1); 440 } 441 } 442 443 void 444 dump_func(struct itype *it, int *idx) 445 { 446 struct imember *im; 447 448 (*idx)++; 449 450 if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0) 451 return; 452 453 printf(" [%u] FUNC (%s) returns: %u args: (", (*idx), 454 (it_name(it) != NULL) ? it_name(it) : "unknown", 455 it->it_refp->it_idx); 456 TAILQ_FOREACH(im, &it->it_members, im_next) { 457 printf("%u%s", im->im_refp->it_idx, 458 TAILQ_NEXT(im, im_next) ? ", " : ""); 459 } 460 printf(")\n"); 461 } 462 463 void 464 dump_obj(struct itype *it, int *idx) 465 { 466 int l; 467 468 (*idx)++; 469 470 l = printf(" [%u] %u", (*idx), it->it_refp->it_idx); 471 printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref); 472 } 473 474 const char * 475 ctf_enc2name(unsigned short enc) 476 { 477 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR", 478 "BOOL", "SIGNED BOOL" }; 479 static char invalid[7]; 480 481 if (enc == CTF_INT_VARARGS) 482 return "VARARGS"; 483 484 if (enc > 0 && enc < nitems(enc_name)) 485 return enc_name[enc - 1]; 486 487 snprintf(invalid, sizeof(invalid), "0x%x", enc); 488 return invalid; 489 } 490