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
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
23 #include <sys/types.h>
29 #include <sys/fcntl.h>
38 static char *g_progname
;
39 static char *g_unique
;
40 static char *g_outfile
;
41 static boolean_t g_req
;
45 #define CTFMERGE_FATAL 1
46 #define CTFMERGE_USAGE 2
48 #define CTFMERGE_DEFAULT_NTHREADS 8
51 ctfmerge_fatal(const char *fmt
, ...)
55 (void) fprintf(stderr
, "%s: ", g_progname
);
57 (void) vfprintf(stderr
, fmt
, ap
);
60 if (g_outfile
!= NULL
)
61 (void) unlink(g_outfile
);
67 ctfmerge_expect_ctf(const char *name
, Elf
*elf
)
69 Elf_Scn
*scn
, *strscn
;
70 Elf_Data
*data
, *strdata
;
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
)
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
++) {
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
)
115 file
= (const char *)((uintptr_t)strdata
->d_buf
+ sym
.st_name
);
117 if (len
< 2 || name
[len
- 2] != '.')
120 if (name
[len
- 1] == 'c')
128 * Go through and construct enough information for this Elf Object to try and do
132 ctfmerge_elfopen(const char *name
, Elf
*elf
, ctf_merge_t
*cmh
)
137 Elf_Data
*ctf_data
, *str_data
, *sym_data
;
138 ctf_sect_t ctfsect
, symsect
, strsect
;
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
));
151 while ((scn
= elf_nextscn(elf
, scn
)) != NULL
) {
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
) {
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
,
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
)
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
);
225 fp
= ctf_bufopen(&ctfsect
, NULL
, NULL
, &err
);
229 if (ctfmerge_expect_ctf(name
, elf
) == B_TRUE
) {
230 ctfmerge_fatal("failed to open file %s: %s\n",
231 name
, ctf_errmsg(err
));
234 if ((err
= ctf_merge_add(cmh
, fp
)) != 0) {
235 ctfmerge_fatal("failed to add input %s: %s\n",
236 name
, ctf_errmsg(err
));
243 ctfmerge_read_archive(const char *name
, int fd
, Elf
*elf
,
247 Elf_Cmd cmd
= ELF_C_READ
;
251 while ((aelf
= elf_begin(fd
, cmd
, elf
)) != NULL
) {
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
) == '/')
262 if (asprintf(&nname
, "%s.%s.%d", name
, arhdr
->ar_name
,
264 ctfmerge_fatal("failed to allocate memory for archive "
265 "%d of file %s\n", cursec
, name
);
267 switch (elf_kind(aelf
)) {
269 ctfmerge_read_archive(nname
, fd
, aelf
, cmh
);
273 ctfmerge_elfopen(nname
, aelf
, cmh
);
278 ctfmerge_fatal("unknown elf kind (%d) in archive %d "
279 "for %s\n", elf_kind(aelf
), cursec
, name
);
283 cmd
= elf_next(aelf
);
284 if (leakelf
== B_FALSE
)
285 (void) elf_end(aelf
);
291 ctfmerge_usage(const char *fmt
, ...)
296 (void) fprintf(stderr
, "%s: ", g_progname
);
298 (void) vfprintf(stderr
, fmt
, ap
);
302 (void) fprintf(stderr
, "Usage: %s [-gt] [-d uniqfile] [-l label] "
303 "[-L labelenv] [-j nthrs] -o outfile file ...\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",
316 main(int argc
, char *argv
[])
319 uint_t nthreads
= CTFMERGE_DEFAULT_NTHREADS
;
320 char *tmpfile
= NULL
, *label
= NULL
;
321 int wflags
= CTF_ELFWRITE_F_COMPRESS
;
327 g_progname
= basename(argv
[0]);
330 * We support a subset of the old CTF merge flags, mostly for
333 while ((c
= getopt(argc
, argv
, ":d:fgj:L:o:t")) != -1) {
339 /* Silently ignored for compatibility */
342 /* Silently ignored for compatibility */
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",
353 nthreads
= (uint_t
)argj
;
359 label
= getenv(optarg
);
368 ctfmerge_usage("Option -%c requires an operand\n",
370 return (CTFMERGE_USAGE
);
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
388 if ((ofd
= open(g_outfile
, O_RDWR
)) < 0)
389 ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile
,
396 ctfmerge_usage("no input files specified");
397 return (CTFMERGE_USAGE
);
400 cmh
= ctf_merge_init(ofd
, &err
);
402 ctfmerge_fatal("failed to create merge handle: %s\n",
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
++) {
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
);
420 if ((e
= elf_begin(fd
, ELF_C_READ
, NULL
)) == NULL
) {
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
)) {
434 if (ctfmerge_expect_ctf(argv
[i
], e
) == B_TRUE
) {
437 ctfmerge_fatal("failed to "
438 "open %s: file was built from C "
439 "sources, but missing CTF\n",
448 ctfmerge_fatal("failed to open %s: "
449 "unsupported ELF file type", argv
[i
]);
452 ctfmerge_read_archive(argv
[i
], fd
, e
, cmh
);
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
));
469 if (g_unique
!= NULL
) {
473 ufp
= ctf_open(g_unique
, &err
);
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
);
484 if ((err
= ctf_merge_label(cmh
, label
)) != 0)
485 ctfmerge_fatal("failed to add label %s: %s\n", label
,
489 err
= ctf_merge_merge(cmh
, &ofp
);
491 ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err
));
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
);
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
);
507 ctfmerge_fatal("failed to rename temporary file: %s\n",
512 return (CTFMERGE_OK
);