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