svc: cast id/force values instead of passing pointers around
[unleashed/lotheac.git] / bin / ctfconvert / ctfconvert.c
blob876855454a851c9be28dd116d4ab6c7cac404466
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 * Create CTF from extant debugging information
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <libelf.h>
29 #include <libctf.h>
30 #include <string.h>
31 #include <libgen.h>
32 #include <limits.h>
33 #include <strings.h>
34 #include <sys/debug.h>
36 #define CTFCONVERT_OK 0
37 #define CTFCONVERT_FATAL 1
38 #define CTFCONVERT_USAGE 2
40 #define CTFCONVERT_DEFAULT_NTHREADS 4
42 static char *ctfconvert_progname;
44 static void
45 ctfconvert_fatal(const char *fmt, ...)
47 va_list ap;
49 (void) fprintf(stderr, "%s: ", ctfconvert_progname);
50 va_start(ap, fmt);
51 (void) vfprintf(stderr, fmt, ap);
52 va_end(ap);
54 exit(CTFCONVERT_FATAL);
58 static void
59 ctfconvert_usage(const char *fmt, ...)
61 if (fmt != NULL) {
62 va_list ap;
64 (void) fprintf(stderr, "%s: ", ctfconvert_progname);
65 va_start(ap, fmt);
66 (void) vfprintf(stderr, fmt, ap);
67 va_end(ap);
70 (void) fprintf(stderr, "Usage: %s [-is] [-j nthrs] [-l label | "
71 "-L labelenv] [-o outfile] input\n"
72 "\n"
73 "\t-i ignore files not built partially from C sources\n"
74 "\t-j use nthrs threads to perform the merge\n"
75 "\t-k keep around original input file on failure\n"
76 "\t-o copy input to outfile and add CTF\n"
77 "\t-l set output container's label to specified value\n"
78 "\t-L set output container's label to value from environment\n",
79 ctfconvert_progname);
83 * This is a bit unfortunate. Traditionally we do type uniquification across all
84 * modules in the kernel, including ip and unix against genunix. However, when
85 * _MACHDEP is defined, then the cpu_t ends up having an additional member
86 * (cpu_m), thus changing the ability for us to uniquify against it. This in
87 * turn causes a lot of type sprawl, as there's a lot of things that end up
88 * referring to the cpu_t and it chains out from there.
90 * So, if we find that a cpu_t has been defined and it has a couple of useful
91 * sentinel members and it does *not* have the cpu_m member, then we will try
92 * and lookup or create a forward declaration to the machcpu, append it to the
93 * end, and update the file.
95 * This currently is only invoked if an undocumented option -X is passed. This
96 * value is private to illumos and it can be changed at any time inside of it,
97 * so if -X wants to be used for something, it should be. The ability to rely on
98 * -X for others is strictly not an interface in any way, shape, or form.
100 * The following struct contains most of the information that we care about and
101 * that we want to validate exists before we decide what to do.
104 typedef struct ctfconvert_fixup {
105 boolean_t cf_cyclic; /* Do we have a cpu_cyclic member */
106 boolean_t cf_mcpu; /* We have a cpu_m member */
107 boolean_t cf_lastpad; /* Is the pad member the last entry */
108 ulong_t cf_padoff; /* offset of the pad */
109 } ctfconvert_fixup_t;
111 /* ARGSUSED */
112 static int
113 ctfconvert_fixup_genunix_cb(const char *name, ctf_id_t tid, ulong_t off,
114 void *arg)
116 ctfconvert_fixup_t *cfp = arg;
118 cfp->cf_lastpad = B_FALSE;
119 if (strcmp(name, "cpu_cyclic") == 0) {
120 cfp->cf_cyclic = B_TRUE;
121 return (0);
124 if (strcmp(name, "cpu_m") == 0) {
125 cfp->cf_mcpu = B_TRUE;
126 return (0);
129 if (strcmp(name, "cpu_m_pad") == 0) {
130 cfp->cf_lastpad = B_TRUE;
131 cfp->cf_padoff = off;
132 return (0);
135 return (0);
138 static void
139 ctfconvert_fixup_genunix(ctf_file_t *fp)
141 ctf_id_t cpuid, mcpu;
142 ssize_t sz;
143 ctfconvert_fixup_t cf;
144 int model, ptrsz;
146 cpuid = ctf_lookup_by_name(fp, "struct cpu");
147 if (cpuid == CTF_ERR)
148 return;
150 if (ctf_type_kind(fp, cpuid) != CTF_K_STRUCT)
151 return;
153 if ((sz = ctf_type_size(fp, cpuid)) == CTF_ERR)
154 return;
156 model = ctf_getmodel(fp);
157 VERIFY(model == CTF_MODEL_ILP32 || model == CTF_MODEL_LP64);
158 ptrsz = model == CTF_MODEL_ILP32 ? 4 : 8;
160 bzero(&cf, sizeof (ctfconvert_fixup_t));
161 if (ctf_member_iter(fp, cpuid, ctfconvert_fixup_genunix_cb, &cf) ==
162 CTF_ERR)
163 return;
166 * Finally, we want to verify that the cpu_m is actually the last member
167 * that we have here.
169 if (cf.cf_cyclic == B_FALSE || cf.cf_mcpu == B_TRUE ||
170 cf.cf_lastpad == B_FALSE) {
171 return;
174 if (cf.cf_padoff + ptrsz * NBBY != sz * NBBY) {
175 return;
179 * Okay, we're going to do this, try to find a struct machcpu. We either
180 * want a forward or a struct. If we find something else, error. If we
181 * find nothing, add a forward and then add the member.
183 mcpu = ctf_lookup_by_name(fp, "struct machcpu");
184 if (mcpu == CTF_ERR) {
185 mcpu = ctf_add_forward(fp, CTF_ADD_NONROOT, "machcpu",
186 CTF_K_STRUCT);
187 if (mcpu == CTF_ERR) {
188 ctfconvert_fatal("failed to add 'struct machcpu' "
189 "forward: %s", ctf_errmsg(ctf_errno(fp)));
191 } else {
192 int kind;
193 if ((kind = ctf_type_kind(fp, mcpu)) == CTF_ERR) {
194 ctfconvert_fatal("failed to get the type kind for "
195 "the struct machcpu: %s",
196 ctf_errmsg(ctf_errno(fp)));
199 if (kind != CTF_K_STRUCT && kind != CTF_K_FORWARD)
200 ctfconvert_fatal("encountered a struct machcpu of the "
201 "wrong type, found type kind %d\n", kind);
204 if (ctf_update(fp) == CTF_ERR) {
205 ctfconvert_fatal("failed to update output file: %s\n",
206 ctf_errmsg(ctf_errno(fp)));
209 if (ctf_add_member(fp, cpuid, "cpu_m", mcpu, sz * NBBY) == CTF_ERR) {
210 ctfconvert_fatal("failed to add the m_cpu member: %s\n",
211 ctf_errmsg(ctf_errno(fp)));
214 if (ctf_update(fp) == CTF_ERR) {
215 ctfconvert_fatal("failed to update output file: %s\n",
216 ctf_errmsg(ctf_errno(fp)));
219 VERIFY(ctf_type_size(fp, cpuid) == sz);
223 main(int argc, char *argv[])
225 int c, ifd, err;
226 boolean_t keep = B_FALSE;
227 uint_t flags = 0;
228 uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
229 const char *outfile = NULL;
230 const char *label = NULL;
231 const char *infile = NULL;
232 char *tmpfile;
233 ctf_file_t *ofp;
234 long argj;
235 char *eptr;
236 char buf[4096];
237 boolean_t optx = B_FALSE;
239 ctfconvert_progname = basename(argv[0]);
241 while ((c = getopt(argc, argv, ":j:kl:L:o:iX")) != -1) {
242 switch (c) {
243 case 'k':
244 keep = B_TRUE;
245 break;
246 case 'l':
247 label = optarg;
248 break;
249 case 'L':
250 label = getenv(optarg);
251 break;
252 case 'j':
253 errno = 0;
254 argj = strtol(optarg, &eptr, 10);
255 if (errno != 0 || argj == LONG_MAX ||
256 argj == LONG_MIN || argj <= 0 ||
257 argj > UINT_MAX || *eptr != '\0') {
258 ctfconvert_fatal("invalid argument for -j: "
259 "%s\n", optarg);
261 nthreads = (uint_t)argj;
262 break;
263 case 'o':
264 outfile = optarg;
265 break;
266 case 'i':
267 flags |= CTF_CONVERT_F_IGNNONC;
268 break;
269 case 'X':
270 optx = B_TRUE;
271 break;
272 case ':':
273 ctfconvert_usage("Option -%c requires an operand\n",
274 optopt);
275 return (CTFCONVERT_USAGE);
276 case '?':
277 ctfconvert_usage("Unknown option: -%c\n", optopt);
278 return (CTFCONVERT_USAGE);
282 argv += optind;
283 argc -= optind;
285 if (argc < 1) {
286 ctfconvert_usage("Missing required input file\n");
287 return (CTFCONVERT_USAGE);
289 infile = argv[0];
291 if (elf_version(EV_CURRENT) == EV_NONE)
292 ctfconvert_fatal("failed to initialize libelf: library is "
293 "out of date\n");
295 ifd = open(infile, O_RDONLY);
296 if (ifd < 0) {
297 ctfconvert_fatal("failed to open input file %s: %s\n", infile,
298 strerror(errno));
302 * By default we remove the input file on failure unless we've been
303 * given an output file or -k has been specified.
305 if (outfile != NULL && strcmp(infile, outfile) != 0)
306 keep = B_TRUE;
308 ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf,
309 sizeof (buf));
310 if (ofp == NULL) {
312 * -i says that we shouldn't concern ourselves with source files
313 * that weren't built from C source code in part. Because this
314 * has been traditionally used across all of illumos, we still
315 * honor it.
317 if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
318 err == ECTF_CONVNOCSRC) {
319 exit(CTFCONVERT_OK);
321 if (keep == B_FALSE)
322 (void) unlink(infile);
323 ctfconvert_fatal("CTF conversion failed: %s\n",
324 err == ECTF_CONVBKERR ? buf : ctf_errmsg(err));
327 if (optx == B_TRUE)
328 ctfconvert_fixup_genunix(ofp);
330 tmpfile = NULL;
331 if (outfile == NULL || strcmp(infile, outfile) == 0) {
332 if (asprintf(&tmpfile, "%s.ctf", infile) == -1) {
333 if (keep == B_FALSE)
334 (void) unlink(infile);
335 ctfconvert_fatal("failed to allocate memory for "
336 "temporary file: %s\n", strerror(errno));
338 outfile = tmpfile;
340 err = ctf_elfwrite(ofp, infile, outfile, CTF_ELFWRITE_F_COMPRESS);
341 if (err == CTF_ERR) {
342 (void) unlink(outfile);
343 if (keep == B_FALSE)
344 (void) unlink(infile);
345 ctfconvert_fatal("failed to write CTF section to output file: "
346 "%s", ctf_errmsg(ctf_errno(ofp)));
348 ctf_close(ofp);
350 if (tmpfile != NULL) {
351 if (rename(tmpfile, infile) != 0) {
352 int e = errno;
353 (void) unlink(outfile);
354 if (keep == B_FALSE)
355 (void) unlink(infile);
356 ctfconvert_fatal("failed to rename temporary file: "
357 "%s\n", strerror(e));
360 free(tmpfile);
362 return (CTFCONVERT_OK);