1 /* $OpenBSD: ctfconv.c,v 1.7 2017/08/12 19:00:08 jasper 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/param.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/exec_elf.h> 23 #include <sys/mman.h> 24 #include <sys/queue.h> 25 #include <sys/tree.h> 26 #include <sys/ctf.h> 27 28 #include <assert.h> 29 #include <err.h> 30 #include <fcntl.h> 31 #include <locale.h> 32 #include <stdio.h> 33 #include <stdint.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "itype.h" 39 #include "xmalloc.h" 40 41 #ifndef nitems 42 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 43 #endif 44 45 #define DEBUG_ABBREV ".debug_abbrev" 46 #define DEBUG_INFO ".debug_info" 47 #define DEBUG_LINE ".debug_line" 48 #define DEBUG_STR ".debug_str" 49 50 __dead void usage(void); 51 int convert(const char *); 52 int generate(const char *, const char *, int); 53 int elf_convert(char *, size_t); 54 void elf_sort(void); 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 *, const char *, size_t, 63 const Elf_Sym **, size_t *); 64 ssize_t elf_getsection(char *, 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 return 1; 185 } 186 if ((uintmax_t)st.st_size > SIZE_MAX) { 187 warnx("file too big to fit memory"); 188 return 1; 189 } 190 191 p = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 192 if (p == MAP_FAILED) 193 err(1, "mmap"); 194 195 if (iself(p, st.st_size)) 196 error = elf_convert(p, st.st_size); 197 198 munmap(p, st.st_size); 199 close(fd); 200 201 return error; 202 } 203 204 const char *dstrbuf; 205 size_t dstrlen; 206 const char *strtab; 207 const Elf_Sym *symtab; 208 size_t strtabsz, nsymb; 209 210 int 211 elf_convert(char *p, size_t filesize) 212 { 213 const char *shstab; 214 const char *infobuf, *abbuf; 215 size_t infolen, ablen; 216 size_t shstabsz; 217 218 /* Find section header string table location and size. */ 219 if (elf_getshstab(p, filesize, &shstab, &shstabsz)) 220 return 1; 221 222 /* Find symbol table location and number of symbols. */ 223 if (elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb) == -1) 224 warnx("symbol table not found"); 225 226 /* Find string table location and size. */ 227 if (elf_getsection(p, ELF_STRTAB, shstab, shstabsz, &strtab, 228 &strtabsz) == -1) 229 warnx("string table not found"); 230 231 /* Find abbreviation location and size. */ 232 if (elf_getsection(p, DEBUG_ABBREV, shstab, shstabsz, &abbuf, 233 &ablen) == -1) { 234 warnx("%s section not found", DEBUG_ABBREV); 235 return 1; 236 } 237 238 if (elf_getsection(p, DEBUG_INFO, shstab, shstabsz, &infobuf, 239 &infolen) == -1) { 240 warnx("%s section not found", DEBUG_INFO); 241 return 1; 242 } 243 244 /* Find string table location and size. */ 245 if (elf_getsection(p, DEBUG_STR, shstab, shstabsz, &dstrbuf, 246 &dstrlen) == -1) 247 warnx("%s section not found", DEBUG_STR); 248 249 dwarf_parse(infobuf, infolen, abbuf, ablen); 250 251 /* Sort functions */ 252 elf_sort(); 253 254 return 0; 255 } 256 257 void 258 elf_sort(void) 259 { 260 struct itype *it, tmp; 261 size_t i; 262 263 memset(&tmp, 0, sizeof(tmp)); 264 for (i = 0; i < nsymb; i++) { 265 const Elf_Sym *st = &symtab[i]; 266 char *sname; 267 268 if (st->st_shndx == SHN_UNDEF || st->st_shndx == SHN_COMMON) 269 continue; 270 271 switch (ELF_ST_TYPE(st->st_info)) { 272 case STT_FUNC: 273 tmp.it_flags = ITF_FUNC; 274 break; 275 case STT_OBJECT: 276 tmp.it_flags = ITF_OBJ; 277 break; 278 default: 279 continue; 280 } 281 282 /* 283 * Skip local suffix 284 * 285 * FIXME: only skip local copies. 286 */ 287 sname = xstrdup(strtab + st->st_name); 288 strlcpy(tmp.it_name, strtok(sname, "."), ITNAME_MAX); 289 it = RB_FIND(isymb_tree, &isymbt, &tmp); 290 strlcpy(tmp.it_name, (strtab + st->st_name), ITNAME_MAX); 291 free(sname); 292 293 if (it == NULL) { 294 /* Insert 'unknown' entry to match symbol order. */ 295 it = it_dup(&tmp); 296 it->it_refp = it; 297 #ifdef DEBUG 298 warnx("symbol not found: %s", it_name(it)); 299 #endif 300 } 301 302 if (it->it_flags & ITF_INSERTED) { 303 #ifdef DEBUG 304 warnx("%s: already inserted", it_name(it)); 305 #endif 306 it = it_dup(it); 307 } 308 309 /* Save symbol index for dump. */ 310 it->it_ref = i; 311 312 it->it_flags |= ITF_INSERTED; 313 if (it->it_flags & ITF_FUNC) 314 TAILQ_INSERT_TAIL(&ifuncq, it, it_symb); 315 else 316 TAILQ_INSERT_TAIL(&iobjq, it, it_symb); 317 } 318 } 319 320 const char * 321 type_name(struct itype *it) 322 { 323 const char *name; 324 325 name = it_name(it); 326 if (name == NULL) 327 return "(anon)"; 328 329 return name; 330 } 331 332 /* Display parsed types a la ctfdump(1) */ 333 void 334 dump_type(struct itype *it) 335 { 336 struct imember *im; 337 338 #ifdef DEBUG 339 switch (it->it_type) { 340 case CTF_K_POINTER: 341 case CTF_K_TYPEDEF: 342 case CTF_K_VOLATILE: 343 case CTF_K_CONST: 344 case CTF_K_RESTRICT: 345 case CTF_K_ARRAY: 346 case CTF_K_FUNCTION: 347 if (it->it_refp == NULL) { 348 printf("unresolved: %s type=%d\n", it_name(it), 349 it->it_type); 350 return; 351 } 352 default: 353 break; 354 } 355 #endif 356 357 switch (it->it_type) { 358 case CTF_K_FLOAT: 359 case CTF_K_INTEGER: 360 printf(" [%u] %s %s encoding=%s offset=0 bits=%u\n", 361 it->it_idx, 362 (it->it_type == CTF_K_INTEGER) ? "INTEGER" : "FLOAT", 363 it_name(it), ctf_enc2name(it->it_enc), it->it_size); 364 break; 365 case CTF_K_POINTER: 366 printf(" <%u> POINTER %s refers to %u\n", it->it_idx, 367 type_name(it), it->it_refp->it_idx); 368 break; 369 case CTF_K_TYPEDEF: 370 printf(" <%u> TYPEDEF %s refers to %u\n", 371 it->it_idx, it_name(it), it->it_refp->it_idx); 372 break; 373 case CTF_K_VOLATILE: 374 printf(" <%u> VOLATILE %s refers to %u\n", it->it_idx, 375 type_name(it), it->it_refp->it_idx); 376 break; 377 case CTF_K_CONST: 378 printf(" <%u> CONST %s refers to %u\n", it->it_idx, 379 type_name(it), it->it_refp->it_idx); 380 break; 381 case CTF_K_RESTRICT: 382 printf(" <%u> RESTRICT %s refers to %u\n", it->it_idx, 383 it_name(it), it->it_refp->it_idx); 384 break; 385 case CTF_K_ARRAY: 386 printf(" [%u] ARRAY %s content: %u index: %u nelems: %u\n", 387 it->it_idx, type_name(it), it->it_refp->it_idx, long_tidx, 388 it->it_nelems); 389 printf("\n"); 390 break; 391 case CTF_K_STRUCT: 392 case CTF_K_UNION: 393 printf(" [%u] %s %s (%u bytes)\n", it->it_idx, 394 (it->it_type == CTF_K_STRUCT) ? "STRUCT" : "UNION", 395 type_name(it), it->it_size); 396 TAILQ_FOREACH(im, &it->it_members, im_next) { 397 printf("\t%s type=%u off=%zd\n", 398 (im_name(im) == NULL) ? "unknown" : im_name(im), 399 im->im_refp ? im->im_refp->it_idx : 0, im->im_off); 400 } 401 printf("\n"); 402 break; 403 case CTF_K_ENUM: 404 printf(" [%u] ENUM %s\n\n", it->it_idx, type_name(it)); 405 break; 406 case CTF_K_FUNCTION: 407 printf(" [%u] FUNCTION (%s) returns: %u args: (", 408 it->it_idx, (it_name(it) != NULL) ? it_name(it) : "anon", 409 it->it_refp->it_idx); 410 TAILQ_FOREACH(im, &it->it_members, im_next) { 411 printf("%u%s", im->im_refp->it_idx, 412 TAILQ_NEXT(im, im_next) ? ", " : ""); 413 } 414 printf(")\n"); 415 break; 416 default: 417 assert(0 == 1); 418 } 419 } 420 421 void 422 dump_func(struct itype *it, int *idx) 423 { 424 struct imember *im; 425 426 (*idx)++; 427 428 if (it->it_type == CTF_K_UNKNOWN && it->it_nelems == 0) 429 return; 430 431 printf(" [%u] FUNC (%s) returns: %u args: (", (*idx), 432 (it_name(it) != NULL) ? it_name(it) : "unknown", 433 it->it_refp->it_idx); 434 TAILQ_FOREACH(im, &it->it_members, im_next) { 435 printf("%u%s", im->im_refp->it_idx, 436 TAILQ_NEXT(im, im_next) ? ", " : ""); 437 } 438 printf(")\n"); 439 } 440 441 void 442 dump_obj(struct itype *it, int *idx) 443 { 444 int l; 445 446 (*idx)++; 447 448 l = printf(" [%u] %u", (*idx), it->it_refp->it_idx); 449 printf("%*s %s (%llu)\n", 14 - l, "", it_name(it), it->it_ref); 450 } 451 452 const char * 453 ctf_enc2name(unsigned short enc) 454 { 455 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR", 456 "BOOL", "SIGNED BOOL" }; 457 static char invalid[7]; 458 459 if (enc == CTF_INT_VARARGS) 460 return "VARARGS"; 461 462 if (enc > 0 && enc < nitems(enc_name)) 463 return enc_name[enc - 1]; 464 465 snprintf(invalid, sizeof(invalid), "0x%x", enc); 466 return invalid; 467 } 468