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