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