1 /* $OpenBSD: ctfconv.c,v 1.13 2017/10/27 08:33:46 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 *); 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 location and number of symbols. */ 225 if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb) == -1) 226 warnx("symbol table not found"); 227 228 /* Find string table location and size. */ 229 if (elf_getsection(p, filesize, ELF_STRTAB, shstab, shstabsz, &strtab, 230 &strtabsz) == -1) 231 warnx("string table not found"); 232 233 /* Find abbreviation location and size. */ 234 if (elf_getsection(p, filesize, DEBUG_ABBREV, shstab, shstabsz, &abbuf, 235 &ablen) == -1) { 236 warnx("%s section not found", DEBUG_ABBREV); 237 return 1; 238 } 239 240 if (elf_getsection(p, filesize, DEBUG_INFO, shstab, shstabsz, &infobuf, 241 &infolen) == -1) { 242 warnx("%s section not found", DEBUG_INFO); 243 return 1; 244 } 245 246 /* Find string table location and size. */ 247 if (elf_getsection(p, filesize, DEBUG_STR, shstab, shstabsz, &dstrbuf, 248 &dstrlen) == -1) 249 warnx("%s section not found", DEBUG_STR); 250 251 dwarf_parse(infobuf, infolen, abbuf, ablen); 252 253 /* Sort functions */ 254 elf_sort(); 255 256 return 0; 257 } 258 259 struct itype * 260 find_symb(struct itype *tmp, size_t stroff) 261 { 262 struct itype *it; 263 char *sname, *p; 264 265 if (strtab == NULL || stroff >= strtabsz) 266 return NULL; 267 268 /* 269 * Skip local suffix 270 * 271 * FIXME: only skip local copies. 272 */ 273 sname = xstrdup(strtab + stroff); 274 if ((p = strtok(sname, ".")) == NULL) { 275 free(sname); 276 return NULL; 277 } 278 279 strlcpy(tmp->it_name, p, ITNAME_MAX); 280 free(sname); 281 it = RB_FIND(isymb_tree, &isymbt, tmp); 282 283 /* Restore original name */ 284 if (it == NULL) 285 strlcpy(tmp->it_name, (strtab + stroff), ITNAME_MAX); 286 287 return it; 288 } 289 290 void 291 elf_sort(void) 292 { 293 struct itype *it, tmp; 294 size_t i; 295 296 memset(&tmp, 0, sizeof(tmp)); 297 for (i = 0; i < nsymb; i++) { 298 const Elf_Sym *st = &symtab[i]; 299 300 if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON) 301 continue; 302 303 switch (ELF_ST_TYPE(st->st_info)) { 304 case STT_FUNC: 305 tmp.it_flags = ITF_FUNC; 306 break; 307 case STT_OBJECT: 308 tmp.it_flags = ITF_OBJ; 309 break; 310 default: 311 continue; 312 } 313 314 it = find_symb(&tmp, st->st_name); 315 if (it == NULL) { 316 /* Insert 'unknown' entry to match symbol order. */ 317 it = it_dup(&tmp); 318 it->it_refp = it; 319 #ifdef DEBUG 320 warnx("symbol not found: %s", it_name(it)); 321 #endif 322 } 323 324 if (it->it_flags & ITF_INSERTED) { 325 #ifdef DEBUG 326 warnx("%s: already inserted", it_name(it)); 327 #endif 328 it = it_dup(it); 329 } 330 331 /* Save symbol index for dump. */ 332 it->it_ref = i; 333 334 it->it_flags |= ITF_INSERTED; 335 if (it->it_flags & ITF_FUNC) 336 TAILQ_INSERT_TAIL(&ifuncq, it, it_symb); 337 else 338 TAILQ_INSERT_TAIL(&iobjq, it, it_symb); 339 } 340 } 341 342 const char * 343 type_name(struct itype *it) 344 { 345 const char *name; 346 347 name = it_name(it); 348 if (name == NULL) 349 return "(anon)"; 350 351 return name; 352 } 353 354 /* Display parsed types a la ctfdump(1) */ 355 void 356 dump_type(struct itype *it) 357 { 358 struct imember *im; 359 360 #ifdef DEBUG 361 switch (it->it_type) { 362 case CTF_K_POINTER: 363 case CTF_K_TYPEDEF: 364 case CTF_K_VOLATILE: 365 case CTF_K_CONST: 366 case CTF_K_RESTRICT: 367 case CTF_K_ARRAY: 368 case CTF_K_FUNCTION: 369 if (it->it_refp == NULL) { 370 printf("unresolved: %s type=%d\n", it_name(it), 371 it->it_type); 372 return; 373 } 374 default: 375 break; 376 } 377 #endif 378 379 switch (it->it_type) { 380 case CTF_K_FLOAT: 381 case CTF_K_INTEGER: 382 printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n", 383 it->it_idx, 384 (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT", 385 it_name(it), ctf_enc2name(it->it_enc), it->it_size); 386 break; 387 case CTF_K_POINTER: 388 printf(" <%u> POINTER %s refers to %u\n", it->it_idx, 389 type_name(it), it->it_refp->it_idx); 390 break; 391 case CTF_K_TYPEDEF: 392 printf(" <%u> TYPEDEF %s refers to %u\n", 393 it->it_idx, it_name(it), it->it_refp->it_idx); 394 break; 395 case CTF_K_VOLATILE: 396 printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx, 397 type_name(it), it->it_refp->it_idx); 398 break; 399 case CTF_K_CONST: 400 printf(" <%u> CONST %s refers to %u\n", it->it_idx, 401 type_name(it), it->it_refp->it_idx); 402 break; 403 case CTF_K_RESTRICT: 404 printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx, 405 it_name(it), it->it_refp->it_idx); 406 break; 407 case CTF_K_ARRAY: 408 printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n", 409 it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx, 410 it->it_nelems); 411 printf("\n"); 412 break; 413 case CTF_K_STRUCT: 414 case CTF_K_UNION: 415 printf(" [%u] %s %s (%u bytes)\n", it->it_idx, 416 (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION", 417 type_name(it), it->it_size); 418 TAILQ_FOREACH(im, &it->it_members, im_next) { 419 printf("\t%s type=%u off=%zd\n", 420 (im_name(im) == NULL) ? "unknown" : im_name(im), 421 im->im_refp ? im->im_refp->it_idx : 0, im->im_off); 422 } 423 printf("\n"); 424 break; 425 case CTF_K_ENUM: 426 printf(" [%u] ENUM %s\n\n", it->it_idx, type_name(it)); 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