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 (c) 2015, Joyent, Inc.
14 */
15
16 /*
17 * Create CTF from extant debugging information
18 */
19
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>
52 va_start(ap, fmt);
53 (void) vfprintf(stderr, fmt, ap);
54 va_end(ap);
55
56 exit(CTFCONVERT_FATAL);
57 }
58
59
60 static void
61 ctfconvert_usage(const char *fmt, ...)
62 {
63 if (fmt != NULL) {
64 va_list ap;
65
66 (void) fprintf(stderr, "%s: ", ctfconvert_progname);
67 va_start(ap, fmt);
68 (void) vfprintf(stderr, fmt, ap);
69 va_end(ap);
70 }
71
72 (void) fprintf(stderr, "Usage: %s [-is] [-j nthrs] [-l label | "
73 "-L labelenv] [-o outfile] input\n"
74 "\n"
75 "\t-i ignore files not built partially from C sources\n"
76 "\t-j use nthrs threads to perform the merge\n"
77 "\t-k keep around original input file on failure\n"
78 "\t-o copy input to outfile and add CTF\n"
79 "\t-l set output container's label to specified value\n"
80 "\t-L set output container's label to value from environment\n",
81 ctfconvert_progname);
82 }
83
84 /*
85 * This is a bit unfortunate. Traditionally we do type uniquification across all
86 * modules in the kernel, including ip and unix against genunix. However, when
87 * _MACHDEP is defined, then the cpu_t ends up having an additional member
88 * (cpu_m), thus changing the ability for us to uniquify against it. This in
89 * turn causes a lot of type sprawl, as there's a lot of things that end up
90 * referring to the cpu_t and it chains out from there.
91 *
92 * So, if we find that a cpu_t has been defined and it has a couple of useful
93 * sentinel members and it does *not* have the cpu_m member, then we will try
94 * and lookup or create a forward declaration to the machcpu, append it to the
95 * end, and update the file.
96 *
97 * This currently is only invoked if an undocumented option -X is passed. This
242 ctfconvert_fatal("failed to execute alternate program %s: %s",
243 altexec, strerror(errno));
244 }
245
246 int
247 main(int argc, char *argv[])
248 {
249 int c, ifd, err;
250 boolean_t keep = B_FALSE;
251 uint_t flags = 0;
252 uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
253 const char *outfile = NULL;
254 const char *label = NULL;
255 const char *infile = NULL;
256 char *tmpfile;
257 ctf_file_t *ofp;
258 long argj;
259 char *eptr;
260 char buf[4096];
261 boolean_t optx = B_FALSE;
262
263 ctfconvert_progname = basename(argv[0]);
264
265 ctfconvert_altexec(argv);
266
267 while ((c = getopt(argc, argv, ":j:kl:L:o:iX")) != -1) {
268 switch (c) {
269 case 'k':
270 keep = B_TRUE;
271 break;
272 case 'l':
273 label = optarg;
274 break;
275 case 'L':
276 label = getenv(optarg);
277 break;
278 case 'j':
279 errno = 0;
280 argj = strtol(optarg, &eptr, 10);
281 if (errno != 0 || argj == LONG_MAX ||
282 argj > 1024 || *eptr != '\0') {
283 ctfconvert_fatal("invalid argument for -j: "
284 "%s\n", optarg);
285 }
286 nthreads = (uint_t)argj;
287 break;
288 case 'o':
289 outfile = optarg;
290 break;
291 case 'i':
292 flags |= CTF_CONVERT_F_IGNNONC;
293 break;
294 case 'X':
295 optx = B_TRUE;
296 break;
297 case ':':
298 ctfconvert_usage("Option -%c requires an operand\n",
299 optopt);
300 return (CTFCONVERT_USAGE);
301 case '?':
302 ctfconvert_usage("Unknown option: -%c\n", optopt);
303 return (CTFCONVERT_USAGE);
304 }
305 }
306
307 argv += optind;
308 argc -= optind;
309
310 if (argc < 1) {
311 ctfconvert_usage("Missing required input file\n");
312 return (CTFCONVERT_USAGE);
313 }
314 infile = argv[0];
315
316 if (elf_version(EV_CURRENT) == EV_NONE)
317 ctfconvert_fatal("failed to initialize libelf: library is "
318 "out of date\n");
319
320 ifd = open(infile, O_RDONLY);
321 if (ifd < 0) {
322 ctfconvert_fatal("failed to open input file %s: %s\n", infile,
323 strerror(errno));
324 }
325
326 /*
327 * By default we remove the input file on failure unless we've been
328 * given an output file or -k has been specified.
329 */
330 if (outfile != NULL && strcmp(infile, outfile) != 0)
331 keep = B_TRUE;
332
333 ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf,
334 sizeof (buf));
335 if (ofp == NULL) {
336 /*
337 * -i says that we shouldn't concern ourselves with source files
338 * that weren't built from C source code in part. Because this
339 * has been traditionally used across all of illumos, we still
340 * honor it.
341 */
342 if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
343 err == ECTF_CONVNOCSRC) {
344 exit(CTFCONVERT_OK);
345 }
346 if (keep == B_FALSE)
347 (void) unlink(infile);
348 ctfconvert_fatal("CTF conversion failed: %s\n",
349 err == ECTF_CONVBKERR ? buf : ctf_errmsg(err));
350 }
351
352 if (optx == B_TRUE)
353 ctfconvert_fixup_genunix(ofp);
354
355 tmpfile = NULL;
356 if (outfile == NULL || strcmp(infile, outfile) == 0) {
357 if (asprintf(&tmpfile, "%s.ctf", infile) == -1) {
358 if (keep == B_FALSE)
359 (void) unlink(infile);
360 ctfconvert_fatal("failed to allocate memory for "
361 "temporary file: %s\n", strerror(errno));
362 }
363 outfile = tmpfile;
364 }
365 err = ctf_elfwrite(ofp, infile, outfile, CTF_ELFWRITE_F_COMPRESS);
366 if (err == CTF_ERR) {
367 (void) unlink(outfile);
368 if (keep == B_FALSE)
369 (void) unlink(infile);
370 ctfconvert_fatal("failed to write CTF section to output file: "
|
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 (c) 2019, Joyent, Inc.
14 */
15
16 /*
17 * Create CTF from extant debugging information
18 */
19
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>
52 va_start(ap, fmt);
53 (void) vfprintf(stderr, fmt, ap);
54 va_end(ap);
55
56 exit(CTFCONVERT_FATAL);
57 }
58
59
60 static void
61 ctfconvert_usage(const char *fmt, ...)
62 {
63 if (fmt != NULL) {
64 va_list ap;
65
66 (void) fprintf(stderr, "%s: ", ctfconvert_progname);
67 va_start(ap, fmt);
68 (void) vfprintf(stderr, fmt, ap);
69 va_end(ap);
70 }
71
72 (void) fprintf(stderr, "Usage: %s [-ims] [-j nthrs] [-l label | "
73 "-L labelenv] [-o outfile] input\n"
74 "\n"
75 "\t-i ignore files not built partially from C sources\n"
76 "\t-j use nthrs threads to perform the merge\n"
77 "\t-k keep around original input file on failure\n"
78 "\t-m allow input to have missing debug info\n"
79 "\t-o copy input to outfile and add CTF\n"
80 "\t-l set output container's label to specified value\n"
81 "\t-L set output container's label to value from environment\n",
82 ctfconvert_progname);
83 }
84
85 /*
86 * This is a bit unfortunate. Traditionally we do type uniquification across all
87 * modules in the kernel, including ip and unix against genunix. However, when
88 * _MACHDEP is defined, then the cpu_t ends up having an additional member
89 * (cpu_m), thus changing the ability for us to uniquify against it. This in
90 * turn causes a lot of type sprawl, as there's a lot of things that end up
91 * referring to the cpu_t and it chains out from there.
92 *
93 * So, if we find that a cpu_t has been defined and it has a couple of useful
94 * sentinel members and it does *not* have the cpu_m member, then we will try
95 * and lookup or create a forward declaration to the machcpu, append it to the
96 * end, and update the file.
97 *
98 * This currently is only invoked if an undocumented option -X is passed. This
243 ctfconvert_fatal("failed to execute alternate program %s: %s",
244 altexec, strerror(errno));
245 }
246
247 int
248 main(int argc, char *argv[])
249 {
250 int c, ifd, err;
251 boolean_t keep = B_FALSE;
252 uint_t flags = 0;
253 uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
254 const char *outfile = NULL;
255 const char *label = NULL;
256 const char *infile = NULL;
257 char *tmpfile;
258 ctf_file_t *ofp;
259 long argj;
260 char *eptr;
261 char buf[4096];
262 boolean_t optx = B_FALSE;
263 boolean_t ignore_non_c = B_FALSE;
264
265 ctfconvert_progname = basename(argv[0]);
266
267 ctfconvert_altexec(argv);
268
269 while ((c = getopt(argc, argv, ":ij:kl:L:mo:X")) != -1) {
270 switch (c) {
271 case 'i':
272 ignore_non_c = B_TRUE;
273 break;
274 case 'j':
275 errno = 0;
276 argj = strtol(optarg, &eptr, 10);
277 if (errno != 0 || argj == LONG_MAX ||
278 argj > 1024 || *eptr != '\0') {
279 ctfconvert_fatal("invalid argument for -j: "
280 "%s\n", optarg);
281 }
282 nthreads = (uint_t)argj;
283 break;
284 case 'k':
285 keep = B_TRUE;
286 break;
287 case 'l':
288 label = optarg;
289 break;
290 case 'L':
291 label = getenv(optarg);
292 break;
293 case 'm':
294 flags |= CTF_ALLOW_MISSING_DEBUG;
295 break;
296 case 'o':
297 outfile = optarg;
298 break;
299 case 'X':
300 optx = B_TRUE;
301 break;
302 case ':':
303 ctfconvert_usage("Option -%c requires an operand\n",
304 optopt);
305 return (CTFCONVERT_USAGE);
306 case '?':
307 ctfconvert_usage("Unknown option: -%c\n", optopt);
308 return (CTFCONVERT_USAGE);
309 }
310 }
311
312 argv += optind;
313 argc -= optind;
314
315 if (argc != 1) {
316 ctfconvert_usage("Exactly one input file is required\n");
317 return (CTFCONVERT_USAGE);
318 }
319 infile = argv[0];
320
321 if (elf_version(EV_CURRENT) == EV_NONE)
322 ctfconvert_fatal("failed to initialize libelf: library is "
323 "out of date\n");
324
325 ifd = open(infile, O_RDONLY);
326 if (ifd < 0) {
327 ctfconvert_fatal("failed to open input file %s: %s\n", infile,
328 strerror(errno));
329 }
330
331 /*
332 * By default we remove the input file on failure unless we've been
333 * given an output file or -k has been specified.
334 */
335 if (outfile != NULL && strcmp(infile, outfile) != 0)
336 keep = B_TRUE;
337
338 ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf,
339 sizeof (buf));
340 if (ofp == NULL) {
341 /*
342 * Normally, ctfconvert requires that its input file has at
343 * least one C-source compilation unit, and that every C-source
344 * compilation unit has DWARF. This is to avoid accidentally
345 * leaving out useful CTF.
346 *
347 * However, for the benefit of intransigent build environments,
348 * the -i and -m options can be used to relax this.
349 */
350 if (err == ECTF_CONVNOCSRC && ignore_non_c) {
351 exit(CTFCONVERT_OK);
352 }
353
354 if (err == ECTF_CONVNODEBUG &&
355 (flags & CTF_ALLOW_MISSING_DEBUG) != 0) {
356 exit(CTFCONVERT_OK);
357 }
358
359 if (keep == B_FALSE)
360 (void) unlink(infile);
361
362 if (err == ECTF_CONVBKERR || err == ECTF_CONVNODEBUG) {
363 ctfconvert_fatal("%s", buf);
364 } else {
365 ctfconvert_fatal("CTF conversion failed: %s\n",
366 ctf_errmsg(err));
367 }
368 }
369
370 if (optx == B_TRUE)
371 ctfconvert_fixup_genunix(ofp);
372
373 tmpfile = NULL;
374 if (outfile == NULL || strcmp(infile, outfile) == 0) {
375 if (asprintf(&tmpfile, "%s.ctf", infile) == -1) {
376 if (keep == B_FALSE)
377 (void) unlink(infile);
378 ctfconvert_fatal("failed to allocate memory for "
379 "temporary file: %s\n", strerror(errno));
380 }
381 outfile = tmpfile;
382 }
383 err = ctf_elfwrite(ofp, infile, outfile, CTF_ELFWRITE_F_COMPRESS);
384 if (err == CTF_ERR) {
385 (void) unlink(outfile);
386 if (keep == B_FALSE)
387 (void) unlink(infile);
388 ctfconvert_fatal("failed to write CTF section to output file: "
|