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