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 (c) 2019, Joyent, Inc. 14 */ 15 16 /* 17 * merge CTF containers 18 */ 19 20 #include <stdio.h> 21 #include <libctf.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 #include <fcntl.h> 25 #include <errno.h> 26 #include <strings.h> 27 #include <assert.h> 28 #include <unistd.h> 29 #include <sys/fcntl.h> 30 #include <stdlib.h> 31 #include <libelf.h> 32 #include <gelf.h> 33 #include <sys/mman.h> 34 #include <libgen.h> 35 #include <stdarg.h> 36 #include <limits.h> 37 38 static char *g_progname; 39 static char *g_unique; 40 static char *g_outfile; 41 static uint_t g_nctf; 42 43 #define CTFMERGE_OK 0 44 #define CTFMERGE_FATAL 1 45 #define CTFMERGE_USAGE 2 46 47 #define CTFMERGE_DEFAULT_NTHREADS 8 48 #define CTFMERGE_ALTEXEC "CTFMERGE_ALTEXEC" 49 50 static void 51 ctfmerge_fatal(const char *fmt, ...) 52 { 53 va_list ap; 54 55 (void) fprintf(stderr, "%s: ", g_progname); 56 va_start(ap, fmt); 57 (void) vfprintf(stderr, fmt, ap); 58 va_end(ap); 59 60 if (g_outfile != NULL) 61 (void) unlink(g_outfile); 62 63 exit(CTFMERGE_FATAL); 64 } 65 66 /* 67 * We failed to find CTF for this file, check if it's OK. If we're not derived 68 * from C, or we have the -m option, we let missing CTF pass. 69 */ 70 static void 71 ctfmerge_check_for_c(const char *name, Elf *elf, uint_t flags) 72 { 73 char errmsg[1024]; 74 75 if (flags & CTF_ALLOW_MISSING_DEBUG) 76 return; 77 78 switch (ctf_has_c_source(elf, errmsg, sizeof (errmsg))) { 79 case CHR_ERROR: 80 ctfmerge_fatal("failed to open %s: %s\n", name, errmsg); 81 break; 82 83 case CHR_NO_C_SOURCE: 84 return; 85 86 default: 87 ctfmerge_fatal("failed to open %s: %s\n", name, 88 ctf_errmsg(ECTF_NOCTFDATA)); 89 break; 90 } 91 } 92 93 /* 94 * Go through and construct enough information for this Elf Object to try and do 95 * a ctf_bufopen(). 96 */ 97 static int 98 ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh, uint_t flags) 99 { 100 GElf_Ehdr ehdr; 101 GElf_Shdr shdr; 102 Elf_Scn *scn; 103 Elf_Data *ctf_data, *str_data, *sym_data; 104 ctf_sect_t ctfsect, symsect, strsect; 105 ctf_file_t *fp; 106 int err; 107 108 if (gelf_getehdr(elf, &ehdr) == NULL) 109 ctfmerge_fatal("failed to get ELF header for %s: %s\n", 110 name, elf_errmsg(elf_errno())); 111 112 bzero(&ctfsect, sizeof (ctf_sect_t)); 113 bzero(&symsect, sizeof (ctf_sect_t)); 114 bzero(&strsect, sizeof (ctf_sect_t)); 115 116 scn = NULL; 117 while ((scn = elf_nextscn(elf, scn)) != NULL) { 118 const char *sname; 119 120 if (gelf_getshdr(scn, &shdr) == NULL) 121 ctfmerge_fatal("failed to get section header for " 122 "file %s: %s\n", name, elf_errmsg(elf_errno())); 123 124 sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); 125 if (shdr.sh_type == SHT_PROGBITS && 126 strcmp(sname, ".SUNW_ctf") == 0) { 127 ctfsect.cts_name = sname; 128 ctfsect.cts_type = shdr.sh_type; 129 ctfsect.cts_flags = shdr.sh_flags; 130 ctfsect.cts_size = shdr.sh_size; 131 ctfsect.cts_entsize = shdr.sh_entsize; 132 ctfsect.cts_offset = (off64_t)shdr.sh_offset; 133 134 ctf_data = elf_getdata(scn, NULL); 135 if (ctf_data == NULL) 136 ctfmerge_fatal("failed to get ELF CTF " 137 "data section for %s: %s\n", name, 138 elf_errmsg(elf_errno())); 139 ctfsect.cts_data = ctf_data->d_buf; 140 } else if (shdr.sh_type == SHT_SYMTAB) { 141 Elf_Scn *strscn; 142 GElf_Shdr strhdr; 143 144 symsect.cts_name = sname; 145 symsect.cts_type = shdr.sh_type; 146 symsect.cts_flags = shdr.sh_flags; 147 symsect.cts_size = shdr.sh_size; 148 symsect.cts_entsize = shdr.sh_entsize; 149 symsect.cts_offset = (off64_t)shdr.sh_offset; 150 151 if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL || 152 gelf_getshdr(strscn, &strhdr) == NULL) 153 ctfmerge_fatal("failed to get " 154 "string table for file %s: %s\n", name, 155 elf_errmsg(elf_errno())); 156 157 strsect.cts_name = elf_strptr(elf, ehdr.e_shstrndx, 158 strhdr.sh_name); 159 strsect.cts_type = strhdr.sh_type; 160 strsect.cts_flags = strhdr.sh_flags; 161 strsect.cts_size = strhdr.sh_size; 162 strsect.cts_entsize = strhdr.sh_entsize; 163 strsect.cts_offset = (off64_t)strhdr.sh_offset; 164 165 sym_data = elf_getdata(scn, NULL); 166 if (sym_data == NULL) 167 ctfmerge_fatal("failed to get ELF CTF " 168 "data section for %s: %s\n", name, 169 elf_errmsg(elf_errno())); 170 symsect.cts_data = sym_data->d_buf; 171 172 str_data = elf_getdata(strscn, NULL); 173 if (str_data == NULL) 174 ctfmerge_fatal("failed to get ELF CTF " 175 "data section for %s: %s\n", name, 176 elf_errmsg(elf_errno())); 177 strsect.cts_data = str_data->d_buf; 178 } 179 } 180 181 if (ctfsect.cts_type == SHT_NULL) { 182 ctfmerge_check_for_c(name, elf, flags); 183 return (ENOENT); 184 } 185 186 if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) { 187 fp = ctf_bufopen(&ctfsect, &symsect, &strsect, &err); 188 } else { 189 fp = ctf_bufopen(&ctfsect, NULL, NULL, &err); 190 } 191 192 if (fp == NULL) { 193 ctfmerge_fatal("failed to open file %s: %s\n", 194 name, ctf_errmsg(err)); 195 } 196 197 if ((err = ctf_merge_add(cmh, fp)) != 0) { 198 ctfmerge_fatal("failed to add input %s: %s\n", 199 name, ctf_errmsg(err)); 200 } 201 202 g_nctf++; 203 return (0); 204 } 205 206 static void 207 ctfmerge_read_archive(const char *name, int fd, Elf *elf, 208 ctf_merge_t *cmh, uint_t flags) 209 { 210 Elf_Cmd cmd = ELF_C_READ; 211 int cursec = 1; 212 Elf *aelf; 213 214 while ((aelf = elf_begin(fd, cmd, elf)) != NULL) { 215 char *nname = NULL; 216 Elf_Arhdr *arhdr; 217 218 if ((arhdr = elf_getarhdr(aelf)) == NULL) 219 ctfmerge_fatal("failed to get archive header %d for " 220 "%s: %s\n", cursec, name, elf_errmsg(elf_errno())); 221 222 cmd = elf_next(aelf); 223 224 if (*(arhdr->ar_name) == '/') 225 goto next; 226 227 if (asprintf(&nname, "%s.%s.%d", name, arhdr->ar_name, 228 cursec) < 0) 229 ctfmerge_fatal("failed to allocate memory for archive " 230 "%d of file %s\n", cursec, name); 231 232 switch (elf_kind(aelf)) { 233 case ELF_K_AR: 234 ctfmerge_read_archive(nname, fd, aelf, cmh, flags); 235 break; 236 case ELF_K_ELF: 237 /* ctfmerge_elfopen() takes ownership of aelf. */ 238 if (ctfmerge_elfopen(nname, aelf, cmh, flags) == 0) 239 aelf = NULL; 240 break; 241 default: 242 ctfmerge_fatal("unknown elf kind (%d) in archive %d " 243 "for %s\n", elf_kind(aelf), cursec, name); 244 break; 245 } 246 247 next: 248 (void) elf_end(aelf); 249 free(nname); 250 cursec++; 251 } 252 } 253 254 static void 255 ctfmerge_file_add(ctf_merge_t *cmh, const char *file, uint_t flags) 256 { 257 Elf *e; 258 int fd; 259 260 if ((fd = open(file, O_RDONLY)) < 0) { 261 ctfmerge_fatal("failed to open file %s: %s\n", 262 file, strerror(errno)); 263 } 264 265 if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 266 (void) close(fd); 267 ctfmerge_fatal("failed to open %s: %s\n", 268 file, elf_errmsg(elf_errno())); 269 } 270 271 switch (elf_kind(e)) { 272 case ELF_K_AR: 273 ctfmerge_read_archive(file, fd, e, cmh, flags); 274 break; 275 276 case ELF_K_ELF: 277 /* ctfmerge_elfopen() takes ownership of e. */ 278 if (ctfmerge_elfopen(file, e, cmh, flags) == 0) 279 e = NULL; 280 break; 281 282 default: 283 ctfmerge_fatal("unknown elf kind (%d) for %s\n", 284 elf_kind(e), file); 285 } 286 287 (void) elf_end(e); 288 (void) close(fd); 289 } 290 291 static void 292 ctfmerge_usage(const char *fmt, ...) 293 { 294 if (fmt != NULL) { 295 va_list ap; 296 297 (void) fprintf(stderr, "%s: ", g_progname); 298 va_start(ap, fmt); 299 (void) vfprintf(stderr, fmt, ap); 300 va_end(ap); 301 } 302 303 (void) fprintf(stderr, "Usage: %s [-m] [-d uniqfile] [-l label] " 304 "[-L labelenv] [-j nthrs] -o outfile file ...\n" 305 "\n" 306 "\t-d uniquify merged output against uniqfile\n" 307 "\t-j use nthrs threads to perform the merge\n" 308 "\t-l set output container's label to specified value\n" 309 "\t-L set output container's label to value from environment\n" 310 "\t-m allow C-based input files to not have CTF\n" 311 "\t-o file to add CTF data to\n", 312 g_progname); 313 } 314 315 static void 316 ctfmerge_altexec(char **argv) 317 { 318 const char *alt; 319 char *altexec; 320 321 alt = getenv(CTFMERGE_ALTEXEC); 322 if (alt == NULL || *alt == '\0') 323 return; 324 325 altexec = strdup(alt); 326 if (altexec == NULL) 327 ctfmerge_fatal("failed to allocate memory for altexec\n"); 328 if (unsetenv(CTFMERGE_ALTEXEC) != 0) 329 ctfmerge_fatal("failed to unset %s from environment: %s\n", 330 CTFMERGE_ALTEXEC, strerror(errno)); 331 332 (void) execv(altexec, argv); 333 ctfmerge_fatal("failed to execute alternate program %s: %s", 334 altexec, strerror(errno)); 335 } 336 337 int 338 main(int argc, char *argv[]) 339 { 340 int err, i, c, ofd; 341 uint_t nthreads = CTFMERGE_DEFAULT_NTHREADS; 342 char *tmpfile = NULL, *label = NULL; 343 int wflags = CTF_ELFWRITE_F_COMPRESS; 344 uint_t flags = 0; 345 ctf_merge_t *cmh; 346 ctf_file_t *ofp; 347 long argj; 348 char *eptr; 349 350 g_progname = basename(argv[0]); 351 352 ctfmerge_altexec(argv); 353 354 /* 355 * We support a subset of the old CTF merge flags, mostly for 356 * compatibility. 357 */ 358 while ((c = getopt(argc, argv, ":d:fgj:l:L:mo:t")) != -1) { 359 switch (c) { 360 case 'd': 361 g_unique = optarg; 362 break; 363 case 'f': 364 /* Silently ignored for compatibility */ 365 break; 366 case 'g': 367 /* Silently ignored for compatibility */ 368 break; 369 case 'j': 370 errno = 0; 371 argj = strtol(optarg, &eptr, 10); 372 if (errno != 0 || argj == LONG_MAX || 373 argj > 1024 || *eptr != '\0') { 374 ctfmerge_fatal("invalid argument for -j: %s\n", 375 optarg); 376 } 377 nthreads = (uint_t)argj; 378 break; 379 case 'l': 380 label = optarg; 381 break; 382 case 'L': 383 label = getenv(optarg); 384 break; 385 case 'm': 386 flags |= CTF_ALLOW_MISSING_DEBUG; 387 break; 388 case 'o': 389 g_outfile = optarg; 390 break; 391 case 't': 392 /* Silently ignored for compatibility */ 393 break; 394 case ':': 395 ctfmerge_usage("Option -%c requires an operand\n", 396 optopt); 397 return (CTFMERGE_USAGE); 398 case '?': 399 ctfmerge_usage("Unknown option: -%c\n", optopt); 400 return (CTFMERGE_USAGE); 401 } 402 } 403 404 if (g_outfile == NULL) { 405 ctfmerge_usage("missing required -o output file\n"); 406 return (CTFMERGE_USAGE); 407 } 408 409 (void) elf_version(EV_CURRENT); 410 411 /* 412 * Obviously this isn't atomic, but at least gives us a good starting 413 * point. 414 */ 415 if ((ofd = open(g_outfile, O_RDWR)) < 0) 416 ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile, 417 strerror(errno)); 418 419 argc -= optind; 420 argv += optind; 421 422 if (argc < 1) { 423 ctfmerge_usage("no input files specified"); 424 return (CTFMERGE_USAGE); 425 } 426 427 cmh = ctf_merge_init(ofd, &err); 428 if (cmh == NULL) 429 ctfmerge_fatal("failed to create merge handle: %s\n", 430 ctf_errmsg(err)); 431 432 if ((err = ctf_merge_set_nthreads(cmh, nthreads)) != 0) 433 ctfmerge_fatal("failed to set parallelism to %u: %s\n", 434 nthreads, ctf_errmsg(err)); 435 436 for (i = 0; i < argc; i++) { 437 ctfmerge_file_add(cmh, argv[i], flags); 438 } 439 440 if (g_nctf == 0) { 441 ctf_merge_fini(cmh); 442 return (0); 443 } 444 445 if (g_unique != NULL) { 446 ctf_file_t *ufp; 447 char *base; 448 449 ufp = ctf_open(g_unique, &err); 450 if (ufp == NULL) { 451 ctfmerge_fatal("failed to open uniquify file %s: %s\n", 452 g_unique, ctf_errmsg(err)); 453 } 454 455 base = basename(g_unique); 456 (void) ctf_merge_uniquify(cmh, ufp, base); 457 } 458 459 if (label != NULL) { 460 if ((err = ctf_merge_label(cmh, label)) != 0) 461 ctfmerge_fatal("failed to add label %s: %s\n", label, 462 ctf_errmsg(err)); 463 } 464 465 err = ctf_merge_merge(cmh, &ofp); 466 if (err != 0) 467 ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err)); 468 ctf_merge_fini(cmh); 469 470 if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1) 471 ctfmerge_fatal("ran out of memory for temporary file name\n"); 472 err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags); 473 if (err == CTF_ERR) { 474 (void) unlink(tmpfile); 475 free(tmpfile); 476 ctfmerge_fatal("encountered a libctf error: %s!\n", 477 ctf_errmsg(ctf_errno(ofp))); 478 } 479 480 if (rename(tmpfile, g_outfile) != 0) { 481 (void) unlink(tmpfile); 482 free(tmpfile); 483 ctfmerge_fatal("failed to rename temporary file: %s\n", 484 strerror(errno)); 485 } 486 free(tmpfile); 487 488 return (CTFMERGE_OK); 489 }