Release 2.33.1
[external/binutils.git] / libctf / ctf-dump.c
1 /* Textual dumping of CTF data.
2    Copyright (C) 2019 Free Software Foundation, Inc.
3
4    This file is part of libctf.
5
6    libctf is free software; you can redistribute it and/or modify it under
7    the terms of the GNU General Public License as published by the Free
8    Software Foundation; either version 3, or (at your option) any later
9    version.
10
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14    See the GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; see the file COPYING.  If not see
18    <http://www.gnu.org/licenses/>.  */
19
20 #include <ctf-impl.h>
21 #include <string.h>
22
23 /* One item to be dumped, in string form.  */
24
25 typedef struct ctf_dump_item
26 {
27   ctf_list_t cdi_list;
28   char *cdi_item;
29 } ctf_dump_item_t;
30
31 /* Cross-call state for dumping.  Basically just enough to track the section in
32    use and a list of return strings.  */
33
34 struct ctf_dump_state
35 {
36   ctf_sect_names_t cds_sect;
37   ctf_file_t *cds_fp;
38   ctf_dump_item_t *cds_current;
39   ctf_list_t cds_items;
40 };
41
42 /* Cross-call state for ctf_dump_member. */
43
44 typedef struct ctf_dump_membstate
45 {
46   char **cdm_str;
47   ctf_file_t *cdm_fp;
48 } ctf_dump_membstate_t;
49
50 static int
51 ctf_dump_append (ctf_dump_state_t *state, char *str)
52 {
53   ctf_dump_item_t *cdi;
54
55   if ((cdi = ctf_alloc (sizeof (struct ctf_dump_item))) == NULL)
56     return (ctf_set_errno (state->cds_fp, ENOMEM));
57
58   cdi->cdi_item = str;
59   ctf_list_append (&state->cds_items, cdi);
60   return 0;
61 }
62
63 static void
64 ctf_dump_free (ctf_dump_state_t *state)
65 {
66   ctf_dump_item_t *cdi, *next_cdi;
67
68   if (state == NULL)
69     return;
70
71   for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
72        cdi = next_cdi)
73     {
74       free (cdi->cdi_item);
75       next_cdi = ctf_list_next (cdi);
76       ctf_free (cdi);
77     }
78 }
79
80 /* Slices need special handling to distinguish them from their referenced
81    type.  */
82
83 static int
84 ctf_is_slice (ctf_file_t *fp, ctf_id_t id, ctf_encoding_t *enc)
85 {
86   int kind = ctf_type_kind (fp, id);
87
88   return (((kind == CTF_K_INTEGER) || (kind == CTF_K_ENUM)
89            || (kind == CTF_K_FLOAT))
90           && ctf_type_reference (fp, id) != CTF_ERR
91           && ctf_type_encoding (fp, id, enc) == 0);
92 }
93
94 /* Return a dump for a single type, without member info: but do show the
95    type's references.  */
96
97 static char *
98 ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id)
99 {
100   ctf_id_t new_id;
101   char *str = NULL, *bit = NULL, *buf = NULL;
102
103   new_id = id;
104   do
105     {
106       ctf_encoding_t enc;
107
108       id = new_id;
109       buf = ctf_type_aname (fp, id);
110       if (!buf)
111         goto oom;
112
113       /* Slices get a different print representation.  */
114
115       if (ctf_is_slice (fp, id, &enc))
116         {
117           ctf_type_encoding (fp, id, &enc);
118           if (asprintf (&bit, " %lx: [slice 0x%x:0x%x]",
119                         id, enc.cte_offset, enc.cte_bits) < 0)
120             goto oom;
121         }
122       else
123         {
124           if (asprintf (&bit, " %lx: %s (size 0x%lx)", id, buf[0] == '\0' ?
125                         "(nameless)" : buf,
126                         (unsigned long) ctf_type_size (fp, id)) < 0)
127             goto oom;
128         }
129       free (buf);
130       buf = NULL;
131       str = ctf_str_append (str, bit);
132       free (bit);
133       bit = NULL;
134
135       new_id = ctf_type_reference (fp, id);
136       if (new_id != CTF_ERR)
137         str = ctf_str_append (str, " ->");
138     } while (new_id != CTF_ERR);
139
140   if (ctf_errno (fp) != ECTF_NOTREF)
141     {
142       free (str);
143       return NULL;
144     }
145
146   return str;
147
148  oom:
149   free (buf);
150   free (str);
151   free (bit);
152   ctf_set_errno (fp, ENOMEM);
153   return NULL;
154 }
155
156 /* Dump a single label into the cds_items.  */
157
158 static int
159 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
160                 void *arg)
161 {
162   char *str;
163   char *typestr;
164   ctf_dump_state_t *state = arg;
165
166   if (asprintf (&str, "%s -> ", name) < 0)
167     return (ctf_set_errno (state->cds_fp, ENOMEM));
168
169   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type)) == NULL)
170     {
171       free (str);
172       return -1;                        /* errno is set for us.  */
173     }
174
175   str = ctf_str_append (str, typestr);
176   free (typestr);
177
178   ctf_dump_append (state, str);
179   return 0;
180 }
181
182 /* Dump all the object entries into the cds_items.  (There is no iterator for
183    this section, so we just do it in a loop, and this function handles all of
184    them, rather than only one.  */
185
186 static int
187 ctf_dump_objts (ctf_file_t *fp, ctf_dump_state_t *state)
188 {
189   size_t i;
190
191   for (i = 0; i < fp->ctf_nsyms; i++)
192     {
193       char *str;
194       char *typestr;
195       const char *sym_name;
196       ctf_id_t type;
197
198       if ((type = ctf_lookup_by_symbol (state->cds_fp, i)) == CTF_ERR)
199         switch (ctf_errno (state->cds_fp))
200           {
201             /* Most errors are just an indication that this symbol is not a data
202                symbol, but this one indicates that we were called wrong, on a
203                CTF file with no associated symbol table.  */
204           case ECTF_NOSYMTAB:
205             return -1;
206           case ECTF_NOTDATA:
207           case ECTF_NOTYPEDAT:
208             continue;
209           }
210
211       /* Variable name.  */
212       sym_name = ctf_lookup_symbol_name (fp, i);
213       if (sym_name[0] == '\0')
214         {
215           if (asprintf (&str, "%lx -> ", (unsigned long) i) < 0)
216             return (ctf_set_errno (fp, ENOMEM));
217         }
218       else
219         {
220           if (asprintf (&str, "%s (%lx) -> ", sym_name, (unsigned long) i) < 0)
221             return (ctf_set_errno (fp, ENOMEM));
222         }
223
224       /* Variable type.  */
225       if ((typestr = ctf_dump_format_type (state->cds_fp, type)) == NULL)
226         {
227           free (str);
228           return -1;                    /* errno is set for us.  */
229         }
230
231       str = ctf_str_append (str, typestr);
232       free (typestr);
233
234       ctf_dump_append (state, str);
235     }
236   return 0;
237 }
238
239 /* Dump all the function entries into the cds_items.  (As above, there is no
240    iterator for this section.)  */
241
242 static int
243 ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
244 {
245   size_t i;
246
247   for (i = 0; i < fp->ctf_nsyms; i++)
248     {
249       char *str ;
250       char *bit;
251       const char *sym_name;
252       ctf_funcinfo_t fi;
253       ctf_id_t type;
254       size_t j;
255       ctf_id_t *args;
256
257       if ((type = ctf_func_info (state->cds_fp, i, &fi)) == CTF_ERR)
258         switch (ctf_errno (state->cds_fp))
259           {
260             /* Most errors are just an indication that this symbol is not a data
261                symbol, but this one indicates that we were called wrong, on a
262                CTF file with no associated symbol table.  */
263           case ECTF_NOSYMTAB:
264             return -1;
265           case ECTF_NOTDATA:
266           case ECTF_NOTYPEDAT:
267             continue;
268           }
269       if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
270         return (ctf_set_errno (fp, ENOMEM));
271
272       /* Return type.  */
273       if ((str = ctf_type_aname (state->cds_fp, type)) == NULL)
274         goto err;
275
276       str = ctf_str_append (str, " ");
277
278       /* Function name.  */
279
280       sym_name = ctf_lookup_symbol_name (fp, i);
281       if (sym_name[0] == '\0')
282         {
283           if (asprintf (&bit, "0x%lx ", (unsigned long) i) < 0)
284             goto oom;
285         }
286       else
287         {
288           if (asprintf (&bit, "%s (0x%lx) ", sym_name, (unsigned long) i) < 0)
289             goto oom;
290         }
291       str = ctf_str_append (str, bit);
292       str = ctf_str_append (str, " (");
293       free (bit);
294
295       /* Function arguments.  */
296
297       if (ctf_func_args (state->cds_fp, i, fi.ctc_argc, args) < 0)
298         goto err;
299
300       for (j = 0; j < fi.ctc_argc; j++)
301         {
302           if ((bit = ctf_type_aname (state->cds_fp, args[j])) == NULL)
303             goto err;
304           str = ctf_str_append (str, bit);
305           if ((j < fi.ctc_argc - 1) || (fi.ctc_flags & CTF_FUNC_VARARG))
306             str = ctf_str_append (str, ", ");
307           free (bit);
308         }
309
310       if (fi.ctc_flags & CTF_FUNC_VARARG)
311         str = ctf_str_append (str, "...");
312       str = ctf_str_append (str, ")");
313
314       free (args);
315       ctf_dump_append (state, str);
316       continue;
317
318     oom:
319       free (args);
320       free (str);
321       return (ctf_set_errno (fp, ENOMEM));
322     err:
323       free (args);
324       free (str);
325       return -1;                /* errno is set for us.  */
326     }
327   return 0;
328 }
329
330 /* Dump a single variable into the cds_items.  */
331 static int
332 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
333 {
334   char *str;
335   char *typestr;
336   ctf_dump_state_t *state = arg;
337
338   if (asprintf (&str, "%s -> ", name) < 0)
339     return (ctf_set_errno (state->cds_fp, ENOMEM));
340
341   if ((typestr = ctf_dump_format_type (state->cds_fp, type)) == NULL)
342     {
343       free (str);
344       return -1;                        /* errno is set for us.  */
345     }
346
347   str = ctf_str_append (str, typestr);
348   free (typestr);
349
350   ctf_dump_append (state, str);
351   return 0;
352 }
353
354 /* Dump a single member into the string in the membstate.  */
355 static int
356 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
357                   int depth, void *arg)
358 {
359   ctf_dump_membstate_t *state = arg;
360   char *typestr = NULL;
361   char *bit = NULL;
362   ctf_encoding_t ep;
363   ssize_t i;
364
365   for (i = 0; i < depth; i++)
366     *state->cdm_str = ctf_str_append (*state->cdm_str, "    ");
367
368   if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
369     goto oom;
370
371   if (asprintf (&bit, "    [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
372                 offset, id, ctf_type_kind (state->cdm_fp, id), typestr, name,
373                 (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
374     goto oom;
375   *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
376   free (typestr);
377   free (bit);
378   typestr = NULL;
379   bit = NULL;
380
381   if ((ctf_type_kind (state->cdm_fp, id) == CTF_K_INTEGER)
382       || (ctf_type_kind (state->cdm_fp, id) == CTF_K_FLOAT)
383       || (ctf_is_slice (state->cdm_fp, id, &ep) == CTF_K_ENUM))
384     {
385       ctf_type_encoding (state->cdm_fp, id, &ep);
386       if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
387                     ep.cte_offset, ep.cte_bits) < 0)
388         goto oom;
389       *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
390       free (bit);
391       bit = NULL;
392     }
393
394   *state->cdm_str = ctf_str_append (*state->cdm_str, ")\n");
395   return 0;
396
397  oom:
398   free (typestr);
399   free (bit);
400   return (ctf_set_errno (state->cdm_fp, ENOMEM));
401 }
402
403 /* Dump a single type into the cds_items.  */
404
405 static int
406 ctf_dump_type (ctf_id_t id, void *arg)
407 {
408   char *str;
409   ctf_dump_state_t *state = arg;
410   ctf_dump_membstate_t membstate = { &str, state->cds_fp };
411   size_t len;
412
413   if ((str = ctf_dump_format_type (state->cds_fp, id)) == NULL)
414     goto err;
415
416   str = ctf_str_append (str, "\n");
417   if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
418     goto err;
419
420   /* Trim off the last linefeed added by ctf_dump_member().  */
421   len = strlen (str);
422   if (str[len-1] == '\n')
423     str[len-1] = '\0';
424
425   ctf_dump_append (state, str);
426   return 0;
427
428  err:
429   free (str);
430   return -1;                            /* errno is set for us.  */
431 }
432
433 /* Dump the string table into the cds_items.  */
434
435 static int
436 ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
437 {
438   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
439
440   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
441          fp->ctf_str[CTF_STRTAB_0].cts_len;)
442     {
443       char *str;
444       if (asprintf (&str, "%lx: %s",
445                     (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
446                     s) < 0)
447         return (ctf_set_errno (fp, ENOMEM));
448       ctf_dump_append (state, str);
449       s += strlen (s) + 1;
450     }
451
452   return 0;
453 }
454
455 /* Dump a particular section of a CTF file, in textual form.  Call with a
456    pointer to a NULL STATE: each call emits a dynamically allocated string
457    containing a description of one entity in the specified section, in order.
458    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
459    has been entirely dumped, the call returns NULL and frees and annuls the
460    STATE, ready for another section to be dumped.  The returned textual content
461    may span multiple lines: between each call the FUNC is called with one
462    textual line at a time, and should return a suitably decorated line (it can
463    allocate a new one and return it if it likes).  */
464
465 char *
466 ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
467           ctf_dump_decorate_f *func, void *arg)
468 {
469   char *str;
470   char *line;
471   ctf_dump_state_t *state = NULL;
472
473   if (*statep == NULL)
474     {
475       /* Data collection.  Transforming a call-at-a-time iterator into a
476          return-at-a-time iterator in a language without call/cc is annoying. It
477          is easiest to simply collect everything at once and then return it bit
478          by bit.  The first call will take (much) longer than otherwise, but the
479          amortized time needed is the same.  */
480
481       if ((*statep = ctf_alloc (sizeof (struct ctf_dump_state))) == NULL)
482         {
483           ctf_set_errno (fp, ENOMEM);
484           goto end;
485         }
486       state = *statep;
487
488       memset (state, 0, sizeof (struct ctf_dump_state));
489       state->cds_fp = fp;
490       state->cds_sect = sect;
491
492       switch (sect)
493         {
494         case CTF_SECT_HEADER:
495           /* Nothing doable (yet): entire header is discarded after read-phase.  */
496           str = strdup ("");
497           break;
498         case CTF_SECT_LABEL:
499           if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
500             {
501               if (ctf_errno (fp) != ECTF_NOLABELDATA)
502                 goto end;               /* errno is set for us.  */
503               ctf_set_errno (fp, 0);
504             }
505           break;
506         case CTF_SECT_OBJT:
507           if (ctf_dump_objts (fp, state) < 0)
508             goto end;                   /* errno is set for us.  */
509           break;
510         case CTF_SECT_FUNC:
511           if (ctf_dump_funcs (fp, state) < 0)
512             goto end;                   /* errno is set for us.  */
513           break;
514         case CTF_SECT_VAR:
515           if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
516             goto end;                   /* errno is set for us.  */
517           break;
518         case CTF_SECT_TYPE:
519           if (ctf_type_iter (fp, ctf_dump_type, state) < 0)
520             goto end;                   /* errno is set for us.  */
521           break;
522         case CTF_SECT_STR:
523           ctf_dump_str (fp, state);
524           break;
525         default:
526           ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
527           goto end;
528         }
529     }
530   else
531     {
532       state = *statep;
533
534       if (state->cds_sect != sect)
535         {
536           ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
537           goto end;
538         }
539     }
540
541   if (state->cds_current == NULL)
542     state->cds_current = ctf_list_next (&state->cds_items);
543   else
544     state->cds_current = ctf_list_next (state->cds_current);
545
546   if (state->cds_current == NULL)
547     goto end;
548
549   /* Hookery.  There is some extra complexity to preserve linefeeds within each
550      item while removing linefeeds at the end.  */
551   if (func)
552     {
553       size_t len;
554
555       str = NULL;
556       for (line = state->cds_current->cdi_item; line && *line; )
557         {
558           char *nline = line;
559           char *ret;
560
561           nline = strchr (line, '\n');
562           if (nline)
563             nline[0] = '\0';
564
565           ret = func (sect, line, arg);
566           str = ctf_str_append (str, ret);
567           str = ctf_str_append (str, "\n");
568           if (ret != line)
569             free (ret);
570
571           if (nline)
572             {
573               nline[0] = '\n';
574               nline++;
575             }
576
577           line = nline;
578         }
579
580       len = strlen (str);
581
582       if (str[len-1] == '\n')
583         str[len-1] = '\0';
584     }
585   else
586     str = strdup (state->cds_current->cdi_item);
587
588   ctf_set_errno (fp, 0);
589   return str;
590
591  end:
592   ctf_dump_free (state);
593   ctf_free (state);
594   ctf_set_errno (fp, 0);
595   *statep = NULL;
596   return NULL;
597 }