1867d70fcSchristos /* Textual dumping of CTF data.
2*c42dbd0eSchristos Copyright (C) 2019-2022 Free Software Foundation, Inc.
3867d70fcSchristos
4867d70fcSchristos This file is part of libctf.
5867d70fcSchristos
6867d70fcSchristos libctf is free software; you can redistribute it and/or modify it under
7867d70fcSchristos the terms of the GNU General Public License as published by the Free
8867d70fcSchristos Software Foundation; either version 3, or (at your option) any later
9867d70fcSchristos version.
10867d70fcSchristos
11867d70fcSchristos This program is distributed in the hope that it will be useful, but
12867d70fcSchristos WITHOUT ANY WARRANTY; without even the implied warranty of
13867d70fcSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14867d70fcSchristos See the GNU General Public License for more details.
15867d70fcSchristos
16867d70fcSchristos You should have received a copy of the GNU General Public License
17867d70fcSchristos along with this program; see the file COPYING. If not see
18867d70fcSchristos <http://www.gnu.org/licenses/>. */
19867d70fcSchristos
20867d70fcSchristos #include <ctf-impl.h>
21867d70fcSchristos #include <string.h>
22867d70fcSchristos
23867d70fcSchristos #define str_append(s, a) ctf_str_append_noerr (s, a)
24867d70fcSchristos
25867d70fcSchristos /* One item to be dumped, in string form. */
26867d70fcSchristos
27867d70fcSchristos typedef struct ctf_dump_item
28867d70fcSchristos {
29867d70fcSchristos ctf_list_t cdi_list;
30867d70fcSchristos char *cdi_item;
31867d70fcSchristos } ctf_dump_item_t;
32867d70fcSchristos
33867d70fcSchristos /* Cross-call state for dumping. Basically just enough to track the section in
34867d70fcSchristos use and a list of return strings. */
35867d70fcSchristos
36867d70fcSchristos struct ctf_dump_state
37867d70fcSchristos {
38867d70fcSchristos ctf_sect_names_t cds_sect;
39*c42dbd0eSchristos ctf_dict_t *cds_fp;
40867d70fcSchristos ctf_dump_item_t *cds_current;
41867d70fcSchristos ctf_list_t cds_items;
42867d70fcSchristos };
43867d70fcSchristos
44867d70fcSchristos /* Cross-call state for ctf_dump_member. */
45867d70fcSchristos
46867d70fcSchristos typedef struct ctf_dump_membstate
47867d70fcSchristos {
48867d70fcSchristos char **cdm_str;
49*c42dbd0eSchristos ctf_dict_t *cdm_fp;
50*c42dbd0eSchristos const char *cdm_toplevel_indent;
51867d70fcSchristos } ctf_dump_membstate_t;
52867d70fcSchristos
53867d70fcSchristos static int
ctf_dump_append(ctf_dump_state_t * state,char * str)54867d70fcSchristos ctf_dump_append (ctf_dump_state_t *state, char *str)
55867d70fcSchristos {
56867d70fcSchristos ctf_dump_item_t *cdi;
57867d70fcSchristos
58867d70fcSchristos if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
59867d70fcSchristos return (ctf_set_errno (state->cds_fp, ENOMEM));
60867d70fcSchristos
61867d70fcSchristos cdi->cdi_item = str;
62867d70fcSchristos ctf_list_append (&state->cds_items, cdi);
63867d70fcSchristos return 0;
64867d70fcSchristos }
65867d70fcSchristos
66867d70fcSchristos static void
ctf_dump_free(ctf_dump_state_t * state)67867d70fcSchristos ctf_dump_free (ctf_dump_state_t *state)
68867d70fcSchristos {
69867d70fcSchristos ctf_dump_item_t *cdi, *next_cdi;
70867d70fcSchristos
71867d70fcSchristos if (state == NULL)
72867d70fcSchristos return;
73867d70fcSchristos
74867d70fcSchristos for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
75867d70fcSchristos cdi = next_cdi)
76867d70fcSchristos {
77867d70fcSchristos free (cdi->cdi_item);
78867d70fcSchristos next_cdi = ctf_list_next (cdi);
79867d70fcSchristos free (cdi);
80867d70fcSchristos }
81867d70fcSchristos }
82867d70fcSchristos
83*c42dbd0eSchristos /* Return a dump for a single type, without member info: but do optionally show
84*c42dbd0eSchristos the type's references. */
85867d70fcSchristos
86*c42dbd0eSchristos #define CTF_FT_REFS 0x2 /* Print referenced types. */
87*c42dbd0eSchristos #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */
88*c42dbd0eSchristos #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */
89867d70fcSchristos
90867d70fcSchristos static char *
ctf_dump_format_type(ctf_dict_t * fp,ctf_id_t id,int flag)91*c42dbd0eSchristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
92867d70fcSchristos {
93867d70fcSchristos ctf_id_t new_id;
94867d70fcSchristos char *str = NULL, *bit = NULL, *buf = NULL;
95867d70fcSchristos
96*c42dbd0eSchristos ctf_set_errno (fp, 0);
97867d70fcSchristos new_id = id;
98867d70fcSchristos do
99867d70fcSchristos {
100*c42dbd0eSchristos ctf_encoding_t ep;
101*c42dbd0eSchristos ctf_arinfo_t ar;
102*c42dbd0eSchristos int kind, unsliced_kind;
103*c42dbd0eSchristos ssize_t size, align;
104867d70fcSchristos const char *nonroot_leader = "";
105867d70fcSchristos const char *nonroot_trailer = "";
106*c42dbd0eSchristos const char *idstr = "";
107867d70fcSchristos
108867d70fcSchristos id = new_id;
109867d70fcSchristos if (flag == CTF_ADD_NONROOT)
110867d70fcSchristos {
111867d70fcSchristos nonroot_leader = "{";
112867d70fcSchristos nonroot_trailer = "}";
113867d70fcSchristos }
114867d70fcSchristos
115867d70fcSchristos buf = ctf_type_aname (fp, id);
116867d70fcSchristos if (!buf)
117867d70fcSchristos {
118867d70fcSchristos if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
119867d70fcSchristos {
120*c42dbd0eSchristos ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
121867d70fcSchristos str = str_append (str, " (type not represented in CTF)");
122*c42dbd0eSchristos return str;
123867d70fcSchristos }
124867d70fcSchristos
125867d70fcSchristos goto err;
126867d70fcSchristos }
127867d70fcSchristos
128*c42dbd0eSchristos if (flag & CTF_FT_ID)
129*c42dbd0eSchristos idstr = "ID ";
130*c42dbd0eSchristos if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
131*c42dbd0eSchristos id, ctf_type_kind (fp, id)) < 0)
132867d70fcSchristos goto oom;
133867d70fcSchristos str = str_append (str, bit);
134867d70fcSchristos free (bit);
135867d70fcSchristos bit = NULL;
136867d70fcSchristos
137*c42dbd0eSchristos if (buf[0] != '\0')
138*c42dbd0eSchristos str = str_append (str, buf);
139*c42dbd0eSchristos
140*c42dbd0eSchristos free (buf);
141*c42dbd0eSchristos buf = NULL;
142*c42dbd0eSchristos
143*c42dbd0eSchristos unsliced_kind = ctf_type_kind_unsliced (fp, id);
144*c42dbd0eSchristos kind = ctf_type_kind (fp, id);
145*c42dbd0eSchristos
146*c42dbd0eSchristos /* Report encodings of everything with an encoding other than enums:
147*c42dbd0eSchristos base-type enums cannot have a nonzero cte_offset or cte_bits value.
148*c42dbd0eSchristos (Slices of them can, but they are of kind CTF_K_SLICE.) */
149*c42dbd0eSchristos if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
150*c42dbd0eSchristos {
151*c42dbd0eSchristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
152*c42dbd0eSchristos && flag & CTF_FT_BITFIELD)
153*c42dbd0eSchristos {
154*c42dbd0eSchristos if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
155*c42dbd0eSchristos goto oom;
156*c42dbd0eSchristos str = str_append (str, bit);
157*c42dbd0eSchristos free (bit);
158*c42dbd0eSchristos bit = NULL;
159*c42dbd0eSchristos }
160*c42dbd0eSchristos
161*c42dbd0eSchristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
162*c42dbd0eSchristos || ep.cte_offset != 0)
163*c42dbd0eSchristos {
164*c42dbd0eSchristos const char *slice = "";
165*c42dbd0eSchristos
166*c42dbd0eSchristos if (unsliced_kind == CTF_K_SLICE)
167*c42dbd0eSchristos slice = "slice ";
168*c42dbd0eSchristos
169*c42dbd0eSchristos if (asprintf (&bit, " [%s0x%x:0x%x]",
170*c42dbd0eSchristos slice, ep.cte_offset, ep.cte_bits) < 0)
171*c42dbd0eSchristos goto oom;
172*c42dbd0eSchristos str = str_append (str, bit);
173*c42dbd0eSchristos free (bit);
174*c42dbd0eSchristos bit = NULL;
175*c42dbd0eSchristos }
176*c42dbd0eSchristos
177*c42dbd0eSchristos if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
178*c42dbd0eSchristos goto oom;
179*c42dbd0eSchristos str = str_append (str, bit);
180*c42dbd0eSchristos free (bit);
181*c42dbd0eSchristos bit = NULL;
182*c42dbd0eSchristos }
183*c42dbd0eSchristos
184*c42dbd0eSchristos size = ctf_type_size (fp, id);
185*c42dbd0eSchristos if (kind != CTF_K_FUNCTION && size >= 0)
186*c42dbd0eSchristos {
187*c42dbd0eSchristos if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
188*c42dbd0eSchristos goto oom;
189*c42dbd0eSchristos
190*c42dbd0eSchristos str = str_append (str, bit);
191*c42dbd0eSchristos free (bit);
192*c42dbd0eSchristos bit = NULL;
193*c42dbd0eSchristos }
194*c42dbd0eSchristos
195*c42dbd0eSchristos align = ctf_type_align (fp, id);
196*c42dbd0eSchristos if (align >= 0)
197*c42dbd0eSchristos {
198*c42dbd0eSchristos if (asprintf (&bit, " (aligned at 0x%lx)",
199*c42dbd0eSchristos (unsigned long int) align) < 0)
200*c42dbd0eSchristos goto oom;
201*c42dbd0eSchristos
202*c42dbd0eSchristos str = str_append (str, bit);
203*c42dbd0eSchristos free (bit);
204*c42dbd0eSchristos bit = NULL;
205*c42dbd0eSchristos }
206*c42dbd0eSchristos
207*c42dbd0eSchristos if (nonroot_trailer[0] != 0)
208*c42dbd0eSchristos str = str_append (str, nonroot_trailer);
209*c42dbd0eSchristos
210*c42dbd0eSchristos /* Just exit after one iteration if we are not showing the types this type
211*c42dbd0eSchristos references. */
212*c42dbd0eSchristos if (!(flag & CTF_FT_REFS))
213*c42dbd0eSchristos return str;
214*c42dbd0eSchristos
215*c42dbd0eSchristos /* Keep going as long as this type references another. We consider arrays
216*c42dbd0eSchristos to "reference" their element type. */
217*c42dbd0eSchristos
218*c42dbd0eSchristos if (kind == CTF_K_ARRAY)
219*c42dbd0eSchristos {
220*c42dbd0eSchristos if (ctf_array_info (fp, id, &ar) < 0)
221*c42dbd0eSchristos goto err;
222*c42dbd0eSchristos new_id = ar.ctr_contents;
223*c42dbd0eSchristos }
224*c42dbd0eSchristos else
225867d70fcSchristos new_id = ctf_type_reference (fp, id);
226867d70fcSchristos if (new_id != CTF_ERR)
227867d70fcSchristos str = str_append (str, " -> ");
228*c42dbd0eSchristos }
229*c42dbd0eSchristos while (new_id != CTF_ERR);
230867d70fcSchristos
231867d70fcSchristos if (ctf_errno (fp) != ECTF_NOTREF)
232867d70fcSchristos {
233867d70fcSchristos free (str);
234867d70fcSchristos return NULL;
235867d70fcSchristos }
236867d70fcSchristos
237867d70fcSchristos return str;
238867d70fcSchristos
239867d70fcSchristos oom:
240867d70fcSchristos ctf_set_errno (fp, errno);
241867d70fcSchristos err:
242*c42dbd0eSchristos ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
243867d70fcSchristos free (buf);
244867d70fcSchristos free (str);
245867d70fcSchristos free (bit);
246867d70fcSchristos return NULL;
247867d70fcSchristos }
248867d70fcSchristos
249867d70fcSchristos /* Dump one string field from the file header into the cds_items. */
250867d70fcSchristos static int
ctf_dump_header_strfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * name,uint32_t value)251*c42dbd0eSchristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
252867d70fcSchristos const char *name, uint32_t value)
253867d70fcSchristos {
254867d70fcSchristos char *str;
255867d70fcSchristos if (value)
256867d70fcSchristos {
257867d70fcSchristos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
258867d70fcSchristos goto err;
259867d70fcSchristos ctf_dump_append (state, str);
260867d70fcSchristos }
261867d70fcSchristos return 0;
262867d70fcSchristos
263867d70fcSchristos err:
264867d70fcSchristos return (ctf_set_errno (fp, errno));
265867d70fcSchristos }
266867d70fcSchristos
267867d70fcSchristos /* Dump one section-offset field from the file header into the cds_items. */
268867d70fcSchristos static int
ctf_dump_header_sectfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * sect,uint32_t off,uint32_t nextoff)269*c42dbd0eSchristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
270867d70fcSchristos const char *sect, uint32_t off, uint32_t nextoff)
271867d70fcSchristos {
272867d70fcSchristos char *str;
273867d70fcSchristos if (nextoff - off)
274867d70fcSchristos {
275867d70fcSchristos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
276867d70fcSchristos (unsigned long) off, (unsigned long) (nextoff - 1),
277867d70fcSchristos (unsigned long) (nextoff - off)) < 0)
278867d70fcSchristos goto err;
279867d70fcSchristos ctf_dump_append (state, str);
280867d70fcSchristos }
281867d70fcSchristos return 0;
282867d70fcSchristos
283867d70fcSchristos err:
284867d70fcSchristos return (ctf_set_errno (fp, errno));
285867d70fcSchristos }
286867d70fcSchristos
287867d70fcSchristos /* Dump the file header into the cds_items. */
288867d70fcSchristos static int
ctf_dump_header(ctf_dict_t * fp,ctf_dump_state_t * state)289*c42dbd0eSchristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
290867d70fcSchristos {
291867d70fcSchristos char *str;
292*c42dbd0eSchristos char *flagstr = NULL;
293867d70fcSchristos const ctf_header_t *hp = fp->ctf_header;
294867d70fcSchristos const char *vertab[] =
295867d70fcSchristos {
296867d70fcSchristos NULL, "CTF_VERSION_1",
297867d70fcSchristos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
298867d70fcSchristos "boundaries)",
299867d70fcSchristos "CTF_VERSION_2",
300867d70fcSchristos "CTF_VERSION_3", NULL
301867d70fcSchristos };
302867d70fcSchristos const char *verstr = NULL;
303867d70fcSchristos
304*c42dbd0eSchristos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
305867d70fcSchristos goto err;
306867d70fcSchristos ctf_dump_append (state, str);
307867d70fcSchristos
308867d70fcSchristos if (hp->cth_version <= CTF_VERSION)
309867d70fcSchristos verstr = vertab[hp->cth_version];
310867d70fcSchristos
311867d70fcSchristos if (verstr == NULL)
312867d70fcSchristos verstr = "(not a valid version)";
313867d70fcSchristos
314867d70fcSchristos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
315867d70fcSchristos verstr) < 0)
316867d70fcSchristos goto err;
317867d70fcSchristos ctf_dump_append (state, str);
318867d70fcSchristos
319867d70fcSchristos /* Everything else is only printed if present. */
320867d70fcSchristos
321*c42dbd0eSchristos /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
322867d70fcSchristos flags representing compression, etc, are turned off as the file is
323867d70fcSchristos decompressed. So we store a copy of the flags before they are changed, for
324867d70fcSchristos the dumper. */
325867d70fcSchristos
326867d70fcSchristos if (fp->ctf_openflags > 0)
327867d70fcSchristos {
328*c42dbd0eSchristos if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
329*c42dbd0eSchristos fp->ctf_openflags & CTF_F_COMPRESS
330*c42dbd0eSchristos ? "CTF_F_COMPRESS": "",
331*c42dbd0eSchristos (fp->ctf_openflags & CTF_F_COMPRESS)
332*c42dbd0eSchristos && (fp->ctf_openflags & ~CTF_F_COMPRESS)
333*c42dbd0eSchristos ? ", " : "",
334*c42dbd0eSchristos fp->ctf_openflags & CTF_F_NEWFUNCINFO
335*c42dbd0eSchristos ? "CTF_F_NEWFUNCINFO" : "",
336*c42dbd0eSchristos (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
337*c42dbd0eSchristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
338*c42dbd0eSchristos ? ", " : "",
339*c42dbd0eSchristos fp->ctf_openflags & CTF_F_IDXSORTED
340*c42dbd0eSchristos ? "CTF_F_IDXSORTED" : "",
341*c42dbd0eSchristos fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
342*c42dbd0eSchristos | CTF_F_IDXSORTED)
343*c42dbd0eSchristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
344*c42dbd0eSchristos | CTF_F_IDXSORTED))
345*c42dbd0eSchristos ? ", " : "",
346*c42dbd0eSchristos fp->ctf_openflags & CTF_F_DYNSTR
347*c42dbd0eSchristos ? "CTF_F_DYNSTR" : "") < 0)
348*c42dbd0eSchristos goto err;
349*c42dbd0eSchristos
350*c42dbd0eSchristos if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
351867d70fcSchristos goto err;
352867d70fcSchristos ctf_dump_append (state, str);
353867d70fcSchristos }
354867d70fcSchristos
355867d70fcSchristos if (ctf_dump_header_strfield (fp, state, "Parent label",
356867d70fcSchristos hp->cth_parlabel) < 0)
357867d70fcSchristos goto err;
358867d70fcSchristos
359867d70fcSchristos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
360867d70fcSchristos goto err;
361867d70fcSchristos
362867d70fcSchristos if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
363867d70fcSchristos hp->cth_cuname) < 0)
364867d70fcSchristos goto err;
365867d70fcSchristos
366867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
367867d70fcSchristos hp->cth_objtoff) < 0)
368867d70fcSchristos goto err;
369867d70fcSchristos
370867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "Data object section",
371867d70fcSchristos hp->cth_objtoff, hp->cth_funcoff) < 0)
372867d70fcSchristos goto err;
373867d70fcSchristos
374867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "Function info section",
375*c42dbd0eSchristos hp->cth_funcoff, hp->cth_objtidxoff) < 0)
376*c42dbd0eSchristos goto err;
377*c42dbd0eSchristos
378*c42dbd0eSchristos if (ctf_dump_header_sectfield (fp, state, "Object index section",
379*c42dbd0eSchristos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
380*c42dbd0eSchristos goto err;
381*c42dbd0eSchristos
382*c42dbd0eSchristos if (ctf_dump_header_sectfield (fp, state, "Function index section",
383*c42dbd0eSchristos hp->cth_funcidxoff, hp->cth_varoff) < 0)
384867d70fcSchristos goto err;
385867d70fcSchristos
386867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "Variable section",
387867d70fcSchristos hp->cth_varoff, hp->cth_typeoff) < 0)
388867d70fcSchristos goto err;
389867d70fcSchristos
390867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "Type section",
391867d70fcSchristos hp->cth_typeoff, hp->cth_stroff) < 0)
392867d70fcSchristos goto err;
393867d70fcSchristos
394867d70fcSchristos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
395867d70fcSchristos hp->cth_stroff + hp->cth_strlen + 1) < 0)
396867d70fcSchristos goto err;
397867d70fcSchristos
398867d70fcSchristos return 0;
399867d70fcSchristos err:
400*c42dbd0eSchristos free (flagstr);
401867d70fcSchristos return (ctf_set_errno (fp, errno));
402867d70fcSchristos }
403867d70fcSchristos
404867d70fcSchristos /* Dump a single label into the cds_items. */
405867d70fcSchristos
406867d70fcSchristos static int
ctf_dump_label(const char * name,const ctf_lblinfo_t * info,void * arg)407867d70fcSchristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
408867d70fcSchristos void *arg)
409867d70fcSchristos {
410867d70fcSchristos char *str;
411867d70fcSchristos char *typestr;
412867d70fcSchristos ctf_dump_state_t *state = arg;
413867d70fcSchristos
414867d70fcSchristos if (asprintf (&str, "%s -> ", name) < 0)
415867d70fcSchristos return (ctf_set_errno (state->cds_fp, errno));
416867d70fcSchristos
417867d70fcSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
418*c42dbd0eSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
419867d70fcSchristos {
420867d70fcSchristos free (str);
421*c42dbd0eSchristos return 0; /* Swallow the error. */
422867d70fcSchristos }
423867d70fcSchristos
424867d70fcSchristos str = str_append (str, typestr);
425867d70fcSchristos free (typestr);
426867d70fcSchristos
427867d70fcSchristos ctf_dump_append (state, str);
428867d70fcSchristos return 0;
429867d70fcSchristos }
430867d70fcSchristos
431*c42dbd0eSchristos /* Dump all the object or function entries into the cds_items. */
432867d70fcSchristos
433867d70fcSchristos static int
ctf_dump_objts(ctf_dict_t * fp,ctf_dump_state_t * state,int functions)434*c42dbd0eSchristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
435867d70fcSchristos {
436*c42dbd0eSchristos const char *name;
437*c42dbd0eSchristos ctf_id_t id;
438*c42dbd0eSchristos ctf_next_t *i = NULL;
439*c42dbd0eSchristos char *str = NULL;
440867d70fcSchristos
441*c42dbd0eSchristos if ((functions && fp->ctf_funcidx_names)
442*c42dbd0eSchristos || (!functions && fp->ctf_objtidx_names))
443*c42dbd0eSchristos str = str_append (str, _("Section is indexed.\n"));
444*c42dbd0eSchristos else if (fp->ctf_symtab.cts_data == NULL)
445*c42dbd0eSchristos str = str_append (str, _("No symbol table.\n"));
446867d70fcSchristos
447*c42dbd0eSchristos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
448867d70fcSchristos {
449*c42dbd0eSchristos char *typestr = NULL;
450867d70fcSchristos
451*c42dbd0eSchristos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type
452*c42dbd0eSchristos has a leading one. */
453*c42dbd0eSchristos if (name)
454867d70fcSchristos {
455*c42dbd0eSchristos if (asprintf (&str, "%s -> ", name) < 0)
456*c42dbd0eSchristos goto oom;
457867d70fcSchristos }
458867d70fcSchristos else
459*c42dbd0eSchristos str = xstrdup ("");
460867d70fcSchristos
461*c42dbd0eSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, id,
462*c42dbd0eSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
463867d70fcSchristos {
464*c42dbd0eSchristos ctf_dump_append (state, str);
465*c42dbd0eSchristos continue; /* Swallow the error. */
466867d70fcSchristos }
467867d70fcSchristos
468867d70fcSchristos str = str_append (str, typestr);
469867d70fcSchristos free (typestr);
470867d70fcSchristos ctf_dump_append (state, str);
471867d70fcSchristos continue;
472867d70fcSchristos
473867d70fcSchristos oom:
474*c42dbd0eSchristos ctf_set_errno (fp, ENOMEM);
475*c42dbd0eSchristos ctf_next_destroy (i);
476*c42dbd0eSchristos return -1;
477867d70fcSchristos }
478867d70fcSchristos return 0;
479867d70fcSchristos }
480867d70fcSchristos
481867d70fcSchristos /* Dump a single variable into the cds_items. */
482867d70fcSchristos static int
ctf_dump_var(const char * name,ctf_id_t type,void * arg)483867d70fcSchristos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
484867d70fcSchristos {
485867d70fcSchristos char *str;
486867d70fcSchristos char *typestr;
487867d70fcSchristos ctf_dump_state_t *state = arg;
488867d70fcSchristos
489867d70fcSchristos if (asprintf (&str, "%s -> ", name) < 0)
490867d70fcSchristos return (ctf_set_errno (state->cds_fp, errno));
491867d70fcSchristos
492867d70fcSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, type,
493*c42dbd0eSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
494867d70fcSchristos {
495867d70fcSchristos free (str);
496*c42dbd0eSchristos return 0; /* Swallow the error. */
497867d70fcSchristos }
498867d70fcSchristos
499867d70fcSchristos str = str_append (str, typestr);
500867d70fcSchristos free (typestr);
501867d70fcSchristos
502867d70fcSchristos ctf_dump_append (state, str);
503867d70fcSchristos return 0;
504867d70fcSchristos }
505867d70fcSchristos
506*c42dbd0eSchristos /* Dump a single struct/union member into the string in the membstate. */
507867d70fcSchristos static int
ctf_dump_member(const char * name,ctf_id_t id,unsigned long offset,int depth,void * arg)508867d70fcSchristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
509867d70fcSchristos int depth, void *arg)
510867d70fcSchristos {
511867d70fcSchristos ctf_dump_membstate_t *state = arg;
512867d70fcSchristos char *typestr = NULL;
513867d70fcSchristos char *bit = NULL;
514867d70fcSchristos
515*c42dbd0eSchristos /* The struct/union itself has already been printed. */
516*c42dbd0eSchristos if (depth == 0)
517867d70fcSchristos return 0;
518867d70fcSchristos
519*c42dbd0eSchristos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
520867d70fcSchristos goto oom;
521*c42dbd0eSchristos *state->cdm_str = str_append (*state->cdm_str, bit);
522*c42dbd0eSchristos free (bit);
523867d70fcSchristos
524*c42dbd0eSchristos if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
525*c42dbd0eSchristos CTF_ADD_ROOT | CTF_FT_BITFIELD
526*c42dbd0eSchristos | CTF_FT_ID)) == NULL)
527*c42dbd0eSchristos return -1; /* errno is set for us. */
528*c42dbd0eSchristos
529*c42dbd0eSchristos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
530867d70fcSchristos goto oom;
531*c42dbd0eSchristos
532867d70fcSchristos *state->cdm_str = str_append (*state->cdm_str, bit);
533867d70fcSchristos free (typestr);
534867d70fcSchristos free (bit);
535867d70fcSchristos typestr = NULL;
536867d70fcSchristos bit = NULL;
537867d70fcSchristos
538867d70fcSchristos return 0;
539867d70fcSchristos
540867d70fcSchristos oom:
541867d70fcSchristos free (typestr);
542867d70fcSchristos free (bit);
543867d70fcSchristos return (ctf_set_errno (state->cdm_fp, errno));
544867d70fcSchristos }
545867d70fcSchristos
546*c42dbd0eSchristos /* Report the number of digits in the hexadecimal representation of a type
547*c42dbd0eSchristos ID. */
548*c42dbd0eSchristos
549*c42dbd0eSchristos static int
type_hex_digits(ctf_id_t id)550*c42dbd0eSchristos type_hex_digits (ctf_id_t id)
551*c42dbd0eSchristos {
552*c42dbd0eSchristos int i = 0;
553*c42dbd0eSchristos
554*c42dbd0eSchristos if (id == 0)
555*c42dbd0eSchristos return 1;
556*c42dbd0eSchristos
557*c42dbd0eSchristos for (; id > 0; id >>= 4, i++);
558*c42dbd0eSchristos return i;
559*c42dbd0eSchristos }
560*c42dbd0eSchristos
561867d70fcSchristos /* Dump a single type into the cds_items. */
562867d70fcSchristos static int
ctf_dump_type(ctf_id_t id,int flag,void * arg)563867d70fcSchristos ctf_dump_type (ctf_id_t id, int flag, void *arg)
564867d70fcSchristos {
565867d70fcSchristos char *str;
566*c42dbd0eSchristos char *indent;
567867d70fcSchristos ctf_dump_state_t *state = arg;
568*c42dbd0eSchristos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
569867d70fcSchristos
570*c42dbd0eSchristos /* Indent neatly. */
571*c42dbd0eSchristos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0)
572*c42dbd0eSchristos return (ctf_set_errno (state->cds_fp, ENOMEM));
573*c42dbd0eSchristos
574*c42dbd0eSchristos /* Dump the type itself. */
575*c42dbd0eSchristos if ((str = ctf_dump_format_type (state->cds_fp, id,
576*c42dbd0eSchristos flag | CTF_FT_REFS)) == NULL)
577867d70fcSchristos goto err;
578867d70fcSchristos str = str_append (str, "\n");
579*c42dbd0eSchristos
580*c42dbd0eSchristos membstate.cdm_toplevel_indent = indent;
581*c42dbd0eSchristos
582*c42dbd0eSchristos /* Member dumping for structs, unions... */
583*c42dbd0eSchristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
584*c42dbd0eSchristos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
585*c42dbd0eSchristos {
586867d70fcSchristos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
587867d70fcSchristos {
588867d70fcSchristos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
589867d70fcSchristos {
590867d70fcSchristos ctf_dump_append (state, str);
591867d70fcSchristos return 0;
592867d70fcSchristos }
593*c42dbd0eSchristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
594*c42dbd0eSchristos _("cannot visit members dumping type 0x%lx"), id);
595867d70fcSchristos goto err;
596867d70fcSchristos }
597*c42dbd0eSchristos }
598867d70fcSchristos
599*c42dbd0eSchristos /* ... and enums, for which we dump the first and last few members and skip
600*c42dbd0eSchristos the ones in the middle. */
601*c42dbd0eSchristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
602*c42dbd0eSchristos {
603*c42dbd0eSchristos int enum_count = ctf_member_count (state->cds_fp, id);
604*c42dbd0eSchristos ctf_next_t *it = NULL;
605*c42dbd0eSchristos int i = 0;
606*c42dbd0eSchristos const char *enumerand;
607*c42dbd0eSchristos char *bit;
608*c42dbd0eSchristos int value;
609*c42dbd0eSchristos
610*c42dbd0eSchristos while ((enumerand = ctf_enum_next (state->cds_fp, id,
611*c42dbd0eSchristos &it, &value)) != NULL)
612*c42dbd0eSchristos {
613*c42dbd0eSchristos i++;
614*c42dbd0eSchristos if ((i > 5) && (i < enum_count - 4))
615*c42dbd0eSchristos continue;
616*c42dbd0eSchristos
617*c42dbd0eSchristos str = str_append (str, indent);
618*c42dbd0eSchristos
619*c42dbd0eSchristos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
620*c42dbd0eSchristos {
621*c42dbd0eSchristos ctf_next_destroy (it);
622*c42dbd0eSchristos goto oom;
623*c42dbd0eSchristos }
624*c42dbd0eSchristos str = str_append (str, bit);
625*c42dbd0eSchristos free (bit);
626*c42dbd0eSchristos
627*c42dbd0eSchristos if ((i == 5) && (enum_count > 10))
628*c42dbd0eSchristos {
629*c42dbd0eSchristos str = str_append (str, indent);
630*c42dbd0eSchristos str = str_append (str, "...\n");
631*c42dbd0eSchristos }
632*c42dbd0eSchristos }
633*c42dbd0eSchristos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
634*c42dbd0eSchristos {
635*c42dbd0eSchristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
636*c42dbd0eSchristos _("cannot visit enumerands dumping type 0x%lx"), id);
637*c42dbd0eSchristos goto err;
638*c42dbd0eSchristos }
639*c42dbd0eSchristos }
640867d70fcSchristos
641867d70fcSchristos ctf_dump_append (state, str);
642*c42dbd0eSchristos free (indent);
643*c42dbd0eSchristos
644867d70fcSchristos return 0;
645867d70fcSchristos
646867d70fcSchristos err:
647*c42dbd0eSchristos free (indent);
648867d70fcSchristos free (str);
649*c42dbd0eSchristos
650*c42dbd0eSchristos /* Swallow the error: don't cause an error in one type to abort all
651*c42dbd0eSchristos type dumping. */
652*c42dbd0eSchristos return 0;
653*c42dbd0eSchristos
654*c42dbd0eSchristos oom:
655*c42dbd0eSchristos free (indent);
656*c42dbd0eSchristos free (str);
657*c42dbd0eSchristos return ctf_set_errno (state->cds_fp, ENOMEM);
658867d70fcSchristos }
659867d70fcSchristos
660867d70fcSchristos /* Dump the string table into the cds_items. */
661867d70fcSchristos
662867d70fcSchristos static int
ctf_dump_str(ctf_dict_t * fp,ctf_dump_state_t * state)663*c42dbd0eSchristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
664867d70fcSchristos {
665867d70fcSchristos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
666867d70fcSchristos
667867d70fcSchristos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
668867d70fcSchristos fp->ctf_str[CTF_STRTAB_0].cts_len;)
669867d70fcSchristos {
670867d70fcSchristos char *str;
671*c42dbd0eSchristos if (asprintf (&str, "0x%lx: %s",
672867d70fcSchristos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
673867d70fcSchristos s) < 0)
674867d70fcSchristos return (ctf_set_errno (fp, errno));
675867d70fcSchristos ctf_dump_append (state, str);
676867d70fcSchristos s += strlen (s) + 1;
677867d70fcSchristos }
678867d70fcSchristos
679867d70fcSchristos return 0;
680867d70fcSchristos }
681867d70fcSchristos
682867d70fcSchristos /* Dump a particular section of a CTF file, in textual form. Call with a
683867d70fcSchristos pointer to a NULL STATE: each call emits a dynamically allocated string
684867d70fcSchristos containing a description of one entity in the specified section, in order.
685867d70fcSchristos Only the first call (with a NULL state) may vary SECT. Once the CTF section
686867d70fcSchristos has been entirely dumped, the call returns NULL and frees and annuls the
687867d70fcSchristos STATE, ready for another section to be dumped. The returned textual content
688867d70fcSchristos may span multiple lines: between each call the FUNC is called with one
689867d70fcSchristos textual line at a time, and should return a suitably decorated line (it can
690867d70fcSchristos allocate a new one and return it if it likes). */
691867d70fcSchristos
692867d70fcSchristos char *
ctf_dump(ctf_dict_t * fp,ctf_dump_state_t ** statep,ctf_sect_names_t sect,ctf_dump_decorate_f * func,void * arg)693*c42dbd0eSchristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
694867d70fcSchristos ctf_dump_decorate_f *func, void *arg)
695867d70fcSchristos {
696867d70fcSchristos char *str;
697867d70fcSchristos char *line;
698867d70fcSchristos ctf_dump_state_t *state = NULL;
699867d70fcSchristos
700867d70fcSchristos if (*statep == NULL)
701867d70fcSchristos {
702867d70fcSchristos /* Data collection. Transforming a call-at-a-time iterator into a
703867d70fcSchristos return-at-a-time iterator in a language without call/cc is annoying. It
704867d70fcSchristos is easiest to simply collect everything at once and then return it bit
705867d70fcSchristos by bit. The first call will take (much) longer than otherwise, but the
706867d70fcSchristos amortized time needed is the same. */
707867d70fcSchristos
708867d70fcSchristos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
709867d70fcSchristos {
710867d70fcSchristos ctf_set_errno (fp, ENOMEM);
711867d70fcSchristos goto end;
712867d70fcSchristos }
713867d70fcSchristos state = *statep;
714867d70fcSchristos
715867d70fcSchristos memset (state, 0, sizeof (struct ctf_dump_state));
716867d70fcSchristos state->cds_fp = fp;
717867d70fcSchristos state->cds_sect = sect;
718867d70fcSchristos
719867d70fcSchristos switch (sect)
720867d70fcSchristos {
721867d70fcSchristos case CTF_SECT_HEADER:
722867d70fcSchristos ctf_dump_header (fp, state);
723867d70fcSchristos break;
724867d70fcSchristos case CTF_SECT_LABEL:
725867d70fcSchristos if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
726867d70fcSchristos {
727867d70fcSchristos if (ctf_errno (fp) != ECTF_NOLABELDATA)
728867d70fcSchristos goto end; /* errno is set for us. */
729867d70fcSchristos ctf_set_errno (fp, 0);
730867d70fcSchristos }
731867d70fcSchristos break;
732867d70fcSchristos case CTF_SECT_OBJT:
733*c42dbd0eSchristos if (ctf_dump_objts (fp, state, 0) < 0)
734867d70fcSchristos goto end; /* errno is set for us. */
735867d70fcSchristos break;
736867d70fcSchristos case CTF_SECT_FUNC:
737*c42dbd0eSchristos if (ctf_dump_objts (fp, state, 1) < 0)
738867d70fcSchristos goto end; /* errno is set for us. */
739867d70fcSchristos break;
740867d70fcSchristos case CTF_SECT_VAR:
741867d70fcSchristos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
742867d70fcSchristos goto end; /* errno is set for us. */
743867d70fcSchristos break;
744867d70fcSchristos case CTF_SECT_TYPE:
745867d70fcSchristos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
746867d70fcSchristos goto end; /* errno is set for us. */
747867d70fcSchristos break;
748867d70fcSchristos case CTF_SECT_STR:
749867d70fcSchristos ctf_dump_str (fp, state);
750867d70fcSchristos break;
751867d70fcSchristos default:
752867d70fcSchristos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
753867d70fcSchristos goto end;
754867d70fcSchristos }
755867d70fcSchristos }
756867d70fcSchristos else
757867d70fcSchristos {
758867d70fcSchristos state = *statep;
759867d70fcSchristos
760867d70fcSchristos if (state->cds_sect != sect)
761867d70fcSchristos {
762867d70fcSchristos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
763867d70fcSchristos goto end;
764867d70fcSchristos }
765867d70fcSchristos }
766867d70fcSchristos
767867d70fcSchristos if (state->cds_current == NULL)
768867d70fcSchristos state->cds_current = ctf_list_next (&state->cds_items);
769867d70fcSchristos else
770867d70fcSchristos state->cds_current = ctf_list_next (state->cds_current);
771867d70fcSchristos
772867d70fcSchristos if (state->cds_current == NULL)
773867d70fcSchristos goto end;
774867d70fcSchristos
775867d70fcSchristos /* Hookery. There is some extra complexity to preserve linefeeds within each
776867d70fcSchristos item while removing linefeeds at the end. */
777867d70fcSchristos if (func)
778867d70fcSchristos {
779867d70fcSchristos size_t len;
780867d70fcSchristos
781867d70fcSchristos str = NULL;
782867d70fcSchristos for (line = state->cds_current->cdi_item; line && *line; )
783867d70fcSchristos {
784867d70fcSchristos char *nline = line;
785867d70fcSchristos char *ret;
786867d70fcSchristos
787867d70fcSchristos nline = strchr (line, '\n');
788867d70fcSchristos if (nline)
789867d70fcSchristos nline[0] = '\0';
790867d70fcSchristos
791867d70fcSchristos ret = func (sect, line, arg);
792867d70fcSchristos str = str_append (str, ret);
793867d70fcSchristos str = str_append (str, "\n");
794867d70fcSchristos if (ret != line)
795867d70fcSchristos free (ret);
796867d70fcSchristos
797867d70fcSchristos if (nline)
798867d70fcSchristos {
799867d70fcSchristos nline[0] = '\n';
800867d70fcSchristos nline++;
801867d70fcSchristos }
802867d70fcSchristos
803867d70fcSchristos line = nline;
804867d70fcSchristos }
805867d70fcSchristos
806867d70fcSchristos len = strlen (str);
807867d70fcSchristos
808867d70fcSchristos if (str[len-1] == '\n')
809867d70fcSchristos str[len-1] = '\0';
810867d70fcSchristos }
811867d70fcSchristos else
812867d70fcSchristos {
813867d70fcSchristos str = strdup (state->cds_current->cdi_item);
814867d70fcSchristos if (!str)
815867d70fcSchristos {
816867d70fcSchristos ctf_set_errno (fp, ENOMEM);
817867d70fcSchristos return str;
818867d70fcSchristos }
819867d70fcSchristos }
820867d70fcSchristos
821867d70fcSchristos ctf_set_errno (fp, 0);
822867d70fcSchristos return str;
823867d70fcSchristos
824867d70fcSchristos end:
825867d70fcSchristos ctf_dump_free (state);
826867d70fcSchristos free (state);
827867d70fcSchristos ctf_set_errno (fp, 0);
828867d70fcSchristos *statep = NULL;
829867d70fcSchristos return NULL;
830867d70fcSchristos }
831