Unleashed v1.4
[unleashed.git] / bin / ctfmerge / ctfmerge.c
blob2b83f2b135787480c4452b0d4f4dbe5469b4c6d9
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.
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.
13 * Copyright (c) 2015, Joyent, Inc.
17 * merge CTF containers
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>
38 static char *g_progname;
39 static char *g_unique;
40 static char *g_outfile;
41 static boolean_t g_req;
42 static uint_t g_nctf;
44 #define CTFMERGE_OK 0
45 #define CTFMERGE_FATAL 1
46 #define CTFMERGE_USAGE 2
48 #define CTFMERGE_DEFAULT_NTHREADS 8
50 static void
51 ctfmerge_fatal(const char *fmt, ...)
53 va_list ap;
55 (void) fprintf(stderr, "%s: ", g_progname);
56 va_start(ap, fmt);
57 (void) vfprintf(stderr, fmt, ap);
58 va_end(ap);
60 if (g_outfile != NULL)
61 (void) unlink(g_outfile);
63 exit(CTFMERGE_FATAL);
66 static boolean_t
67 ctfmerge_expect_ctf(const char *name, Elf *elf)
69 Elf_Scn *scn, *strscn;
70 Elf_Data *data, *strdata;
71 GElf_Shdr shdr;
72 ulong_t i;
74 if (g_req == B_FALSE)
75 return (B_FALSE);
77 scn = NULL;
78 while ((scn = elf_nextscn(elf, scn)) != NULL) {
79 if (gelf_getshdr(scn, &shdr) == NULL) {
80 ctfmerge_fatal("failed to get section header for file "
81 "%s: %s\n", name, elf_errmsg(elf_errno()));
84 if (shdr.sh_type == SHT_SYMTAB)
85 break;
88 if (scn == NULL)
89 return (B_FALSE);
91 if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL)
92 ctfmerge_fatal("failed to get section header for file %s: %s\n",
93 name, elf_errmsg(elf_errno()));
95 if ((data = elf_getdata(scn, NULL)) == NULL)
96 ctfmerge_fatal("failed to read symbol table for %s: %s\n",
97 name, elf_errmsg(elf_errno()));
99 if ((strdata = elf_getdata(strscn, NULL)) == NULL)
100 ctfmerge_fatal("failed to read string table for %s: %s\n",
101 name, elf_errmsg(elf_errno()));
103 for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
104 GElf_Sym sym;
105 const char *file;
106 size_t len;
108 if (gelf_getsym(data, i, &sym) == NULL)
109 ctfmerge_fatal("failed to read symbol table entry %d "
110 "for %s: %s\n", i, name, elf_errmsg(elf_errno()));
112 if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
113 continue;
115 file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
116 len = strlen(file);
117 if (len < 2 || name[len - 2] != '.')
118 continue;
120 if (name[len - 1] == 'c')
121 return (B_TRUE);
124 return (B_FALSE);
128 * Go through and construct enough information for this Elf Object to try and do
129 * a ctf_bufopen().
131 static void
132 ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh)
134 GElf_Ehdr ehdr;
135 GElf_Shdr shdr;
136 Elf_Scn *scn;
137 Elf_Data *ctf_data, *str_data, *sym_data;
138 ctf_sect_t ctfsect, symsect, strsect;
139 ctf_file_t *fp;
140 int err;
142 if (gelf_getehdr(elf, &ehdr) == NULL)
143 ctfmerge_fatal("failed to get ELF header for %s: %s\n",
144 name, elf_errmsg(elf_errno()));
146 bzero(&ctfsect, sizeof (ctf_sect_t));
147 bzero(&symsect, sizeof (ctf_sect_t));
148 bzero(&strsect, sizeof (ctf_sect_t));
150 scn = NULL;
151 while ((scn = elf_nextscn(elf, scn)) != NULL) {
152 const char *sname;
154 if (gelf_getshdr(scn, &shdr) == NULL)
155 ctfmerge_fatal("failed to get section header for "
156 "file %s: %s\n", name, elf_errmsg(elf_errno()));
158 sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
159 if (shdr.sh_type == SHT_PROGBITS &&
160 strcmp(sname, ".SUNW_ctf") == 0) {
161 ctfsect.cts_name = sname;
162 ctfsect.cts_type = shdr.sh_type;
163 ctfsect.cts_flags = shdr.sh_flags;
164 ctfsect.cts_size = shdr.sh_size;
165 ctfsect.cts_entsize = shdr.sh_entsize;
166 ctfsect.cts_offset = (off64_t)shdr.sh_offset;
168 ctf_data = elf_getdata(scn, NULL);
169 if (ctf_data == NULL)
170 ctfmerge_fatal("failed to get ELF CTF "
171 "data section for %s: %s\n", name,
172 elf_errmsg(elf_errno()));
173 ctfsect.cts_data = ctf_data->d_buf;
174 } else if (shdr.sh_type == SHT_SYMTAB) {
175 Elf_Scn *strscn;
176 GElf_Shdr strhdr;
178 symsect.cts_name = sname;
179 symsect.cts_type = shdr.sh_type;
180 symsect.cts_flags = shdr.sh_flags;
181 symsect.cts_size = shdr.sh_size;
182 symsect.cts_entsize = shdr.sh_entsize;
183 symsect.cts_offset = (off64_t)shdr.sh_offset;
185 if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL ||
186 gelf_getshdr(strscn, &strhdr) == NULL)
187 ctfmerge_fatal("failed to get "
188 "string table for file %s: %s\n", name,
189 elf_errmsg(elf_errno()));
191 strsect.cts_name = elf_strptr(elf, ehdr.e_shstrndx,
192 strhdr.sh_name);
193 strsect.cts_type = strhdr.sh_type;
194 strsect.cts_flags = strhdr.sh_flags;
195 strsect.cts_size = strhdr.sh_size;
196 strsect.cts_entsize = strhdr.sh_entsize;
197 strsect.cts_offset = (off64_t)strhdr.sh_offset;
199 sym_data = elf_getdata(scn, NULL);
200 if (sym_data == NULL)
201 ctfmerge_fatal("failed to get ELF CTF "
202 "data section for %s: %s\n", name,
203 elf_errmsg(elf_errno()));
204 symsect.cts_data = sym_data->d_buf;
206 str_data = elf_getdata(strscn, NULL);
207 if (str_data == NULL)
208 ctfmerge_fatal("failed to get ELF CTF "
209 "data section for %s: %s\n", name,
210 elf_errmsg(elf_errno()));
211 strsect.cts_data = str_data->d_buf;
215 if (ctfsect.cts_type == SHT_NULL) {
216 if (ctfmerge_expect_ctf(name, elf) == B_FALSE)
217 return;
218 ctfmerge_fatal("failed to open %s: %s\n", name,
219 ctf_errmsg(ECTF_NOCTFDATA));
222 if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) {
223 fp = ctf_bufopen(&ctfsect, &symsect, &strsect, &err);
224 } else {
225 fp = ctf_bufopen(&ctfsect, NULL, NULL, &err);
228 if (fp == NULL) {
229 if (ctfmerge_expect_ctf(name, elf) == B_TRUE) {
230 ctfmerge_fatal("failed to open file %s: %s\n",
231 name, ctf_errmsg(err));
233 } else {
234 if ((err = ctf_merge_add(cmh, fp)) != 0) {
235 ctfmerge_fatal("failed to add input %s: %s\n",
236 name, ctf_errmsg(err));
238 g_nctf++;
242 static void
243 ctfmerge_read_archive(const char *name, int fd, Elf *elf,
244 ctf_merge_t *cmh)
246 Elf *aelf;
247 Elf_Cmd cmd = ELF_C_READ;
248 int cursec = 1;
249 char *nname;
251 while ((aelf = elf_begin(fd, cmd, elf)) != NULL) {
252 Elf_Arhdr *arhdr;
253 boolean_t leakelf = B_FALSE;
255 if ((arhdr = elf_getarhdr(aelf)) == NULL)
256 ctfmerge_fatal("failed to get archive header %d for "
257 "%s: %s\n", cursec, name, elf_errmsg(elf_errno()));
259 if (*(arhdr->ar_name) == '/')
260 goto next;
262 if (asprintf(&nname, "%s.%s.%d", name, arhdr->ar_name,
263 cursec) < 0)
264 ctfmerge_fatal("failed to allocate memory for archive "
265 "%d of file %s\n", cursec, name);
267 switch (elf_kind(aelf)) {
268 case ELF_K_AR:
269 ctfmerge_read_archive(nname, fd, aelf, cmh);
270 free(nname);
271 break;
272 case ELF_K_ELF:
273 ctfmerge_elfopen(nname, aelf, cmh);
274 free(nname);
275 leakelf = B_TRUE;
276 break;
277 default:
278 ctfmerge_fatal("unknown elf kind (%d) in archive %d "
279 "for %s\n", elf_kind(aelf), cursec, name);
282 next:
283 cmd = elf_next(aelf);
284 if (leakelf == B_FALSE)
285 (void) elf_end(aelf);
286 cursec++;
290 static void
291 ctfmerge_usage(const char *fmt, ...)
293 if (fmt != NULL) {
294 va_list ap;
296 (void) fprintf(stderr, "%s: ", g_progname);
297 va_start(ap, fmt);
298 (void) vfprintf(stderr, fmt, ap);
299 va_end(ap);
302 (void) fprintf(stderr, "Usage: %s [-gt] [-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-g do not remove source debug information (STABS, DWARF)\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-o file to add CTF data to\n"
311 "\t-t require CTF data from all inputs built from C sources\n",
312 g_progname);
316 main(int argc, char *argv[])
318 int err, i, c, ofd;
319 uint_t nthreads = CTFMERGE_DEFAULT_NTHREADS;
320 char *tmpfile = NULL, *label = NULL;
321 int wflags = CTF_ELFWRITE_F_COMPRESS;
322 ctf_file_t *ofp;
323 ctf_merge_t *cmh;
324 long argj;
325 char *eptr;
327 g_progname = basename(argv[0]);
330 * We support a subset of the old CTF merge flags, mostly for
331 * compatability.
333 while ((c = getopt(argc, argv, ":d:fgj:L:o: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 == LONG_MIN || argj <= 0 ||
349 argj > UINT_MAX || *eptr != '\0') {
350 ctfmerge_fatal("invalid argument for -j: %s\n",
351 optarg);
353 nthreads = (uint_t)argj;
354 break;
355 case 'l':
356 label = optarg;
357 break;
358 case 'L':
359 label = getenv(optarg);
360 break;
361 case 'o':
362 g_outfile = optarg;
363 break;
364 case 't':
365 g_req = B_TRUE;
366 break;
367 case ':':
368 ctfmerge_usage("Option -%c requires an operand\n",
369 optopt);
370 return (CTFMERGE_USAGE);
371 case '?':
372 ctfmerge_usage("Unknown option: -%c\n", optopt);
373 return (CTFMERGE_USAGE);
377 if (g_outfile == NULL) {
378 ctfmerge_usage("missing required -o output file\n");
379 return (CTFMERGE_USAGE);
382 (void) elf_version(EV_CURRENT);
385 * Obviously this isn't atomic, but at least gives us a good starting
386 * point.
388 if ((ofd = open(g_outfile, O_RDWR)) < 0)
389 ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile,
390 strerror(errno));
392 argc -= optind;
393 argv += optind;
395 if (argc < 1) {
396 ctfmerge_usage("no input files specified");
397 return (CTFMERGE_USAGE);
400 cmh = ctf_merge_init(ofd, &err);
401 if (cmh == NULL)
402 ctfmerge_fatal("failed to create merge handle: %s\n",
403 ctf_errmsg(err));
405 if ((err = ctf_merge_set_nthreads(cmh, nthreads)) != 0)
406 ctfmerge_fatal("failed to set parallelism to %d: %s\n",
407 nthreads, ctf_errmsg(err));
409 for (i = 0; i < argc; i++) {
410 ctf_file_t *ifp;
411 int fd;
413 if ((fd = open(argv[i], O_RDONLY)) < 0)
414 ctfmerge_fatal("failed to open file %s: %s\n",
415 argv[i], strerror(errno));
416 ifp = ctf_fdopen(fd, &err);
417 if (ifp == NULL) {
418 Elf *e;
420 if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
421 (void) close(fd);
422 ctfmerge_fatal("failed to open %s: %s\n",
423 argv[i], ctf_errmsg(err));
427 * It's an ELF file, check if we have an archive or if
428 * we're expecting CTF here.
430 switch (elf_kind(e)) {
431 case ELF_K_AR:
432 break;
433 case ELF_K_ELF:
434 if (ctfmerge_expect_ctf(argv[i], e) == B_TRUE) {
435 (void) elf_end(e);
436 (void) close(fd);
437 ctfmerge_fatal("failed to "
438 "open %s: file was built from C "
439 "sources, but missing CTF\n",
440 argv[i]);
442 (void) elf_end(e);
443 (void) close(fd);
444 continue;
445 default:
446 (void) elf_end(e);
447 (void) close(fd);
448 ctfmerge_fatal("failed to open %s: "
449 "unsupported ELF file type", argv[i]);
452 ctfmerge_read_archive(argv[i], fd, e, cmh);
453 (void) elf_end(e);
454 (void) close(fd);
455 continue;
457 (void) close(fd);
458 if ((err = ctf_merge_add(cmh, ifp)) != 0)
459 ctfmerge_fatal("failed to add input %s: %s\n",
460 argv[i], ctf_errmsg(err));
461 g_nctf++;
464 if (g_nctf == 0) {
465 ctf_merge_fini(cmh);
466 return (0);
469 if (g_unique != NULL) {
470 ctf_file_t *ufp;
471 char *base;
473 ufp = ctf_open(g_unique, &err);
474 if (ufp == NULL) {
475 ctfmerge_fatal("failed to open uniquify file %s: %s\n",
476 g_unique, ctf_errmsg(err));
479 base = basename(g_unique);
480 (void) ctf_merge_uniquify(cmh, ufp, base);
483 if (label != NULL) {
484 if ((err = ctf_merge_label(cmh, label)) != 0)
485 ctfmerge_fatal("failed to add label %s: %s\n", label,
486 ctf_errmsg(err));
489 err = ctf_merge_merge(cmh, &ofp);
490 if (err != 0)
491 ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err));
492 ctf_merge_fini(cmh);
494 if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1)
495 ctfmerge_fatal("ran out of memory for temporary file name\n");
496 err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags);
497 if (err == CTF_ERR) {
498 (void) unlink(tmpfile);
499 free(tmpfile);
500 ctfmerge_fatal("encountered a libctf error: %s!\n",
501 ctf_errmsg(ctf_errno(ofp)));
504 if (rename(tmpfile, g_outfile) != 0) {
505 (void) unlink(tmpfile);
506 free(tmpfile);
507 ctfmerge_fatal("failed to rename temporary file: %s\n",
508 strerror(errno));
510 free(tmpfile);
512 return (CTFMERGE_OK);