1 /* Textual dumping of CTF data.
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
4 This file is part of libctf.
6 libctf is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not see
18 <http://www.gnu.org/licenses/>. */
23 #define str_append(s, a) ctf_str_append_noerr (s, a)
25 /* One item to be dumped, in string form. */
27 typedef struct ctf_dump_item
33 /* Cross-call state for dumping. Basically just enough to track the section in
34 use and a list of return strings. */
38 ctf_sect_names_t cds_sect
;
40 ctf_dump_item_t
*cds_current
;
44 /* Cross-call state for ctf_dump_member. */
46 typedef struct ctf_dump_membstate
50 } ctf_dump_membstate_t
;
53 ctf_dump_append (ctf_dump_state_t
*state
, char *str
)
57 if ((cdi
= malloc (sizeof (struct ctf_dump_item
))) == NULL
)
58 return (ctf_set_errno (state
->cds_fp
, ENOMEM
));
61 ctf_list_append (&state
->cds_items
, cdi
);
66 ctf_dump_free (ctf_dump_state_t
*state
)
68 ctf_dump_item_t
*cdi
, *next_cdi
;
73 for (cdi
= ctf_list_next (&state
->cds_items
); cdi
!= NULL
;
77 next_cdi
= ctf_list_next (cdi
);
82 /* Return a dump for a single type, without member info: but do show the
86 ctf_dump_format_type (ctf_dict_t
*fp
, ctf_id_t id
, int flag
)
89 char *str
= NULL
, *bit
= NULL
, *buf
= NULL
;
95 const char *nonroot_leader
= "";
96 const char *nonroot_trailer
= "";
99 if (flag
== CTF_ADD_NONROOT
)
101 nonroot_leader
= "{";
102 nonroot_trailer
= "}";
105 buf
= ctf_type_aname (fp
, id
);
108 if (id
== 0 || ctf_errno (fp
) == ECTF_NONREPRESENTABLE
)
110 str
= str_append (str
, " (type not represented in CTF)");
111 ctf_set_errno (fp
, ECTF_NOTREF
);
118 if (asprintf (&bit
, " %s%lx: ", nonroot_leader
, id
) < 0)
120 str
= str_append (str
, bit
);
126 str
= str_append (str
, buf
);
127 str
= str_append (str
, " ");
133 /* Slices get a different print representation. */
134 if (ctf_type_kind_unsliced (fp
, id
) == CTF_K_SLICE
)
136 ctf_type_encoding (fp
, id
, &enc
);
137 if (asprintf (&bit
, "[slice 0x%x:0x%x] ",
138 enc
.cte_offset
, enc
.cte_bits
) < 0)
141 else if (ctf_type_kind (fp
, id
) == CTF_K_INTEGER
)
143 ctf_type_encoding (fp
, id
, &enc
);
144 if (asprintf (&bit
, "[0x%x:0x%x] ",
145 enc
.cte_offset
, enc
.cte_bits
) < 0)
148 str
= str_append (str
, bit
);
152 if (asprintf (&bit
, "(size 0x%lx)%s",
153 (unsigned long) ctf_type_size (fp
, id
),
154 nonroot_trailer
) < 0)
157 str
= str_append (str
, bit
);
161 new_id
= ctf_type_reference (fp
, id
);
162 if (new_id
!= CTF_ERR
)
163 str
= str_append (str
, " ->");
164 } while (new_id
!= CTF_ERR
);
166 if (ctf_errno (fp
) != ECTF_NOTREF
)
175 ctf_set_errno (fp
, errno
);
177 ctf_err_warn (fp
, 1, 0, _("cannot format name dumping type 0x%lx"), id
);
184 /* Dump one string field from the file header into the cds_items. */
186 ctf_dump_header_strfield (ctf_dict_t
*fp
, ctf_dump_state_t
*state
,
187 const char *name
, uint32_t value
)
192 if (asprintf (&str
, "%s: %s\n", name
, ctf_strptr (fp
, value
)) < 0)
194 ctf_dump_append (state
, str
);
199 return (ctf_set_errno (fp
, errno
));
202 /* Dump one section-offset field from the file header into the cds_items. */
204 ctf_dump_header_sectfield (ctf_dict_t
*fp
, ctf_dump_state_t
*state
,
205 const char *sect
, uint32_t off
, uint32_t nextoff
)
210 if (asprintf (&str
, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect
,
211 (unsigned long) off
, (unsigned long) (nextoff
- 1),
212 (unsigned long) (nextoff
- off
)) < 0)
214 ctf_dump_append (state
, str
);
219 return (ctf_set_errno (fp
, errno
));
222 /* Dump the file header into the cds_items. */
224 ctf_dump_header (ctf_dict_t
*fp
, ctf_dump_state_t
*state
)
227 char *flagstr
= NULL
;
228 const ctf_header_t
*hp
= fp
->ctf_header
;
229 const char *vertab
[] =
231 NULL
, "CTF_VERSION_1",
232 "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
235 "CTF_VERSION_3", NULL
237 const char *verstr
= NULL
;
239 if (asprintf (&str
, "Magic number: %x\n", hp
->cth_magic
) < 0)
241 ctf_dump_append (state
, str
);
243 if (hp
->cth_version
<= CTF_VERSION
)
244 verstr
= vertab
[hp
->cth_version
];
247 verstr
= "(not a valid version)";
249 if (asprintf (&str
, "Version: %i (%s)\n", hp
->cth_version
,
252 ctf_dump_append (state
, str
);
254 /* Everything else is only printed if present. */
256 /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
257 flags representing compression, etc, are turned off as the file is
258 decompressed. So we store a copy of the flags before they are changed, for
261 if (fp
->ctf_openflags
> 0)
263 if (asprintf (&flagstr
, "%s%s%s%s%s%s%s",
264 fp
->ctf_openflags
& CTF_F_COMPRESS
265 ? "CTF_F_COMPRESS": "",
266 (fp
->ctf_openflags
& CTF_F_COMPRESS
)
267 && (fp
->ctf_openflags
& ~CTF_F_COMPRESS
)
269 fp
->ctf_openflags
& CTF_F_NEWFUNCINFO
270 ? "CTF_F_NEWFUNCINFO" : "",
271 (fp
->ctf_openflags
& (CTF_F_COMPRESS
| CTF_F_NEWFUNCINFO
))
272 && (fp
->ctf_openflags
& ~(CTF_F_COMPRESS
| CTF_F_NEWFUNCINFO
))
274 fp
->ctf_openflags
& CTF_F_IDXSORTED
275 ? "CTF_F_IDXSORTED" : "",
276 fp
->ctf_openflags
& (CTF_F_COMPRESS
| CTF_F_NEWFUNCINFO
278 && (fp
->ctf_openflags
& ~(CTF_F_COMPRESS
| CTF_F_NEWFUNCINFO
281 fp
->ctf_openflags
& CTF_F_DYNSTR
282 ? "CTF_F_DYNSTR" : "") < 0)
285 if (asprintf (&str
, "Flags: 0x%x (%s)", fp
->ctf_openflags
, flagstr
) < 0)
287 ctf_dump_append (state
, str
);
290 if (ctf_dump_header_strfield (fp
, state
, "Parent label",
291 hp
->cth_parlabel
) < 0)
294 if (ctf_dump_header_strfield (fp
, state
, "Parent name", hp
->cth_parname
) < 0)
297 if (ctf_dump_header_strfield (fp
, state
, "Compilation unit name",
301 if (ctf_dump_header_sectfield (fp
, state
, "Label section", hp
->cth_lbloff
,
302 hp
->cth_objtoff
) < 0)
305 if (ctf_dump_header_sectfield (fp
, state
, "Data object section",
306 hp
->cth_objtoff
, hp
->cth_funcoff
) < 0)
309 if (ctf_dump_header_sectfield (fp
, state
, "Function info section",
310 hp
->cth_funcoff
, hp
->cth_objtidxoff
) < 0)
313 if (ctf_dump_header_sectfield (fp
, state
, "Object index section",
314 hp
->cth_objtidxoff
, hp
->cth_funcidxoff
) < 0)
317 if (ctf_dump_header_sectfield (fp
, state
, "Function index section",
318 hp
->cth_funcidxoff
, hp
->cth_varoff
) < 0)
321 if (ctf_dump_header_sectfield (fp
, state
, "Variable section",
322 hp
->cth_varoff
, hp
->cth_typeoff
) < 0)
325 if (ctf_dump_header_sectfield (fp
, state
, "Type section",
326 hp
->cth_typeoff
, hp
->cth_stroff
) < 0)
329 if (ctf_dump_header_sectfield (fp
, state
, "String section", hp
->cth_stroff
,
330 hp
->cth_stroff
+ hp
->cth_strlen
+ 1) < 0)
336 return (ctf_set_errno (fp
, errno
));
339 /* Dump a single label into the cds_items. */
342 ctf_dump_label (const char *name
, const ctf_lblinfo_t
*info
,
347 ctf_dump_state_t
*state
= arg
;
349 if (asprintf (&str
, "%s -> ", name
) < 0)
350 return (ctf_set_errno (state
->cds_fp
, errno
));
352 if ((typestr
= ctf_dump_format_type (state
->cds_fp
, info
->ctb_type
,
353 CTF_ADD_ROOT
)) == NULL
)
356 return 0; /* Swallow the error. */
359 str
= str_append (str
, typestr
);
362 ctf_dump_append (state
, str
);
366 /* Dump all the object or function entries into the cds_items. */
369 ctf_dump_objts (ctf_dict_t
*fp
, ctf_dump_state_t
*state
, int functions
)
373 ctf_next_t
*i
= NULL
;
376 if ((functions
&& fp
->ctf_funcidx_names
)
377 || (!functions
&& fp
->ctf_objtidx_names
))
378 str
= str_append (str
, _("Section is indexed.\n"));
379 else if (fp
->ctf_symtab
.cts_data
== NULL
)
380 str
= str_append (str
, _("No symbol table.\n"));
382 while ((id
= ctf_symbol_next (fp
, &i
, &name
, functions
)) != CTF_ERR
)
384 char *typestr
= NULL
;
387 /* Emit the name, if we know it. */
390 if (asprintf (&str
, "%s -> ", name
) < 0)
396 if ((typestr
= ctf_type_aname (fp
, id
)) == NULL
)
398 if (id
== 0 || ctf_errno (fp
) == ECTF_NONREPRESENTABLE
)
400 if (asprintf (&typestr
, " (%s)", _("type not represented in CTF")) < 0)
406 if (asprintf (&typestr
, _("error: %s"), ctf_errmsg (ctf_errno (fp
))) < 0)
413 str
= str_append (str
, typestr
);
414 str
= str_append (str
, "\n");
415 ctf_dump_append (state
, str
);
419 ctf_set_errno (fp
, ENOMEM
);
420 ctf_next_destroy (i
);
423 str
= str_append (str
, typestr
);
425 ctf_dump_append (state
, str
);
426 ctf_next_destroy (i
);
427 return err
; /* errno is set for us. */
432 /* Dump a single variable into the cds_items. */
434 ctf_dump_var (const char *name
, ctf_id_t type
, void *arg
)
438 ctf_dump_state_t
*state
= arg
;
440 if (asprintf (&str
, "%s -> ", name
) < 0)
441 return (ctf_set_errno (state
->cds_fp
, errno
));
443 if ((typestr
= ctf_dump_format_type (state
->cds_fp
, type
,
444 CTF_ADD_ROOT
)) == NULL
)
447 return 0; /* Swallow the error. */
450 str
= str_append (str
, typestr
);
453 ctf_dump_append (state
, str
);
457 /* Dump a single member into the string in the membstate. */
459 ctf_dump_member (const char *name
, ctf_id_t id
, unsigned long offset
,
460 int depth
, void *arg
)
462 ctf_dump_membstate_t
*state
= arg
;
463 char *typestr
= NULL
;
466 int has_encoding
= 0;
469 for (i
= 0; i
< depth
; i
++)
470 *state
->cdm_str
= str_append (*state
->cdm_str
, " ");
472 if ((typestr
= ctf_type_aname (state
->cdm_fp
, id
)) == NULL
)
474 if (id
== 0 || ctf_errno (state
->cdm_fp
) == ECTF_NONREPRESENTABLE
)
476 if (asprintf (&bit
, " [0x%lx] (type not represented in CTF)",
480 *state
->cdm_str
= str_append (*state
->cdm_str
, bit
);
486 return -1; /* errno is set for us. */
489 if (ctf_type_encoding (state
->cdm_fp
, id
, &ep
) == 0)
492 ctf_type_encoding (state
->cdm_fp
, id
, &ep
);
494 if (asprintf (&bit
, " [0x%lx] (ID 0x%lx) (kind %i) %s%s%s:%i "
495 "(aligned at 0x%lx", offset
, id
,
496 ctf_type_kind (state
->cdm_fp
, id
), typestr
,
497 (name
[0] != 0 && typestr
[0] != 0) ? " " : "", name
,
498 ep
.cte_bits
, (unsigned long) ctf_type_align (state
->cdm_fp
,
504 if (asprintf (&bit
, " [0x%lx] (ID 0x%lx) (kind %i) %s%s%s "
505 "(aligned at 0x%lx", offset
, id
,
506 ctf_type_kind (state
->cdm_fp
, id
), typestr
,
507 (name
[0] != 0 && typestr
[0] != 0) ? " " : "", name
,
508 (unsigned long) ctf_type_align (state
->cdm_fp
, id
)) < 0)
512 *state
->cdm_str
= str_append (*state
->cdm_str
, bit
);
520 if (asprintf (&bit
, ", format 0x%x, offset:bits 0x%x:0x%x", ep
.cte_format
,
521 ep
.cte_offset
, ep
.cte_bits
) < 0)
523 *state
->cdm_str
= str_append (*state
->cdm_str
, bit
);
528 *state
->cdm_str
= str_append (*state
->cdm_str
, ")\n");
534 return (ctf_set_errno (state
->cdm_fp
, errno
));
537 /* Dump a single type into the cds_items. */
539 ctf_dump_type (ctf_id_t id
, int flag
, void *arg
)
542 ctf_dump_state_t
*state
= arg
;
543 ctf_dump_membstate_t membstate
= { &str
, state
->cds_fp
};
546 if ((str
= ctf_dump_format_type (state
->cds_fp
, id
, flag
)) == NULL
)
549 str
= str_append (str
, "\n");
550 if ((ctf_type_visit (state
->cds_fp
, id
, ctf_dump_member
, &membstate
)) < 0)
552 if (id
== 0 || ctf_errno (state
->cds_fp
) == ECTF_NONREPRESENTABLE
)
554 ctf_dump_append (state
, str
);
557 ctf_err_warn (state
->cds_fp
, 1, ctf_errno (state
->cds_fp
),
558 _("cannot visit members dumping type 0x%lx"), id
);
562 /* Trim off the last linefeed added by ctf_dump_member(). */
564 if (str
[len
-1] == '\n')
567 ctf_dump_append (state
, str
);
572 return 0; /* Swallow the error. */
575 /* Dump the string table into the cds_items. */
578 ctf_dump_str (ctf_dict_t
*fp
, ctf_dump_state_t
*state
)
580 const char *s
= fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
;
582 for (; s
< fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
+
583 fp
->ctf_str
[CTF_STRTAB_0
].cts_len
;)
586 if (asprintf (&str
, "%lx: %s",
587 (unsigned long) (s
- fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
),
589 return (ctf_set_errno (fp
, errno
));
590 ctf_dump_append (state
, str
);
597 /* Dump a particular section of a CTF file, in textual form. Call with a
598 pointer to a NULL STATE: each call emits a dynamically allocated string
599 containing a description of one entity in the specified section, in order.
600 Only the first call (with a NULL state) may vary SECT. Once the CTF section
601 has been entirely dumped, the call returns NULL and frees and annuls the
602 STATE, ready for another section to be dumped. The returned textual content
603 may span multiple lines: between each call the FUNC is called with one
604 textual line at a time, and should return a suitably decorated line (it can
605 allocate a new one and return it if it likes). */
608 ctf_dump (ctf_dict_t
*fp
, ctf_dump_state_t
**statep
, ctf_sect_names_t sect
,
609 ctf_dump_decorate_f
*func
, void *arg
)
613 ctf_dump_state_t
*state
= NULL
;
617 /* Data collection. Transforming a call-at-a-time iterator into a
618 return-at-a-time iterator in a language without call/cc is annoying. It
619 is easiest to simply collect everything at once and then return it bit
620 by bit. The first call will take (much) longer than otherwise, but the
621 amortized time needed is the same. */
623 if ((*statep
= malloc (sizeof (struct ctf_dump_state
))) == NULL
)
625 ctf_set_errno (fp
, ENOMEM
);
630 memset (state
, 0, sizeof (struct ctf_dump_state
));
632 state
->cds_sect
= sect
;
636 case CTF_SECT_HEADER
:
637 ctf_dump_header (fp
, state
);
640 if (ctf_label_iter (fp
, ctf_dump_label
, state
) < 0)
642 if (ctf_errno (fp
) != ECTF_NOLABELDATA
)
643 goto end
; /* errno is set for us. */
644 ctf_set_errno (fp
, 0);
648 if (ctf_dump_objts (fp
, state
, 0) < 0)
649 goto end
; /* errno is set for us. */
652 if (ctf_dump_objts (fp
, state
, 1) < 0)
653 goto end
; /* errno is set for us. */
656 if (ctf_variable_iter (fp
, ctf_dump_var
, state
) < 0)
657 goto end
; /* errno is set for us. */
660 if (ctf_type_iter_all (fp
, ctf_dump_type
, state
) < 0)
661 goto end
; /* errno is set for us. */
664 ctf_dump_str (fp
, state
);
667 ctf_set_errno (fp
, ECTF_DUMPSECTUNKNOWN
);
675 if (state
->cds_sect
!= sect
)
677 ctf_set_errno (fp
, ECTF_DUMPSECTCHANGED
);
682 if (state
->cds_current
== NULL
)
683 state
->cds_current
= ctf_list_next (&state
->cds_items
);
685 state
->cds_current
= ctf_list_next (state
->cds_current
);
687 if (state
->cds_current
== NULL
)
690 /* Hookery. There is some extra complexity to preserve linefeeds within each
691 item while removing linefeeds at the end. */
697 for (line
= state
->cds_current
->cdi_item
; line
&& *line
; )
702 nline
= strchr (line
, '\n');
706 ret
= func (sect
, line
, arg
);
707 str
= str_append (str
, ret
);
708 str
= str_append (str
, "\n");
723 if (str
[len
-1] == '\n')
728 str
= strdup (state
->cds_current
->cdi_item
);
731 ctf_set_errno (fp
, ENOMEM
);
736 ctf_set_errno (fp
, 0);
740 ctf_dump_free (state
);
742 ctf_set_errno (fp
, 0);