ctf-dump.c revision 1.1.1.2 1 1.1 christos /* Textual dumping of CTF data.
2 1.1.1.2 christos Copyright (C) 2019-2022 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 christos if (flag == CTF_ADD_NONROOT)
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 christos ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
243 1.1 christos free (buf);
244 1.1 christos free (str);
245 1.1 christos free (bit);
246 1.1 christos return NULL;
247 1.1 christos }
248 1.1 christos
249 1.1 christos /* Dump one string field from the file header into the cds_items. */
250 1.1 christos static int
251 1.1.1.2 christos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
252 1.1 christos const char *name, uint32_t value)
253 1.1 christos {
254 1.1 christos char *str;
255 1.1 christos if (value)
256 1.1 christos {
257 1.1 christos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
258 1.1 christos goto err;
259 1.1 christos ctf_dump_append (state, str);
260 1.1 christos }
261 1.1 christos return 0;
262 1.1 christos
263 1.1 christos err:
264 1.1 christos return (ctf_set_errno (fp, errno));
265 1.1 christos }
266 1.1 christos
267 1.1 christos /* Dump one section-offset field from the file header into the cds_items. */
268 1.1 christos static int
269 1.1.1.2 christos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
270 1.1 christos const char *sect, uint32_t off, uint32_t nextoff)
271 1.1 christos {
272 1.1 christos char *str;
273 1.1 christos if (nextoff - off)
274 1.1 christos {
275 1.1 christos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
276 1.1 christos (unsigned long) off, (unsigned long) (nextoff - 1),
277 1.1 christos (unsigned long) (nextoff - off)) < 0)
278 1.1 christos goto err;
279 1.1 christos ctf_dump_append (state, str);
280 1.1 christos }
281 1.1 christos return 0;
282 1.1 christos
283 1.1 christos err:
284 1.1 christos return (ctf_set_errno (fp, errno));
285 1.1 christos }
286 1.1 christos
287 1.1 christos /* Dump the file header into the cds_items. */
288 1.1 christos static int
289 1.1.1.2 christos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
290 1.1 christos {
291 1.1 christos char *str;
292 1.1.1.2 christos char *flagstr = NULL;
293 1.1 christos const ctf_header_t *hp = fp->ctf_header;
294 1.1 christos const char *vertab[] =
295 1.1 christos {
296 1.1 christos NULL, "CTF_VERSION_1",
297 1.1 christos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
298 1.1 christos "boundaries)",
299 1.1 christos "CTF_VERSION_2",
300 1.1 christos "CTF_VERSION_3", NULL
301 1.1 christos };
302 1.1 christos const char *verstr = NULL;
303 1.1 christos
304 1.1.1.2 christos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
305 1.1 christos goto err;
306 1.1 christos ctf_dump_append (state, str);
307 1.1 christos
308 1.1 christos if (hp->cth_version <= CTF_VERSION)
309 1.1 christos verstr = vertab[hp->cth_version];
310 1.1 christos
311 1.1 christos if (verstr == NULL)
312 1.1 christos verstr = "(not a valid version)";
313 1.1 christos
314 1.1 christos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
315 1.1 christos verstr) < 0)
316 1.1 christos goto err;
317 1.1 christos ctf_dump_append (state, str);
318 1.1 christos
319 1.1 christos /* Everything else is only printed if present. */
320 1.1 christos
321 1.1.1.2 christos /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
322 1.1 christos flags representing compression, etc, are turned off as the file is
323 1.1 christos decompressed. So we store a copy of the flags before they are changed, for
324 1.1 christos the dumper. */
325 1.1 christos
326 1.1 christos if (fp->ctf_openflags > 0)
327 1.1 christos {
328 1.1.1.2 christos if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
329 1.1.1.2 christos fp->ctf_openflags & CTF_F_COMPRESS
330 1.1.1.2 christos ? "CTF_F_COMPRESS": "",
331 1.1.1.2 christos (fp->ctf_openflags & CTF_F_COMPRESS)
332 1.1.1.2 christos && (fp->ctf_openflags & ~CTF_F_COMPRESS)
333 1.1.1.2 christos ? ", " : "",
334 1.1.1.2 christos fp->ctf_openflags & CTF_F_NEWFUNCINFO
335 1.1.1.2 christos ? "CTF_F_NEWFUNCINFO" : "",
336 1.1.1.2 christos (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
337 1.1.1.2 christos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
338 1.1.1.2 christos ? ", " : "",
339 1.1.1.2 christos fp->ctf_openflags & CTF_F_IDXSORTED
340 1.1.1.2 christos ? "CTF_F_IDXSORTED" : "",
341 1.1.1.2 christos fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
342 1.1.1.2 christos | 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 christos ctf_dump_append (state, str);
353 1.1 christos }
354 1.1 christos
355 1.1 christos if (ctf_dump_header_strfield (fp, state, "Parent label",
356 1.1 christos hp->cth_parlabel) < 0)
357 1.1 christos goto err;
358 1.1 christos
359 1.1 christos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
360 1.1 christos goto err;
361 1.1 christos
362 1.1 christos if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
363 1.1 christos hp->cth_cuname) < 0)
364 1.1 christos goto err;
365 1.1 christos
366 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
367 1.1 christos hp->cth_objtoff) < 0)
368 1.1 christos goto err;
369 1.1 christos
370 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Data object section",
371 1.1 christos hp->cth_objtoff, hp->cth_funcoff) < 0)
372 1.1 christos goto err;
373 1.1 christos
374 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Function info section",
375 1.1.1.2 christos hp->cth_funcoff, hp->cth_objtidxoff) < 0)
376 1.1.1.2 christos goto err;
377 1.1.1.2 christos
378 1.1.1.2 christos if (ctf_dump_header_sectfield (fp, state, "Object index section",
379 1.1.1.2 christos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
380 1.1.1.2 christos goto err;
381 1.1.1.2 christos
382 1.1.1.2 christos if (ctf_dump_header_sectfield (fp, state, "Function index section",
383 1.1.1.2 christos hp->cth_funcidxoff, hp->cth_varoff) < 0)
384 1.1 christos goto err;
385 1.1 christos
386 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Variable section",
387 1.1 christos hp->cth_varoff, hp->cth_typeoff) < 0)
388 1.1 christos goto err;
389 1.1 christos
390 1.1 christos if (ctf_dump_header_sectfield (fp, state, "Type section",
391 1.1 christos hp->cth_typeoff, hp->cth_stroff) < 0)
392 1.1 christos goto err;
393 1.1 christos
394 1.1 christos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
395 1.1 christos hp->cth_stroff + hp->cth_strlen + 1) < 0)
396 1.1 christos goto err;
397 1.1 christos
398 1.1 christos return 0;
399 1.1 christos err:
400 1.1.1.2 christos free (flagstr);
401 1.1 christos return (ctf_set_errno (fp, errno));
402 1.1 christos }
403 1.1 christos
404 1.1 christos /* Dump a single label into the cds_items. */
405 1.1 christos
406 1.1 christos static int
407 1.1 christos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
408 1.1 christos void *arg)
409 1.1 christos {
410 1.1 christos char *str;
411 1.1 christos char *typestr;
412 1.1 christos ctf_dump_state_t *state = arg;
413 1.1 christos
414 1.1 christos if (asprintf (&str, "%s -> ", name) < 0)
415 1.1 christos return (ctf_set_errno (state->cds_fp, errno));
416 1.1 christos
417 1.1 christos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
418 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
419 1.1 christos {
420 1.1 christos free (str);
421 1.1 christos return 0; /* Swallow the error. */
422 1.1 christos }
423 1.1 christos
424 1.1 christos str = str_append (str, typestr);
425 1.1 christos free (typestr);
426 1.1 christos
427 1.1 christos ctf_dump_append (state, str);
428 1.1 christos return 0;
429 1.1 christos }
430 1.1 christos
431 1.1.1.2 christos /* Dump all the object or function entries into the cds_items. */
432 1.1 christos
433 1.1 christos static int
434 1.1.1.2 christos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
435 1.1 christos {
436 1.1.1.2 christos const char *name;
437 1.1.1.2 christos ctf_id_t id;
438 1.1.1.2 christos ctf_next_t *i = NULL;
439 1.1.1.2 christos char *str = NULL;
440 1.1 christos
441 1.1.1.2 christos if ((functions && fp->ctf_funcidx_names)
442 1.1.1.2 christos || (!functions && fp->ctf_objtidx_names))
443 1.1.1.2 christos str = str_append (str, _("Section is indexed.\n"));
444 1.1.1.2 christos else if (fp->ctf_symtab.cts_data == NULL)
445 1.1.1.2 christos str = str_append (str, _("No symbol table.\n"));
446 1.1.1.2 christos
447 1.1.1.2 christos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
448 1.1 christos {
449 1.1.1.2 christos char *typestr = NULL;
450 1.1 christos
451 1.1.1.2 christos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type
452 1.1.1.2 christos has a leading one. */
453 1.1.1.2 christos if (name)
454 1.1 christos {
455 1.1.1.2 christos if (asprintf (&str, "%s -> ", name) < 0)
456 1.1.1.2 christos goto oom;
457 1.1 christos }
458 1.1 christos else
459 1.1.1.2 christos str = xstrdup ("");
460 1.1 christos
461 1.1.1.2 christos if ((typestr = ctf_dump_format_type (state->cds_fp, id,
462 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
463 1.1 christos {
464 1.1.1.2 christos ctf_dump_append (state, str);
465 1.1.1.2 christos continue; /* Swallow the error. */
466 1.1 christos }
467 1.1 christos
468 1.1 christos str = str_append (str, typestr);
469 1.1 christos free (typestr);
470 1.1 christos ctf_dump_append (state, str);
471 1.1 christos continue;
472 1.1 christos
473 1.1 christos oom:
474 1.1.1.2 christos ctf_set_errno (fp, ENOMEM);
475 1.1.1.2 christos ctf_next_destroy (i);
476 1.1.1.2 christos return -1;
477 1.1 christos }
478 1.1 christos return 0;
479 1.1 christos }
480 1.1 christos
481 1.1 christos /* Dump a single variable into the cds_items. */
482 1.1 christos static int
483 1.1 christos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
484 1.1 christos {
485 1.1 christos char *str;
486 1.1 christos char *typestr;
487 1.1 christos ctf_dump_state_t *state = arg;
488 1.1 christos
489 1.1 christos if (asprintf (&str, "%s -> ", name) < 0)
490 1.1 christos return (ctf_set_errno (state->cds_fp, errno));
491 1.1 christos
492 1.1 christos if ((typestr = ctf_dump_format_type (state->cds_fp, type,
493 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
494 1.1 christos {
495 1.1 christos free (str);
496 1.1 christos return 0; /* Swallow the error. */
497 1.1 christos }
498 1.1 christos
499 1.1 christos str = str_append (str, typestr);
500 1.1 christos free (typestr);
501 1.1 christos
502 1.1 christos ctf_dump_append (state, str);
503 1.1 christos return 0;
504 1.1 christos }
505 1.1 christos
506 1.1.1.2 christos /* Dump a single struct/union member into the string in the membstate. */
507 1.1 christos static int
508 1.1 christos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
509 1.1.1.2 christos int depth, void *arg)
510 1.1 christos {
511 1.1 christos ctf_dump_membstate_t *state = arg;
512 1.1 christos char *typestr = NULL;
513 1.1 christos char *bit = NULL;
514 1.1 christos
515 1.1.1.2 christos /* The struct/union itself has already been printed. */
516 1.1.1.2 christos if (depth == 0)
517 1.1.1.2 christos return 0;
518 1.1 christos
519 1.1.1.2 christos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
520 1.1.1.2 christos goto oom;
521 1.1.1.2 christos *state->cdm_str = str_append (*state->cdm_str, bit);
522 1.1.1.2 christos free (bit);
523 1.1 christos
524 1.1.1.2 christos if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
525 1.1.1.2 christos CTF_ADD_ROOT | CTF_FT_BITFIELD
526 1.1.1.2 christos | CTF_FT_ID)) == NULL)
527 1.1.1.2 christos return -1; /* errno is set for us. */
528 1.1 christos
529 1.1.1.2 christos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
530 1.1.1.2 christos goto oom;
531 1.1 christos
532 1.1 christos *state->cdm_str = str_append (*state->cdm_str, bit);
533 1.1 christos free (typestr);
534 1.1 christos free (bit);
535 1.1 christos typestr = NULL;
536 1.1 christos bit = NULL;
537 1.1 christos
538 1.1 christos return 0;
539 1.1 christos
540 1.1 christos oom:
541 1.1 christos free (typestr);
542 1.1 christos free (bit);
543 1.1 christos return (ctf_set_errno (state->cdm_fp, errno));
544 1.1 christos }
545 1.1 christos
546 1.1.1.2 christos /* Report the number of digits in the hexadecimal representation of a type
547 1.1.1.2 christos ID. */
548 1.1.1.2 christos
549 1.1.1.2 christos static int
550 1.1.1.2 christos type_hex_digits (ctf_id_t id)
551 1.1.1.2 christos {
552 1.1.1.2 christos int i = 0;
553 1.1.1.2 christos
554 1.1.1.2 christos if (id == 0)
555 1.1.1.2 christos return 1;
556 1.1.1.2 christos
557 1.1.1.2 christos for (; id > 0; id >>= 4, i++);
558 1.1.1.2 christos return i;
559 1.1.1.2 christos }
560 1.1.1.2 christos
561 1.1 christos /* Dump a single type into the cds_items. */
562 1.1 christos static int
563 1.1 christos ctf_dump_type (ctf_id_t id, int flag, void *arg)
564 1.1 christos {
565 1.1 christos char *str;
566 1.1.1.2 christos char *indent;
567 1.1 christos ctf_dump_state_t *state = arg;
568 1.1.1.2 christos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
569 1.1 christos
570 1.1.1.2 christos /* Indent neatly. */
571 1.1.1.2 christos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0)
572 1.1.1.2 christos return (ctf_set_errno (state->cds_fp, ENOMEM));
573 1.1 christos
574 1.1.1.2 christos /* Dump the type itself. */
575 1.1.1.2 christos if ((str = ctf_dump_format_type (state->cds_fp, id,
576 1.1.1.2 christos flag | CTF_FT_REFS)) == NULL)
577 1.1.1.2 christos goto err;
578 1.1 christos str = str_append (str, "\n");
579 1.1.1.2 christos
580 1.1.1.2 christos membstate.cdm_toplevel_indent = indent;
581 1.1.1.2 christos
582 1.1.1.2 christos /* Member dumping for structs, unions... */
583 1.1.1.2 christos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
584 1.1.1.2 christos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
585 1.1 christos {
586 1.1.1.2 christos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
587 1.1 christos {
588 1.1.1.2 christos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
589 1.1.1.2 christos {
590 1.1.1.2 christos ctf_dump_append (state, str);
591 1.1.1.2 christos return 0;
592 1.1.1.2 christos }
593 1.1.1.2 christos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
594 1.1.1.2 christos _("cannot visit members dumping type 0x%lx"), id);
595 1.1.1.2 christos goto err;
596 1.1 christos }
597 1.1 christos }
598 1.1 christos
599 1.1.1.2 christos /* ... and enums, for which we dump the first and last few members and skip
600 1.1.1.2 christos the ones in the middle. */
601 1.1.1.2 christos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
602 1.1.1.2 christos {
603 1.1.1.2 christos int enum_count = ctf_member_count (state->cds_fp, id);
604 1.1.1.2 christos ctf_next_t *it = NULL;
605 1.1.1.2 christos int i = 0;
606 1.1.1.2 christos const char *enumerand;
607 1.1.1.2 christos char *bit;
608 1.1.1.2 christos int value;
609 1.1.1.2 christos
610 1.1.1.2 christos while ((enumerand = ctf_enum_next (state->cds_fp, id,
611 1.1.1.2 christos &it, &value)) != NULL)
612 1.1.1.2 christos {
613 1.1.1.2 christos i++;
614 1.1.1.2 christos if ((i > 5) && (i < enum_count - 4))
615 1.1.1.2 christos continue;
616 1.1.1.2 christos
617 1.1.1.2 christos str = str_append (str, indent);
618 1.1.1.2 christos
619 1.1.1.2 christos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
620 1.1.1.2 christos {
621 1.1.1.2 christos ctf_next_destroy (it);
622 1.1.1.2 christos goto oom;
623 1.1.1.2 christos }
624 1.1.1.2 christos str = str_append (str, bit);
625 1.1.1.2 christos free (bit);
626 1.1.1.2 christos
627 1.1.1.2 christos if ((i == 5) && (enum_count > 10))
628 1.1.1.2 christos {
629 1.1.1.2 christos str = str_append (str, indent);
630 1.1.1.2 christos str = str_append (str, "...\n");
631 1.1.1.2 christos }
632 1.1.1.2 christos }
633 1.1.1.2 christos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
634 1.1.1.2 christos {
635 1.1.1.2 christos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
636 1.1.1.2 christos _("cannot visit enumerands dumping type 0x%lx"), id);
637 1.1.1.2 christos goto err;
638 1.1.1.2 christos }
639 1.1.1.2 christos }
640 1.1 christos
641 1.1 christos ctf_dump_append (state, str);
642 1.1.1.2 christos free (indent);
643 1.1.1.2 christos
644 1.1 christos return 0;
645 1.1 christos
646 1.1 christos err:
647 1.1.1.2 christos free (indent);
648 1.1.1.2 christos free (str);
649 1.1.1.2 christos
650 1.1.1.2 christos /* Swallow the error: don't cause an error in one type to abort all
651 1.1.1.2 christos type dumping. */
652 1.1.1.2 christos return 0;
653 1.1.1.2 christos
654 1.1.1.2 christos oom:
655 1.1.1.2 christos free (indent);
656 1.1 christos free (str);
657 1.1.1.2 christos return ctf_set_errno (state->cds_fp, ENOMEM);
658 1.1 christos }
659 1.1 christos
660 1.1 christos /* Dump the string table into the cds_items. */
661 1.1 christos
662 1.1 christos static int
663 1.1.1.2 christos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
664 1.1 christos {
665 1.1 christos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
666 1.1 christos
667 1.1 christos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
668 1.1 christos fp->ctf_str[CTF_STRTAB_0].cts_len;)
669 1.1 christos {
670 1.1 christos char *str;
671 1.1.1.2 christos if (asprintf (&str, "0x%lx: %s",
672 1.1 christos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
673 1.1 christos s) < 0)
674 1.1 christos return (ctf_set_errno (fp, errno));
675 1.1 christos ctf_dump_append (state, str);
676 1.1 christos s += strlen (s) + 1;
677 1.1 christos }
678 1.1 christos
679 1.1 christos return 0;
680 1.1 christos }
681 1.1 christos
682 1.1 christos /* Dump a particular section of a CTF file, in textual form. Call with a
683 1.1 christos pointer to a NULL STATE: each call emits a dynamically allocated string
684 1.1 christos containing a description of one entity in the specified section, in order.
685 1.1 christos Only the first call (with a NULL state) may vary SECT. Once the CTF section
686 1.1 christos has been entirely dumped, the call returns NULL and frees and annuls the
687 1.1 christos STATE, ready for another section to be dumped. The returned textual content
688 1.1 christos may span multiple lines: between each call the FUNC is called with one
689 1.1 christos textual line at a time, and should return a suitably decorated line (it can
690 1.1 christos allocate a new one and return it if it likes). */
691 1.1 christos
692 1.1 christos char *
693 1.1.1.2 christos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
694 1.1 christos ctf_dump_decorate_f *func, void *arg)
695 1.1 christos {
696 1.1 christos char *str;
697 1.1 christos char *line;
698 1.1 christos ctf_dump_state_t *state = NULL;
699 1.1 christos
700 1.1 christos if (*statep == NULL)
701 1.1 christos {
702 1.1 christos /* Data collection. Transforming a call-at-a-time iterator into a
703 1.1 christos return-at-a-time iterator in a language without call/cc is annoying. It
704 1.1 christos is easiest to simply collect everything at once and then return it bit
705 1.1 christos by bit. The first call will take (much) longer than otherwise, but the
706 1.1 christos amortized time needed is the same. */
707 1.1 christos
708 1.1 christos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
709 1.1 christos {
710 1.1 christos ctf_set_errno (fp, ENOMEM);
711 1.1 christos goto end;
712 1.1 christos }
713 1.1 christos state = *statep;
714 1.1 christos
715 1.1 christos memset (state, 0, sizeof (struct ctf_dump_state));
716 1.1 christos state->cds_fp = fp;
717 1.1 christos state->cds_sect = sect;
718 1.1 christos
719 1.1 christos switch (sect)
720 1.1 christos {
721 1.1 christos case CTF_SECT_HEADER:
722 1.1 christos ctf_dump_header (fp, state);
723 1.1 christos break;
724 1.1 christos case CTF_SECT_LABEL:
725 1.1 christos if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
726 1.1 christos {
727 1.1 christos if (ctf_errno (fp) != ECTF_NOLABELDATA)
728 1.1 christos goto end; /* errno is set for us. */
729 1.1 christos ctf_set_errno (fp, 0);
730 1.1 christos }
731 1.1 christos break;
732 1.1 christos case CTF_SECT_OBJT:
733 1.1.1.2 christos if (ctf_dump_objts (fp, state, 0) < 0)
734 1.1 christos goto end; /* errno is set for us. */
735 1.1 christos break;
736 1.1 christos case CTF_SECT_FUNC:
737 1.1.1.2 christos if (ctf_dump_objts (fp, state, 1) < 0)
738 1.1 christos goto end; /* errno is set for us. */
739 1.1 christos break;
740 1.1 christos case CTF_SECT_VAR:
741 1.1 christos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
742 1.1 christos goto end; /* errno is set for us. */
743 1.1 christos break;
744 1.1 christos case CTF_SECT_TYPE:
745 1.1 christos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
746 1.1 christos goto end; /* errno is set for us. */
747 1.1 christos break;
748 1.1 christos case CTF_SECT_STR:
749 1.1 christos ctf_dump_str (fp, state);
750 1.1 christos break;
751 1.1 christos default:
752 1.1 christos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
753 1.1 christos goto end;
754 1.1 christos }
755 1.1 christos }
756 1.1 christos else
757 1.1 christos {
758 1.1 christos state = *statep;
759 1.1 christos
760 1.1 christos if (state->cds_sect != sect)
761 1.1 christos {
762 1.1 christos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
763 1.1 christos goto end;
764 1.1 christos }
765 1.1 christos }
766 1.1 christos
767 1.1 christos if (state->cds_current == NULL)
768 1.1 christos state->cds_current = ctf_list_next (&state->cds_items);
769 1.1 christos else
770 1.1 christos state->cds_current = ctf_list_next (state->cds_current);
771 1.1 christos
772 1.1 christos if (state->cds_current == NULL)
773 1.1 christos goto end;
774 1.1 christos
775 1.1 christos /* Hookery. There is some extra complexity to preserve linefeeds within each
776 1.1 christos item while removing linefeeds at the end. */
777 1.1 christos if (func)
778 1.1 christos {
779 1.1 christos size_t len;
780 1.1 christos
781 1.1 christos str = NULL;
782 1.1 christos for (line = state->cds_current->cdi_item; line && *line; )
783 1.1 christos {
784 1.1 christos char *nline = line;
785 1.1 christos char *ret;
786 1.1 christos
787 1.1 christos nline = strchr (line, '\n');
788 1.1 christos if (nline)
789 1.1 christos nline[0] = '\0';
790 1.1 christos
791 1.1 christos ret = func (sect, line, arg);
792 1.1 christos str = str_append (str, ret);
793 1.1 christos str = str_append (str, "\n");
794 1.1 christos if (ret != line)
795 1.1 christos free (ret);
796 1.1 christos
797 1.1 christos if (nline)
798 1.1 christos {
799 1.1 christos nline[0] = '\n';
800 1.1 christos nline++;
801 1.1 christos }
802 1.1 christos
803 1.1 christos line = nline;
804 1.1 christos }
805 1.1 christos
806 1.1 christos len = strlen (str);
807 1.1 christos
808 1.1 christos if (str[len-1] == '\n')
809 1.1 christos str[len-1] = '\0';
810 1.1 christos }
811 1.1 christos else
812 1.1 christos {
813 1.1 christos str = strdup (state->cds_current->cdi_item);
814 1.1 christos if (!str)
815 1.1 christos {
816 1.1 christos ctf_set_errno (fp, ENOMEM);
817 1.1 christos return str;
818 1.1 christos }
819 1.1 christos }
820 1.1 christos
821 1.1 christos ctf_set_errno (fp, 0);
822 1.1 christos return str;
823 1.1 christos
824 1.1 christos end:
825 1.1 christos ctf_dump_free (state);
826 1.1 christos free (state);
827 1.1 christos ctf_set_errno (fp, 0);
828 1.1 christos *statep = NULL;
829 1.1 christos return NULL;
830 1.1 christos }
831