ctf-dump.c revision 1.1.1.4 1 1.1 christos /* Textual dumping of CTF data.
2 1.1.1.3 christos Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 1.1 christos
4 1.1 christos This file is part of libctf.
5 1.1 christos
6 1.1 christos libctf is free software; you can redistribute it and/or modify it under
7 1.1 christos the terms of the GNU General Public License as published by the Free
8 1.1 christos Software Foundation; either version 3, or (at your option) any later
9 1.1 christos version.
10 1.1 christos
11 1.1 christos This program is distributed in the hope that it will be useful, but
12 1.1 christos WITHOUT ANY WARRANTY; without even the implied warranty of
13 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 1.1 christos See the GNU General Public License for more details.
15 1.1 christos
16 1.1 christos You should have received a copy of the GNU General Public License
17 1.1 christos along with this program; see the file COPYING. If not see
18 1.1 christos <http://www.gnu.org/licenses/>. */
19 1.1 christos
20 1.1 christos #include <ctf-impl.h>
21 1.1 christos #include <string.h>
22 1.1 christos
23 1.1 christos #define str_append(s, a) ctf_str_append_noerr (s, a)
24 1.1 christos
25 1.1 christos /* One item to be dumped, in string form. */
26 1.1 christos
27 1.1 christos typedef struct ctf_dump_item
28 1.1 christos {
29 1.1 christos ctf_list_t cdi_list;
30 1.1 christos char *cdi_item;
31 1.1 christos } ctf_dump_item_t;
32 1.1 christos
33 1.1 christos /* Cross-call state for dumping. Basically just enough to track the section in
34 1.1 christos use and a list of return strings. */
35 1.1 christos
36 1.1 christos struct ctf_dump_state
37 1.1 christos {
38 1.1 christos ctf_sect_names_t cds_sect;
39 1.1.1.2 christos ctf_dict_t *cds_fp;
40 1.1 christos ctf_dump_item_t *cds_current;
41 1.1 christos ctf_list_t cds_items;
42 1.1 christos };
43 1.1 christos
44 1.1 christos /* Cross-call state for ctf_dump_member. */
45 1.1 christos
46 1.1 christos typedef struct ctf_dump_membstate
47 1.1 christos {
48 1.1 christos char **cdm_str;
49 1.1.1.2 christos ctf_dict_t *cdm_fp;
50 1.1.1.2 christos const char *cdm_toplevel_indent;
51 1.1 christos } ctf_dump_membstate_t;
52 1.1 christos
53 1.1 christos static int
54 1.1 christos ctf_dump_append (ctf_dump_state_t *state, char *str)
55 1.1 christos {
56 1.1 christos ctf_dump_item_t *cdi;
57 1.1 christos
58 1.1 christos if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
59 1.1 christos return (ctf_set_errno (state->cds_fp, ENOMEM));
60 1.1 christos
61 1.1 christos cdi->cdi_item = str;
62 1.1 christos ctf_list_append (&state->cds_items, cdi);
63 1.1 christos return 0;
64 1.1 christos }
65 1.1 christos
66 1.1 christos static void
67 1.1 christos ctf_dump_free (ctf_dump_state_t *state)
68 1.1 christos {
69 1.1 christos ctf_dump_item_t *cdi, *next_cdi;
70 1.1 christos
71 1.1 christos if (state == NULL)
72 1.1 christos return;
73 1.1 christos
74 1.1 christos for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
75 1.1 christos cdi = next_cdi)
76 1.1 christos {
77 1.1 christos free (cdi->cdi_item);
78 1.1 christos next_cdi = ctf_list_next (cdi);
79 1.1 christos free (cdi);
80 1.1 christos }
81 1.1 christos }
82 1.1 christos
83 1.1.1.2 christos /* Return a dump for a single type, without member info: but do optionally show
84 1.1.1.2 christos the type's references. */
85 1.1.1.2 christos
86 1.1.1.2 christos #define CTF_FT_REFS 0x2 /* Print referenced types. */
87 1.1.1.2 christos #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */
88 1.1.1.2 christos #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */
89 1.1 christos
90 1.1 christos static char *
91 1.1.1.2 christos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
92 1.1 christos {
93 1.1 christos ctf_id_t new_id;
94 1.1 christos char *str = NULL, *bit = NULL, *buf = NULL;
95 1.1 christos
96 1.1.1.2 christos ctf_set_errno (fp, 0);
97 1.1 christos new_id = id;
98 1.1 christos do
99 1.1 christos {
100 1.1.1.2 christos ctf_encoding_t ep;
101 1.1.1.2 christos ctf_arinfo_t ar;
102 1.1.1.2 christos int kind, unsliced_kind;
103 1.1.1.2 christos ssize_t size, align;
104 1.1 christos const char *nonroot_leader = "";
105 1.1 christos const char *nonroot_trailer = "";
106 1.1.1.2 christos const char *idstr = "";
107 1.1 christos
108 1.1 christos id = new_id;
109 1.1.1.4 christos if (!(flag & CTF_ADD_ROOT))
110 1.1 christos {
111 1.1 christos nonroot_leader = "{";
112 1.1 christos nonroot_trailer = "}";
113 1.1 christos }
114 1.1 christos
115 1.1 christos buf = ctf_type_aname (fp, id);
116 1.1 christos if (!buf)
117 1.1 christos {
118 1.1 christos if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
119 1.1 christos {
120 1.1.1.2 christos ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
121 1.1 christos str = str_append (str, " (type not represented in CTF)");
122 1.1.1.2 christos return str;
123 1.1 christos }
124 1.1 christos
125 1.1 christos goto err;
126 1.1 christos }
127 1.1 christos
128 1.1.1.2 christos if (flag & CTF_FT_ID)
129 1.1.1.2 christos idstr = "ID ";
130 1.1.1.2 christos if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
131 1.1.1.2 christos id, ctf_type_kind (fp, id)) < 0)
132 1.1 christos goto oom;
133 1.1 christos str = str_append (str, bit);
134 1.1 christos free (bit);
135 1.1 christos bit = NULL;
136 1.1 christos
137 1.1 christos if (buf[0] != '\0')
138 1.1.1.2 christos str = str_append (str, buf);
139 1.1 christos
140 1.1 christos free (buf);
141 1.1 christos buf = NULL;
142 1.1 christos
143 1.1.1.2 christos unsliced_kind = ctf_type_kind_unsliced (fp, id);
144 1.1.1.2 christos kind = ctf_type_kind (fp, id);
145 1.1.1.2 christos
146 1.1.1.2 christos /* Report encodings of everything with an encoding other than enums:
147 1.1.1.2 christos base-type enums cannot have a nonzero cte_offset or cte_bits value.
148 1.1.1.2 christos (Slices of them can, but they are of kind CTF_K_SLICE.) */
149 1.1.1.2 christos if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
150 1.1 christos {
151 1.1.1.2 christos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
152 1.1.1.2 christos && flag & CTF_FT_BITFIELD)
153 1.1.1.2 christos {
154 1.1.1.2 christos if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
155 1.1.1.2 christos goto oom;
156 1.1.1.2 christos str = str_append (str, bit);
157 1.1.1.2 christos free (bit);
158 1.1.1.2 christos bit = NULL;
159 1.1.1.2 christos }
160 1.1.1.2 christos
161 1.1.1.2 christos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
162 1.1.1.2 christos || ep.cte_offset != 0)
163 1.1.1.2 christos {
164 1.1.1.2 christos const char *slice = "";
165 1.1.1.2 christos
166 1.1.1.2 christos if (unsliced_kind == CTF_K_SLICE)
167 1.1.1.2 christos slice = "slice ";
168 1.1.1.2 christos
169 1.1.1.2 christos if (asprintf (&bit, " [%s0x%x:0x%x]",
170 1.1.1.2 christos slice, ep.cte_offset, ep.cte_bits) < 0)
171 1.1.1.2 christos goto oom;
172 1.1.1.2 christos str = str_append (str, bit);
173 1.1.1.2 christos free (bit);
174 1.1.1.2 christos bit = NULL;
175 1.1.1.2 christos }
176 1.1.1.2 christos
177 1.1.1.2 christos if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
178 1.1 christos goto oom;
179 1.1.1.2 christos str = str_append (str, bit);
180 1.1.1.2 christos free (bit);
181 1.1.1.2 christos bit = NULL;
182 1.1 christos }
183 1.1.1.2 christos
184 1.1.1.2 christos size = ctf_type_size (fp, id);
185 1.1.1.2 christos if (kind != CTF_K_FUNCTION && size >= 0)
186 1.1 christos {
187 1.1.1.2 christos if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
188 1.1 christos goto oom;
189 1.1.1.2 christos
190 1.1.1.2 christos str = str_append (str, bit);
191 1.1.1.2 christos free (bit);
192 1.1.1.2 christos bit = NULL;
193 1.1 christos }
194 1.1 christos
195 1.1.1.2 christos align = ctf_type_align (fp, id);
196 1.1.1.2 christos if (align >= 0)
197 1.1.1.2 christos {
198 1.1.1.2 christos if (asprintf (&bit, " (aligned at 0x%lx)",
199 1.1.1.2 christos (unsigned long int) align) < 0)
200 1.1.1.2 christos goto oom;
201 1.1 christos
202 1.1.1.2 christos str = str_append (str, bit);
203 1.1.1.2 christos free (bit);
204 1.1.1.2 christos bit = NULL;
205 1.1.1.2 christos }
206 1.1.1.2 christos
207 1.1.1.2 christos if (nonroot_trailer[0] != 0)
208 1.1.1.2 christos str = str_append (str, nonroot_trailer);
209 1.1.1.2 christos
210 1.1.1.2 christos /* Just exit after one iteration if we are not showing the types this type
211 1.1.1.2 christos references. */
212 1.1.1.2 christos if (!(flag & CTF_FT_REFS))
213 1.1.1.2 christos return str;
214 1.1 christos
215 1.1.1.2 christos /* Keep going as long as this type references another. We consider arrays
216 1.1.1.2 christos to "reference" their element type. */
217 1.1.1.2 christos
218 1.1.1.2 christos if (kind == CTF_K_ARRAY)
219 1.1.1.2 christos {
220 1.1.1.2 christos if (ctf_array_info (fp, id, &ar) < 0)
221 1.1.1.2 christos goto err;
222 1.1.1.2 christos new_id = ar.ctr_contents;
223 1.1.1.2 christos }
224 1.1.1.2 christos else
225 1.1.1.2 christos new_id = ctf_type_reference (fp, id);
226 1.1 christos if (new_id != CTF_ERR)
227 1.1.1.2 christos str = str_append (str, " -> ");
228 1.1.1.2 christos }
229 1.1.1.2 christos while (new_id != CTF_ERR);
230 1.1 christos
231 1.1 christos if (ctf_errno (fp) != ECTF_NOTREF)
232 1.1 christos {
233 1.1 christos free (str);
234 1.1 christos return NULL;
235 1.1 christos }
236 1.1 christos
237 1.1 christos return str;
238 1.1 christos
239 1.1 christos oom:
240 1.1 christos ctf_set_errno (fp, errno);
241 1.1 christos err:
242 1.1.1.3 christos ctf_err_warn (fp, 1, ctf_errno (fp), _("cannot format name dumping type 0x%lx"),
243 1.1.1.3 christos id);
244 1.1 christos free (buf);
245 1.1 christos free (str);
246 1.1 christos free (bit);
247 1.1 christos return NULL;
248 1.1 christos }
249 1.1 christos
250 1.1 christos /* Dump one string field from the file header into the cds_items. */
251 1.1 christos static int
252 1.1.1.2 christos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
253 1.1 christos const char *name, uint32_t value)
254 1.1 christos {
255 1.1 christos char *str;
256 1.1 christos if (value)
257 1.1 christos {
258 1.1 christos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
259 1.1 christos goto err;
260 1.1 christos ctf_dump_append (state, str);
261 1.1 christos }
262 1.1 christos return 0;
263 1.1 christos
264 1.1 christos err:
265 1.1 christos return (ctf_set_errno (fp, errno));
266 1.1 christos }
267 1.1 christos
268 1.1 christos /* Dump one section-offset field from the file header into the cds_items. */
269 1.1 christos static int
270 1.1.1.2 christos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
271 1.1 christos const char *sect, uint32_t off, uint32_t nextoff)
272 1.1 christos {
273 1.1 christos char *str;
274 1.1 christos if (nextoff - off)
275 1.1 christos {
276 1.1 christos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
277 1.1 christos (unsigned long) off, (unsigned long) (nextoff - 1),
278 1.1 christos (unsigned long) (nextoff - off)) < 0)
279 1.1 christos goto err;
280 1.1 christos ctf_dump_append (state, str);
281 1.1 christos }
282 1.1 christos return 0;
283 1.1 christos
284 1.1 christos err:
285 1.1 christos return (ctf_set_errno (fp, errno));
286 1.1 christos }
287 1.1 christos
288 1.1 christos /* Dump the file header into the cds_items. */
289 1.1 christos static int
290 1.1.1.2 christos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
291 1.1 christos {
292 1.1 christos char *str;
293 1.1.1.2 christos char *flagstr = NULL;
294 1.1 christos const ctf_header_t *hp = fp->ctf_header;
295 1.1 christos const char *vertab[] =
296 1.1 christos {
297 1.1 christos NULL, "CTF_VERSION_1",
298 1.1 christos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
299 1.1 christos "boundaries)",
300 1.1 christos "CTF_VERSION_2",
301 1.1 christos "CTF_VERSION_3", NULL
302 1.1 christos };
303 1.1 christos const char *verstr = NULL;
304 1.1 christos
305 1.1.1.2 christos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
306 1.1 christos goto err;
307 1.1 christos ctf_dump_append (state, str);
308 1.1 christos
309 1.1 christos if (hp->cth_version <= CTF_VERSION)
310 1.1 christos verstr = vertab[hp->cth_version];
311 1.1 christos
312 1.1 christos if (verstr == NULL)
313 1.1 christos verstr = "(not a valid version)";
314 1.1 christos
315 1.1 christos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
316 1.1 christos verstr) < 0)
317 1.1 christos goto err;
318 1.1 christos ctf_dump_append (state, str);
319 1.1 christos
320 1.1 christos /* Everything else is only printed if present. */
321 1.1 christos
322 1.1.1.2 christos /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
323 1.1 christos flags representing compression, etc, are turned off as the file is
324 1.1 christos decompressed. So we store a copy of the flags before they are changed, for
325 1.1 christos the dumper. */
326 1.1 christos
327 1.1 christos if (fp->ctf_openflags > 0)
328 1.1 christos {
329 1.1.1.2 christos if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
330 1.1.1.2 christos fp->ctf_openflags & CTF_F_COMPRESS
331 1.1.1.2 christos ? "CTF_F_COMPRESS": "",
332 1.1.1.2 christos (fp->ctf_openflags & CTF_F_COMPRESS)
333 1.1.1.2 christos && (fp->ctf_openflags & ~CTF_F_COMPRESS)
334 1.1.1.2 christos ? ", " : "",
335 1.1.1.2 christos fp->ctf_openflags & CTF_F_NEWFUNCINFO
336 1.1.1.2 christos ? "CTF_F_NEWFUNCINFO" : "",
337 1.1.1.3 christos (fp->ctf_openflags & (CTF_F_NEWFUNCINFO))
338 1.1.1.2 christos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
339 1.1.1.2 christos ? ", " : "",
340 1.1.1.2 christos fp->ctf_openflags & CTF_F_IDXSORTED
341 1.1.1.2 christos ? "CTF_F_IDXSORTED" : "",
342 1.1.1.3 christos fp->ctf_openflags & (CTF_F_IDXSORTED)
343 1.1.1.2 christos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
344 1.1.1.2 christos | CTF_F_IDXSORTED))
345 1.1.1.2 christos ? ", " : "",
346 1.1.1.2 christos fp->ctf_openflags & CTF_F_DYNSTR
347 1.1.1.2 christos ? "CTF_F_DYNSTR" : "") < 0)
348 1.1.1.2 christos goto err;
349 1.1.1.2 christos
350 1.1.1.2 christos if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
351 1.1 christos goto err;
352 1.1.1.4 christos free (flagstr);
353 1.1 christos ctf_dump_append (state, str);
354 1.1 christos }
355 1.1 christos
356 1.1 christos if (ctf_dump_header_strfield (fp, state, "Parent label",
357 1.1 christos hp->cth_parlabel) < 0)
358 1.1 christos goto err;
359 1.1 christos
360 1.1 christos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
361 1.1 christos goto err;
362 1.1 christos
363 1.1 christos if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
364 1.1 christos hp->cth_cuname) < 0)
365 1.1 christos goto err;
366 1.1 christos
367 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
368 1.1 christos hp->cth_objtoff) < 0)
369 1.1 christos goto err;
370 1.1 christos
371 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Data object section",
372 1.1 christos hp->cth_objtoff, hp->cth_funcoff) < 0)
373 1.1 christos goto err;
374 1.1 christos
375 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Function info section",
376 1.1.1.2 christos hp->cth_funcoff, hp->cth_objtidxoff) < 0)
377 1.1.1.2 christos goto err;
378 1.1.1.2 christos
379 1.1.1.2 christos if (ctf_dump_header_sectfield (fp, state, "Object index section",
380 1.1.1.2 christos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
381 1.1.1.2 christos goto err;
382 1.1.1.2 christos
383 1.1.1.2 christos if (ctf_dump_header_sectfield (fp, state, "Function index section",
384 1.1.1.2 christos hp->cth_funcidxoff, hp->cth_varoff) < 0)
385 1.1 christos goto err;
386 1.1 christos
387 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Variable section",
388 1.1 christos hp->cth_varoff, hp->cth_typeoff) < 0)
389 1.1 christos goto err;
390 1.1 christos
391 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Type section",
392 1.1 christos hp->cth_typeoff, hp->cth_stroff) < 0)
393 1.1 christos goto err;
394 1.1 christos
395 1.1 christos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
396 1.1 christos hp->cth_stroff + hp->cth_strlen + 1) < 0)
397 1.1 christos goto err;
398 1.1 christos
399 1.1 christos return 0;
400 1.1 christos err:
401 1.1.1.2 christos free (flagstr);
402 1.1 christos return (ctf_set_errno (fp, errno));
403 1.1 christos }
404 1.1 christos
405 1.1 christos /* Dump a single label into the cds_items. */
406 1.1 christos
407 1.1 christos static int
408 1.1 christos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
409 1.1 christos void *arg)
410 1.1 christos {
411 1.1 christos char *str;
412 1.1 christos char *typestr;
413 1.1 christos ctf_dump_state_t *state = arg;
414 1.1 christos
415 1.1 christos if (asprintf (&str, "%s -> ", name) < 0)
416 1.1 christos return (ctf_set_errno (state->cds_fp, errno));
417 1.1 christos
418 1.1 christos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
419 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
420 1.1 christos {
421 1.1 christos free (str);
422 1.1 christos return 0; /* Swallow the error. */
423 1.1 christos }
424 1.1 christos
425 1.1 christos str = str_append (str, typestr);
426 1.1 christos free (typestr);
427 1.1 christos
428 1.1 christos ctf_dump_append (state, str);
429 1.1 christos return 0;
430 1.1 christos }
431 1.1 christos
432 1.1.1.2 christos /* Dump all the object or function entries into the cds_items. */
433 1.1 christos
434 1.1 christos static int
435 1.1.1.2 christos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
436 1.1 christos {
437 1.1.1.2 christos const char *name;
438 1.1.1.2 christos ctf_id_t id;
439 1.1.1.2 christos ctf_next_t *i = NULL;
440 1.1.1.2 christos char *str = NULL;
441 1.1 christos
442 1.1.1.2 christos if ((functions && fp->ctf_funcidx_names)
443 1.1.1.2 christos || (!functions && fp->ctf_objtidx_names))
444 1.1.1.2 christos str = str_append (str, _("Section is indexed.\n"));
445 1.1.1.3 christos else if (fp->ctf_ext_symtab.cts_data == NULL)
446 1.1.1.2 christos str = str_append (str, _("No symbol table.\n"));
447 1.1.1.2 christos
448 1.1.1.2 christos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
449 1.1 christos {
450 1.1.1.2 christos char *typestr = NULL;
451 1.1 christos
452 1.1.1.2 christos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type
453 1.1.1.2 christos has a leading one. */
454 1.1.1.2 christos if (name)
455 1.1 christos {
456 1.1.1.2 christos if (asprintf (&str, "%s -> ", name) < 0)
457 1.1.1.2 christos goto oom;
458 1.1 christos }
459 1.1 christos else
460 1.1.1.2 christos str = xstrdup ("");
461 1.1 christos
462 1.1.1.2 christos if ((typestr = ctf_dump_format_type (state->cds_fp, id,
463 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
464 1.1 christos {
465 1.1.1.2 christos ctf_dump_append (state, str);
466 1.1.1.2 christos continue; /* Swallow the error. */
467 1.1 christos }
468 1.1 christos
469 1.1 christos str = str_append (str, typestr);
470 1.1 christos free (typestr);
471 1.1 christos ctf_dump_append (state, str);
472 1.1 christos continue;
473 1.1 christos
474 1.1 christos oom:
475 1.1.1.2 christos ctf_set_errno (fp, ENOMEM);
476 1.1.1.2 christos ctf_next_destroy (i);
477 1.1.1.2 christos return -1;
478 1.1 christos }
479 1.1 christos return 0;
480 1.1 christos }
481 1.1 christos
482 1.1 christos /* Dump a single variable into the cds_items. */
483 1.1 christos static int
484 1.1 christos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
485 1.1 christos {
486 1.1 christos char *str;
487 1.1 christos char *typestr;
488 1.1 christos ctf_dump_state_t *state = arg;
489 1.1 christos
490 1.1 christos if (asprintf (&str, "%s -> ", name) < 0)
491 1.1 christos return (ctf_set_errno (state->cds_fp, errno));
492 1.1 christos
493 1.1 christos if ((typestr = ctf_dump_format_type (state->cds_fp, type,
494 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
495 1.1 christos {
496 1.1 christos free (str);
497 1.1 christos return 0; /* Swallow the error. */
498 1.1 christos }
499 1.1 christos
500 1.1 christos str = str_append (str, typestr);
501 1.1 christos free (typestr);
502 1.1 christos
503 1.1 christos ctf_dump_append (state, str);
504 1.1 christos return 0;
505 1.1 christos }
506 1.1 christos
507 1.1.1.2 christos /* Dump a single struct/union member into the string in the membstate. */
508 1.1 christos static int
509 1.1 christos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
510 1.1.1.2 christos int depth, void *arg)
511 1.1 christos {
512 1.1 christos ctf_dump_membstate_t *state = arg;
513 1.1 christos char *typestr = NULL;
514 1.1 christos char *bit = NULL;
515 1.1 christos
516 1.1.1.2 christos /* The struct/union itself has already been printed. */
517 1.1.1.2 christos if (depth == 0)
518 1.1.1.2 christos return 0;
519 1.1 christos
520 1.1.1.2 christos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
521 1.1.1.2 christos goto oom;
522 1.1.1.2 christos *state->cdm_str = str_append (*state->cdm_str, bit);
523 1.1.1.2 christos free (bit);
524 1.1 christos
525 1.1.1.2 christos if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
526 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_BITFIELD
527 1.1.1.2 christos | CTF_FT_ID)) == NULL)
528 1.1.1.2 christos return -1; /* errno is set for us. */
529 1.1 christos
530 1.1.1.2 christos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
531 1.1.1.2 christos goto oom;
532 1.1 christos
533 1.1 christos *state->cdm_str = str_append (*state->cdm_str, bit);
534 1.1 christos free (typestr);
535 1.1 christos free (bit);
536 1.1 christos typestr = NULL;
537 1.1 christos bit = NULL;
538 1.1 christos
539 1.1 christos return 0;
540 1.1 christos
541 1.1 christos oom:
542 1.1 christos free (typestr);
543 1.1 christos free (bit);
544 1.1 christos return (ctf_set_errno (state->cdm_fp, errno));
545 1.1 christos }
546 1.1 christos
547 1.1.1.2 christos /* Report the number of digits in the hexadecimal representation of a type
548 1.1.1.2 christos ID. */
549 1.1.1.2 christos
550 1.1.1.2 christos static int
551 1.1.1.2 christos type_hex_digits (ctf_id_t id)
552 1.1.1.2 christos {
553 1.1.1.2 christos int i = 0;
554 1.1.1.2 christos
555 1.1.1.2 christos if (id == 0)
556 1.1.1.2 christos return 1;
557 1.1.1.2 christos
558 1.1.1.2 christos for (; id > 0; id >>= 4, i++);
559 1.1.1.2 christos return i;
560 1.1.1.2 christos }
561 1.1.1.2 christos
562 1.1 christos /* Dump a single type into the cds_items. */
563 1.1 christos static int
564 1.1 christos ctf_dump_type (ctf_id_t id, int flag, void *arg)
565 1.1 christos {
566 1.1 christos char *str;
567 1.1.1.2 christos char *indent;
568 1.1 christos ctf_dump_state_t *state = arg;
569 1.1.1.2 christos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
570 1.1 christos
571 1.1.1.2 christos /* Indent neatly. */
572 1.1.1.2 christos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0)
573 1.1.1.2 christos return (ctf_set_errno (state->cds_fp, ENOMEM));
574 1.1 christos
575 1.1.1.2 christos /* Dump the type itself. */
576 1.1.1.2 christos if ((str = ctf_dump_format_type (state->cds_fp, id,
577 1.1.1.2 christos flag | CTF_FT_REFS)) == NULL)
578 1.1.1.2 christos goto err;
579 1.1 christos str = str_append (str, "\n");
580 1.1.1.2 christos
581 1.1.1.2 christos membstate.cdm_toplevel_indent = indent;
582 1.1.1.2 christos
583 1.1.1.2 christos /* Member dumping for structs, unions... */
584 1.1.1.2 christos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
585 1.1.1.2 christos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
586 1.1 christos {
587 1.1.1.2 christos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
588 1.1 christos {
589 1.1.1.2 christos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
590 1.1.1.2 christos {
591 1.1.1.2 christos ctf_dump_append (state, str);
592 1.1.1.2 christos return 0;
593 1.1.1.2 christos }
594 1.1.1.2 christos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
595 1.1.1.2 christos _("cannot visit members dumping type 0x%lx"), id);
596 1.1.1.2 christos goto err;
597 1.1 christos }
598 1.1 christos }
599 1.1 christos
600 1.1.1.2 christos /* ... and enums, for which we dump the first and last few members and skip
601 1.1.1.2 christos the ones in the middle. */
602 1.1.1.2 christos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
603 1.1.1.2 christos {
604 1.1.1.2 christos int enum_count = ctf_member_count (state->cds_fp, id);
605 1.1.1.2 christos ctf_next_t *it = NULL;
606 1.1.1.2 christos int i = 0;
607 1.1.1.2 christos const char *enumerand;
608 1.1.1.2 christos char *bit;
609 1.1.1.2 christos int value;
610 1.1.1.2 christos
611 1.1.1.2 christos while ((enumerand = ctf_enum_next (state->cds_fp, id,
612 1.1.1.2 christos &it, &value)) != NULL)
613 1.1.1.2 christos {
614 1.1.1.2 christos i++;
615 1.1.1.2 christos if ((i > 5) && (i < enum_count - 4))
616 1.1.1.2 christos continue;
617 1.1.1.2 christos
618 1.1.1.2 christos str = str_append (str, indent);
619 1.1.1.2 christos
620 1.1.1.2 christos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
621 1.1.1.2 christos {
622 1.1.1.2 christos ctf_next_destroy (it);
623 1.1.1.2 christos goto oom;
624 1.1.1.2 christos }
625 1.1.1.2 christos str = str_append (str, bit);
626 1.1.1.2 christos free (bit);
627 1.1.1.2 christos
628 1.1.1.2 christos if ((i == 5) && (enum_count > 10))
629 1.1.1.2 christos {
630 1.1.1.2 christos str = str_append (str, indent);
631 1.1.1.2 christos str = str_append (str, "...\n");
632 1.1.1.2 christos }
633 1.1.1.2 christos }
634 1.1.1.2 christos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
635 1.1.1.2 christos {
636 1.1.1.2 christos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
637 1.1.1.2 christos _("cannot visit enumerands dumping type 0x%lx"), id);
638 1.1.1.2 christos goto err;
639 1.1.1.2 christos }
640 1.1.1.2 christos }
641 1.1 christos
642 1.1 christos ctf_dump_append (state, str);
643 1.1.1.2 christos free (indent);
644 1.1.1.2 christos
645 1.1 christos return 0;
646 1.1 christos
647 1.1 christos err:
648 1.1.1.2 christos free (indent);
649 1.1.1.2 christos free (str);
650 1.1.1.2 christos
651 1.1.1.2 christos /* Swallow the error: don't cause an error in one type to abort all
652 1.1.1.2 christos type dumping. */
653 1.1.1.2 christos return 0;
654 1.1.1.2 christos
655 1.1.1.2 christos oom:
656 1.1.1.2 christos free (indent);
657 1.1 christos free (str);
658 1.1.1.2 christos return ctf_set_errno (state->cds_fp, ENOMEM);
659 1.1 christos }
660 1.1 christos
661 1.1 christos /* Dump the string table into the cds_items. */
662 1.1 christos
663 1.1 christos static int
664 1.1.1.2 christos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
665 1.1 christos {
666 1.1 christos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
667 1.1 christos
668 1.1 christos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
669 1.1 christos fp->ctf_str[CTF_STRTAB_0].cts_len;)
670 1.1 christos {
671 1.1 christos char *str;
672 1.1.1.2 christos if (asprintf (&str, "0x%lx: %s",
673 1.1 christos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
674 1.1 christos s) < 0)
675 1.1 christos return (ctf_set_errno (fp, errno));
676 1.1 christos ctf_dump_append (state, str);
677 1.1 christos s += strlen (s) + 1;
678 1.1 christos }
679 1.1 christos
680 1.1 christos return 0;
681 1.1 christos }
682 1.1 christos
683 1.1 christos /* Dump a particular section of a CTF file, in textual form. Call with a
684 1.1 christos pointer to a NULL STATE: each call emits a dynamically allocated string
685 1.1 christos containing a description of one entity in the specified section, in order.
686 1.1 christos Only the first call (with a NULL state) may vary SECT. Once the CTF section
687 1.1 christos has been entirely dumped, the call returns NULL and frees and annuls the
688 1.1 christos STATE, ready for another section to be dumped. The returned textual content
689 1.1 christos may span multiple lines: between each call the FUNC is called with one
690 1.1 christos textual line at a time, and should return a suitably decorated line (it can
691 1.1 christos allocate a new one and return it if it likes). */
692 1.1 christos
693 1.1 christos char *
694 1.1.1.2 christos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
695 1.1 christos ctf_dump_decorate_f *func, void *arg)
696 1.1 christos {
697 1.1 christos char *str;
698 1.1 christos char *line;
699 1.1 christos ctf_dump_state_t *state = NULL;
700 1.1 christos
701 1.1 christos if (*statep == NULL)
702 1.1 christos {
703 1.1 christos /* Data collection. Transforming a call-at-a-time iterator into a
704 1.1 christos return-at-a-time iterator in a language without call/cc is annoying. It
705 1.1 christos is easiest to simply collect everything at once and then return it bit
706 1.1 christos by bit. The first call will take (much) longer than otherwise, but the
707 1.1 christos amortized time needed is the same. */
708 1.1 christos
709 1.1 christos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
710 1.1 christos {
711 1.1 christos ctf_set_errno (fp, ENOMEM);
712 1.1 christos goto end;
713 1.1 christos }
714 1.1 christos state = *statep;
715 1.1 christos
716 1.1 christos memset (state, 0, sizeof (struct ctf_dump_state));
717 1.1 christos state->cds_fp = fp;
718 1.1 christos state->cds_sect = sect;
719 1.1 christos
720 1.1 christos switch (sect)
721 1.1 christos {
722 1.1 christos case CTF_SECT_HEADER:
723 1.1 christos ctf_dump_header (fp, state);
724 1.1 christos break;
725 1.1 christos case CTF_SECT_LABEL:
726 1.1 christos if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
727 1.1 christos {
728 1.1 christos if (ctf_errno (fp) != ECTF_NOLABELDATA)
729 1.1 christos goto end; /* errno is set for us. */
730 1.1 christos ctf_set_errno (fp, 0);
731 1.1 christos }
732 1.1 christos break;
733 1.1 christos case CTF_SECT_OBJT:
734 1.1.1.2 christos if (ctf_dump_objts (fp, state, 0) < 0)
735 1.1 christos goto end; /* errno is set for us. */
736 1.1 christos break;
737 1.1 christos case CTF_SECT_FUNC:
738 1.1.1.2 christos if (ctf_dump_objts (fp, state, 1) < 0)
739 1.1 christos goto end; /* errno is set for us. */
740 1.1 christos break;
741 1.1 christos case CTF_SECT_VAR:
742 1.1 christos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
743 1.1 christos goto end; /* errno is set for us. */
744 1.1 christos break;
745 1.1 christos case CTF_SECT_TYPE:
746 1.1 christos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
747 1.1 christos goto end; /* errno is set for us. */
748 1.1 christos break;
749 1.1 christos case CTF_SECT_STR:
750 1.1 christos ctf_dump_str (fp, state);
751 1.1 christos break;
752 1.1 christos default:
753 1.1 christos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
754 1.1 christos goto end;
755 1.1 christos }
756 1.1 christos }
757 1.1 christos else
758 1.1 christos {
759 1.1 christos state = *statep;
760 1.1 christos
761 1.1 christos if (state->cds_sect != sect)
762 1.1 christos {
763 1.1 christos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
764 1.1 christos goto end;
765 1.1 christos }
766 1.1 christos }
767 1.1 christos
768 1.1 christos if (state->cds_current == NULL)
769 1.1 christos state->cds_current = ctf_list_next (&state->cds_items);
770 1.1 christos else
771 1.1 christos state->cds_current = ctf_list_next (state->cds_current);
772 1.1 christos
773 1.1 christos if (state->cds_current == NULL)
774 1.1 christos goto end;
775 1.1 christos
776 1.1 christos /* Hookery. There is some extra complexity to preserve linefeeds within each
777 1.1 christos item while removing linefeeds at the end. */
778 1.1 christos if (func)
779 1.1 christos {
780 1.1 christos size_t len;
781 1.1 christos
782 1.1 christos str = NULL;
783 1.1 christos for (line = state->cds_current->cdi_item; line && *line; )
784 1.1 christos {
785 1.1 christos char *nline = line;
786 1.1 christos char *ret;
787 1.1 christos
788 1.1 christos nline = strchr (line, '\n');
789 1.1 christos if (nline)
790 1.1 christos nline[0] = '\0';
791 1.1 christos
792 1.1 christos ret = func (sect, line, arg);
793 1.1 christos str = str_append (str, ret);
794 1.1 christos str = str_append (str, "\n");
795 1.1 christos if (ret != line)
796 1.1 christos free (ret);
797 1.1 christos
798 1.1 christos if (nline)
799 1.1 christos {
800 1.1 christos nline[0] = '\n';
801 1.1 christos nline++;
802 1.1 christos }
803 1.1 christos
804 1.1 christos line = nline;
805 1.1 christos }
806 1.1 christos
807 1.1 christos len = strlen (str);
808 1.1 christos
809 1.1 christos if (str[len-1] == '\n')
810 1.1 christos str[len-1] = '\0';
811 1.1 christos }
812 1.1 christos else
813 1.1 christos {
814 1.1 christos str = strdup (state->cds_current->cdi_item);
815 1.1 christos if (!str)
816 1.1 christos {
817 1.1 christos ctf_set_errno (fp, ENOMEM);
818 1.1.1.4 christos return NULL;
819 1.1 christos }
820 1.1 christos }
821 1.1 christos
822 1.1 christos ctf_set_errno (fp, 0);
823 1.1 christos return str;
824 1.1 christos
825 1.1 christos end:
826 1.1 christos ctf_dump_free (state);
827 1.1 christos free (state);
828 1.1 christos ctf_set_errno (fp, 0);
829 1.1 christos *statep = NULL;
830 1.1 christos return NULL;
831 1.1 christos }
832