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 * Create CTF from extant debugging information
24 #include <sys/types.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
;
45 ctfconvert_fatal(const char *fmt
, ...)
49 (void) fprintf(stderr
, "%s: ", ctfconvert_progname
);
51 (void) vfprintf(stderr
, fmt
, ap
);
54 exit(CTFCONVERT_FATAL
);
59 ctfconvert_usage(const char *fmt
, ...)
64 (void) fprintf(stderr
, "%s: ", ctfconvert_progname
);
66 (void) vfprintf(stderr
, fmt
, ap
);
70 (void) fprintf(stderr
, "Usage: %s [-is] [-j nthrs] [-l label | "
71 "-L labelenv] [-o outfile] input\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",
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
;
113 ctfconvert_fixup_genunix_cb(const char *name
, ctf_id_t tid
, ulong_t off
,
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
;
124 if (strcmp(name
, "cpu_m") == 0) {
125 cfp
->cf_mcpu
= B_TRUE
;
129 if (strcmp(name
, "cpu_m_pad") == 0) {
130 cfp
->cf_lastpad
= B_TRUE
;
131 cfp
->cf_padoff
= off
;
139 ctfconvert_fixup_genunix(ctf_file_t
*fp
)
141 ctf_id_t cpuid
, mcpu
;
143 ctfconvert_fixup_t cf
;
146 cpuid
= ctf_lookup_by_name(fp
, "struct cpu");
147 if (cpuid
== CTF_ERR
)
150 if (ctf_type_kind(fp
, cpuid
) != CTF_K_STRUCT
)
153 if ((sz
= ctf_type_size(fp
, cpuid
)) == CTF_ERR
)
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
) ==
166 * Finally, we want to verify that the cpu_m is actually the last member
169 if (cf
.cf_cyclic
== B_FALSE
|| cf
.cf_mcpu
== B_TRUE
||
170 cf
.cf_lastpad
== B_FALSE
) {
174 if (cf
.cf_padoff
+ ptrsz
* NBBY
!= sz
* NBBY
) {
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",
187 if (mcpu
== CTF_ERR
) {
188 ctfconvert_fatal("failed to add 'struct machcpu' "
189 "forward: %s", ctf_errmsg(ctf_errno(fp
)));
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
[])
226 boolean_t keep
= B_FALSE
;
228 uint_t nthreads
= CTFCONVERT_DEFAULT_NTHREADS
;
229 const char *outfile
= NULL
;
230 const char *label
= NULL
;
231 const char *infile
= NULL
;
237 boolean_t optx
= B_FALSE
;
239 ctfconvert_progname
= basename(argv
[0]);
241 while ((c
= getopt(argc
, argv
, ":j:kl:L:o:iX")) != -1) {
250 label
= getenv(optarg
);
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: "
261 nthreads
= (uint_t
)argj
;
267 flags
|= CTF_CONVERT_F_IGNNONC
;
273 ctfconvert_usage("Option -%c requires an operand\n",
275 return (CTFCONVERT_USAGE
);
277 ctfconvert_usage("Unknown option: -%c\n", optopt
);
278 return (CTFCONVERT_USAGE
);
286 ctfconvert_usage("Missing required input file\n");
287 return (CTFCONVERT_USAGE
);
291 if (elf_version(EV_CURRENT
) == EV_NONE
)
292 ctfconvert_fatal("failed to initialize libelf: library is "
295 ifd
= open(infile
, O_RDONLY
);
297 ctfconvert_fatal("failed to open input file %s: %s\n", infile
,
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)
308 ofp
= ctf_fdconvert(ifd
, label
, nthreads
, flags
, &err
, buf
,
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
317 if ((flags
& CTF_CONVERT_F_IGNNONC
) != 0 &&
318 err
== ECTF_CONVNOCSRC
) {
322 (void) unlink(infile
);
323 ctfconvert_fatal("CTF conversion failed: %s\n",
324 err
== ECTF_CONVBKERR
? buf
: ctf_errmsg(err
));
328 ctfconvert_fixup_genunix(ofp
);
331 if (outfile
== NULL
|| strcmp(infile
, outfile
) == 0) {
332 if (asprintf(&tmpfile
, "%s.ctf", infile
) == -1) {
334 (void) unlink(infile
);
335 ctfconvert_fatal("failed to allocate memory for "
336 "temporary file: %s\n", strerror(errno
));
340 err
= ctf_elfwrite(ofp
, infile
, outfile
, CTF_ELFWRITE_F_COMPRESS
);
341 if (err
== CTF_ERR
) {
342 (void) unlink(outfile
);
344 (void) unlink(infile
);
345 ctfconvert_fatal("failed to write CTF section to output file: "
346 "%s", ctf_errmsg(ctf_errno(ofp
)));
350 if (tmpfile
!= NULL
) {
351 if (rename(tmpfile
, infile
) != 0) {
353 (void) unlink(outfile
);
355 (void) unlink(infile
);
356 ctfconvert_fatal("failed to rename temporary file: "
357 "%s\n", strerror(e
));
362 return (CTFCONVERT_OK
);