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