1 /* Textual dumping of CTF data.
2 Copyright (C) 2019 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 /* One item to be dumped, in string form. */
25 typedef struct ctf_dump_item
31 /* Cross-call state for dumping. Basically just enough to track the section in
32 use and a list of return strings. */
36 ctf_sect_names_t cds_sect
;
38 ctf_dump_item_t
*cds_current
;
42 /* Cross-call state for ctf_dump_member. */
44 typedef struct ctf_dump_membstate
48 } ctf_dump_membstate_t
;
51 ctf_dump_append (ctf_dump_state_t
*state
, char *str
)
55 if ((cdi
= ctf_alloc (sizeof (struct ctf_dump_item
))) == NULL
)
56 return (ctf_set_errno (state
->cds_fp
, ENOMEM
));
59 ctf_list_append (&state
->cds_items
, cdi
);
64 ctf_dump_free (ctf_dump_state_t
*state
)
66 ctf_dump_item_t
*cdi
, *next_cdi
;
71 for (cdi
= ctf_list_next (&state
->cds_items
); cdi
!= NULL
;
75 next_cdi
= ctf_list_next (cdi
);
80 /* Slices need special handling to distinguish them from their referenced
84 ctf_is_slice (ctf_file_t
*fp
, ctf_id_t id
, ctf_encoding_t
*enc
)
86 int kind
= ctf_type_kind (fp
, id
);
88 return (((kind
== CTF_K_INTEGER
) || (kind
== CTF_K_ENUM
)
89 || (kind
== CTF_K_FLOAT
))
90 && ctf_type_reference (fp
, id
) != CTF_ERR
91 && ctf_type_encoding (fp
, id
, enc
) == 0);
94 /* Return a dump for a single type, without member info: but do show the
98 ctf_dump_format_type (ctf_file_t
*fp
, ctf_id_t id
, int flag
)
101 char *str
= NULL
, *bit
= NULL
, *buf
= NULL
;
107 const char *nonroot_leader
= "";
108 const char *nonroot_trailer
= "";
111 if (flag
== CTF_ADD_NONROOT
)
113 nonroot_leader
= "{";
114 nonroot_trailer
= "}";
117 buf
= ctf_type_aname (fp
, id
);
121 /* Slices get a different print representation. */
123 if (ctf_is_slice (fp
, id
, &enc
))
125 ctf_type_encoding (fp
, id
, &enc
);
126 if (asprintf (&bit
, " %s%lx: [slice 0x%x:0x%x]%s",
127 nonroot_leader
, id
, enc
.cte_offset
, enc
.cte_bits
,
128 nonroot_trailer
) < 0)
133 if (asprintf (&bit
, " %s%lx: %s (size 0x%lx)%s", nonroot_leader
,
134 id
, buf
[0] == '\0' ? "(nameless)" : buf
,
135 (unsigned long) ctf_type_size (fp
, id
),
136 nonroot_trailer
) < 0)
141 str
= ctf_str_append (str
, bit
);
145 new_id
= ctf_type_reference (fp
, id
);
146 if (new_id
!= CTF_ERR
)
147 str
= ctf_str_append (str
, " ->");
148 } while (new_id
!= CTF_ERR
);
150 if (ctf_errno (fp
) != ECTF_NOTREF
)
162 ctf_set_errno (fp
, ENOMEM
);
166 /* Dump one string field from the file header into the cds_items. */
168 ctf_dump_header_strfield (ctf_file_t
*fp
, ctf_dump_state_t
*state
,
169 const char *name
, uint32_t value
)
174 if (asprintf (&str
, "%s: %s\n", name
, ctf_strptr (fp
, value
)) < 0)
176 ctf_dump_append (state
, str
);
181 return (ctf_set_errno (fp
, -ENOMEM
));
184 /* Dump one section-offset field from the file header into the cds_items. */
186 ctf_dump_header_sectfield (ctf_file_t
*fp
, ctf_dump_state_t
*state
,
187 const char *sect
, uint32_t off
, uint32_t nextoff
)
192 if (asprintf (&str
, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect
,
193 (unsigned long) off
, (unsigned long) (nextoff
- 1),
194 (unsigned long) (nextoff
- off
)) < 0)
196 ctf_dump_append (state
, str
);
201 return (ctf_set_errno (fp
, -ENOMEM
));
204 /* Dump the file header into the cds_items. */
206 ctf_dump_header (ctf_file_t
*fp
, ctf_dump_state_t
*state
)
209 const ctf_header_t
*hp
= fp
->ctf_header
;
210 const char *vertab
[] =
212 NULL
, "CTF_VERSION_1",
213 "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
216 "CTF_VERSION_3", NULL
218 const char *verstr
= NULL
;
220 if (asprintf (&str
, "Magic number: %x\n", hp
->cth_magic
) < 0)
222 ctf_dump_append (state
, str
);
224 if (hp
->cth_version
<= CTF_VERSION
)
225 verstr
= vertab
[hp
->cth_version
];
228 verstr
= "(not a valid version)";
230 if (asprintf (&str
, "Version: %i (%s)\n", hp
->cth_version
,
233 ctf_dump_append (state
, str
);
235 /* Everything else is only printed if present. */
237 /* The flags are unusual in that they represent the ctf_file_t *in memory*:
238 flags representing compression, etc, are turned off as the file is
239 decompressed. So we store a copy of the flags before they are changed, for
242 if (fp
->ctf_openflags
> 0)
244 if (fp
->ctf_openflags
)
245 if (asprintf (&str
, "Flags: 0x%x (%s)", fp
->ctf_openflags
,
246 fp
->ctf_openflags
& CTF_F_COMPRESS
? "CTF_F_COMPRESS"
249 ctf_dump_append (state
, str
);
252 if (ctf_dump_header_strfield (fp
, state
, "Parent label",
253 hp
->cth_parlabel
) < 0)
256 if (ctf_dump_header_strfield (fp
, state
, "Parent name", hp
->cth_parname
) < 0)
259 if (ctf_dump_header_strfield (fp
, state
, "Compilation unit name",
263 if (ctf_dump_header_sectfield (fp
, state
, "Label section", hp
->cth_lbloff
,
264 hp
->cth_objtoff
) < 0)
267 if (ctf_dump_header_sectfield (fp
, state
, "Data object section",
268 hp
->cth_objtoff
, hp
->cth_funcoff
) < 0)
271 if (ctf_dump_header_sectfield (fp
, state
, "Function info section",
272 hp
->cth_funcoff
, hp
->cth_varoff
) < 0)
275 if (ctf_dump_header_sectfield (fp
, state
, "Variable section",
276 hp
->cth_varoff
, hp
->cth_typeoff
) < 0)
279 if (ctf_dump_header_sectfield (fp
, state
, "Type section",
280 hp
->cth_typeoff
, hp
->cth_stroff
) < 0)
283 if (ctf_dump_header_sectfield (fp
, state
, "String section", hp
->cth_stroff
,
284 hp
->cth_stroff
+ hp
->cth_strlen
+ 1) < 0)
289 return (ctf_set_errno (fp
, -ENOMEM
));
292 /* Dump a single label into the cds_items. */
295 ctf_dump_label (const char *name
, const ctf_lblinfo_t
*info
,
300 ctf_dump_state_t
*state
= arg
;
302 if (asprintf (&str
, "%s -> ", name
) < 0)
303 return (ctf_set_errno (state
->cds_fp
, ENOMEM
));
305 if ((typestr
= ctf_dump_format_type (state
->cds_fp
, info
->ctb_type
,
306 CTF_ADD_ROOT
)) == NULL
)
309 return -1; /* errno is set for us. */
312 str
= ctf_str_append (str
, typestr
);
315 ctf_dump_append (state
, str
);
319 /* Dump all the object entries into the cds_items. (There is no iterator for
320 this section, so we just do it in a loop, and this function handles all of
321 them, rather than only one. */
324 ctf_dump_objts (ctf_file_t
*fp
, ctf_dump_state_t
*state
)
328 for (i
= 0; i
< fp
->ctf_nsyms
; i
++)
332 const char *sym_name
;
335 if ((type
= ctf_lookup_by_symbol (state
->cds_fp
, i
)) == CTF_ERR
)
336 switch (ctf_errno (state
->cds_fp
))
338 /* Most errors are just an indication that this symbol is not a data
339 symbol, but this one indicates that we were called wrong, on a
340 CTF file with no associated symbol table. */
349 sym_name
= ctf_lookup_symbol_name (fp
, i
);
350 if (sym_name
[0] == '\0')
352 if (asprintf (&str
, "%lx -> ", (unsigned long) i
) < 0)
353 return (ctf_set_errno (fp
, ENOMEM
));
357 if (asprintf (&str
, "%s (%lx) -> ", sym_name
, (unsigned long) i
) < 0)
358 return (ctf_set_errno (fp
, ENOMEM
));
362 if ((typestr
= ctf_dump_format_type (state
->cds_fp
, type
,
363 CTF_ADD_ROOT
)) == NULL
)
366 return -1; /* errno is set for us. */
369 str
= ctf_str_append (str
, typestr
);
372 ctf_dump_append (state
, str
);
377 /* Dump all the function entries into the cds_items. (As above, there is no
378 iterator for this section.) */
381 ctf_dump_funcs (ctf_file_t
*fp
, ctf_dump_state_t
*state
)
385 for (i
= 0; i
< fp
->ctf_nsyms
; i
++)
389 const char *sym_name
;
395 if ((type
= ctf_func_info (state
->cds_fp
, i
, &fi
)) == CTF_ERR
)
396 switch (ctf_errno (state
->cds_fp
))
398 /* Most errors are just an indication that this symbol is not a data
399 symbol, but this one indicates that we were called wrong, on a
400 CTF file with no associated symbol table. */
408 if ((args
= calloc (fi
.ctc_argc
, sizeof (ctf_id_t
))) == NULL
)
409 return (ctf_set_errno (fp
, ENOMEM
));
412 if ((str
= ctf_type_aname (state
->cds_fp
, type
)) == NULL
)
415 str
= ctf_str_append (str
, " ");
419 sym_name
= ctf_lookup_symbol_name (fp
, i
);
420 if (sym_name
[0] == '\0')
422 if (asprintf (&bit
, "0x%lx ", (unsigned long) i
) < 0)
427 if (asprintf (&bit
, "%s (0x%lx) ", sym_name
, (unsigned long) i
) < 0)
430 str
= ctf_str_append (str
, bit
);
431 str
= ctf_str_append (str
, " (");
434 /* Function arguments. */
436 if (ctf_func_args (state
->cds_fp
, i
, fi
.ctc_argc
, args
) < 0)
439 for (j
= 0; j
< fi
.ctc_argc
; j
++)
441 if ((bit
= ctf_type_aname (state
->cds_fp
, args
[j
])) == NULL
)
443 str
= ctf_str_append (str
, bit
);
444 if ((j
< fi
.ctc_argc
- 1) || (fi
.ctc_flags
& CTF_FUNC_VARARG
))
445 str
= ctf_str_append (str
, ", ");
449 if (fi
.ctc_flags
& CTF_FUNC_VARARG
)
450 str
= ctf_str_append (str
, "...");
451 str
= ctf_str_append (str
, ")");
454 ctf_dump_append (state
, str
);
460 return (ctf_set_errno (fp
, ENOMEM
));
464 return -1; /* errno is set for us. */
469 /* Dump a single variable into the cds_items. */
471 ctf_dump_var (const char *name
, ctf_id_t type
, void *arg
)
475 ctf_dump_state_t
*state
= arg
;
477 if (asprintf (&str
, "%s -> ", name
) < 0)
478 return (ctf_set_errno (state
->cds_fp
, ENOMEM
));
480 if ((typestr
= ctf_dump_format_type (state
->cds_fp
, type
,
481 CTF_ADD_ROOT
)) == NULL
)
484 return -1; /* errno is set for us. */
487 str
= ctf_str_append (str
, typestr
);
490 ctf_dump_append (state
, str
);
494 /* Dump a single member into the string in the membstate. */
496 ctf_dump_member (const char *name
, ctf_id_t id
, unsigned long offset
,
497 int depth
, void *arg
)
499 ctf_dump_membstate_t
*state
= arg
;
500 char *typestr
= NULL
;
505 for (i
= 0; i
< depth
; i
++)
506 *state
->cdm_str
= ctf_str_append (*state
->cdm_str
, " ");
508 if ((typestr
= ctf_type_aname (state
->cdm_fp
, id
)) == NULL
)
511 if (asprintf (&bit
, " [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
512 offset
, id
, ctf_type_kind (state
->cdm_fp
, id
), typestr
, name
,
513 (unsigned long) ctf_type_align (state
->cdm_fp
, id
)) < 0)
515 *state
->cdm_str
= ctf_str_append (*state
->cdm_str
, bit
);
521 if ((ctf_type_kind (state
->cdm_fp
, id
) == CTF_K_INTEGER
)
522 || (ctf_type_kind (state
->cdm_fp
, id
) == CTF_K_FLOAT
)
523 || (ctf_is_slice (state
->cdm_fp
, id
, &ep
) == CTF_K_ENUM
))
525 ctf_type_encoding (state
->cdm_fp
, id
, &ep
);
526 if (asprintf (&bit
, ", format 0x%x, offset:bits 0x%x:0x%x", ep
.cte_format
,
527 ep
.cte_offset
, ep
.cte_bits
) < 0)
529 *state
->cdm_str
= ctf_str_append (*state
->cdm_str
, bit
);
534 *state
->cdm_str
= ctf_str_append (*state
->cdm_str
, ")\n");
540 return (ctf_set_errno (state
->cdm_fp
, ENOMEM
));
543 /* Dump a single type into the cds_items. */
546 ctf_dump_type (ctf_id_t id
, int flag
, void *arg
)
549 ctf_dump_state_t
*state
= arg
;
550 ctf_dump_membstate_t membstate
= { &str
, state
->cds_fp
};
553 if ((str
= ctf_dump_format_type (state
->cds_fp
, id
, flag
)) == NULL
)
556 str
= ctf_str_append (str
, "\n");
557 if ((ctf_type_visit (state
->cds_fp
, id
, ctf_dump_member
, &membstate
)) < 0)
560 /* Trim off the last linefeed added by ctf_dump_member(). */
562 if (str
[len
-1] == '\n')
565 ctf_dump_append (state
, str
);
570 return -1; /* errno is set for us. */
573 /* Dump the string table into the cds_items. */
576 ctf_dump_str (ctf_file_t
*fp
, ctf_dump_state_t
*state
)
578 const char *s
= fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
;
580 for (; s
< fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
+
581 fp
->ctf_str
[CTF_STRTAB_0
].cts_len
;)
584 if (asprintf (&str
, "%lx: %s",
585 (unsigned long) (s
- fp
->ctf_str
[CTF_STRTAB_0
].cts_strs
),
587 return (ctf_set_errno (fp
, ENOMEM
));
588 ctf_dump_append (state
, str
);
595 /* Dump a particular section of a CTF file, in textual form. Call with a
596 pointer to a NULL STATE: each call emits a dynamically allocated string
597 containing a description of one entity in the specified section, in order.
598 Only the first call (with a NULL state) may vary SECT. Once the CTF section
599 has been entirely dumped, the call returns NULL and frees and annuls the
600 STATE, ready for another section to be dumped. The returned textual content
601 may span multiple lines: between each call the FUNC is called with one
602 textual line at a time, and should return a suitably decorated line (it can
603 allocate a new one and return it if it likes). */
606 ctf_dump (ctf_file_t
*fp
, ctf_dump_state_t
**statep
, ctf_sect_names_t sect
,
607 ctf_dump_decorate_f
*func
, void *arg
)
611 ctf_dump_state_t
*state
= NULL
;
615 /* Data collection. Transforming a call-at-a-time iterator into a
616 return-at-a-time iterator in a language without call/cc is annoying. It
617 is easiest to simply collect everything at once and then return it bit
618 by bit. The first call will take (much) longer than otherwise, but the
619 amortized time needed is the same. */
621 if ((*statep
= ctf_alloc (sizeof (struct ctf_dump_state
))) == NULL
)
623 ctf_set_errno (fp
, ENOMEM
);
628 memset (state
, 0, sizeof (struct ctf_dump_state
));
630 state
->cds_sect
= sect
;
634 case CTF_SECT_HEADER
:
635 ctf_dump_header (fp
, state
);
638 if (ctf_label_iter (fp
, ctf_dump_label
, state
) < 0)
640 if (ctf_errno (fp
) != ECTF_NOLABELDATA
)
641 goto end
; /* errno is set for us. */
642 ctf_set_errno (fp
, 0);
646 if (ctf_dump_objts (fp
, state
) < 0)
647 goto end
; /* errno is set for us. */
650 if (ctf_dump_funcs (fp
, state
) < 0)
651 goto end
; /* errno is set for us. */
654 if (ctf_variable_iter (fp
, ctf_dump_var
, state
) < 0)
655 goto end
; /* errno is set for us. */
658 if (ctf_type_iter_all (fp
, ctf_dump_type
, state
) < 0)
659 goto end
; /* errno is set for us. */
662 ctf_dump_str (fp
, state
);
665 ctf_set_errno (fp
, ECTF_DUMPSECTUNKNOWN
);
673 if (state
->cds_sect
!= sect
)
675 ctf_set_errno (fp
, ECTF_DUMPSECTCHANGED
);
680 if (state
->cds_current
== NULL
)
681 state
->cds_current
= ctf_list_next (&state
->cds_items
);
683 state
->cds_current
= ctf_list_next (state
->cds_current
);
685 if (state
->cds_current
== NULL
)
688 /* Hookery. There is some extra complexity to preserve linefeeds within each
689 item while removing linefeeds at the end. */
695 for (line
= state
->cds_current
->cdi_item
; line
&& *line
; )
700 nline
= strchr (line
, '\n');
704 ret
= func (sect
, line
, arg
);
705 str
= ctf_str_append (str
, ret
);
706 str
= ctf_str_append (str
, "\n");
721 if (str
[len
-1] == '\n')
725 str
= strdup (state
->cds_current
->cdi_item
);
727 ctf_set_errno (fp
, 0);
731 ctf_dump_free (state
);
733 ctf_set_errno (fp
, 0);