16f4ced0bSchristos /* Textual dumping of CTF data.
2*cb63e24eSchristos Copyright (C) 2019-2024 Free Software Foundation, Inc.
36f4ced0bSchristos
46f4ced0bSchristos This file is part of libctf.
56f4ced0bSchristos
66f4ced0bSchristos libctf is free software; you can redistribute it and/or modify it under
76f4ced0bSchristos the terms of the GNU General Public License as published by the Free
86f4ced0bSchristos Software Foundation; either version 3, or (at your option) any later
96f4ced0bSchristos version.
106f4ced0bSchristos
116f4ced0bSchristos This program is distributed in the hope that it will be useful, but
126f4ced0bSchristos WITHOUT ANY WARRANTY; without even the implied warranty of
136f4ced0bSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
146f4ced0bSchristos See the GNU General Public License for more details.
156f4ced0bSchristos
166f4ced0bSchristos You should have received a copy of the GNU General Public License
176f4ced0bSchristos along with this program; see the file COPYING. If not see
186f4ced0bSchristos <http://www.gnu.org/licenses/>. */
196f4ced0bSchristos
206f4ced0bSchristos #include <ctf-impl.h>
216f4ced0bSchristos #include <string.h>
226f4ced0bSchristos
236f4ced0bSchristos #define str_append(s, a) ctf_str_append_noerr (s, a)
246f4ced0bSchristos
256f4ced0bSchristos /* One item to be dumped, in string form. */
266f4ced0bSchristos
276f4ced0bSchristos typedef struct ctf_dump_item
286f4ced0bSchristos {
296f4ced0bSchristos ctf_list_t cdi_list;
306f4ced0bSchristos char *cdi_item;
316f4ced0bSchristos } ctf_dump_item_t;
326f4ced0bSchristos
336f4ced0bSchristos /* Cross-call state for dumping. Basically just enough to track the section in
346f4ced0bSchristos use and a list of return strings. */
356f4ced0bSchristos
366f4ced0bSchristos struct ctf_dump_state
376f4ced0bSchristos {
386f4ced0bSchristos ctf_sect_names_t cds_sect;
394f645668Schristos ctf_dict_t *cds_fp;
406f4ced0bSchristos ctf_dump_item_t *cds_current;
416f4ced0bSchristos ctf_list_t cds_items;
426f4ced0bSchristos };
436f4ced0bSchristos
446f4ced0bSchristos /* Cross-call state for ctf_dump_member. */
456f4ced0bSchristos
466f4ced0bSchristos typedef struct ctf_dump_membstate
476f4ced0bSchristos {
486f4ced0bSchristos char **cdm_str;
494f645668Schristos ctf_dict_t *cdm_fp;
504f645668Schristos const char *cdm_toplevel_indent;
516f4ced0bSchristos } ctf_dump_membstate_t;
526f4ced0bSchristos
536f4ced0bSchristos static int
ctf_dump_append(ctf_dump_state_t * state,char * str)546f4ced0bSchristos ctf_dump_append (ctf_dump_state_t *state, char *str)
556f4ced0bSchristos {
566f4ced0bSchristos ctf_dump_item_t *cdi;
576f4ced0bSchristos
586f4ced0bSchristos if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
596f4ced0bSchristos return (ctf_set_errno (state->cds_fp, ENOMEM));
606f4ced0bSchristos
616f4ced0bSchristos cdi->cdi_item = str;
626f4ced0bSchristos ctf_list_append (&state->cds_items, cdi);
636f4ced0bSchristos return 0;
646f4ced0bSchristos }
656f4ced0bSchristos
666f4ced0bSchristos static void
ctf_dump_free(ctf_dump_state_t * state)676f4ced0bSchristos ctf_dump_free (ctf_dump_state_t *state)
686f4ced0bSchristos {
696f4ced0bSchristos ctf_dump_item_t *cdi, *next_cdi;
706f4ced0bSchristos
716f4ced0bSchristos if (state == NULL)
726f4ced0bSchristos return;
736f4ced0bSchristos
746f4ced0bSchristos for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
756f4ced0bSchristos cdi = next_cdi)
766f4ced0bSchristos {
776f4ced0bSchristos free (cdi->cdi_item);
786f4ced0bSchristos next_cdi = ctf_list_next (cdi);
796f4ced0bSchristos free (cdi);
806f4ced0bSchristos }
816f4ced0bSchristos }
826f4ced0bSchristos
834f645668Schristos /* Return a dump for a single type, without member info: but do optionally show
844f645668Schristos the type's references. */
856f4ced0bSchristos
864f645668Schristos #define CTF_FT_REFS 0x2 /* Print referenced types. */
874f645668Schristos #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */
884f645668Schristos #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */
896f4ced0bSchristos
906f4ced0bSchristos static char *
ctf_dump_format_type(ctf_dict_t * fp,ctf_id_t id,int flag)914f645668Schristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
926f4ced0bSchristos {
936f4ced0bSchristos ctf_id_t new_id;
946f4ced0bSchristos char *str = NULL, *bit = NULL, *buf = NULL;
956f4ced0bSchristos
964f645668Schristos ctf_set_errno (fp, 0);
976f4ced0bSchristos new_id = id;
986f4ced0bSchristos do
996f4ced0bSchristos {
1004f645668Schristos ctf_encoding_t ep;
1014f645668Schristos ctf_arinfo_t ar;
1024f645668Schristos int kind, unsliced_kind;
1034f645668Schristos ssize_t size, align;
1046f4ced0bSchristos const char *nonroot_leader = "";
1056f4ced0bSchristos const char *nonroot_trailer = "";
1064f645668Schristos const char *idstr = "";
1076f4ced0bSchristos
1086f4ced0bSchristos id = new_id;
1096f4ced0bSchristos if (flag == CTF_ADD_NONROOT)
1106f4ced0bSchristos {
1116f4ced0bSchristos nonroot_leader = "{";
1126f4ced0bSchristos nonroot_trailer = "}";
1136f4ced0bSchristos }
1146f4ced0bSchristos
1156f4ced0bSchristos buf = ctf_type_aname (fp, id);
1166f4ced0bSchristos if (!buf)
1176f4ced0bSchristos {
1186f4ced0bSchristos if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
1196f4ced0bSchristos {
1204f645668Schristos ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
1216f4ced0bSchristos str = str_append (str, " (type not represented in CTF)");
1224f645668Schristos return str;
1236f4ced0bSchristos }
1246f4ced0bSchristos
1256f4ced0bSchristos goto err;
1266f4ced0bSchristos }
1276f4ced0bSchristos
1284f645668Schristos if (flag & CTF_FT_ID)
1294f645668Schristos idstr = "ID ";
1304f645668Schristos if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
1314f645668Schristos id, ctf_type_kind (fp, id)) < 0)
1326f4ced0bSchristos goto oom;
1336f4ced0bSchristos str = str_append (str, bit);
1346f4ced0bSchristos free (bit);
1356f4ced0bSchristos bit = NULL;
1366f4ced0bSchristos
1374f645668Schristos if (buf[0] != '\0')
1384f645668Schristos str = str_append (str, buf);
1394f645668Schristos
1404f645668Schristos free (buf);
1414f645668Schristos buf = NULL;
1424f645668Schristos
1434f645668Schristos unsliced_kind = ctf_type_kind_unsliced (fp, id);
1444f645668Schristos kind = ctf_type_kind (fp, id);
1454f645668Schristos
1464f645668Schristos /* Report encodings of everything with an encoding other than enums:
1474f645668Schristos base-type enums cannot have a nonzero cte_offset or cte_bits value.
1484f645668Schristos (Slices of them can, but they are of kind CTF_K_SLICE.) */
1494f645668Schristos if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
1504f645668Schristos {
1514f645668Schristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
1524f645668Schristos && flag & CTF_FT_BITFIELD)
1534f645668Schristos {
1544f645668Schristos if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
1554f645668Schristos goto oom;
1564f645668Schristos str = str_append (str, bit);
1574f645668Schristos free (bit);
1584f645668Schristos bit = NULL;
1594f645668Schristos }
1604f645668Schristos
1614f645668Schristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
1624f645668Schristos || ep.cte_offset != 0)
1634f645668Schristos {
1644f645668Schristos const char *slice = "";
1654f645668Schristos
1664f645668Schristos if (unsliced_kind == CTF_K_SLICE)
1674f645668Schristos slice = "slice ";
1684f645668Schristos
1694f645668Schristos if (asprintf (&bit, " [%s0x%x:0x%x]",
1704f645668Schristos slice, ep.cte_offset, ep.cte_bits) < 0)
1714f645668Schristos goto oom;
1724f645668Schristos str = str_append (str, bit);
1734f645668Schristos free (bit);
1744f645668Schristos bit = NULL;
1754f645668Schristos }
1764f645668Schristos
1774f645668Schristos if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
1784f645668Schristos goto oom;
1794f645668Schristos str = str_append (str, bit);
1804f645668Schristos free (bit);
1814f645668Schristos bit = NULL;
1824f645668Schristos }
1834f645668Schristos
1844f645668Schristos size = ctf_type_size (fp, id);
1854f645668Schristos if (kind != CTF_K_FUNCTION && size >= 0)
1864f645668Schristos {
1874f645668Schristos if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
1884f645668Schristos goto oom;
1894f645668Schristos
1904f645668Schristos str = str_append (str, bit);
1914f645668Schristos free (bit);
1924f645668Schristos bit = NULL;
1934f645668Schristos }
1944f645668Schristos
1954f645668Schristos align = ctf_type_align (fp, id);
1964f645668Schristos if (align >= 0)
1974f645668Schristos {
1984f645668Schristos if (asprintf (&bit, " (aligned at 0x%lx)",
1994f645668Schristos (unsigned long int) align) < 0)
2004f645668Schristos goto oom;
2014f645668Schristos
2024f645668Schristos str = str_append (str, bit);
2034f645668Schristos free (bit);
2044f645668Schristos bit = NULL;
2054f645668Schristos }
2064f645668Schristos
2074f645668Schristos if (nonroot_trailer[0] != 0)
2084f645668Schristos str = str_append (str, nonroot_trailer);
2094f645668Schristos
2104f645668Schristos /* Just exit after one iteration if we are not showing the types this type
2114f645668Schristos references. */
2124f645668Schristos if (!(flag & CTF_FT_REFS))
2134f645668Schristos return str;
2144f645668Schristos
2154f645668Schristos /* Keep going as long as this type references another. We consider arrays
2164f645668Schristos to "reference" their element type. */
2174f645668Schristos
2184f645668Schristos if (kind == CTF_K_ARRAY)
2194f645668Schristos {
2204f645668Schristos if (ctf_array_info (fp, id, &ar) < 0)
2214f645668Schristos goto err;
2224f645668Schristos new_id = ar.ctr_contents;
2234f645668Schristos }
2244f645668Schristos else
2256f4ced0bSchristos new_id = ctf_type_reference (fp, id);
2266f4ced0bSchristos if (new_id != CTF_ERR)
2276f4ced0bSchristos str = str_append (str, " -> ");
2284f645668Schristos }
2294f645668Schristos while (new_id != CTF_ERR);
2306f4ced0bSchristos
2316f4ced0bSchristos if (ctf_errno (fp) != ECTF_NOTREF)
2326f4ced0bSchristos {
2336f4ced0bSchristos free (str);
2346f4ced0bSchristos return NULL;
2356f4ced0bSchristos }
2366f4ced0bSchristos
2376f4ced0bSchristos return str;
2386f4ced0bSchristos
2396f4ced0bSchristos oom:
2406f4ced0bSchristos ctf_set_errno (fp, errno);
2416f4ced0bSchristos err:
2424f645668Schristos ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
2436f4ced0bSchristos free (buf);
2446f4ced0bSchristos free (str);
2456f4ced0bSchristos free (bit);
2466f4ced0bSchristos return NULL;
2476f4ced0bSchristos }
2486f4ced0bSchristos
2496f4ced0bSchristos /* Dump one string field from the file header into the cds_items. */
2506f4ced0bSchristos static int
ctf_dump_header_strfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * name,uint32_t value)2514f645668Schristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2526f4ced0bSchristos const char *name, uint32_t value)
2536f4ced0bSchristos {
2546f4ced0bSchristos char *str;
2556f4ced0bSchristos if (value)
2566f4ced0bSchristos {
2576f4ced0bSchristos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
2586f4ced0bSchristos goto err;
2596f4ced0bSchristos ctf_dump_append (state, str);
2606f4ced0bSchristos }
2616f4ced0bSchristos return 0;
2626f4ced0bSchristos
2636f4ced0bSchristos err:
2646f4ced0bSchristos return (ctf_set_errno (fp, errno));
2656f4ced0bSchristos }
2666f4ced0bSchristos
2676f4ced0bSchristos /* Dump one section-offset field from the file header into the cds_items. */
2686f4ced0bSchristos static int
ctf_dump_header_sectfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * sect,uint32_t off,uint32_t nextoff)2694f645668Schristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2706f4ced0bSchristos const char *sect, uint32_t off, uint32_t nextoff)
2716f4ced0bSchristos {
2726f4ced0bSchristos char *str;
2736f4ced0bSchristos if (nextoff - off)
2746f4ced0bSchristos {
2756f4ced0bSchristos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
2766f4ced0bSchristos (unsigned long) off, (unsigned long) (nextoff - 1),
2776f4ced0bSchristos (unsigned long) (nextoff - off)) < 0)
2786f4ced0bSchristos goto err;
2796f4ced0bSchristos ctf_dump_append (state, str);
2806f4ced0bSchristos }
2816f4ced0bSchristos return 0;
2826f4ced0bSchristos
2836f4ced0bSchristos err:
2846f4ced0bSchristos return (ctf_set_errno (fp, errno));
2856f4ced0bSchristos }
2866f4ced0bSchristos
2876f4ced0bSchristos /* Dump the file header into the cds_items. */
2886f4ced0bSchristos static int
ctf_dump_header(ctf_dict_t * fp,ctf_dump_state_t * state)2894f645668Schristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
2906f4ced0bSchristos {
2916f4ced0bSchristos char *str;
2924f645668Schristos char *flagstr = NULL;
2936f4ced0bSchristos const ctf_header_t *hp = fp->ctf_header;
2946f4ced0bSchristos const char *vertab[] =
2956f4ced0bSchristos {
2966f4ced0bSchristos NULL, "CTF_VERSION_1",
2976f4ced0bSchristos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
2986f4ced0bSchristos "boundaries)",
2996f4ced0bSchristos "CTF_VERSION_2",
3006f4ced0bSchristos "CTF_VERSION_3", NULL
3016f4ced0bSchristos };
3026f4ced0bSchristos const char *verstr = NULL;
3036f4ced0bSchristos
3044f645668Schristos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
3056f4ced0bSchristos goto err;
3066f4ced0bSchristos ctf_dump_append (state, str);
3076f4ced0bSchristos
3086f4ced0bSchristos if (hp->cth_version <= CTF_VERSION)
3096f4ced0bSchristos verstr = vertab[hp->cth_version];
3106f4ced0bSchristos
3116f4ced0bSchristos if (verstr == NULL)
3126f4ced0bSchristos verstr = "(not a valid version)";
3136f4ced0bSchristos
3146f4ced0bSchristos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
3156f4ced0bSchristos verstr) < 0)
3166f4ced0bSchristos goto err;
3176f4ced0bSchristos ctf_dump_append (state, str);
3186f4ced0bSchristos
3196f4ced0bSchristos /* Everything else is only printed if present. */
3206f4ced0bSchristos
3214f645668Schristos /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
3226f4ced0bSchristos flags representing compression, etc, are turned off as the file is
3236f4ced0bSchristos decompressed. So we store a copy of the flags before they are changed, for
3246f4ced0bSchristos the dumper. */
3256f4ced0bSchristos
3266f4ced0bSchristos if (fp->ctf_openflags > 0)
3276f4ced0bSchristos {
3284f645668Schristos if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
3294f645668Schristos fp->ctf_openflags & CTF_F_COMPRESS
3304f645668Schristos ? "CTF_F_COMPRESS": "",
3314f645668Schristos (fp->ctf_openflags & CTF_F_COMPRESS)
3324f645668Schristos && (fp->ctf_openflags & ~CTF_F_COMPRESS)
3334f645668Schristos ? ", " : "",
3344f645668Schristos fp->ctf_openflags & CTF_F_NEWFUNCINFO
3354f645668Schristos ? "CTF_F_NEWFUNCINFO" : "",
3364f645668Schristos (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
3374f645668Schristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
3384f645668Schristos ? ", " : "",
3394f645668Schristos fp->ctf_openflags & CTF_F_IDXSORTED
3404f645668Schristos ? "CTF_F_IDXSORTED" : "",
3414f645668Schristos fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
3424f645668Schristos | CTF_F_IDXSORTED)
3434f645668Schristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
3444f645668Schristos | CTF_F_IDXSORTED))
3454f645668Schristos ? ", " : "",
3464f645668Schristos fp->ctf_openflags & CTF_F_DYNSTR
3474f645668Schristos ? "CTF_F_DYNSTR" : "") < 0)
3484f645668Schristos goto err;
3494f645668Schristos
3504f645668Schristos if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
3516f4ced0bSchristos goto err;
3526f4ced0bSchristos ctf_dump_append (state, str);
3536f4ced0bSchristos }
3546f4ced0bSchristos
3556f4ced0bSchristos if (ctf_dump_header_strfield (fp, state, "Parent label",
3566f4ced0bSchristos hp->cth_parlabel) < 0)
3576f4ced0bSchristos goto err;
3586f4ced0bSchristos
3596f4ced0bSchristos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
3606f4ced0bSchristos goto err;
3616f4ced0bSchristos
3626f4ced0bSchristos if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
3636f4ced0bSchristos hp->cth_cuname) < 0)
3646f4ced0bSchristos goto err;
3656f4ced0bSchristos
3666f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
3676f4ced0bSchristos hp->cth_objtoff) < 0)
3686f4ced0bSchristos goto err;
3696f4ced0bSchristos
3706f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "Data object section",
3716f4ced0bSchristos hp->cth_objtoff, hp->cth_funcoff) < 0)
3726f4ced0bSchristos goto err;
3736f4ced0bSchristos
3746f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "Function info section",
3754f645668Schristos hp->cth_funcoff, hp->cth_objtidxoff) < 0)
3764f645668Schristos goto err;
3774f645668Schristos
3784f645668Schristos if (ctf_dump_header_sectfield (fp, state, "Object index section",
3794f645668Schristos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
3804f645668Schristos goto err;
3814f645668Schristos
3824f645668Schristos if (ctf_dump_header_sectfield (fp, state, "Function index section",
3834f645668Schristos hp->cth_funcidxoff, hp->cth_varoff) < 0)
3846f4ced0bSchristos goto err;
3856f4ced0bSchristos
3866f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "Variable section",
3876f4ced0bSchristos hp->cth_varoff, hp->cth_typeoff) < 0)
3886f4ced0bSchristos goto err;
3896f4ced0bSchristos
3906f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "Type section",
3916f4ced0bSchristos hp->cth_typeoff, hp->cth_stroff) < 0)
3926f4ced0bSchristos goto err;
3936f4ced0bSchristos
3946f4ced0bSchristos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
3956f4ced0bSchristos hp->cth_stroff + hp->cth_strlen + 1) < 0)
3966f4ced0bSchristos goto err;
3976f4ced0bSchristos
3986f4ced0bSchristos return 0;
3996f4ced0bSchristos err:
4004f645668Schristos free (flagstr);
4016f4ced0bSchristos return (ctf_set_errno (fp, errno));
4026f4ced0bSchristos }
4036f4ced0bSchristos
4046f4ced0bSchristos /* Dump a single label into the cds_items. */
4056f4ced0bSchristos
4066f4ced0bSchristos static int
ctf_dump_label(const char * name,const ctf_lblinfo_t * info,void * arg)4076f4ced0bSchristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
4086f4ced0bSchristos void *arg)
4096f4ced0bSchristos {
4106f4ced0bSchristos char *str;
4116f4ced0bSchristos char *typestr;
4126f4ced0bSchristos ctf_dump_state_t *state = arg;
4136f4ced0bSchristos
4146f4ced0bSchristos if (asprintf (&str, "%s -> ", name) < 0)
4156f4ced0bSchristos return (ctf_set_errno (state->cds_fp, errno));
4166f4ced0bSchristos
4176f4ced0bSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
4184f645668Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4196f4ced0bSchristos {
4206f4ced0bSchristos free (str);
4214f645668Schristos return 0; /* Swallow the error. */
4226f4ced0bSchristos }
4236f4ced0bSchristos
4246f4ced0bSchristos str = str_append (str, typestr);
4256f4ced0bSchristos free (typestr);
4266f4ced0bSchristos
4276f4ced0bSchristos ctf_dump_append (state, str);
4286f4ced0bSchristos return 0;
4296f4ced0bSchristos }
4306f4ced0bSchristos
4314f645668Schristos /* Dump all the object or function entries into the cds_items. */
4326f4ced0bSchristos
4336f4ced0bSchristos static int
ctf_dump_objts(ctf_dict_t * fp,ctf_dump_state_t * state,int functions)4344f645668Schristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
4356f4ced0bSchristos {
4364f645668Schristos const char *name;
4374f645668Schristos ctf_id_t id;
4384f645668Schristos ctf_next_t *i = NULL;
4394f645668Schristos char *str = NULL;
4406f4ced0bSchristos
4414f645668Schristos if ((functions && fp->ctf_funcidx_names)
4424f645668Schristos || (!functions && fp->ctf_objtidx_names))
4434f645668Schristos str = str_append (str, _("Section is indexed.\n"));
4444f645668Schristos else if (fp->ctf_symtab.cts_data == NULL)
4454f645668Schristos str = str_append (str, _("No symbol table.\n"));
4466f4ced0bSchristos
4474f645668Schristos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
4486f4ced0bSchristos {
4494f645668Schristos char *typestr = NULL;
4506f4ced0bSchristos
4514f645668Schristos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type
4524f645668Schristos has a leading one. */
4534f645668Schristos if (name)
4546f4ced0bSchristos {
4554f645668Schristos if (asprintf (&str, "%s -> ", name) < 0)
4564f645668Schristos goto oom;
4576f4ced0bSchristos }
4586f4ced0bSchristos else
4594f645668Schristos str = xstrdup ("");
4606f4ced0bSchristos
4614f645668Schristos if ((typestr = ctf_dump_format_type (state->cds_fp, id,
4624f645668Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4636f4ced0bSchristos {
4644f645668Schristos ctf_dump_append (state, str);
4654f645668Schristos continue; /* Swallow the error. */
4666f4ced0bSchristos }
4676f4ced0bSchristos
4686f4ced0bSchristos str = str_append (str, typestr);
4696f4ced0bSchristos free (typestr);
4706f4ced0bSchristos ctf_dump_append (state, str);
4716f4ced0bSchristos continue;
4726f4ced0bSchristos
4736f4ced0bSchristos oom:
4744f645668Schristos ctf_set_errno (fp, ENOMEM);
4754f645668Schristos ctf_next_destroy (i);
4764f645668Schristos return -1;
4776f4ced0bSchristos }
4786f4ced0bSchristos return 0;
4796f4ced0bSchristos }
4806f4ced0bSchristos
4816f4ced0bSchristos /* Dump a single variable into the cds_items. */
4826f4ced0bSchristos static int
ctf_dump_var(const char * name,ctf_id_t type,void * arg)4836f4ced0bSchristos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
4846f4ced0bSchristos {
4856f4ced0bSchristos char *str;
4866f4ced0bSchristos char *typestr;
4876f4ced0bSchristos ctf_dump_state_t *state = arg;
4886f4ced0bSchristos
4896f4ced0bSchristos if (asprintf (&str, "%s -> ", name) < 0)
4906f4ced0bSchristos return (ctf_set_errno (state->cds_fp, errno));
4916f4ced0bSchristos
4926f4ced0bSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, type,
4934f645668Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4946f4ced0bSchristos {
4956f4ced0bSchristos free (str);
4964f645668Schristos return 0; /* Swallow the error. */
4976f4ced0bSchristos }
4986f4ced0bSchristos
4996f4ced0bSchristos str = str_append (str, typestr);
5006f4ced0bSchristos free (typestr);
5016f4ced0bSchristos
5026f4ced0bSchristos ctf_dump_append (state, str);
5036f4ced0bSchristos return 0;
5046f4ced0bSchristos }
5056f4ced0bSchristos
5064f645668Schristos /* Dump a single struct/union member into the string in the membstate. */
5076f4ced0bSchristos static int
ctf_dump_member(const char * name,ctf_id_t id,unsigned long offset,int depth,void * arg)5086f4ced0bSchristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
5096f4ced0bSchristos int depth, void *arg)
5106f4ced0bSchristos {
5116f4ced0bSchristos ctf_dump_membstate_t *state = arg;
5126f4ced0bSchristos char *typestr = NULL;
5136f4ced0bSchristos char *bit = NULL;
5146f4ced0bSchristos
5154f645668Schristos /* The struct/union itself has already been printed. */
5164f645668Schristos if (depth == 0)
5176f4ced0bSchristos return 0;
5186f4ced0bSchristos
5194f645668Schristos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
5206f4ced0bSchristos goto oom;
5214f645668Schristos *state->cdm_str = str_append (*state->cdm_str, bit);
5224f645668Schristos free (bit);
5236f4ced0bSchristos
5244f645668Schristos if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
5254f645668Schristos CTF_ADD_ROOT | CTF_FT_BITFIELD
5264f645668Schristos | CTF_FT_ID)) == NULL)
5274f645668Schristos return -1; /* errno is set for us. */
5284f645668Schristos
5294f645668Schristos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
5306f4ced0bSchristos goto oom;
5314f645668Schristos
5326f4ced0bSchristos *state->cdm_str = str_append (*state->cdm_str, bit);
5336f4ced0bSchristos free (typestr);
5346f4ced0bSchristos free (bit);
5356f4ced0bSchristos typestr = NULL;
5366f4ced0bSchristos bit = NULL;
5376f4ced0bSchristos
5386f4ced0bSchristos return 0;
5396f4ced0bSchristos
5406f4ced0bSchristos oom:
5416f4ced0bSchristos free (typestr);
5426f4ced0bSchristos free (bit);
5436f4ced0bSchristos return (ctf_set_errno (state->cdm_fp, errno));
5446f4ced0bSchristos }
5456f4ced0bSchristos
5464f645668Schristos /* Report the number of digits in the hexadecimal representation of a type
5474f645668Schristos ID. */
5484f645668Schristos
5494f645668Schristos static int
type_hex_digits(ctf_id_t id)5504f645668Schristos type_hex_digits (ctf_id_t id)
5514f645668Schristos {
5524f645668Schristos int i = 0;
5534f645668Schristos
5544f645668Schristos if (id == 0)
5554f645668Schristos return 1;
5564f645668Schristos
5574f645668Schristos for (; id > 0; id >>= 4, i++);
5584f645668Schristos return i;
5594f645668Schristos }
5604f645668Schristos
5616f4ced0bSchristos /* Dump a single type into the cds_items. */
5626f4ced0bSchristos static int
ctf_dump_type(ctf_id_t id,int flag,void * arg)5636f4ced0bSchristos ctf_dump_type (ctf_id_t id, int flag, void *arg)
5646f4ced0bSchristos {
5656f4ced0bSchristos char *str;
5664f645668Schristos char *indent;
5676f4ced0bSchristos ctf_dump_state_t *state = arg;
5684f645668Schristos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
5696f4ced0bSchristos
5704f645668Schristos /* Indent neatly. */
5714f645668Schristos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0)
5724f645668Schristos return (ctf_set_errno (state->cds_fp, ENOMEM));
5734f645668Schristos
5744f645668Schristos /* Dump the type itself. */
5754f645668Schristos if ((str = ctf_dump_format_type (state->cds_fp, id,
5764f645668Schristos flag | CTF_FT_REFS)) == NULL)
5776f4ced0bSchristos goto err;
5786f4ced0bSchristos str = str_append (str, "\n");
5794f645668Schristos
5804f645668Schristos membstate.cdm_toplevel_indent = indent;
5814f645668Schristos
5824f645668Schristos /* Member dumping for structs, unions... */
5834f645668Schristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
5844f645668Schristos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
5854f645668Schristos {
5866f4ced0bSchristos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
5876f4ced0bSchristos {
5886f4ced0bSchristos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
5896f4ced0bSchristos {
5906f4ced0bSchristos ctf_dump_append (state, str);
5916f4ced0bSchristos return 0;
5926f4ced0bSchristos }
5934f645668Schristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
5944f645668Schristos _("cannot visit members dumping type 0x%lx"), id);
5956f4ced0bSchristos goto err;
5966f4ced0bSchristos }
5974f645668Schristos }
5986f4ced0bSchristos
5994f645668Schristos /* ... and enums, for which we dump the first and last few members and skip
6004f645668Schristos the ones in the middle. */
6014f645668Schristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
6024f645668Schristos {
6034f645668Schristos int enum_count = ctf_member_count (state->cds_fp, id);
6044f645668Schristos ctf_next_t *it = NULL;
6054f645668Schristos int i = 0;
6064f645668Schristos const char *enumerand;
6074f645668Schristos char *bit;
6084f645668Schristos int value;
6094f645668Schristos
6104f645668Schristos while ((enumerand = ctf_enum_next (state->cds_fp, id,
6114f645668Schristos &it, &value)) != NULL)
6124f645668Schristos {
6134f645668Schristos i++;
6144f645668Schristos if ((i > 5) && (i < enum_count - 4))
6154f645668Schristos continue;
6164f645668Schristos
6174f645668Schristos str = str_append (str, indent);
6184f645668Schristos
6194f645668Schristos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
6204f645668Schristos {
6214f645668Schristos ctf_next_destroy (it);
6224f645668Schristos goto oom;
6234f645668Schristos }
6244f645668Schristos str = str_append (str, bit);
6254f645668Schristos free (bit);
6264f645668Schristos
6274f645668Schristos if ((i == 5) && (enum_count > 10))
6284f645668Schristos {
6294f645668Schristos str = str_append (str, indent);
6304f645668Schristos str = str_append (str, "...\n");
6314f645668Schristos }
6324f645668Schristos }
6334f645668Schristos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
6344f645668Schristos {
6354f645668Schristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
6364f645668Schristos _("cannot visit enumerands dumping type 0x%lx"), id);
6374f645668Schristos goto err;
6384f645668Schristos }
6394f645668Schristos }
6406f4ced0bSchristos
6416f4ced0bSchristos ctf_dump_append (state, str);
6424f645668Schristos free (indent);
6434f645668Schristos
6446f4ced0bSchristos return 0;
6456f4ced0bSchristos
6466f4ced0bSchristos err:
6474f645668Schristos free (indent);
6486f4ced0bSchristos free (str);
6494f645668Schristos
6504f645668Schristos /* Swallow the error: don't cause an error in one type to abort all
6514f645668Schristos type dumping. */
6524f645668Schristos return 0;
6534f645668Schristos
6544f645668Schristos oom:
6554f645668Schristos free (indent);
6564f645668Schristos free (str);
6574f645668Schristos return ctf_set_errno (state->cds_fp, ENOMEM);
6586f4ced0bSchristos }
6596f4ced0bSchristos
6606f4ced0bSchristos /* Dump the string table into the cds_items. */
6616f4ced0bSchristos
6626f4ced0bSchristos static int
ctf_dump_str(ctf_dict_t * fp,ctf_dump_state_t * state)6634f645668Schristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
6646f4ced0bSchristos {
6656f4ced0bSchristos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
6666f4ced0bSchristos
6676f4ced0bSchristos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
6686f4ced0bSchristos fp->ctf_str[CTF_STRTAB_0].cts_len;)
6696f4ced0bSchristos {
6706f4ced0bSchristos char *str;
6714f645668Schristos if (asprintf (&str, "0x%lx: %s",
6726f4ced0bSchristos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
6736f4ced0bSchristos s) < 0)
6746f4ced0bSchristos return (ctf_set_errno (fp, errno));
6756f4ced0bSchristos ctf_dump_append (state, str);
6766f4ced0bSchristos s += strlen (s) + 1;
6776f4ced0bSchristos }
6786f4ced0bSchristos
6796f4ced0bSchristos return 0;
6806f4ced0bSchristos }
6816f4ced0bSchristos
6826f4ced0bSchristos /* Dump a particular section of a CTF file, in textual form. Call with a
6836f4ced0bSchristos pointer to a NULL STATE: each call emits a dynamically allocated string
6846f4ced0bSchristos containing a description of one entity in the specified section, in order.
6856f4ced0bSchristos Only the first call (with a NULL state) may vary SECT. Once the CTF section
6866f4ced0bSchristos has been entirely dumped, the call returns NULL and frees and annuls the
6876f4ced0bSchristos STATE, ready for another section to be dumped. The returned textual content
6886f4ced0bSchristos may span multiple lines: between each call the FUNC is called with one
6896f4ced0bSchristos textual line at a time, and should return a suitably decorated line (it can
6906f4ced0bSchristos allocate a new one and return it if it likes). */
6916f4ced0bSchristos
6926f4ced0bSchristos char *
ctf_dump(ctf_dict_t * fp,ctf_dump_state_t ** statep,ctf_sect_names_t sect,ctf_dump_decorate_f * func,void * arg)6934f645668Schristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
6946f4ced0bSchristos ctf_dump_decorate_f *func, void *arg)
6956f4ced0bSchristos {
6966f4ced0bSchristos char *str;
6976f4ced0bSchristos char *line;
6986f4ced0bSchristos ctf_dump_state_t *state = NULL;
6996f4ced0bSchristos
7006f4ced0bSchristos if (*statep == NULL)
7016f4ced0bSchristos {
7026f4ced0bSchristos /* Data collection. Transforming a call-at-a-time iterator into a
7036f4ced0bSchristos return-at-a-time iterator in a language without call/cc is annoying. It
7046f4ced0bSchristos is easiest to simply collect everything at once and then return it bit
7056f4ced0bSchristos by bit. The first call will take (much) longer than otherwise, but the
7066f4ced0bSchristos amortized time needed is the same. */
7076f4ced0bSchristos
7086f4ced0bSchristos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
7096f4ced0bSchristos {
7106f4ced0bSchristos ctf_set_errno (fp, ENOMEM);
7116f4ced0bSchristos goto end;
7126f4ced0bSchristos }
7136f4ced0bSchristos state = *statep;
7146f4ced0bSchristos
7156f4ced0bSchristos memset (state, 0, sizeof (struct ctf_dump_state));
7166f4ced0bSchristos state->cds_fp = fp;
7176f4ced0bSchristos state->cds_sect = sect;
7186f4ced0bSchristos
7196f4ced0bSchristos switch (sect)
7206f4ced0bSchristos {
7216f4ced0bSchristos case CTF_SECT_HEADER:
7226f4ced0bSchristos ctf_dump_header (fp, state);
7236f4ced0bSchristos break;
7246f4ced0bSchristos case CTF_SECT_LABEL:
7256f4ced0bSchristos if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
7266f4ced0bSchristos {
7276f4ced0bSchristos if (ctf_errno (fp) != ECTF_NOLABELDATA)
7286f4ced0bSchristos goto end; /* errno is set for us. */
7296f4ced0bSchristos ctf_set_errno (fp, 0);
7306f4ced0bSchristos }
7316f4ced0bSchristos break;
7326f4ced0bSchristos case CTF_SECT_OBJT:
7334f645668Schristos if (ctf_dump_objts (fp, state, 0) < 0)
7346f4ced0bSchristos goto end; /* errno is set for us. */
7356f4ced0bSchristos break;
7366f4ced0bSchristos case CTF_SECT_FUNC:
7374f645668Schristos if (ctf_dump_objts (fp, state, 1) < 0)
7386f4ced0bSchristos goto end; /* errno is set for us. */
7396f4ced0bSchristos break;
7406f4ced0bSchristos case CTF_SECT_VAR:
7416f4ced0bSchristos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
7426f4ced0bSchristos goto end; /* errno is set for us. */
7436f4ced0bSchristos break;
7446f4ced0bSchristos case CTF_SECT_TYPE:
7456f4ced0bSchristos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
7466f4ced0bSchristos goto end; /* errno is set for us. */
7476f4ced0bSchristos break;
7486f4ced0bSchristos case CTF_SECT_STR:
7496f4ced0bSchristos ctf_dump_str (fp, state);
7506f4ced0bSchristos break;
7516f4ced0bSchristos default:
7526f4ced0bSchristos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
7536f4ced0bSchristos goto end;
7546f4ced0bSchristos }
7556f4ced0bSchristos }
7566f4ced0bSchristos else
7576f4ced0bSchristos {
7586f4ced0bSchristos state = *statep;
7596f4ced0bSchristos
7606f4ced0bSchristos if (state->cds_sect != sect)
7616f4ced0bSchristos {
7626f4ced0bSchristos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
7636f4ced0bSchristos goto end;
7646f4ced0bSchristos }
7656f4ced0bSchristos }
7666f4ced0bSchristos
7676f4ced0bSchristos if (state->cds_current == NULL)
7686f4ced0bSchristos state->cds_current = ctf_list_next (&state->cds_items);
7696f4ced0bSchristos else
7706f4ced0bSchristos state->cds_current = ctf_list_next (state->cds_current);
7716f4ced0bSchristos
7726f4ced0bSchristos if (state->cds_current == NULL)
7736f4ced0bSchristos goto end;
7746f4ced0bSchristos
7756f4ced0bSchristos /* Hookery. There is some extra complexity to preserve linefeeds within each
7766f4ced0bSchristos item while removing linefeeds at the end. */
7776f4ced0bSchristos if (func)
7786f4ced0bSchristos {
7796f4ced0bSchristos size_t len;
7806f4ced0bSchristos
7816f4ced0bSchristos str = NULL;
7826f4ced0bSchristos for (line = state->cds_current->cdi_item; line && *line; )
7836f4ced0bSchristos {
7846f4ced0bSchristos char *nline = line;
7856f4ced0bSchristos char *ret;
7866f4ced0bSchristos
7876f4ced0bSchristos nline = strchr (line, '\n');
7886f4ced0bSchristos if (nline)
7896f4ced0bSchristos nline[0] = '\0';
7906f4ced0bSchristos
7916f4ced0bSchristos ret = func (sect, line, arg);
7926f4ced0bSchristos str = str_append (str, ret);
7936f4ced0bSchristos str = str_append (str, "\n");
7946f4ced0bSchristos if (ret != line)
7956f4ced0bSchristos free (ret);
7966f4ced0bSchristos
7976f4ced0bSchristos if (nline)
7986f4ced0bSchristos {
7996f4ced0bSchristos nline[0] = '\n';
8006f4ced0bSchristos nline++;
8016f4ced0bSchristos }
8026f4ced0bSchristos
8036f4ced0bSchristos line = nline;
8046f4ced0bSchristos }
8056f4ced0bSchristos
8066f4ced0bSchristos len = strlen (str);
8076f4ced0bSchristos
8086f4ced0bSchristos if (str[len-1] == '\n')
8096f4ced0bSchristos str[len-1] = '\0';
8106f4ced0bSchristos }
8116f4ced0bSchristos else
8126f4ced0bSchristos {
8136f4ced0bSchristos str = strdup (state->cds_current->cdi_item);
8146f4ced0bSchristos if (!str)
8156f4ced0bSchristos {
8166f4ced0bSchristos ctf_set_errno (fp, ENOMEM);
8176f4ced0bSchristos return str;
8186f4ced0bSchristos }
8196f4ced0bSchristos }
8206f4ced0bSchristos
8216f4ced0bSchristos ctf_set_errno (fp, 0);
8226f4ced0bSchristos return str;
8236f4ced0bSchristos
8246f4ced0bSchristos end:
8256f4ced0bSchristos ctf_dump_free (state);
8266f4ced0bSchristos free (state);
8276f4ced0bSchristos ctf_set_errno (fp, 0);
8286f4ced0bSchristos *statep = NULL;
8296f4ced0bSchristos return NULL;
8306f4ced0bSchristos }
831