17c2aad20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 27c2aad20Sopenharmony_ci 37c2aad20Sopenharmony_ci/* 47c2aad20Sopenharmony_ci * BTF-to-C type converter. 57c2aad20Sopenharmony_ci * 67c2aad20Sopenharmony_ci * Copyright (c) 2019 Facebook 77c2aad20Sopenharmony_ci */ 87c2aad20Sopenharmony_ci 97c2aad20Sopenharmony_ci#include <stdbool.h> 107c2aad20Sopenharmony_ci#include <stddef.h> 117c2aad20Sopenharmony_ci#include <stdlib.h> 127c2aad20Sopenharmony_ci#include <string.h> 137c2aad20Sopenharmony_ci#include <ctype.h> 147c2aad20Sopenharmony_ci#include <endian.h> 157c2aad20Sopenharmony_ci#include <errno.h> 167c2aad20Sopenharmony_ci#include <limits.h> 177c2aad20Sopenharmony_ci#include <linux/err.h> 187c2aad20Sopenharmony_ci#include <linux/btf.h> 197c2aad20Sopenharmony_ci#include <linux/kernel.h> 207c2aad20Sopenharmony_ci#include "btf.h" 217c2aad20Sopenharmony_ci#include "hashmap.h" 227c2aad20Sopenharmony_ci#include "libbpf.h" 237c2aad20Sopenharmony_ci#include "libbpf_internal.h" 247c2aad20Sopenharmony_ci 257c2aad20Sopenharmony_cistatic const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; 267c2aad20Sopenharmony_cistatic const size_t PREFIX_CNT = sizeof(PREFIXES) - 1; 277c2aad20Sopenharmony_ci 287c2aad20Sopenharmony_cistatic const char *pfx(int lvl) 297c2aad20Sopenharmony_ci{ 307c2aad20Sopenharmony_ci return lvl >= PREFIX_CNT ? PREFIXES : &PREFIXES[PREFIX_CNT - lvl]; 317c2aad20Sopenharmony_ci} 327c2aad20Sopenharmony_ci 337c2aad20Sopenharmony_cienum btf_dump_type_order_state { 347c2aad20Sopenharmony_ci NOT_ORDERED, 357c2aad20Sopenharmony_ci ORDERING, 367c2aad20Sopenharmony_ci ORDERED, 377c2aad20Sopenharmony_ci}; 387c2aad20Sopenharmony_ci 397c2aad20Sopenharmony_cienum btf_dump_type_emit_state { 407c2aad20Sopenharmony_ci NOT_EMITTED, 417c2aad20Sopenharmony_ci EMITTING, 427c2aad20Sopenharmony_ci EMITTED, 437c2aad20Sopenharmony_ci}; 447c2aad20Sopenharmony_ci 457c2aad20Sopenharmony_ci/* per-type auxiliary state */ 467c2aad20Sopenharmony_cistruct btf_dump_type_aux_state { 477c2aad20Sopenharmony_ci /* topological sorting state */ 487c2aad20Sopenharmony_ci enum btf_dump_type_order_state order_state: 2; 497c2aad20Sopenharmony_ci /* emitting state used to determine the need for forward declaration */ 507c2aad20Sopenharmony_ci enum btf_dump_type_emit_state emit_state: 2; 517c2aad20Sopenharmony_ci /* whether forward declaration was already emitted */ 527c2aad20Sopenharmony_ci __u8 fwd_emitted: 1; 537c2aad20Sopenharmony_ci /* whether unique non-duplicate name was already assigned */ 547c2aad20Sopenharmony_ci __u8 name_resolved: 1; 557c2aad20Sopenharmony_ci /* whether type is referenced from any other type */ 567c2aad20Sopenharmony_ci __u8 referenced: 1; 577c2aad20Sopenharmony_ci}; 587c2aad20Sopenharmony_ci 597c2aad20Sopenharmony_ci/* indent string length; one indent string is added for each indent level */ 607c2aad20Sopenharmony_ci#define BTF_DATA_INDENT_STR_LEN 32 617c2aad20Sopenharmony_ci 627c2aad20Sopenharmony_ci/* 637c2aad20Sopenharmony_ci * Common internal data for BTF type data dump operations. 647c2aad20Sopenharmony_ci */ 657c2aad20Sopenharmony_cistruct btf_dump_data { 667c2aad20Sopenharmony_ci const void *data_end; /* end of valid data to show */ 677c2aad20Sopenharmony_ci bool compact; 687c2aad20Sopenharmony_ci bool skip_names; 697c2aad20Sopenharmony_ci bool emit_zeroes; 707c2aad20Sopenharmony_ci __u8 indent_lvl; /* base indent level */ 717c2aad20Sopenharmony_ci char indent_str[BTF_DATA_INDENT_STR_LEN]; 727c2aad20Sopenharmony_ci /* below are used during iteration */ 737c2aad20Sopenharmony_ci int depth; 747c2aad20Sopenharmony_ci bool is_array_member; 757c2aad20Sopenharmony_ci bool is_array_terminated; 767c2aad20Sopenharmony_ci bool is_array_char; 777c2aad20Sopenharmony_ci}; 787c2aad20Sopenharmony_ci 797c2aad20Sopenharmony_cistruct btf_dump { 807c2aad20Sopenharmony_ci const struct btf *btf; 817c2aad20Sopenharmony_ci btf_dump_printf_fn_t printf_fn; 827c2aad20Sopenharmony_ci void *cb_ctx; 837c2aad20Sopenharmony_ci int ptr_sz; 847c2aad20Sopenharmony_ci bool strip_mods; 857c2aad20Sopenharmony_ci bool skip_anon_defs; 867c2aad20Sopenharmony_ci int last_id; 877c2aad20Sopenharmony_ci 887c2aad20Sopenharmony_ci /* per-type auxiliary state */ 897c2aad20Sopenharmony_ci struct btf_dump_type_aux_state *type_states; 907c2aad20Sopenharmony_ci size_t type_states_cap; 917c2aad20Sopenharmony_ci /* per-type optional cached unique name, must be freed, if present */ 927c2aad20Sopenharmony_ci const char **cached_names; 937c2aad20Sopenharmony_ci size_t cached_names_cap; 947c2aad20Sopenharmony_ci 957c2aad20Sopenharmony_ci /* topo-sorted list of dependent type definitions */ 967c2aad20Sopenharmony_ci __u32 *emit_queue; 977c2aad20Sopenharmony_ci int emit_queue_cap; 987c2aad20Sopenharmony_ci int emit_queue_cnt; 997c2aad20Sopenharmony_ci 1007c2aad20Sopenharmony_ci /* 1017c2aad20Sopenharmony_ci * stack of type declarations (e.g., chain of modifiers, arrays, 1027c2aad20Sopenharmony_ci * funcs, etc) 1037c2aad20Sopenharmony_ci */ 1047c2aad20Sopenharmony_ci __u32 *decl_stack; 1057c2aad20Sopenharmony_ci int decl_stack_cap; 1067c2aad20Sopenharmony_ci int decl_stack_cnt; 1077c2aad20Sopenharmony_ci 1087c2aad20Sopenharmony_ci /* maps struct/union/enum name to a number of name occurrences */ 1097c2aad20Sopenharmony_ci struct hashmap *type_names; 1107c2aad20Sopenharmony_ci /* 1117c2aad20Sopenharmony_ci * maps typedef identifiers and enum value names to a number of such 1127c2aad20Sopenharmony_ci * name occurrences 1137c2aad20Sopenharmony_ci */ 1147c2aad20Sopenharmony_ci struct hashmap *ident_names; 1157c2aad20Sopenharmony_ci /* 1167c2aad20Sopenharmony_ci * data for typed display; allocated if needed. 1177c2aad20Sopenharmony_ci */ 1187c2aad20Sopenharmony_ci struct btf_dump_data *typed_dump; 1197c2aad20Sopenharmony_ci}; 1207c2aad20Sopenharmony_ci 1217c2aad20Sopenharmony_cistatic size_t str_hash_fn(long key, void *ctx) 1227c2aad20Sopenharmony_ci{ 1237c2aad20Sopenharmony_ci return str_hash((void *)key); 1247c2aad20Sopenharmony_ci} 1257c2aad20Sopenharmony_ci 1267c2aad20Sopenharmony_cistatic bool str_equal_fn(long a, long b, void *ctx) 1277c2aad20Sopenharmony_ci{ 1287c2aad20Sopenharmony_ci return strcmp((void *)a, (void *)b) == 0; 1297c2aad20Sopenharmony_ci} 1307c2aad20Sopenharmony_ci 1317c2aad20Sopenharmony_cistatic const char *btf_name_of(const struct btf_dump *d, __u32 name_off) 1327c2aad20Sopenharmony_ci{ 1337c2aad20Sopenharmony_ci return btf__name_by_offset(d->btf, name_off); 1347c2aad20Sopenharmony_ci} 1357c2aad20Sopenharmony_ci 1367c2aad20Sopenharmony_cistatic void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...) 1377c2aad20Sopenharmony_ci{ 1387c2aad20Sopenharmony_ci va_list args; 1397c2aad20Sopenharmony_ci 1407c2aad20Sopenharmony_ci va_start(args, fmt); 1417c2aad20Sopenharmony_ci d->printf_fn(d->cb_ctx, fmt, args); 1427c2aad20Sopenharmony_ci va_end(args); 1437c2aad20Sopenharmony_ci} 1447c2aad20Sopenharmony_ci 1457c2aad20Sopenharmony_cistatic int btf_dump_mark_referenced(struct btf_dump *d); 1467c2aad20Sopenharmony_cistatic int btf_dump_resize(struct btf_dump *d); 1477c2aad20Sopenharmony_ci 1487c2aad20Sopenharmony_cistruct btf_dump *btf_dump__new(const struct btf *btf, 1497c2aad20Sopenharmony_ci btf_dump_printf_fn_t printf_fn, 1507c2aad20Sopenharmony_ci void *ctx, 1517c2aad20Sopenharmony_ci const struct btf_dump_opts *opts) 1527c2aad20Sopenharmony_ci{ 1537c2aad20Sopenharmony_ci struct btf_dump *d; 1547c2aad20Sopenharmony_ci int err; 1557c2aad20Sopenharmony_ci 1567c2aad20Sopenharmony_ci if (!OPTS_VALID(opts, btf_dump_opts)) 1577c2aad20Sopenharmony_ci return libbpf_err_ptr(-EINVAL); 1587c2aad20Sopenharmony_ci 1597c2aad20Sopenharmony_ci if (!printf_fn) 1607c2aad20Sopenharmony_ci return libbpf_err_ptr(-EINVAL); 1617c2aad20Sopenharmony_ci 1627c2aad20Sopenharmony_ci d = calloc(1, sizeof(struct btf_dump)); 1637c2aad20Sopenharmony_ci if (!d) 1647c2aad20Sopenharmony_ci return libbpf_err_ptr(-ENOMEM); 1657c2aad20Sopenharmony_ci 1667c2aad20Sopenharmony_ci d->btf = btf; 1677c2aad20Sopenharmony_ci d->printf_fn = printf_fn; 1687c2aad20Sopenharmony_ci d->cb_ctx = ctx; 1697c2aad20Sopenharmony_ci d->ptr_sz = btf__pointer_size(btf) ? : sizeof(void *); 1707c2aad20Sopenharmony_ci 1717c2aad20Sopenharmony_ci d->type_names = hashmap__new(str_hash_fn, str_equal_fn, NULL); 1727c2aad20Sopenharmony_ci if (IS_ERR(d->type_names)) { 1737c2aad20Sopenharmony_ci err = PTR_ERR(d->type_names); 1747c2aad20Sopenharmony_ci d->type_names = NULL; 1757c2aad20Sopenharmony_ci goto err; 1767c2aad20Sopenharmony_ci } 1777c2aad20Sopenharmony_ci d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL); 1787c2aad20Sopenharmony_ci if (IS_ERR(d->ident_names)) { 1797c2aad20Sopenharmony_ci err = PTR_ERR(d->ident_names); 1807c2aad20Sopenharmony_ci d->ident_names = NULL; 1817c2aad20Sopenharmony_ci goto err; 1827c2aad20Sopenharmony_ci } 1837c2aad20Sopenharmony_ci 1847c2aad20Sopenharmony_ci err = btf_dump_resize(d); 1857c2aad20Sopenharmony_ci if (err) 1867c2aad20Sopenharmony_ci goto err; 1877c2aad20Sopenharmony_ci 1887c2aad20Sopenharmony_ci return d; 1897c2aad20Sopenharmony_cierr: 1907c2aad20Sopenharmony_ci btf_dump__free(d); 1917c2aad20Sopenharmony_ci return libbpf_err_ptr(err); 1927c2aad20Sopenharmony_ci} 1937c2aad20Sopenharmony_ci 1947c2aad20Sopenharmony_cistatic int btf_dump_resize(struct btf_dump *d) 1957c2aad20Sopenharmony_ci{ 1967c2aad20Sopenharmony_ci int err, last_id = btf__type_cnt(d->btf) - 1; 1977c2aad20Sopenharmony_ci 1987c2aad20Sopenharmony_ci if (last_id <= d->last_id) 1997c2aad20Sopenharmony_ci return 0; 2007c2aad20Sopenharmony_ci 2017c2aad20Sopenharmony_ci if (libbpf_ensure_mem((void **)&d->type_states, &d->type_states_cap, 2027c2aad20Sopenharmony_ci sizeof(*d->type_states), last_id + 1)) 2037c2aad20Sopenharmony_ci return -ENOMEM; 2047c2aad20Sopenharmony_ci if (libbpf_ensure_mem((void **)&d->cached_names, &d->cached_names_cap, 2057c2aad20Sopenharmony_ci sizeof(*d->cached_names), last_id + 1)) 2067c2aad20Sopenharmony_ci return -ENOMEM; 2077c2aad20Sopenharmony_ci 2087c2aad20Sopenharmony_ci if (d->last_id == 0) { 2097c2aad20Sopenharmony_ci /* VOID is special */ 2107c2aad20Sopenharmony_ci d->type_states[0].order_state = ORDERED; 2117c2aad20Sopenharmony_ci d->type_states[0].emit_state = EMITTED; 2127c2aad20Sopenharmony_ci } 2137c2aad20Sopenharmony_ci 2147c2aad20Sopenharmony_ci /* eagerly determine referenced types for anon enums */ 2157c2aad20Sopenharmony_ci err = btf_dump_mark_referenced(d); 2167c2aad20Sopenharmony_ci if (err) 2177c2aad20Sopenharmony_ci return err; 2187c2aad20Sopenharmony_ci 2197c2aad20Sopenharmony_ci d->last_id = last_id; 2207c2aad20Sopenharmony_ci return 0; 2217c2aad20Sopenharmony_ci} 2227c2aad20Sopenharmony_ci 2237c2aad20Sopenharmony_cistatic void btf_dump_free_names(struct hashmap *map) 2247c2aad20Sopenharmony_ci{ 2257c2aad20Sopenharmony_ci size_t bkt; 2267c2aad20Sopenharmony_ci struct hashmap_entry *cur; 2277c2aad20Sopenharmony_ci 2287c2aad20Sopenharmony_ci hashmap__for_each_entry(map, cur, bkt) 2297c2aad20Sopenharmony_ci free((void *)cur->pkey); 2307c2aad20Sopenharmony_ci 2317c2aad20Sopenharmony_ci hashmap__free(map); 2327c2aad20Sopenharmony_ci} 2337c2aad20Sopenharmony_ci 2347c2aad20Sopenharmony_civoid btf_dump__free(struct btf_dump *d) 2357c2aad20Sopenharmony_ci{ 2367c2aad20Sopenharmony_ci int i; 2377c2aad20Sopenharmony_ci 2387c2aad20Sopenharmony_ci if (IS_ERR_OR_NULL(d)) 2397c2aad20Sopenharmony_ci return; 2407c2aad20Sopenharmony_ci 2417c2aad20Sopenharmony_ci free(d->type_states); 2427c2aad20Sopenharmony_ci if (d->cached_names) { 2437c2aad20Sopenharmony_ci /* any set cached name is owned by us and should be freed */ 2447c2aad20Sopenharmony_ci for (i = 0; i <= d->last_id; i++) { 2457c2aad20Sopenharmony_ci if (d->cached_names[i]) 2467c2aad20Sopenharmony_ci free((void *)d->cached_names[i]); 2477c2aad20Sopenharmony_ci } 2487c2aad20Sopenharmony_ci } 2497c2aad20Sopenharmony_ci free(d->cached_names); 2507c2aad20Sopenharmony_ci free(d->emit_queue); 2517c2aad20Sopenharmony_ci free(d->decl_stack); 2527c2aad20Sopenharmony_ci btf_dump_free_names(d->type_names); 2537c2aad20Sopenharmony_ci btf_dump_free_names(d->ident_names); 2547c2aad20Sopenharmony_ci 2557c2aad20Sopenharmony_ci free(d); 2567c2aad20Sopenharmony_ci} 2577c2aad20Sopenharmony_ci 2587c2aad20Sopenharmony_cistatic int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr); 2597c2aad20Sopenharmony_cistatic void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id); 2607c2aad20Sopenharmony_ci 2617c2aad20Sopenharmony_ci/* 2627c2aad20Sopenharmony_ci * Dump BTF type in a compilable C syntax, including all the necessary 2637c2aad20Sopenharmony_ci * dependent types, necessary for compilation. If some of the dependent types 2647c2aad20Sopenharmony_ci * were already emitted as part of previous btf_dump__dump_type() invocation 2657c2aad20Sopenharmony_ci * for another type, they won't be emitted again. This API allows callers to 2667c2aad20Sopenharmony_ci * filter out BTF types according to user-defined criterias and emitted only 2677c2aad20Sopenharmony_ci * minimal subset of types, necessary to compile everything. Full struct/union 2687c2aad20Sopenharmony_ci * definitions will still be emitted, even if the only usage is through 2697c2aad20Sopenharmony_ci * pointer and could be satisfied with just a forward declaration. 2707c2aad20Sopenharmony_ci * 2717c2aad20Sopenharmony_ci * Dumping is done in two high-level passes: 2727c2aad20Sopenharmony_ci * 1. Topologically sort type definitions to satisfy C rules of compilation. 2737c2aad20Sopenharmony_ci * 2. Emit type definitions in C syntax. 2747c2aad20Sopenharmony_ci * 2757c2aad20Sopenharmony_ci * Returns 0 on success; <0, otherwise. 2767c2aad20Sopenharmony_ci */ 2777c2aad20Sopenharmony_ciint btf_dump__dump_type(struct btf_dump *d, __u32 id) 2787c2aad20Sopenharmony_ci{ 2797c2aad20Sopenharmony_ci int err, i; 2807c2aad20Sopenharmony_ci 2817c2aad20Sopenharmony_ci if (id >= btf__type_cnt(d->btf)) 2827c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 2837c2aad20Sopenharmony_ci 2847c2aad20Sopenharmony_ci err = btf_dump_resize(d); 2857c2aad20Sopenharmony_ci if (err) 2867c2aad20Sopenharmony_ci return libbpf_err(err); 2877c2aad20Sopenharmony_ci 2887c2aad20Sopenharmony_ci d->emit_queue_cnt = 0; 2897c2aad20Sopenharmony_ci err = btf_dump_order_type(d, id, false); 2907c2aad20Sopenharmony_ci if (err < 0) 2917c2aad20Sopenharmony_ci return libbpf_err(err); 2927c2aad20Sopenharmony_ci 2937c2aad20Sopenharmony_ci for (i = 0; i < d->emit_queue_cnt; i++) 2947c2aad20Sopenharmony_ci btf_dump_emit_type(d, d->emit_queue[i], 0 /*top-level*/); 2957c2aad20Sopenharmony_ci 2967c2aad20Sopenharmony_ci return 0; 2977c2aad20Sopenharmony_ci} 2987c2aad20Sopenharmony_ci 2997c2aad20Sopenharmony_ci/* 3007c2aad20Sopenharmony_ci * Mark all types that are referenced from any other type. This is used to 3017c2aad20Sopenharmony_ci * determine top-level anonymous enums that need to be emitted as an 3027c2aad20Sopenharmony_ci * independent type declarations. 3037c2aad20Sopenharmony_ci * Anonymous enums come in two flavors: either embedded in a struct's field 3047c2aad20Sopenharmony_ci * definition, in which case they have to be declared inline as part of field 3057c2aad20Sopenharmony_ci * type declaration; or as a top-level anonymous enum, typically used for 3067c2aad20Sopenharmony_ci * declaring global constants. It's impossible to distinguish between two 3077c2aad20Sopenharmony_ci * without knowning whether given enum type was referenced from other type: 3087c2aad20Sopenharmony_ci * top-level anonymous enum won't be referenced by anything, while embedded 3097c2aad20Sopenharmony_ci * one will. 3107c2aad20Sopenharmony_ci */ 3117c2aad20Sopenharmony_cistatic int btf_dump_mark_referenced(struct btf_dump *d) 3127c2aad20Sopenharmony_ci{ 3137c2aad20Sopenharmony_ci int i, j, n = btf__type_cnt(d->btf); 3147c2aad20Sopenharmony_ci const struct btf_type *t; 3157c2aad20Sopenharmony_ci __u16 vlen; 3167c2aad20Sopenharmony_ci 3177c2aad20Sopenharmony_ci for (i = d->last_id + 1; i < n; i++) { 3187c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, i); 3197c2aad20Sopenharmony_ci vlen = btf_vlen(t); 3207c2aad20Sopenharmony_ci 3217c2aad20Sopenharmony_ci switch (btf_kind(t)) { 3227c2aad20Sopenharmony_ci case BTF_KIND_INT: 3237c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 3247c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 3257c2aad20Sopenharmony_ci case BTF_KIND_FWD: 3267c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 3277c2aad20Sopenharmony_ci break; 3287c2aad20Sopenharmony_ci 3297c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 3307c2aad20Sopenharmony_ci case BTF_KIND_CONST: 3317c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 3327c2aad20Sopenharmony_ci case BTF_KIND_PTR: 3337c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: 3347c2aad20Sopenharmony_ci case BTF_KIND_FUNC: 3357c2aad20Sopenharmony_ci case BTF_KIND_VAR: 3367c2aad20Sopenharmony_ci case BTF_KIND_DECL_TAG: 3377c2aad20Sopenharmony_ci case BTF_KIND_TYPE_TAG: 3387c2aad20Sopenharmony_ci d->type_states[t->type].referenced = 1; 3397c2aad20Sopenharmony_ci break; 3407c2aad20Sopenharmony_ci 3417c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: { 3427c2aad20Sopenharmony_ci const struct btf_array *a = btf_array(t); 3437c2aad20Sopenharmony_ci 3447c2aad20Sopenharmony_ci d->type_states[a->index_type].referenced = 1; 3457c2aad20Sopenharmony_ci d->type_states[a->type].referenced = 1; 3467c2aad20Sopenharmony_ci break; 3477c2aad20Sopenharmony_ci } 3487c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 3497c2aad20Sopenharmony_ci case BTF_KIND_UNION: { 3507c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 3517c2aad20Sopenharmony_ci 3527c2aad20Sopenharmony_ci for (j = 0; j < vlen; j++, m++) 3537c2aad20Sopenharmony_ci d->type_states[m->type].referenced = 1; 3547c2aad20Sopenharmony_ci break; 3557c2aad20Sopenharmony_ci } 3567c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: { 3577c2aad20Sopenharmony_ci const struct btf_param *p = btf_params(t); 3587c2aad20Sopenharmony_ci 3597c2aad20Sopenharmony_ci for (j = 0; j < vlen; j++, p++) 3607c2aad20Sopenharmony_ci d->type_states[p->type].referenced = 1; 3617c2aad20Sopenharmony_ci break; 3627c2aad20Sopenharmony_ci } 3637c2aad20Sopenharmony_ci case BTF_KIND_DATASEC: { 3647c2aad20Sopenharmony_ci const struct btf_var_secinfo *v = btf_var_secinfos(t); 3657c2aad20Sopenharmony_ci 3667c2aad20Sopenharmony_ci for (j = 0; j < vlen; j++, v++) 3677c2aad20Sopenharmony_ci d->type_states[v->type].referenced = 1; 3687c2aad20Sopenharmony_ci break; 3697c2aad20Sopenharmony_ci } 3707c2aad20Sopenharmony_ci default: 3717c2aad20Sopenharmony_ci return -EINVAL; 3727c2aad20Sopenharmony_ci } 3737c2aad20Sopenharmony_ci } 3747c2aad20Sopenharmony_ci return 0; 3757c2aad20Sopenharmony_ci} 3767c2aad20Sopenharmony_ci 3777c2aad20Sopenharmony_cistatic int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id) 3787c2aad20Sopenharmony_ci{ 3797c2aad20Sopenharmony_ci __u32 *new_queue; 3807c2aad20Sopenharmony_ci size_t new_cap; 3817c2aad20Sopenharmony_ci 3827c2aad20Sopenharmony_ci if (d->emit_queue_cnt >= d->emit_queue_cap) { 3837c2aad20Sopenharmony_ci new_cap = max(16, d->emit_queue_cap * 3 / 2); 3847c2aad20Sopenharmony_ci new_queue = libbpf_reallocarray(d->emit_queue, new_cap, sizeof(new_queue[0])); 3857c2aad20Sopenharmony_ci if (!new_queue) 3867c2aad20Sopenharmony_ci return -ENOMEM; 3877c2aad20Sopenharmony_ci d->emit_queue = new_queue; 3887c2aad20Sopenharmony_ci d->emit_queue_cap = new_cap; 3897c2aad20Sopenharmony_ci } 3907c2aad20Sopenharmony_ci 3917c2aad20Sopenharmony_ci d->emit_queue[d->emit_queue_cnt++] = id; 3927c2aad20Sopenharmony_ci return 0; 3937c2aad20Sopenharmony_ci} 3947c2aad20Sopenharmony_ci 3957c2aad20Sopenharmony_ci/* 3967c2aad20Sopenharmony_ci * Determine order of emitting dependent types and specified type to satisfy 3977c2aad20Sopenharmony_ci * C compilation rules. This is done through topological sorting with an 3987c2aad20Sopenharmony_ci * additional complication which comes from C rules. The main idea for C is 3997c2aad20Sopenharmony_ci * that if some type is "embedded" into a struct/union, it's size needs to be 4007c2aad20Sopenharmony_ci * known at the time of definition of containing type. E.g., for: 4017c2aad20Sopenharmony_ci * 4027c2aad20Sopenharmony_ci * struct A {}; 4037c2aad20Sopenharmony_ci * struct B { struct A x; } 4047c2aad20Sopenharmony_ci * 4057c2aad20Sopenharmony_ci * struct A *HAS* to be defined before struct B, because it's "embedded", 4067c2aad20Sopenharmony_ci * i.e., it is part of struct B layout. But in the following case: 4077c2aad20Sopenharmony_ci * 4087c2aad20Sopenharmony_ci * struct A; 4097c2aad20Sopenharmony_ci * struct B { struct A *x; } 4107c2aad20Sopenharmony_ci * struct A {}; 4117c2aad20Sopenharmony_ci * 4127c2aad20Sopenharmony_ci * it's enough to just have a forward declaration of struct A at the time of 4137c2aad20Sopenharmony_ci * struct B definition, as struct B has a pointer to struct A, so the size of 4147c2aad20Sopenharmony_ci * field x is known without knowing struct A size: it's sizeof(void *). 4157c2aad20Sopenharmony_ci * 4167c2aad20Sopenharmony_ci * Unfortunately, there are some trickier cases we need to handle, e.g.: 4177c2aad20Sopenharmony_ci * 4187c2aad20Sopenharmony_ci * struct A {}; // if this was forward-declaration: compilation error 4197c2aad20Sopenharmony_ci * struct B { 4207c2aad20Sopenharmony_ci * struct { // anonymous struct 4217c2aad20Sopenharmony_ci * struct A y; 4227c2aad20Sopenharmony_ci * } *x; 4237c2aad20Sopenharmony_ci * }; 4247c2aad20Sopenharmony_ci * 4257c2aad20Sopenharmony_ci * In this case, struct B's field x is a pointer, so it's size is known 4267c2aad20Sopenharmony_ci * regardless of the size of (anonymous) struct it points to. But because this 4277c2aad20Sopenharmony_ci * struct is anonymous and thus defined inline inside struct B, *and* it 4287c2aad20Sopenharmony_ci * embeds struct A, compiler requires full definition of struct A to be known 4297c2aad20Sopenharmony_ci * before struct B can be defined. This creates a transitive dependency 4307c2aad20Sopenharmony_ci * between struct A and struct B. If struct A was forward-declared before 4317c2aad20Sopenharmony_ci * struct B definition and fully defined after struct B definition, that would 4327c2aad20Sopenharmony_ci * trigger compilation error. 4337c2aad20Sopenharmony_ci * 4347c2aad20Sopenharmony_ci * All this means that while we are doing topological sorting on BTF type 4357c2aad20Sopenharmony_ci * graph, we need to determine relationships between different types (graph 4367c2aad20Sopenharmony_ci * nodes): 4377c2aad20Sopenharmony_ci * - weak link (relationship) between X and Y, if Y *CAN* be 4387c2aad20Sopenharmony_ci * forward-declared at the point of X definition; 4397c2aad20Sopenharmony_ci * - strong link, if Y *HAS* to be fully-defined before X can be defined. 4407c2aad20Sopenharmony_ci * 4417c2aad20Sopenharmony_ci * The rule is as follows. Given a chain of BTF types from X to Y, if there is 4427c2aad20Sopenharmony_ci * BTF_KIND_PTR type in the chain and at least one non-anonymous type 4437c2aad20Sopenharmony_ci * Z (excluding X, including Y), then link is weak. Otherwise, it's strong. 4447c2aad20Sopenharmony_ci * Weak/strong relationship is determined recursively during DFS traversal and 4457c2aad20Sopenharmony_ci * is returned as a result from btf_dump_order_type(). 4467c2aad20Sopenharmony_ci * 4477c2aad20Sopenharmony_ci * btf_dump_order_type() is trying to avoid unnecessary forward declarations, 4487c2aad20Sopenharmony_ci * but it is not guaranteeing that no extraneous forward declarations will be 4497c2aad20Sopenharmony_ci * emitted. 4507c2aad20Sopenharmony_ci * 4517c2aad20Sopenharmony_ci * To avoid extra work, algorithm marks some of BTF types as ORDERED, when 4527c2aad20Sopenharmony_ci * it's done with them, but not for all (e.g., VOLATILE, CONST, RESTRICT, 4537c2aad20Sopenharmony_ci * ARRAY, FUNC_PROTO), as weak/strong semantics for those depends on the 4547c2aad20Sopenharmony_ci * entire graph path, so depending where from one came to that BTF type, it 4557c2aad20Sopenharmony_ci * might cause weak or strong ordering. For types like STRUCT/UNION/INT/ENUM, 4567c2aad20Sopenharmony_ci * once they are processed, there is no need to do it again, so they are 4577c2aad20Sopenharmony_ci * marked as ORDERED. We can mark PTR as ORDERED as well, as it semi-forces 4587c2aad20Sopenharmony_ci * weak link, unless subsequent referenced STRUCT/UNION/ENUM is anonymous. But 4597c2aad20Sopenharmony_ci * in any case, once those are processed, no need to do it again, as the 4607c2aad20Sopenharmony_ci * result won't change. 4617c2aad20Sopenharmony_ci * 4627c2aad20Sopenharmony_ci * Returns: 4637c2aad20Sopenharmony_ci * - 1, if type is part of strong link (so there is strong topological 4647c2aad20Sopenharmony_ci * ordering requirements); 4657c2aad20Sopenharmony_ci * - 0, if type is part of weak link (so can be satisfied through forward 4667c2aad20Sopenharmony_ci * declaration); 4677c2aad20Sopenharmony_ci * - <0, on error (e.g., unsatisfiable type loop detected). 4687c2aad20Sopenharmony_ci */ 4697c2aad20Sopenharmony_cistatic int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) 4707c2aad20Sopenharmony_ci{ 4717c2aad20Sopenharmony_ci /* 4727c2aad20Sopenharmony_ci * Order state is used to detect strong link cycles, but only for BTF 4737c2aad20Sopenharmony_ci * kinds that are or could be an independent definition (i.e., 4747c2aad20Sopenharmony_ci * stand-alone fwd decl, enum, typedef, struct, union). Ptrs, arrays, 4757c2aad20Sopenharmony_ci * func_protos, modifiers are just means to get to these definitions. 4767c2aad20Sopenharmony_ci * Int/void don't need definitions, they are assumed to be always 4777c2aad20Sopenharmony_ci * properly defined. We also ignore datasec, var, and funcs for now. 4787c2aad20Sopenharmony_ci * So for all non-defining kinds, we never even set ordering state, 4797c2aad20Sopenharmony_ci * for defining kinds we set ORDERING and subsequently ORDERED if it 4807c2aad20Sopenharmony_ci * forms a strong link. 4817c2aad20Sopenharmony_ci */ 4827c2aad20Sopenharmony_ci struct btf_dump_type_aux_state *tstate = &d->type_states[id]; 4837c2aad20Sopenharmony_ci const struct btf_type *t; 4847c2aad20Sopenharmony_ci __u16 vlen; 4857c2aad20Sopenharmony_ci int err, i; 4867c2aad20Sopenharmony_ci 4877c2aad20Sopenharmony_ci /* return true, letting typedefs know that it's ok to be emitted */ 4887c2aad20Sopenharmony_ci if (tstate->order_state == ORDERED) 4897c2aad20Sopenharmony_ci return 1; 4907c2aad20Sopenharmony_ci 4917c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 4927c2aad20Sopenharmony_ci 4937c2aad20Sopenharmony_ci if (tstate->order_state == ORDERING) { 4947c2aad20Sopenharmony_ci /* type loop, but resolvable through fwd declaration */ 4957c2aad20Sopenharmony_ci if (btf_is_composite(t) && through_ptr && t->name_off != 0) 4967c2aad20Sopenharmony_ci return 0; 4977c2aad20Sopenharmony_ci pr_warn("unsatisfiable type cycle, id:[%u]\n", id); 4987c2aad20Sopenharmony_ci return -ELOOP; 4997c2aad20Sopenharmony_ci } 5007c2aad20Sopenharmony_ci 5017c2aad20Sopenharmony_ci switch (btf_kind(t)) { 5027c2aad20Sopenharmony_ci case BTF_KIND_INT: 5037c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 5047c2aad20Sopenharmony_ci tstate->order_state = ORDERED; 5057c2aad20Sopenharmony_ci return 0; 5067c2aad20Sopenharmony_ci 5077c2aad20Sopenharmony_ci case BTF_KIND_PTR: 5087c2aad20Sopenharmony_ci err = btf_dump_order_type(d, t->type, true); 5097c2aad20Sopenharmony_ci tstate->order_state = ORDERED; 5107c2aad20Sopenharmony_ci return err; 5117c2aad20Sopenharmony_ci 5127c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: 5137c2aad20Sopenharmony_ci return btf_dump_order_type(d, btf_array(t)->type, false); 5147c2aad20Sopenharmony_ci 5157c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 5167c2aad20Sopenharmony_ci case BTF_KIND_UNION: { 5177c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 5187c2aad20Sopenharmony_ci /* 5197c2aad20Sopenharmony_ci * struct/union is part of strong link, only if it's embedded 5207c2aad20Sopenharmony_ci * (so no ptr in a path) or it's anonymous (so has to be 5217c2aad20Sopenharmony_ci * defined inline, even if declared through ptr) 5227c2aad20Sopenharmony_ci */ 5237c2aad20Sopenharmony_ci if (through_ptr && t->name_off != 0) 5247c2aad20Sopenharmony_ci return 0; 5257c2aad20Sopenharmony_ci 5267c2aad20Sopenharmony_ci tstate->order_state = ORDERING; 5277c2aad20Sopenharmony_ci 5287c2aad20Sopenharmony_ci vlen = btf_vlen(t); 5297c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, m++) { 5307c2aad20Sopenharmony_ci err = btf_dump_order_type(d, m->type, false); 5317c2aad20Sopenharmony_ci if (err < 0) 5327c2aad20Sopenharmony_ci return err; 5337c2aad20Sopenharmony_ci } 5347c2aad20Sopenharmony_ci 5357c2aad20Sopenharmony_ci if (t->name_off != 0) { 5367c2aad20Sopenharmony_ci err = btf_dump_add_emit_queue_id(d, id); 5377c2aad20Sopenharmony_ci if (err < 0) 5387c2aad20Sopenharmony_ci return err; 5397c2aad20Sopenharmony_ci } 5407c2aad20Sopenharmony_ci 5417c2aad20Sopenharmony_ci tstate->order_state = ORDERED; 5427c2aad20Sopenharmony_ci return 1; 5437c2aad20Sopenharmony_ci } 5447c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 5457c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 5467c2aad20Sopenharmony_ci case BTF_KIND_FWD: 5477c2aad20Sopenharmony_ci /* 5487c2aad20Sopenharmony_ci * non-anonymous or non-referenced enums are top-level 5497c2aad20Sopenharmony_ci * declarations and should be emitted. Same logic can be 5507c2aad20Sopenharmony_ci * applied to FWDs, it won't hurt anyways. 5517c2aad20Sopenharmony_ci */ 5527c2aad20Sopenharmony_ci if (t->name_off != 0 || !tstate->referenced) { 5537c2aad20Sopenharmony_ci err = btf_dump_add_emit_queue_id(d, id); 5547c2aad20Sopenharmony_ci if (err) 5557c2aad20Sopenharmony_ci return err; 5567c2aad20Sopenharmony_ci } 5577c2aad20Sopenharmony_ci tstate->order_state = ORDERED; 5587c2aad20Sopenharmony_ci return 1; 5597c2aad20Sopenharmony_ci 5607c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: { 5617c2aad20Sopenharmony_ci int is_strong; 5627c2aad20Sopenharmony_ci 5637c2aad20Sopenharmony_ci is_strong = btf_dump_order_type(d, t->type, through_ptr); 5647c2aad20Sopenharmony_ci if (is_strong < 0) 5657c2aad20Sopenharmony_ci return is_strong; 5667c2aad20Sopenharmony_ci 5677c2aad20Sopenharmony_ci /* typedef is similar to struct/union w.r.t. fwd-decls */ 5687c2aad20Sopenharmony_ci if (through_ptr && !is_strong) 5697c2aad20Sopenharmony_ci return 0; 5707c2aad20Sopenharmony_ci 5717c2aad20Sopenharmony_ci /* typedef is always a named definition */ 5727c2aad20Sopenharmony_ci err = btf_dump_add_emit_queue_id(d, id); 5737c2aad20Sopenharmony_ci if (err) 5747c2aad20Sopenharmony_ci return err; 5757c2aad20Sopenharmony_ci 5767c2aad20Sopenharmony_ci d->type_states[id].order_state = ORDERED; 5777c2aad20Sopenharmony_ci return 1; 5787c2aad20Sopenharmony_ci } 5797c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 5807c2aad20Sopenharmony_ci case BTF_KIND_CONST: 5817c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 5827c2aad20Sopenharmony_ci case BTF_KIND_TYPE_TAG: 5837c2aad20Sopenharmony_ci return btf_dump_order_type(d, t->type, through_ptr); 5847c2aad20Sopenharmony_ci 5857c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: { 5867c2aad20Sopenharmony_ci const struct btf_param *p = btf_params(t); 5877c2aad20Sopenharmony_ci bool is_strong; 5887c2aad20Sopenharmony_ci 5897c2aad20Sopenharmony_ci err = btf_dump_order_type(d, t->type, through_ptr); 5907c2aad20Sopenharmony_ci if (err < 0) 5917c2aad20Sopenharmony_ci return err; 5927c2aad20Sopenharmony_ci is_strong = err > 0; 5937c2aad20Sopenharmony_ci 5947c2aad20Sopenharmony_ci vlen = btf_vlen(t); 5957c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, p++) { 5967c2aad20Sopenharmony_ci err = btf_dump_order_type(d, p->type, through_ptr); 5977c2aad20Sopenharmony_ci if (err < 0) 5987c2aad20Sopenharmony_ci return err; 5997c2aad20Sopenharmony_ci if (err > 0) 6007c2aad20Sopenharmony_ci is_strong = true; 6017c2aad20Sopenharmony_ci } 6027c2aad20Sopenharmony_ci return is_strong; 6037c2aad20Sopenharmony_ci } 6047c2aad20Sopenharmony_ci case BTF_KIND_FUNC: 6057c2aad20Sopenharmony_ci case BTF_KIND_VAR: 6067c2aad20Sopenharmony_ci case BTF_KIND_DATASEC: 6077c2aad20Sopenharmony_ci case BTF_KIND_DECL_TAG: 6087c2aad20Sopenharmony_ci d->type_states[id].order_state = ORDERED; 6097c2aad20Sopenharmony_ci return 0; 6107c2aad20Sopenharmony_ci 6117c2aad20Sopenharmony_ci default: 6127c2aad20Sopenharmony_ci return -EINVAL; 6137c2aad20Sopenharmony_ci } 6147c2aad20Sopenharmony_ci} 6157c2aad20Sopenharmony_ci 6167c2aad20Sopenharmony_cistatic void btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, 6177c2aad20Sopenharmony_ci const struct btf_type *t); 6187c2aad20Sopenharmony_ci 6197c2aad20Sopenharmony_cistatic void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id, 6207c2aad20Sopenharmony_ci const struct btf_type *t); 6217c2aad20Sopenharmony_cistatic void btf_dump_emit_struct_def(struct btf_dump *d, __u32 id, 6227c2aad20Sopenharmony_ci const struct btf_type *t, int lvl); 6237c2aad20Sopenharmony_ci 6247c2aad20Sopenharmony_cistatic void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id, 6257c2aad20Sopenharmony_ci const struct btf_type *t); 6267c2aad20Sopenharmony_cistatic void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id, 6277c2aad20Sopenharmony_ci const struct btf_type *t, int lvl); 6287c2aad20Sopenharmony_ci 6297c2aad20Sopenharmony_cistatic void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id, 6307c2aad20Sopenharmony_ci const struct btf_type *t); 6317c2aad20Sopenharmony_ci 6327c2aad20Sopenharmony_cistatic void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id, 6337c2aad20Sopenharmony_ci const struct btf_type *t, int lvl); 6347c2aad20Sopenharmony_ci 6357c2aad20Sopenharmony_ci/* a local view into a shared stack */ 6367c2aad20Sopenharmony_cistruct id_stack { 6377c2aad20Sopenharmony_ci const __u32 *ids; 6387c2aad20Sopenharmony_ci int cnt; 6397c2aad20Sopenharmony_ci}; 6407c2aad20Sopenharmony_ci 6417c2aad20Sopenharmony_cistatic void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, 6427c2aad20Sopenharmony_ci const char *fname, int lvl); 6437c2aad20Sopenharmony_cistatic void btf_dump_emit_type_chain(struct btf_dump *d, 6447c2aad20Sopenharmony_ci struct id_stack *decl_stack, 6457c2aad20Sopenharmony_ci const char *fname, int lvl); 6467c2aad20Sopenharmony_ci 6477c2aad20Sopenharmony_cistatic const char *btf_dump_type_name(struct btf_dump *d, __u32 id); 6487c2aad20Sopenharmony_cistatic const char *btf_dump_ident_name(struct btf_dump *d, __u32 id); 6497c2aad20Sopenharmony_cistatic size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, 6507c2aad20Sopenharmony_ci const char *orig_name); 6517c2aad20Sopenharmony_ci 6527c2aad20Sopenharmony_cistatic bool btf_dump_is_blacklisted(struct btf_dump *d, __u32 id) 6537c2aad20Sopenharmony_ci{ 6547c2aad20Sopenharmony_ci const struct btf_type *t = btf__type_by_id(d->btf, id); 6557c2aad20Sopenharmony_ci 6567c2aad20Sopenharmony_ci /* __builtin_va_list is a compiler built-in, which causes compilation 6577c2aad20Sopenharmony_ci * errors, when compiling w/ different compiler, then used to compile 6587c2aad20Sopenharmony_ci * original code (e.g., GCC to compile kernel, Clang to use generated 6597c2aad20Sopenharmony_ci * C header from BTF). As it is built-in, it should be already defined 6607c2aad20Sopenharmony_ci * properly internally in compiler. 6617c2aad20Sopenharmony_ci */ 6627c2aad20Sopenharmony_ci if (t->name_off == 0) 6637c2aad20Sopenharmony_ci return false; 6647c2aad20Sopenharmony_ci return strcmp(btf_name_of(d, t->name_off), "__builtin_va_list") == 0; 6657c2aad20Sopenharmony_ci} 6667c2aad20Sopenharmony_ci 6677c2aad20Sopenharmony_ci/* 6687c2aad20Sopenharmony_ci * Emit C-syntax definitions of types from chains of BTF types. 6697c2aad20Sopenharmony_ci * 6707c2aad20Sopenharmony_ci * High-level handling of determining necessary forward declarations are handled 6717c2aad20Sopenharmony_ci * by btf_dump_emit_type() itself, but all nitty-gritty details of emitting type 6727c2aad20Sopenharmony_ci * declarations/definitions in C syntax are handled by a combo of 6737c2aad20Sopenharmony_ci * btf_dump_emit_type_decl()/btf_dump_emit_type_chain() w/ delegation to 6747c2aad20Sopenharmony_ci * corresponding btf_dump_emit_*_{def,fwd}() functions. 6757c2aad20Sopenharmony_ci * 6767c2aad20Sopenharmony_ci * We also keep track of "containing struct/union type ID" to determine when 6777c2aad20Sopenharmony_ci * we reference it from inside and thus can avoid emitting unnecessary forward 6787c2aad20Sopenharmony_ci * declaration. 6797c2aad20Sopenharmony_ci * 6807c2aad20Sopenharmony_ci * This algorithm is designed in such a way, that even if some error occurs 6817c2aad20Sopenharmony_ci * (either technical, e.g., out of memory, or logical, i.e., malformed BTF 6827c2aad20Sopenharmony_ci * that doesn't comply to C rules completely), algorithm will try to proceed 6837c2aad20Sopenharmony_ci * and produce as much meaningful output as possible. 6847c2aad20Sopenharmony_ci */ 6857c2aad20Sopenharmony_cistatic void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id) 6867c2aad20Sopenharmony_ci{ 6877c2aad20Sopenharmony_ci struct btf_dump_type_aux_state *tstate = &d->type_states[id]; 6887c2aad20Sopenharmony_ci bool top_level_def = cont_id == 0; 6897c2aad20Sopenharmony_ci const struct btf_type *t; 6907c2aad20Sopenharmony_ci __u16 kind; 6917c2aad20Sopenharmony_ci 6927c2aad20Sopenharmony_ci if (tstate->emit_state == EMITTED) 6937c2aad20Sopenharmony_ci return; 6947c2aad20Sopenharmony_ci 6957c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 6967c2aad20Sopenharmony_ci kind = btf_kind(t); 6977c2aad20Sopenharmony_ci 6987c2aad20Sopenharmony_ci if (tstate->emit_state == EMITTING) { 6997c2aad20Sopenharmony_ci if (tstate->fwd_emitted) 7007c2aad20Sopenharmony_ci return; 7017c2aad20Sopenharmony_ci 7027c2aad20Sopenharmony_ci switch (kind) { 7037c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 7047c2aad20Sopenharmony_ci case BTF_KIND_UNION: 7057c2aad20Sopenharmony_ci /* 7067c2aad20Sopenharmony_ci * if we are referencing a struct/union that we are 7077c2aad20Sopenharmony_ci * part of - then no need for fwd declaration 7087c2aad20Sopenharmony_ci */ 7097c2aad20Sopenharmony_ci if (id == cont_id) 7107c2aad20Sopenharmony_ci return; 7117c2aad20Sopenharmony_ci if (t->name_off == 0) { 7127c2aad20Sopenharmony_ci pr_warn("anonymous struct/union loop, id:[%u]\n", 7137c2aad20Sopenharmony_ci id); 7147c2aad20Sopenharmony_ci return; 7157c2aad20Sopenharmony_ci } 7167c2aad20Sopenharmony_ci btf_dump_emit_struct_fwd(d, id, t); 7177c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 7187c2aad20Sopenharmony_ci tstate->fwd_emitted = 1; 7197c2aad20Sopenharmony_ci break; 7207c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: 7217c2aad20Sopenharmony_ci /* 7227c2aad20Sopenharmony_ci * for typedef fwd_emitted means typedef definition 7237c2aad20Sopenharmony_ci * was emitted, but it can be used only for "weak" 7247c2aad20Sopenharmony_ci * references through pointer only, not for embedding 7257c2aad20Sopenharmony_ci */ 7267c2aad20Sopenharmony_ci if (!btf_dump_is_blacklisted(d, id)) { 7277c2aad20Sopenharmony_ci btf_dump_emit_typedef_def(d, id, t, 0); 7287c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 7297c2aad20Sopenharmony_ci } 7307c2aad20Sopenharmony_ci tstate->fwd_emitted = 1; 7317c2aad20Sopenharmony_ci break; 7327c2aad20Sopenharmony_ci default: 7337c2aad20Sopenharmony_ci break; 7347c2aad20Sopenharmony_ci } 7357c2aad20Sopenharmony_ci 7367c2aad20Sopenharmony_ci return; 7377c2aad20Sopenharmony_ci } 7387c2aad20Sopenharmony_ci 7397c2aad20Sopenharmony_ci switch (kind) { 7407c2aad20Sopenharmony_ci case BTF_KIND_INT: 7417c2aad20Sopenharmony_ci /* Emit type alias definitions if necessary */ 7427c2aad20Sopenharmony_ci btf_dump_emit_missing_aliases(d, id, t); 7437c2aad20Sopenharmony_ci 7447c2aad20Sopenharmony_ci tstate->emit_state = EMITTED; 7457c2aad20Sopenharmony_ci break; 7467c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 7477c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 7487c2aad20Sopenharmony_ci if (top_level_def) { 7497c2aad20Sopenharmony_ci btf_dump_emit_enum_def(d, id, t, 0); 7507c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 7517c2aad20Sopenharmony_ci } 7527c2aad20Sopenharmony_ci tstate->emit_state = EMITTED; 7537c2aad20Sopenharmony_ci break; 7547c2aad20Sopenharmony_ci case BTF_KIND_PTR: 7557c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 7567c2aad20Sopenharmony_ci case BTF_KIND_CONST: 7577c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 7587c2aad20Sopenharmony_ci case BTF_KIND_TYPE_TAG: 7597c2aad20Sopenharmony_ci btf_dump_emit_type(d, t->type, cont_id); 7607c2aad20Sopenharmony_ci break; 7617c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: 7627c2aad20Sopenharmony_ci btf_dump_emit_type(d, btf_array(t)->type, cont_id); 7637c2aad20Sopenharmony_ci break; 7647c2aad20Sopenharmony_ci case BTF_KIND_FWD: 7657c2aad20Sopenharmony_ci btf_dump_emit_fwd_def(d, id, t); 7667c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 7677c2aad20Sopenharmony_ci tstate->emit_state = EMITTED; 7687c2aad20Sopenharmony_ci break; 7697c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: 7707c2aad20Sopenharmony_ci tstate->emit_state = EMITTING; 7717c2aad20Sopenharmony_ci btf_dump_emit_type(d, t->type, id); 7727c2aad20Sopenharmony_ci /* 7737c2aad20Sopenharmony_ci * typedef can server as both definition and forward 7747c2aad20Sopenharmony_ci * declaration; at this stage someone depends on 7757c2aad20Sopenharmony_ci * typedef as a forward declaration (refers to it 7767c2aad20Sopenharmony_ci * through pointer), so unless we already did it, 7777c2aad20Sopenharmony_ci * emit typedef as a forward declaration 7787c2aad20Sopenharmony_ci */ 7797c2aad20Sopenharmony_ci if (!tstate->fwd_emitted && !btf_dump_is_blacklisted(d, id)) { 7807c2aad20Sopenharmony_ci btf_dump_emit_typedef_def(d, id, t, 0); 7817c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 7827c2aad20Sopenharmony_ci } 7837c2aad20Sopenharmony_ci tstate->emit_state = EMITTED; 7847c2aad20Sopenharmony_ci break; 7857c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 7867c2aad20Sopenharmony_ci case BTF_KIND_UNION: 7877c2aad20Sopenharmony_ci tstate->emit_state = EMITTING; 7887c2aad20Sopenharmony_ci /* if it's a top-level struct/union definition or struct/union 7897c2aad20Sopenharmony_ci * is anonymous, then in C we'll be emitting all fields and 7907c2aad20Sopenharmony_ci * their types (as opposed to just `struct X`), so we need to 7917c2aad20Sopenharmony_ci * make sure that all types, referenced from struct/union 7927c2aad20Sopenharmony_ci * members have necessary forward-declarations, where 7937c2aad20Sopenharmony_ci * applicable 7947c2aad20Sopenharmony_ci */ 7957c2aad20Sopenharmony_ci if (top_level_def || t->name_off == 0) { 7967c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 7977c2aad20Sopenharmony_ci __u16 vlen = btf_vlen(t); 7987c2aad20Sopenharmony_ci int i, new_cont_id; 7997c2aad20Sopenharmony_ci 8007c2aad20Sopenharmony_ci new_cont_id = t->name_off == 0 ? cont_id : id; 8017c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, m++) 8027c2aad20Sopenharmony_ci btf_dump_emit_type(d, m->type, new_cont_id); 8037c2aad20Sopenharmony_ci } else if (!tstate->fwd_emitted && id != cont_id) { 8047c2aad20Sopenharmony_ci btf_dump_emit_struct_fwd(d, id, t); 8057c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 8067c2aad20Sopenharmony_ci tstate->fwd_emitted = 1; 8077c2aad20Sopenharmony_ci } 8087c2aad20Sopenharmony_ci 8097c2aad20Sopenharmony_ci if (top_level_def) { 8107c2aad20Sopenharmony_ci btf_dump_emit_struct_def(d, id, t, 0); 8117c2aad20Sopenharmony_ci btf_dump_printf(d, ";\n\n"); 8127c2aad20Sopenharmony_ci tstate->emit_state = EMITTED; 8137c2aad20Sopenharmony_ci } else { 8147c2aad20Sopenharmony_ci tstate->emit_state = NOT_EMITTED; 8157c2aad20Sopenharmony_ci } 8167c2aad20Sopenharmony_ci break; 8177c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: { 8187c2aad20Sopenharmony_ci const struct btf_param *p = btf_params(t); 8197c2aad20Sopenharmony_ci __u16 n = btf_vlen(t); 8207c2aad20Sopenharmony_ci int i; 8217c2aad20Sopenharmony_ci 8227c2aad20Sopenharmony_ci btf_dump_emit_type(d, t->type, cont_id); 8237c2aad20Sopenharmony_ci for (i = 0; i < n; i++, p++) 8247c2aad20Sopenharmony_ci btf_dump_emit_type(d, p->type, cont_id); 8257c2aad20Sopenharmony_ci 8267c2aad20Sopenharmony_ci break; 8277c2aad20Sopenharmony_ci } 8287c2aad20Sopenharmony_ci default: 8297c2aad20Sopenharmony_ci break; 8307c2aad20Sopenharmony_ci } 8317c2aad20Sopenharmony_ci} 8327c2aad20Sopenharmony_ci 8337c2aad20Sopenharmony_cistatic bool btf_is_struct_packed(const struct btf *btf, __u32 id, 8347c2aad20Sopenharmony_ci const struct btf_type *t) 8357c2aad20Sopenharmony_ci{ 8367c2aad20Sopenharmony_ci const struct btf_member *m; 8377c2aad20Sopenharmony_ci int max_align = 1, align, i, bit_sz; 8387c2aad20Sopenharmony_ci __u16 vlen; 8397c2aad20Sopenharmony_ci 8407c2aad20Sopenharmony_ci m = btf_members(t); 8417c2aad20Sopenharmony_ci vlen = btf_vlen(t); 8427c2aad20Sopenharmony_ci /* all non-bitfield fields have to be naturally aligned */ 8437c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, m++) { 8447c2aad20Sopenharmony_ci align = btf__align_of(btf, m->type); 8457c2aad20Sopenharmony_ci bit_sz = btf_member_bitfield_size(t, i); 8467c2aad20Sopenharmony_ci if (align && bit_sz == 0 && m->offset % (8 * align) != 0) 8477c2aad20Sopenharmony_ci return true; 8487c2aad20Sopenharmony_ci max_align = max(align, max_align); 8497c2aad20Sopenharmony_ci } 8507c2aad20Sopenharmony_ci /* size of a non-packed struct has to be a multiple of its alignment */ 8517c2aad20Sopenharmony_ci if (t->size % max_align != 0) 8527c2aad20Sopenharmony_ci return true; 8537c2aad20Sopenharmony_ci /* 8547c2aad20Sopenharmony_ci * if original struct was marked as packed, but its layout is 8557c2aad20Sopenharmony_ci * naturally aligned, we'll detect that it's not packed 8567c2aad20Sopenharmony_ci */ 8577c2aad20Sopenharmony_ci return false; 8587c2aad20Sopenharmony_ci} 8597c2aad20Sopenharmony_ci 8607c2aad20Sopenharmony_cistatic void btf_dump_emit_bit_padding(const struct btf_dump *d, 8617c2aad20Sopenharmony_ci int cur_off, int next_off, int next_align, 8627c2aad20Sopenharmony_ci bool in_bitfield, int lvl) 8637c2aad20Sopenharmony_ci{ 8647c2aad20Sopenharmony_ci const struct { 8657c2aad20Sopenharmony_ci const char *name; 8667c2aad20Sopenharmony_ci int bits; 8677c2aad20Sopenharmony_ci } pads[] = { 8687c2aad20Sopenharmony_ci {"long", d->ptr_sz * 8}, {"int", 32}, {"short", 16}, {"char", 8} 8697c2aad20Sopenharmony_ci }; 8707c2aad20Sopenharmony_ci int new_off, pad_bits, bits, i; 8717c2aad20Sopenharmony_ci const char *pad_type; 8727c2aad20Sopenharmony_ci 8737c2aad20Sopenharmony_ci if (cur_off >= next_off) 8747c2aad20Sopenharmony_ci return; /* no gap */ 8757c2aad20Sopenharmony_ci 8767c2aad20Sopenharmony_ci /* For filling out padding we want to take advantage of 8777c2aad20Sopenharmony_ci * natural alignment rules to minimize unnecessary explicit 8787c2aad20Sopenharmony_ci * padding. First, we find the largest type (among long, int, 8797c2aad20Sopenharmony_ci * short, or char) that can be used to force naturally aligned 8807c2aad20Sopenharmony_ci * boundary. Once determined, we'll use such type to fill in 8817c2aad20Sopenharmony_ci * the remaining padding gap. In some cases we can rely on 8827c2aad20Sopenharmony_ci * compiler filling some gaps, but sometimes we need to force 8837c2aad20Sopenharmony_ci * alignment to close natural alignment with markers like 8847c2aad20Sopenharmony_ci * `long: 0` (this is always the case for bitfields). Note 8857c2aad20Sopenharmony_ci * that even if struct itself has, let's say 4-byte alignment 8867c2aad20Sopenharmony_ci * (i.e., it only uses up to int-aligned types), using `long: 8877c2aad20Sopenharmony_ci * X;` explicit padding doesn't actually change struct's 8887c2aad20Sopenharmony_ci * overall alignment requirements, but compiler does take into 8897c2aad20Sopenharmony_ci * account that type's (long, in this example) natural 8907c2aad20Sopenharmony_ci * alignment requirements when adding implicit padding. We use 8917c2aad20Sopenharmony_ci * this fact heavily and don't worry about ruining correct 8927c2aad20Sopenharmony_ci * struct alignment requirement. 8937c2aad20Sopenharmony_ci */ 8947c2aad20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pads); i++) { 8957c2aad20Sopenharmony_ci pad_bits = pads[i].bits; 8967c2aad20Sopenharmony_ci pad_type = pads[i].name; 8977c2aad20Sopenharmony_ci 8987c2aad20Sopenharmony_ci new_off = roundup(cur_off, pad_bits); 8997c2aad20Sopenharmony_ci if (new_off <= next_off) 9007c2aad20Sopenharmony_ci break; 9017c2aad20Sopenharmony_ci } 9027c2aad20Sopenharmony_ci 9037c2aad20Sopenharmony_ci if (new_off > cur_off && new_off <= next_off) { 9047c2aad20Sopenharmony_ci /* We need explicit `<type>: 0` aligning mark if next 9057c2aad20Sopenharmony_ci * field is right on alignment offset and its 9067c2aad20Sopenharmony_ci * alignment requirement is less strict than <type>'s 9077c2aad20Sopenharmony_ci * alignment (so compiler won't naturally align to the 9087c2aad20Sopenharmony_ci * offset we expect), or if subsequent `<type>: X`, 9097c2aad20Sopenharmony_ci * will actually completely fit in the remaining hole, 9107c2aad20Sopenharmony_ci * making compiler basically ignore `<type>: X` 9117c2aad20Sopenharmony_ci * completely. 9127c2aad20Sopenharmony_ci */ 9137c2aad20Sopenharmony_ci if (in_bitfield || 9147c2aad20Sopenharmony_ci (new_off == next_off && roundup(cur_off, next_align * 8) != new_off) || 9157c2aad20Sopenharmony_ci (new_off != next_off && next_off - new_off <= new_off - cur_off)) 9167c2aad20Sopenharmony_ci /* but for bitfields we'll emit explicit bit count */ 9177c2aad20Sopenharmony_ci btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, 9187c2aad20Sopenharmony_ci in_bitfield ? new_off - cur_off : 0); 9197c2aad20Sopenharmony_ci cur_off = new_off; 9207c2aad20Sopenharmony_ci } 9217c2aad20Sopenharmony_ci 9227c2aad20Sopenharmony_ci /* Now we know we start at naturally aligned offset for a chosen 9237c2aad20Sopenharmony_ci * padding type (long, int, short, or char), and so the rest is just 9247c2aad20Sopenharmony_ci * a straightforward filling of remaining padding gap with full 9257c2aad20Sopenharmony_ci * `<type>: sizeof(<type>);` markers, except for the last one, which 9267c2aad20Sopenharmony_ci * might need smaller than sizeof(<type>) padding. 9277c2aad20Sopenharmony_ci */ 9287c2aad20Sopenharmony_ci while (cur_off != next_off) { 9297c2aad20Sopenharmony_ci bits = min(next_off - cur_off, pad_bits); 9307c2aad20Sopenharmony_ci if (bits == pad_bits) { 9317c2aad20Sopenharmony_ci btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits); 9327c2aad20Sopenharmony_ci cur_off += bits; 9337c2aad20Sopenharmony_ci continue; 9347c2aad20Sopenharmony_ci } 9357c2aad20Sopenharmony_ci /* For the remainder padding that doesn't cover entire 9367c2aad20Sopenharmony_ci * pad_type bit length, we pick the smallest necessary type. 9377c2aad20Sopenharmony_ci * This is pure aesthetics, we could have just used `long`, 9387c2aad20Sopenharmony_ci * but having smallest necessary one communicates better the 9397c2aad20Sopenharmony_ci * scale of the padding gap. 9407c2aad20Sopenharmony_ci */ 9417c2aad20Sopenharmony_ci for (i = ARRAY_SIZE(pads) - 1; i >= 0; i--) { 9427c2aad20Sopenharmony_ci pad_type = pads[i].name; 9437c2aad20Sopenharmony_ci pad_bits = pads[i].bits; 9447c2aad20Sopenharmony_ci if (pad_bits < bits) 9457c2aad20Sopenharmony_ci continue; 9467c2aad20Sopenharmony_ci 9477c2aad20Sopenharmony_ci btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, bits); 9487c2aad20Sopenharmony_ci cur_off += bits; 9497c2aad20Sopenharmony_ci break; 9507c2aad20Sopenharmony_ci } 9517c2aad20Sopenharmony_ci } 9527c2aad20Sopenharmony_ci} 9537c2aad20Sopenharmony_ci 9547c2aad20Sopenharmony_cistatic void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id, 9557c2aad20Sopenharmony_ci const struct btf_type *t) 9567c2aad20Sopenharmony_ci{ 9577c2aad20Sopenharmony_ci btf_dump_printf(d, "%s%s%s", 9587c2aad20Sopenharmony_ci btf_is_struct(t) ? "struct" : "union", 9597c2aad20Sopenharmony_ci t->name_off ? " " : "", 9607c2aad20Sopenharmony_ci btf_dump_type_name(d, id)); 9617c2aad20Sopenharmony_ci} 9627c2aad20Sopenharmony_ci 9637c2aad20Sopenharmony_cistatic void btf_dump_emit_struct_def(struct btf_dump *d, 9647c2aad20Sopenharmony_ci __u32 id, 9657c2aad20Sopenharmony_ci const struct btf_type *t, 9667c2aad20Sopenharmony_ci int lvl) 9677c2aad20Sopenharmony_ci{ 9687c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 9697c2aad20Sopenharmony_ci bool is_struct = btf_is_struct(t); 9707c2aad20Sopenharmony_ci bool packed, prev_bitfield = false; 9717c2aad20Sopenharmony_ci int align, i, off = 0; 9727c2aad20Sopenharmony_ci __u16 vlen = btf_vlen(t); 9737c2aad20Sopenharmony_ci 9747c2aad20Sopenharmony_ci align = btf__align_of(d->btf, id); 9757c2aad20Sopenharmony_ci packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0; 9767c2aad20Sopenharmony_ci 9777c2aad20Sopenharmony_ci btf_dump_printf(d, "%s%s%s {", 9787c2aad20Sopenharmony_ci is_struct ? "struct" : "union", 9797c2aad20Sopenharmony_ci t->name_off ? " " : "", 9807c2aad20Sopenharmony_ci btf_dump_type_name(d, id)); 9817c2aad20Sopenharmony_ci 9827c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, m++) { 9837c2aad20Sopenharmony_ci const char *fname; 9847c2aad20Sopenharmony_ci int m_off, m_sz, m_align; 9857c2aad20Sopenharmony_ci bool in_bitfield; 9867c2aad20Sopenharmony_ci 9877c2aad20Sopenharmony_ci fname = btf_name_of(d, m->name_off); 9887c2aad20Sopenharmony_ci m_sz = btf_member_bitfield_size(t, i); 9897c2aad20Sopenharmony_ci m_off = btf_member_bit_offset(t, i); 9907c2aad20Sopenharmony_ci m_align = packed ? 1 : btf__align_of(d->btf, m->type); 9917c2aad20Sopenharmony_ci 9927c2aad20Sopenharmony_ci in_bitfield = prev_bitfield && m_sz != 0; 9937c2aad20Sopenharmony_ci 9947c2aad20Sopenharmony_ci btf_dump_emit_bit_padding(d, off, m_off, m_align, in_bitfield, lvl + 1); 9957c2aad20Sopenharmony_ci btf_dump_printf(d, "\n%s", pfx(lvl + 1)); 9967c2aad20Sopenharmony_ci btf_dump_emit_type_decl(d, m->type, fname, lvl + 1); 9977c2aad20Sopenharmony_ci 9987c2aad20Sopenharmony_ci if (m_sz) { 9997c2aad20Sopenharmony_ci btf_dump_printf(d, ": %d", m_sz); 10007c2aad20Sopenharmony_ci off = m_off + m_sz; 10017c2aad20Sopenharmony_ci prev_bitfield = true; 10027c2aad20Sopenharmony_ci } else { 10037c2aad20Sopenharmony_ci m_sz = max((__s64)0, btf__resolve_size(d->btf, m->type)); 10047c2aad20Sopenharmony_ci off = m_off + m_sz * 8; 10057c2aad20Sopenharmony_ci prev_bitfield = false; 10067c2aad20Sopenharmony_ci } 10077c2aad20Sopenharmony_ci 10087c2aad20Sopenharmony_ci btf_dump_printf(d, ";"); 10097c2aad20Sopenharmony_ci } 10107c2aad20Sopenharmony_ci 10117c2aad20Sopenharmony_ci /* pad at the end, if necessary */ 10127c2aad20Sopenharmony_ci if (is_struct) 10137c2aad20Sopenharmony_ci btf_dump_emit_bit_padding(d, off, t->size * 8, align, false, lvl + 1); 10147c2aad20Sopenharmony_ci 10157c2aad20Sopenharmony_ci /* 10167c2aad20Sopenharmony_ci * Keep `struct empty {}` on a single line, 10177c2aad20Sopenharmony_ci * only print newline when there are regular or padding fields. 10187c2aad20Sopenharmony_ci */ 10197c2aad20Sopenharmony_ci if (vlen || t->size) { 10207c2aad20Sopenharmony_ci btf_dump_printf(d, "\n"); 10217c2aad20Sopenharmony_ci btf_dump_printf(d, "%s}", pfx(lvl)); 10227c2aad20Sopenharmony_ci } else { 10237c2aad20Sopenharmony_ci btf_dump_printf(d, "}"); 10247c2aad20Sopenharmony_ci } 10257c2aad20Sopenharmony_ci if (packed) 10267c2aad20Sopenharmony_ci btf_dump_printf(d, " __attribute__((packed))"); 10277c2aad20Sopenharmony_ci} 10287c2aad20Sopenharmony_ci 10297c2aad20Sopenharmony_cistatic const char *missing_base_types[][2] = { 10307c2aad20Sopenharmony_ci /* 10317c2aad20Sopenharmony_ci * GCC emits typedefs to its internal __PolyX_t types when compiling Arm 10327c2aad20Sopenharmony_ci * SIMD intrinsics. Alias them to standard base types. 10337c2aad20Sopenharmony_ci */ 10347c2aad20Sopenharmony_ci { "__Poly8_t", "unsigned char" }, 10357c2aad20Sopenharmony_ci { "__Poly16_t", "unsigned short" }, 10367c2aad20Sopenharmony_ci { "__Poly64_t", "unsigned long long" }, 10377c2aad20Sopenharmony_ci { "__Poly128_t", "unsigned __int128" }, 10387c2aad20Sopenharmony_ci}; 10397c2aad20Sopenharmony_ci 10407c2aad20Sopenharmony_cistatic void btf_dump_emit_missing_aliases(struct btf_dump *d, __u32 id, 10417c2aad20Sopenharmony_ci const struct btf_type *t) 10427c2aad20Sopenharmony_ci{ 10437c2aad20Sopenharmony_ci const char *name = btf_dump_type_name(d, id); 10447c2aad20Sopenharmony_ci int i; 10457c2aad20Sopenharmony_ci 10467c2aad20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(missing_base_types); i++) { 10477c2aad20Sopenharmony_ci if (strcmp(name, missing_base_types[i][0]) == 0) { 10487c2aad20Sopenharmony_ci btf_dump_printf(d, "typedef %s %s;\n\n", 10497c2aad20Sopenharmony_ci missing_base_types[i][1], name); 10507c2aad20Sopenharmony_ci break; 10517c2aad20Sopenharmony_ci } 10527c2aad20Sopenharmony_ci } 10537c2aad20Sopenharmony_ci} 10547c2aad20Sopenharmony_ci 10557c2aad20Sopenharmony_cistatic void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id, 10567c2aad20Sopenharmony_ci const struct btf_type *t) 10577c2aad20Sopenharmony_ci{ 10587c2aad20Sopenharmony_ci btf_dump_printf(d, "enum %s", btf_dump_type_name(d, id)); 10597c2aad20Sopenharmony_ci} 10607c2aad20Sopenharmony_ci 10617c2aad20Sopenharmony_cistatic void btf_dump_emit_enum32_val(struct btf_dump *d, 10627c2aad20Sopenharmony_ci const struct btf_type *t, 10637c2aad20Sopenharmony_ci int lvl, __u16 vlen) 10647c2aad20Sopenharmony_ci{ 10657c2aad20Sopenharmony_ci const struct btf_enum *v = btf_enum(t); 10667c2aad20Sopenharmony_ci bool is_signed = btf_kflag(t); 10677c2aad20Sopenharmony_ci const char *fmt_str; 10687c2aad20Sopenharmony_ci const char *name; 10697c2aad20Sopenharmony_ci size_t dup_cnt; 10707c2aad20Sopenharmony_ci int i; 10717c2aad20Sopenharmony_ci 10727c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, v++) { 10737c2aad20Sopenharmony_ci name = btf_name_of(d, v->name_off); 10747c2aad20Sopenharmony_ci /* enumerators share namespace with typedef idents */ 10757c2aad20Sopenharmony_ci dup_cnt = btf_dump_name_dups(d, d->ident_names, name); 10767c2aad20Sopenharmony_ci if (dup_cnt > 1) { 10777c2aad20Sopenharmony_ci fmt_str = is_signed ? "\n%s%s___%zd = %d," : "\n%s%s___%zd = %u,"; 10787c2aad20Sopenharmony_ci btf_dump_printf(d, fmt_str, pfx(lvl + 1), name, dup_cnt, v->val); 10797c2aad20Sopenharmony_ci } else { 10807c2aad20Sopenharmony_ci fmt_str = is_signed ? "\n%s%s = %d," : "\n%s%s = %u,"; 10817c2aad20Sopenharmony_ci btf_dump_printf(d, fmt_str, pfx(lvl + 1), name, v->val); 10827c2aad20Sopenharmony_ci } 10837c2aad20Sopenharmony_ci } 10847c2aad20Sopenharmony_ci} 10857c2aad20Sopenharmony_ci 10867c2aad20Sopenharmony_cistatic void btf_dump_emit_enum64_val(struct btf_dump *d, 10877c2aad20Sopenharmony_ci const struct btf_type *t, 10887c2aad20Sopenharmony_ci int lvl, __u16 vlen) 10897c2aad20Sopenharmony_ci{ 10907c2aad20Sopenharmony_ci const struct btf_enum64 *v = btf_enum64(t); 10917c2aad20Sopenharmony_ci bool is_signed = btf_kflag(t); 10927c2aad20Sopenharmony_ci const char *fmt_str; 10937c2aad20Sopenharmony_ci const char *name; 10947c2aad20Sopenharmony_ci size_t dup_cnt; 10957c2aad20Sopenharmony_ci __u64 val; 10967c2aad20Sopenharmony_ci int i; 10977c2aad20Sopenharmony_ci 10987c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, v++) { 10997c2aad20Sopenharmony_ci name = btf_name_of(d, v->name_off); 11007c2aad20Sopenharmony_ci dup_cnt = btf_dump_name_dups(d, d->ident_names, name); 11017c2aad20Sopenharmony_ci val = btf_enum64_value(v); 11027c2aad20Sopenharmony_ci if (dup_cnt > 1) { 11037c2aad20Sopenharmony_ci fmt_str = is_signed ? "\n%s%s___%zd = %lldLL," 11047c2aad20Sopenharmony_ci : "\n%s%s___%zd = %lluULL,"; 11057c2aad20Sopenharmony_ci btf_dump_printf(d, fmt_str, 11067c2aad20Sopenharmony_ci pfx(lvl + 1), name, dup_cnt, 11077c2aad20Sopenharmony_ci (unsigned long long)val); 11087c2aad20Sopenharmony_ci } else { 11097c2aad20Sopenharmony_ci fmt_str = is_signed ? "\n%s%s = %lldLL," 11107c2aad20Sopenharmony_ci : "\n%s%s = %lluULL,"; 11117c2aad20Sopenharmony_ci btf_dump_printf(d, fmt_str, 11127c2aad20Sopenharmony_ci pfx(lvl + 1), name, 11137c2aad20Sopenharmony_ci (unsigned long long)val); 11147c2aad20Sopenharmony_ci } 11157c2aad20Sopenharmony_ci } 11167c2aad20Sopenharmony_ci} 11177c2aad20Sopenharmony_cistatic void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id, 11187c2aad20Sopenharmony_ci const struct btf_type *t, 11197c2aad20Sopenharmony_ci int lvl) 11207c2aad20Sopenharmony_ci{ 11217c2aad20Sopenharmony_ci __u16 vlen = btf_vlen(t); 11227c2aad20Sopenharmony_ci 11237c2aad20Sopenharmony_ci btf_dump_printf(d, "enum%s%s", 11247c2aad20Sopenharmony_ci t->name_off ? " " : "", 11257c2aad20Sopenharmony_ci btf_dump_type_name(d, id)); 11267c2aad20Sopenharmony_ci 11277c2aad20Sopenharmony_ci if (!vlen) 11287c2aad20Sopenharmony_ci return; 11297c2aad20Sopenharmony_ci 11307c2aad20Sopenharmony_ci btf_dump_printf(d, " {"); 11317c2aad20Sopenharmony_ci if (btf_is_enum(t)) 11327c2aad20Sopenharmony_ci btf_dump_emit_enum32_val(d, t, lvl, vlen); 11337c2aad20Sopenharmony_ci else 11347c2aad20Sopenharmony_ci btf_dump_emit_enum64_val(d, t, lvl, vlen); 11357c2aad20Sopenharmony_ci btf_dump_printf(d, "\n%s}", pfx(lvl)); 11367c2aad20Sopenharmony_ci 11377c2aad20Sopenharmony_ci /* special case enums with special sizes */ 11387c2aad20Sopenharmony_ci if (t->size == 1) { 11397c2aad20Sopenharmony_ci /* one-byte enums can be forced with mode(byte) attribute */ 11407c2aad20Sopenharmony_ci btf_dump_printf(d, " __attribute__((mode(byte)))"); 11417c2aad20Sopenharmony_ci } else if (t->size == 8 && d->ptr_sz == 8) { 11427c2aad20Sopenharmony_ci /* enum can be 8-byte sized if one of the enumerator values 11437c2aad20Sopenharmony_ci * doesn't fit in 32-bit integer, or by adding mode(word) 11447c2aad20Sopenharmony_ci * attribute (but probably only on 64-bit architectures); do 11457c2aad20Sopenharmony_ci * our best here to try to satisfy the contract without adding 11467c2aad20Sopenharmony_ci * unnecessary attributes 11477c2aad20Sopenharmony_ci */ 11487c2aad20Sopenharmony_ci bool needs_word_mode; 11497c2aad20Sopenharmony_ci 11507c2aad20Sopenharmony_ci if (btf_is_enum(t)) { 11517c2aad20Sopenharmony_ci /* enum can't represent 64-bit values, so we need word mode */ 11527c2aad20Sopenharmony_ci needs_word_mode = true; 11537c2aad20Sopenharmony_ci } else { 11547c2aad20Sopenharmony_ci /* enum64 needs mode(word) if none of its values has 11557c2aad20Sopenharmony_ci * non-zero upper 32-bits (which means that all values 11567c2aad20Sopenharmony_ci * fit in 32-bit integers and won't cause compiler to 11577c2aad20Sopenharmony_ci * bump enum to be 64-bit naturally 11587c2aad20Sopenharmony_ci */ 11597c2aad20Sopenharmony_ci int i; 11607c2aad20Sopenharmony_ci 11617c2aad20Sopenharmony_ci needs_word_mode = true; 11627c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++) { 11637c2aad20Sopenharmony_ci if (btf_enum64(t)[i].val_hi32 != 0) { 11647c2aad20Sopenharmony_ci needs_word_mode = false; 11657c2aad20Sopenharmony_ci break; 11667c2aad20Sopenharmony_ci } 11677c2aad20Sopenharmony_ci } 11687c2aad20Sopenharmony_ci } 11697c2aad20Sopenharmony_ci if (needs_word_mode) 11707c2aad20Sopenharmony_ci btf_dump_printf(d, " __attribute__((mode(word)))"); 11717c2aad20Sopenharmony_ci } 11727c2aad20Sopenharmony_ci 11737c2aad20Sopenharmony_ci} 11747c2aad20Sopenharmony_ci 11757c2aad20Sopenharmony_cistatic void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id, 11767c2aad20Sopenharmony_ci const struct btf_type *t) 11777c2aad20Sopenharmony_ci{ 11787c2aad20Sopenharmony_ci const char *name = btf_dump_type_name(d, id); 11797c2aad20Sopenharmony_ci 11807c2aad20Sopenharmony_ci if (btf_kflag(t)) 11817c2aad20Sopenharmony_ci btf_dump_printf(d, "union %s", name); 11827c2aad20Sopenharmony_ci else 11837c2aad20Sopenharmony_ci btf_dump_printf(d, "struct %s", name); 11847c2aad20Sopenharmony_ci} 11857c2aad20Sopenharmony_ci 11867c2aad20Sopenharmony_cistatic void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id, 11877c2aad20Sopenharmony_ci const struct btf_type *t, int lvl) 11887c2aad20Sopenharmony_ci{ 11897c2aad20Sopenharmony_ci const char *name = btf_dump_ident_name(d, id); 11907c2aad20Sopenharmony_ci 11917c2aad20Sopenharmony_ci /* 11927c2aad20Sopenharmony_ci * Old GCC versions are emitting invalid typedef for __gnuc_va_list 11937c2aad20Sopenharmony_ci * pointing to VOID. This generates warnings from btf_dump() and 11947c2aad20Sopenharmony_ci * results in uncompilable header file, so we are fixing it up here 11957c2aad20Sopenharmony_ci * with valid typedef into __builtin_va_list. 11967c2aad20Sopenharmony_ci */ 11977c2aad20Sopenharmony_ci if (t->type == 0 && strcmp(name, "__gnuc_va_list") == 0) { 11987c2aad20Sopenharmony_ci btf_dump_printf(d, "typedef __builtin_va_list __gnuc_va_list"); 11997c2aad20Sopenharmony_ci return; 12007c2aad20Sopenharmony_ci } 12017c2aad20Sopenharmony_ci 12027c2aad20Sopenharmony_ci btf_dump_printf(d, "typedef "); 12037c2aad20Sopenharmony_ci btf_dump_emit_type_decl(d, t->type, name, lvl); 12047c2aad20Sopenharmony_ci} 12057c2aad20Sopenharmony_ci 12067c2aad20Sopenharmony_cistatic int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id) 12077c2aad20Sopenharmony_ci{ 12087c2aad20Sopenharmony_ci __u32 *new_stack; 12097c2aad20Sopenharmony_ci size_t new_cap; 12107c2aad20Sopenharmony_ci 12117c2aad20Sopenharmony_ci if (d->decl_stack_cnt >= d->decl_stack_cap) { 12127c2aad20Sopenharmony_ci new_cap = max(16, d->decl_stack_cap * 3 / 2); 12137c2aad20Sopenharmony_ci new_stack = libbpf_reallocarray(d->decl_stack, new_cap, sizeof(new_stack[0])); 12147c2aad20Sopenharmony_ci if (!new_stack) 12157c2aad20Sopenharmony_ci return -ENOMEM; 12167c2aad20Sopenharmony_ci d->decl_stack = new_stack; 12177c2aad20Sopenharmony_ci d->decl_stack_cap = new_cap; 12187c2aad20Sopenharmony_ci } 12197c2aad20Sopenharmony_ci 12207c2aad20Sopenharmony_ci d->decl_stack[d->decl_stack_cnt++] = id; 12217c2aad20Sopenharmony_ci 12227c2aad20Sopenharmony_ci return 0; 12237c2aad20Sopenharmony_ci} 12247c2aad20Sopenharmony_ci 12257c2aad20Sopenharmony_ci/* 12267c2aad20Sopenharmony_ci * Emit type declaration (e.g., field type declaration in a struct or argument 12277c2aad20Sopenharmony_ci * declaration in function prototype) in correct C syntax. 12287c2aad20Sopenharmony_ci * 12297c2aad20Sopenharmony_ci * For most types it's trivial, but there are few quirky type declaration 12307c2aad20Sopenharmony_ci * cases worth mentioning: 12317c2aad20Sopenharmony_ci * - function prototypes (especially nesting of function prototypes); 12327c2aad20Sopenharmony_ci * - arrays; 12337c2aad20Sopenharmony_ci * - const/volatile/restrict for pointers vs other types. 12347c2aad20Sopenharmony_ci * 12357c2aad20Sopenharmony_ci * For a good discussion of *PARSING* C syntax (as a human), see 12367c2aad20Sopenharmony_ci * Peter van der Linden's "Expert C Programming: Deep C Secrets", 12377c2aad20Sopenharmony_ci * Ch.3 "Unscrambling Declarations in C". 12387c2aad20Sopenharmony_ci * 12397c2aad20Sopenharmony_ci * It won't help with BTF to C conversion much, though, as it's an opposite 12407c2aad20Sopenharmony_ci * problem. So we came up with this algorithm in reverse to van der Linden's 12417c2aad20Sopenharmony_ci * parsing algorithm. It goes from structured BTF representation of type 12427c2aad20Sopenharmony_ci * declaration to a valid compilable C syntax. 12437c2aad20Sopenharmony_ci * 12447c2aad20Sopenharmony_ci * For instance, consider this C typedef: 12457c2aad20Sopenharmony_ci * typedef const int * const * arr[10] arr_t; 12467c2aad20Sopenharmony_ci * It will be represented in BTF with this chain of BTF types: 12477c2aad20Sopenharmony_ci * [typedef] -> [array] -> [ptr] -> [const] -> [ptr] -> [const] -> [int] 12487c2aad20Sopenharmony_ci * 12497c2aad20Sopenharmony_ci * Notice how [const] modifier always goes before type it modifies in BTF type 12507c2aad20Sopenharmony_ci * graph, but in C syntax, const/volatile/restrict modifiers are written to 12517c2aad20Sopenharmony_ci * the right of pointers, but to the left of other types. There are also other 12527c2aad20Sopenharmony_ci * quirks, like function pointers, arrays of them, functions returning other 12537c2aad20Sopenharmony_ci * functions, etc. 12547c2aad20Sopenharmony_ci * 12557c2aad20Sopenharmony_ci * We handle that by pushing all the types to a stack, until we hit "terminal" 12567c2aad20Sopenharmony_ci * type (int/enum/struct/union/fwd). Then depending on the kind of a type on 12577c2aad20Sopenharmony_ci * top of a stack, modifiers are handled differently. Array/function pointers 12587c2aad20Sopenharmony_ci * have also wildly different syntax and how nesting of them are done. See 12597c2aad20Sopenharmony_ci * code for authoritative definition. 12607c2aad20Sopenharmony_ci * 12617c2aad20Sopenharmony_ci * To avoid allocating new stack for each independent chain of BTF types, we 12627c2aad20Sopenharmony_ci * share one bigger stack, with each chain working only on its own local view 12637c2aad20Sopenharmony_ci * of a stack frame. Some care is required to "pop" stack frames after 12647c2aad20Sopenharmony_ci * processing type declaration chain. 12657c2aad20Sopenharmony_ci */ 12667c2aad20Sopenharmony_ciint btf_dump__emit_type_decl(struct btf_dump *d, __u32 id, 12677c2aad20Sopenharmony_ci const struct btf_dump_emit_type_decl_opts *opts) 12687c2aad20Sopenharmony_ci{ 12697c2aad20Sopenharmony_ci const char *fname; 12707c2aad20Sopenharmony_ci int lvl, err; 12717c2aad20Sopenharmony_ci 12727c2aad20Sopenharmony_ci if (!OPTS_VALID(opts, btf_dump_emit_type_decl_opts)) 12737c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 12747c2aad20Sopenharmony_ci 12757c2aad20Sopenharmony_ci err = btf_dump_resize(d); 12767c2aad20Sopenharmony_ci if (err) 12777c2aad20Sopenharmony_ci return libbpf_err(err); 12787c2aad20Sopenharmony_ci 12797c2aad20Sopenharmony_ci fname = OPTS_GET(opts, field_name, ""); 12807c2aad20Sopenharmony_ci lvl = OPTS_GET(opts, indent_level, 0); 12817c2aad20Sopenharmony_ci d->strip_mods = OPTS_GET(opts, strip_mods, false); 12827c2aad20Sopenharmony_ci btf_dump_emit_type_decl(d, id, fname, lvl); 12837c2aad20Sopenharmony_ci d->strip_mods = false; 12847c2aad20Sopenharmony_ci return 0; 12857c2aad20Sopenharmony_ci} 12867c2aad20Sopenharmony_ci 12877c2aad20Sopenharmony_cistatic void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, 12887c2aad20Sopenharmony_ci const char *fname, int lvl) 12897c2aad20Sopenharmony_ci{ 12907c2aad20Sopenharmony_ci struct id_stack decl_stack; 12917c2aad20Sopenharmony_ci const struct btf_type *t; 12927c2aad20Sopenharmony_ci int err, stack_start; 12937c2aad20Sopenharmony_ci 12947c2aad20Sopenharmony_ci stack_start = d->decl_stack_cnt; 12957c2aad20Sopenharmony_ci for (;;) { 12967c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 12977c2aad20Sopenharmony_ci if (d->strip_mods && btf_is_mod(t)) 12987c2aad20Sopenharmony_ci goto skip_mod; 12997c2aad20Sopenharmony_ci 13007c2aad20Sopenharmony_ci err = btf_dump_push_decl_stack_id(d, id); 13017c2aad20Sopenharmony_ci if (err < 0) { 13027c2aad20Sopenharmony_ci /* 13037c2aad20Sopenharmony_ci * if we don't have enough memory for entire type decl 13047c2aad20Sopenharmony_ci * chain, restore stack, emit warning, and try to 13057c2aad20Sopenharmony_ci * proceed nevertheless 13067c2aad20Sopenharmony_ci */ 13077c2aad20Sopenharmony_ci pr_warn("not enough memory for decl stack:%d", err); 13087c2aad20Sopenharmony_ci d->decl_stack_cnt = stack_start; 13097c2aad20Sopenharmony_ci return; 13107c2aad20Sopenharmony_ci } 13117c2aad20Sopenharmony_ciskip_mod: 13127c2aad20Sopenharmony_ci /* VOID */ 13137c2aad20Sopenharmony_ci if (id == 0) 13147c2aad20Sopenharmony_ci break; 13157c2aad20Sopenharmony_ci 13167c2aad20Sopenharmony_ci switch (btf_kind(t)) { 13177c2aad20Sopenharmony_ci case BTF_KIND_PTR: 13187c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 13197c2aad20Sopenharmony_ci case BTF_KIND_CONST: 13207c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 13217c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: 13227c2aad20Sopenharmony_ci case BTF_KIND_TYPE_TAG: 13237c2aad20Sopenharmony_ci id = t->type; 13247c2aad20Sopenharmony_ci break; 13257c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: 13267c2aad20Sopenharmony_ci id = btf_array(t)->type; 13277c2aad20Sopenharmony_ci break; 13287c2aad20Sopenharmony_ci case BTF_KIND_INT: 13297c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 13307c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 13317c2aad20Sopenharmony_ci case BTF_KIND_FWD: 13327c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 13337c2aad20Sopenharmony_ci case BTF_KIND_UNION: 13347c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: 13357c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 13367c2aad20Sopenharmony_ci goto done; 13377c2aad20Sopenharmony_ci default: 13387c2aad20Sopenharmony_ci pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n", 13397c2aad20Sopenharmony_ci btf_kind(t), id); 13407c2aad20Sopenharmony_ci goto done; 13417c2aad20Sopenharmony_ci } 13427c2aad20Sopenharmony_ci } 13437c2aad20Sopenharmony_cidone: 13447c2aad20Sopenharmony_ci /* 13457c2aad20Sopenharmony_ci * We might be inside a chain of declarations (e.g., array of function 13467c2aad20Sopenharmony_ci * pointers returning anonymous (so inlined) structs, having another 13477c2aad20Sopenharmony_ci * array field). Each of those needs its own "stack frame" to handle 13487c2aad20Sopenharmony_ci * emitting of declarations. Those stack frames are non-overlapping 13497c2aad20Sopenharmony_ci * portions of shared btf_dump->decl_stack. To make it a bit nicer to 13507c2aad20Sopenharmony_ci * handle this set of nested stacks, we create a view corresponding to 13517c2aad20Sopenharmony_ci * our own "stack frame" and work with it as an independent stack. 13527c2aad20Sopenharmony_ci * We'll need to clean up after emit_type_chain() returns, though. 13537c2aad20Sopenharmony_ci */ 13547c2aad20Sopenharmony_ci decl_stack.ids = d->decl_stack + stack_start; 13557c2aad20Sopenharmony_ci decl_stack.cnt = d->decl_stack_cnt - stack_start; 13567c2aad20Sopenharmony_ci btf_dump_emit_type_chain(d, &decl_stack, fname, lvl); 13577c2aad20Sopenharmony_ci /* 13587c2aad20Sopenharmony_ci * emit_type_chain() guarantees that it will pop its entire decl_stack 13597c2aad20Sopenharmony_ci * frame before returning. But it works with a read-only view into 13607c2aad20Sopenharmony_ci * decl_stack, so it doesn't actually pop anything from the 13617c2aad20Sopenharmony_ci * perspective of shared btf_dump->decl_stack, per se. We need to 13627c2aad20Sopenharmony_ci * reset decl_stack state to how it was before us to avoid it growing 13637c2aad20Sopenharmony_ci * all the time. 13647c2aad20Sopenharmony_ci */ 13657c2aad20Sopenharmony_ci d->decl_stack_cnt = stack_start; 13667c2aad20Sopenharmony_ci} 13677c2aad20Sopenharmony_ci 13687c2aad20Sopenharmony_cistatic void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack) 13697c2aad20Sopenharmony_ci{ 13707c2aad20Sopenharmony_ci const struct btf_type *t; 13717c2aad20Sopenharmony_ci __u32 id; 13727c2aad20Sopenharmony_ci 13737c2aad20Sopenharmony_ci while (decl_stack->cnt) { 13747c2aad20Sopenharmony_ci id = decl_stack->ids[decl_stack->cnt - 1]; 13757c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 13767c2aad20Sopenharmony_ci 13777c2aad20Sopenharmony_ci switch (btf_kind(t)) { 13787c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 13797c2aad20Sopenharmony_ci btf_dump_printf(d, "volatile "); 13807c2aad20Sopenharmony_ci break; 13817c2aad20Sopenharmony_ci case BTF_KIND_CONST: 13827c2aad20Sopenharmony_ci btf_dump_printf(d, "const "); 13837c2aad20Sopenharmony_ci break; 13847c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 13857c2aad20Sopenharmony_ci btf_dump_printf(d, "restrict "); 13867c2aad20Sopenharmony_ci break; 13877c2aad20Sopenharmony_ci default: 13887c2aad20Sopenharmony_ci return; 13897c2aad20Sopenharmony_ci } 13907c2aad20Sopenharmony_ci decl_stack->cnt--; 13917c2aad20Sopenharmony_ci } 13927c2aad20Sopenharmony_ci} 13937c2aad20Sopenharmony_ci 13947c2aad20Sopenharmony_cistatic void btf_dump_drop_mods(struct btf_dump *d, struct id_stack *decl_stack) 13957c2aad20Sopenharmony_ci{ 13967c2aad20Sopenharmony_ci const struct btf_type *t; 13977c2aad20Sopenharmony_ci __u32 id; 13987c2aad20Sopenharmony_ci 13997c2aad20Sopenharmony_ci while (decl_stack->cnt) { 14007c2aad20Sopenharmony_ci id = decl_stack->ids[decl_stack->cnt - 1]; 14017c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 14027c2aad20Sopenharmony_ci if (!btf_is_mod(t)) 14037c2aad20Sopenharmony_ci return; 14047c2aad20Sopenharmony_ci decl_stack->cnt--; 14057c2aad20Sopenharmony_ci } 14067c2aad20Sopenharmony_ci} 14077c2aad20Sopenharmony_ci 14087c2aad20Sopenharmony_cistatic void btf_dump_emit_name(const struct btf_dump *d, 14097c2aad20Sopenharmony_ci const char *name, bool last_was_ptr) 14107c2aad20Sopenharmony_ci{ 14117c2aad20Sopenharmony_ci bool separate = name[0] && !last_was_ptr; 14127c2aad20Sopenharmony_ci 14137c2aad20Sopenharmony_ci btf_dump_printf(d, "%s%s", separate ? " " : "", name); 14147c2aad20Sopenharmony_ci} 14157c2aad20Sopenharmony_ci 14167c2aad20Sopenharmony_cistatic void btf_dump_emit_type_chain(struct btf_dump *d, 14177c2aad20Sopenharmony_ci struct id_stack *decls, 14187c2aad20Sopenharmony_ci const char *fname, int lvl) 14197c2aad20Sopenharmony_ci{ 14207c2aad20Sopenharmony_ci /* 14217c2aad20Sopenharmony_ci * last_was_ptr is used to determine if we need to separate pointer 14227c2aad20Sopenharmony_ci * asterisk (*) from previous part of type signature with space, so 14237c2aad20Sopenharmony_ci * that we get `int ***`, instead of `int * * *`. We default to true 14247c2aad20Sopenharmony_ci * for cases where we have single pointer in a chain. E.g., in ptr -> 14257c2aad20Sopenharmony_ci * func_proto case. func_proto will start a new emit_type_chain call 14267c2aad20Sopenharmony_ci * with just ptr, which should be emitted as (*) or (*<fname>), so we 14277c2aad20Sopenharmony_ci * don't want to prepend space for that last pointer. 14287c2aad20Sopenharmony_ci */ 14297c2aad20Sopenharmony_ci bool last_was_ptr = true; 14307c2aad20Sopenharmony_ci const struct btf_type *t; 14317c2aad20Sopenharmony_ci const char *name; 14327c2aad20Sopenharmony_ci __u16 kind; 14337c2aad20Sopenharmony_ci __u32 id; 14347c2aad20Sopenharmony_ci 14357c2aad20Sopenharmony_ci while (decls->cnt) { 14367c2aad20Sopenharmony_ci id = decls->ids[--decls->cnt]; 14377c2aad20Sopenharmony_ci if (id == 0) { 14387c2aad20Sopenharmony_ci /* VOID is a special snowflake */ 14397c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14407c2aad20Sopenharmony_ci btf_dump_printf(d, "void"); 14417c2aad20Sopenharmony_ci last_was_ptr = false; 14427c2aad20Sopenharmony_ci continue; 14437c2aad20Sopenharmony_ci } 14447c2aad20Sopenharmony_ci 14457c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 14467c2aad20Sopenharmony_ci kind = btf_kind(t); 14477c2aad20Sopenharmony_ci 14487c2aad20Sopenharmony_ci switch (kind) { 14497c2aad20Sopenharmony_ci case BTF_KIND_INT: 14507c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 14517c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14527c2aad20Sopenharmony_ci name = btf_name_of(d, t->name_off); 14537c2aad20Sopenharmony_ci btf_dump_printf(d, "%s", name); 14547c2aad20Sopenharmony_ci break; 14557c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 14567c2aad20Sopenharmony_ci case BTF_KIND_UNION: 14577c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14587c2aad20Sopenharmony_ci /* inline anonymous struct/union */ 14597c2aad20Sopenharmony_ci if (t->name_off == 0 && !d->skip_anon_defs) 14607c2aad20Sopenharmony_ci btf_dump_emit_struct_def(d, id, t, lvl); 14617c2aad20Sopenharmony_ci else 14627c2aad20Sopenharmony_ci btf_dump_emit_struct_fwd(d, id, t); 14637c2aad20Sopenharmony_ci break; 14647c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 14657c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 14667c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14677c2aad20Sopenharmony_ci /* inline anonymous enum */ 14687c2aad20Sopenharmony_ci if (t->name_off == 0 && !d->skip_anon_defs) 14697c2aad20Sopenharmony_ci btf_dump_emit_enum_def(d, id, t, lvl); 14707c2aad20Sopenharmony_ci else 14717c2aad20Sopenharmony_ci btf_dump_emit_enum_fwd(d, id, t); 14727c2aad20Sopenharmony_ci break; 14737c2aad20Sopenharmony_ci case BTF_KIND_FWD: 14747c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14757c2aad20Sopenharmony_ci btf_dump_emit_fwd_def(d, id, t); 14767c2aad20Sopenharmony_ci break; 14777c2aad20Sopenharmony_ci case BTF_KIND_TYPEDEF: 14787c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14797c2aad20Sopenharmony_ci btf_dump_printf(d, "%s", btf_dump_ident_name(d, id)); 14807c2aad20Sopenharmony_ci break; 14817c2aad20Sopenharmony_ci case BTF_KIND_PTR: 14827c2aad20Sopenharmony_ci btf_dump_printf(d, "%s", last_was_ptr ? "*" : " *"); 14837c2aad20Sopenharmony_ci break; 14847c2aad20Sopenharmony_ci case BTF_KIND_VOLATILE: 14857c2aad20Sopenharmony_ci btf_dump_printf(d, " volatile"); 14867c2aad20Sopenharmony_ci break; 14877c2aad20Sopenharmony_ci case BTF_KIND_CONST: 14887c2aad20Sopenharmony_ci btf_dump_printf(d, " const"); 14897c2aad20Sopenharmony_ci break; 14907c2aad20Sopenharmony_ci case BTF_KIND_RESTRICT: 14917c2aad20Sopenharmony_ci btf_dump_printf(d, " restrict"); 14927c2aad20Sopenharmony_ci break; 14937c2aad20Sopenharmony_ci case BTF_KIND_TYPE_TAG: 14947c2aad20Sopenharmony_ci btf_dump_emit_mods(d, decls); 14957c2aad20Sopenharmony_ci name = btf_name_of(d, t->name_off); 14967c2aad20Sopenharmony_ci btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name); 14977c2aad20Sopenharmony_ci break; 14987c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: { 14997c2aad20Sopenharmony_ci const struct btf_array *a = btf_array(t); 15007c2aad20Sopenharmony_ci const struct btf_type *next_t; 15017c2aad20Sopenharmony_ci __u32 next_id; 15027c2aad20Sopenharmony_ci bool multidim; 15037c2aad20Sopenharmony_ci /* 15047c2aad20Sopenharmony_ci * GCC has a bug 15057c2aad20Sopenharmony_ci * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8354) 15067c2aad20Sopenharmony_ci * which causes it to emit extra const/volatile 15077c2aad20Sopenharmony_ci * modifiers for an array, if array's element type has 15087c2aad20Sopenharmony_ci * const/volatile modifiers. Clang doesn't do that. 15097c2aad20Sopenharmony_ci * In general, it doesn't seem very meaningful to have 15107c2aad20Sopenharmony_ci * a const/volatile modifier for array, so we are 15117c2aad20Sopenharmony_ci * going to silently skip them here. 15127c2aad20Sopenharmony_ci */ 15137c2aad20Sopenharmony_ci btf_dump_drop_mods(d, decls); 15147c2aad20Sopenharmony_ci 15157c2aad20Sopenharmony_ci if (decls->cnt == 0) { 15167c2aad20Sopenharmony_ci btf_dump_emit_name(d, fname, last_was_ptr); 15177c2aad20Sopenharmony_ci btf_dump_printf(d, "[%u]", a->nelems); 15187c2aad20Sopenharmony_ci return; 15197c2aad20Sopenharmony_ci } 15207c2aad20Sopenharmony_ci 15217c2aad20Sopenharmony_ci next_id = decls->ids[decls->cnt - 1]; 15227c2aad20Sopenharmony_ci next_t = btf__type_by_id(d->btf, next_id); 15237c2aad20Sopenharmony_ci multidim = btf_is_array(next_t); 15247c2aad20Sopenharmony_ci /* we need space if we have named non-pointer */ 15257c2aad20Sopenharmony_ci if (fname[0] && !last_was_ptr) 15267c2aad20Sopenharmony_ci btf_dump_printf(d, " "); 15277c2aad20Sopenharmony_ci /* no parentheses for multi-dimensional array */ 15287c2aad20Sopenharmony_ci if (!multidim) 15297c2aad20Sopenharmony_ci btf_dump_printf(d, "("); 15307c2aad20Sopenharmony_ci btf_dump_emit_type_chain(d, decls, fname, lvl); 15317c2aad20Sopenharmony_ci if (!multidim) 15327c2aad20Sopenharmony_ci btf_dump_printf(d, ")"); 15337c2aad20Sopenharmony_ci btf_dump_printf(d, "[%u]", a->nelems); 15347c2aad20Sopenharmony_ci return; 15357c2aad20Sopenharmony_ci } 15367c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: { 15377c2aad20Sopenharmony_ci const struct btf_param *p = btf_params(t); 15387c2aad20Sopenharmony_ci __u16 vlen = btf_vlen(t); 15397c2aad20Sopenharmony_ci int i; 15407c2aad20Sopenharmony_ci 15417c2aad20Sopenharmony_ci /* 15427c2aad20Sopenharmony_ci * GCC emits extra volatile qualifier for 15437c2aad20Sopenharmony_ci * __attribute__((noreturn)) function pointers. Clang 15447c2aad20Sopenharmony_ci * doesn't do it. It's a GCC quirk for backwards 15457c2aad20Sopenharmony_ci * compatibility with code written for GCC <2.5. So, 15467c2aad20Sopenharmony_ci * similarly to extra qualifiers for array, just drop 15477c2aad20Sopenharmony_ci * them, instead of handling them. 15487c2aad20Sopenharmony_ci */ 15497c2aad20Sopenharmony_ci btf_dump_drop_mods(d, decls); 15507c2aad20Sopenharmony_ci if (decls->cnt) { 15517c2aad20Sopenharmony_ci btf_dump_printf(d, " ("); 15527c2aad20Sopenharmony_ci btf_dump_emit_type_chain(d, decls, fname, lvl); 15537c2aad20Sopenharmony_ci btf_dump_printf(d, ")"); 15547c2aad20Sopenharmony_ci } else { 15557c2aad20Sopenharmony_ci btf_dump_emit_name(d, fname, last_was_ptr); 15567c2aad20Sopenharmony_ci } 15577c2aad20Sopenharmony_ci btf_dump_printf(d, "("); 15587c2aad20Sopenharmony_ci /* 15597c2aad20Sopenharmony_ci * Clang for BPF target generates func_proto with no 15607c2aad20Sopenharmony_ci * args as a func_proto with a single void arg (e.g., 15617c2aad20Sopenharmony_ci * `int (*f)(void)` vs just `int (*f)()`). We are 15627c2aad20Sopenharmony_ci * going to pretend there are no args for such case. 15637c2aad20Sopenharmony_ci */ 15647c2aad20Sopenharmony_ci if (vlen == 1 && p->type == 0) { 15657c2aad20Sopenharmony_ci btf_dump_printf(d, ")"); 15667c2aad20Sopenharmony_ci return; 15677c2aad20Sopenharmony_ci } 15687c2aad20Sopenharmony_ci 15697c2aad20Sopenharmony_ci for (i = 0; i < vlen; i++, p++) { 15707c2aad20Sopenharmony_ci if (i > 0) 15717c2aad20Sopenharmony_ci btf_dump_printf(d, ", "); 15727c2aad20Sopenharmony_ci 15737c2aad20Sopenharmony_ci /* last arg of type void is vararg */ 15747c2aad20Sopenharmony_ci if (i == vlen - 1 && p->type == 0) { 15757c2aad20Sopenharmony_ci btf_dump_printf(d, "..."); 15767c2aad20Sopenharmony_ci break; 15777c2aad20Sopenharmony_ci } 15787c2aad20Sopenharmony_ci 15797c2aad20Sopenharmony_ci name = btf_name_of(d, p->name_off); 15807c2aad20Sopenharmony_ci btf_dump_emit_type_decl(d, p->type, name, lvl); 15817c2aad20Sopenharmony_ci } 15827c2aad20Sopenharmony_ci 15837c2aad20Sopenharmony_ci btf_dump_printf(d, ")"); 15847c2aad20Sopenharmony_ci return; 15857c2aad20Sopenharmony_ci } 15867c2aad20Sopenharmony_ci default: 15877c2aad20Sopenharmony_ci pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n", 15887c2aad20Sopenharmony_ci kind, id); 15897c2aad20Sopenharmony_ci return; 15907c2aad20Sopenharmony_ci } 15917c2aad20Sopenharmony_ci 15927c2aad20Sopenharmony_ci last_was_ptr = kind == BTF_KIND_PTR; 15937c2aad20Sopenharmony_ci } 15947c2aad20Sopenharmony_ci 15957c2aad20Sopenharmony_ci btf_dump_emit_name(d, fname, last_was_ptr); 15967c2aad20Sopenharmony_ci} 15977c2aad20Sopenharmony_ci 15987c2aad20Sopenharmony_ci/* show type name as (type_name) */ 15997c2aad20Sopenharmony_cistatic void btf_dump_emit_type_cast(struct btf_dump *d, __u32 id, 16007c2aad20Sopenharmony_ci bool top_level) 16017c2aad20Sopenharmony_ci{ 16027c2aad20Sopenharmony_ci const struct btf_type *t; 16037c2aad20Sopenharmony_ci 16047c2aad20Sopenharmony_ci /* for array members, we don't bother emitting type name for each 16057c2aad20Sopenharmony_ci * member to avoid the redundancy of 16067c2aad20Sopenharmony_ci * .name = (char[4])[(char)'f',(char)'o',(char)'o',] 16077c2aad20Sopenharmony_ci */ 16087c2aad20Sopenharmony_ci if (d->typed_dump->is_array_member) 16097c2aad20Sopenharmony_ci return; 16107c2aad20Sopenharmony_ci 16117c2aad20Sopenharmony_ci /* avoid type name specification for variable/section; it will be done 16127c2aad20Sopenharmony_ci * for the associated variable value(s). 16137c2aad20Sopenharmony_ci */ 16147c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 16157c2aad20Sopenharmony_ci if (btf_is_var(t) || btf_is_datasec(t)) 16167c2aad20Sopenharmony_ci return; 16177c2aad20Sopenharmony_ci 16187c2aad20Sopenharmony_ci if (top_level) 16197c2aad20Sopenharmony_ci btf_dump_printf(d, "("); 16207c2aad20Sopenharmony_ci 16217c2aad20Sopenharmony_ci d->skip_anon_defs = true; 16227c2aad20Sopenharmony_ci d->strip_mods = true; 16237c2aad20Sopenharmony_ci btf_dump_emit_type_decl(d, id, "", 0); 16247c2aad20Sopenharmony_ci d->strip_mods = false; 16257c2aad20Sopenharmony_ci d->skip_anon_defs = false; 16267c2aad20Sopenharmony_ci 16277c2aad20Sopenharmony_ci if (top_level) 16287c2aad20Sopenharmony_ci btf_dump_printf(d, ")"); 16297c2aad20Sopenharmony_ci} 16307c2aad20Sopenharmony_ci 16317c2aad20Sopenharmony_ci/* return number of duplicates (occurrences) of a given name */ 16327c2aad20Sopenharmony_cistatic size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, 16337c2aad20Sopenharmony_ci const char *orig_name) 16347c2aad20Sopenharmony_ci{ 16357c2aad20Sopenharmony_ci char *old_name, *new_name; 16367c2aad20Sopenharmony_ci size_t dup_cnt = 0; 16377c2aad20Sopenharmony_ci int err; 16387c2aad20Sopenharmony_ci 16397c2aad20Sopenharmony_ci new_name = strdup(orig_name); 16407c2aad20Sopenharmony_ci if (!new_name) 16417c2aad20Sopenharmony_ci return 1; 16427c2aad20Sopenharmony_ci 16437c2aad20Sopenharmony_ci (void)hashmap__find(name_map, orig_name, &dup_cnt); 16447c2aad20Sopenharmony_ci dup_cnt++; 16457c2aad20Sopenharmony_ci 16467c2aad20Sopenharmony_ci err = hashmap__set(name_map, new_name, dup_cnt, &old_name, NULL); 16477c2aad20Sopenharmony_ci if (err) 16487c2aad20Sopenharmony_ci free(new_name); 16497c2aad20Sopenharmony_ci 16507c2aad20Sopenharmony_ci free(old_name); 16517c2aad20Sopenharmony_ci 16527c2aad20Sopenharmony_ci return dup_cnt; 16537c2aad20Sopenharmony_ci} 16547c2aad20Sopenharmony_ci 16557c2aad20Sopenharmony_cistatic const char *btf_dump_resolve_name(struct btf_dump *d, __u32 id, 16567c2aad20Sopenharmony_ci struct hashmap *name_map) 16577c2aad20Sopenharmony_ci{ 16587c2aad20Sopenharmony_ci struct btf_dump_type_aux_state *s = &d->type_states[id]; 16597c2aad20Sopenharmony_ci const struct btf_type *t = btf__type_by_id(d->btf, id); 16607c2aad20Sopenharmony_ci const char *orig_name = btf_name_of(d, t->name_off); 16617c2aad20Sopenharmony_ci const char **cached_name = &d->cached_names[id]; 16627c2aad20Sopenharmony_ci size_t dup_cnt; 16637c2aad20Sopenharmony_ci 16647c2aad20Sopenharmony_ci if (t->name_off == 0) 16657c2aad20Sopenharmony_ci return ""; 16667c2aad20Sopenharmony_ci 16677c2aad20Sopenharmony_ci if (s->name_resolved) 16687c2aad20Sopenharmony_ci return *cached_name ? *cached_name : orig_name; 16697c2aad20Sopenharmony_ci 16707c2aad20Sopenharmony_ci if (btf_is_fwd(t) || (btf_is_enum(t) && btf_vlen(t) == 0)) { 16717c2aad20Sopenharmony_ci s->name_resolved = 1; 16727c2aad20Sopenharmony_ci return orig_name; 16737c2aad20Sopenharmony_ci } 16747c2aad20Sopenharmony_ci 16757c2aad20Sopenharmony_ci dup_cnt = btf_dump_name_dups(d, name_map, orig_name); 16767c2aad20Sopenharmony_ci if (dup_cnt > 1) { 16777c2aad20Sopenharmony_ci const size_t max_len = 256; 16787c2aad20Sopenharmony_ci char new_name[max_len]; 16797c2aad20Sopenharmony_ci 16807c2aad20Sopenharmony_ci snprintf(new_name, max_len, "%s___%zu", orig_name, dup_cnt); 16817c2aad20Sopenharmony_ci *cached_name = strdup(new_name); 16827c2aad20Sopenharmony_ci } 16837c2aad20Sopenharmony_ci 16847c2aad20Sopenharmony_ci s->name_resolved = 1; 16857c2aad20Sopenharmony_ci return *cached_name ? *cached_name : orig_name; 16867c2aad20Sopenharmony_ci} 16877c2aad20Sopenharmony_ci 16887c2aad20Sopenharmony_cistatic const char *btf_dump_type_name(struct btf_dump *d, __u32 id) 16897c2aad20Sopenharmony_ci{ 16907c2aad20Sopenharmony_ci return btf_dump_resolve_name(d, id, d->type_names); 16917c2aad20Sopenharmony_ci} 16927c2aad20Sopenharmony_ci 16937c2aad20Sopenharmony_cistatic const char *btf_dump_ident_name(struct btf_dump *d, __u32 id) 16947c2aad20Sopenharmony_ci{ 16957c2aad20Sopenharmony_ci return btf_dump_resolve_name(d, id, d->ident_names); 16967c2aad20Sopenharmony_ci} 16977c2aad20Sopenharmony_ci 16987c2aad20Sopenharmony_cistatic int btf_dump_dump_type_data(struct btf_dump *d, 16997c2aad20Sopenharmony_ci const char *fname, 17007c2aad20Sopenharmony_ci const struct btf_type *t, 17017c2aad20Sopenharmony_ci __u32 id, 17027c2aad20Sopenharmony_ci const void *data, 17037c2aad20Sopenharmony_ci __u8 bits_offset, 17047c2aad20Sopenharmony_ci __u8 bit_sz); 17057c2aad20Sopenharmony_ci 17067c2aad20Sopenharmony_cistatic const char *btf_dump_data_newline(struct btf_dump *d) 17077c2aad20Sopenharmony_ci{ 17087c2aad20Sopenharmony_ci return d->typed_dump->compact || d->typed_dump->depth == 0 ? "" : "\n"; 17097c2aad20Sopenharmony_ci} 17107c2aad20Sopenharmony_ci 17117c2aad20Sopenharmony_cistatic const char *btf_dump_data_delim(struct btf_dump *d) 17127c2aad20Sopenharmony_ci{ 17137c2aad20Sopenharmony_ci return d->typed_dump->depth == 0 ? "" : ","; 17147c2aad20Sopenharmony_ci} 17157c2aad20Sopenharmony_ci 17167c2aad20Sopenharmony_cistatic void btf_dump_data_pfx(struct btf_dump *d) 17177c2aad20Sopenharmony_ci{ 17187c2aad20Sopenharmony_ci int i, lvl = d->typed_dump->indent_lvl + d->typed_dump->depth; 17197c2aad20Sopenharmony_ci 17207c2aad20Sopenharmony_ci if (d->typed_dump->compact) 17217c2aad20Sopenharmony_ci return; 17227c2aad20Sopenharmony_ci 17237c2aad20Sopenharmony_ci for (i = 0; i < lvl; i++) 17247c2aad20Sopenharmony_ci btf_dump_printf(d, "%s", d->typed_dump->indent_str); 17257c2aad20Sopenharmony_ci} 17267c2aad20Sopenharmony_ci 17277c2aad20Sopenharmony_ci/* A macro is used here as btf_type_value[s]() appends format specifiers 17287c2aad20Sopenharmony_ci * to the format specifier passed in; these do the work of appending 17297c2aad20Sopenharmony_ci * delimiters etc while the caller simply has to specify the type values 17307c2aad20Sopenharmony_ci * in the format specifier + value(s). 17317c2aad20Sopenharmony_ci */ 17327c2aad20Sopenharmony_ci#define btf_dump_type_values(d, fmt, ...) \ 17337c2aad20Sopenharmony_ci btf_dump_printf(d, fmt "%s%s", \ 17347c2aad20Sopenharmony_ci ##__VA_ARGS__, \ 17357c2aad20Sopenharmony_ci btf_dump_data_delim(d), \ 17367c2aad20Sopenharmony_ci btf_dump_data_newline(d)) 17377c2aad20Sopenharmony_ci 17387c2aad20Sopenharmony_cistatic int btf_dump_unsupported_data(struct btf_dump *d, 17397c2aad20Sopenharmony_ci const struct btf_type *t, 17407c2aad20Sopenharmony_ci __u32 id) 17417c2aad20Sopenharmony_ci{ 17427c2aad20Sopenharmony_ci btf_dump_printf(d, "<unsupported kind:%u>", btf_kind(t)); 17437c2aad20Sopenharmony_ci return -ENOTSUP; 17447c2aad20Sopenharmony_ci} 17457c2aad20Sopenharmony_ci 17467c2aad20Sopenharmony_cistatic int btf_dump_get_bitfield_value(struct btf_dump *d, 17477c2aad20Sopenharmony_ci const struct btf_type *t, 17487c2aad20Sopenharmony_ci const void *data, 17497c2aad20Sopenharmony_ci __u8 bits_offset, 17507c2aad20Sopenharmony_ci __u8 bit_sz, 17517c2aad20Sopenharmony_ci __u64 *value) 17527c2aad20Sopenharmony_ci{ 17537c2aad20Sopenharmony_ci __u16 left_shift_bits, right_shift_bits; 17547c2aad20Sopenharmony_ci const __u8 *bytes = data; 17557c2aad20Sopenharmony_ci __u8 nr_copy_bits; 17567c2aad20Sopenharmony_ci __u64 num = 0; 17577c2aad20Sopenharmony_ci int i; 17587c2aad20Sopenharmony_ci 17597c2aad20Sopenharmony_ci /* Maximum supported bitfield size is 64 bits */ 17607c2aad20Sopenharmony_ci if (t->size > 8) { 17617c2aad20Sopenharmony_ci pr_warn("unexpected bitfield size %d\n", t->size); 17627c2aad20Sopenharmony_ci return -EINVAL; 17637c2aad20Sopenharmony_ci } 17647c2aad20Sopenharmony_ci 17657c2aad20Sopenharmony_ci /* Bitfield value retrieval is done in two steps; first relevant bytes are 17667c2aad20Sopenharmony_ci * stored in num, then we left/right shift num to eliminate irrelevant bits. 17677c2aad20Sopenharmony_ci */ 17687c2aad20Sopenharmony_ci#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 17697c2aad20Sopenharmony_ci for (i = t->size - 1; i >= 0; i--) 17707c2aad20Sopenharmony_ci num = num * 256 + bytes[i]; 17717c2aad20Sopenharmony_ci nr_copy_bits = bit_sz + bits_offset; 17727c2aad20Sopenharmony_ci#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 17737c2aad20Sopenharmony_ci for (i = 0; i < t->size; i++) 17747c2aad20Sopenharmony_ci num = num * 256 + bytes[i]; 17757c2aad20Sopenharmony_ci nr_copy_bits = t->size * 8 - bits_offset; 17767c2aad20Sopenharmony_ci#else 17777c2aad20Sopenharmony_ci# error "Unrecognized __BYTE_ORDER__" 17787c2aad20Sopenharmony_ci#endif 17797c2aad20Sopenharmony_ci left_shift_bits = 64 - nr_copy_bits; 17807c2aad20Sopenharmony_ci right_shift_bits = 64 - bit_sz; 17817c2aad20Sopenharmony_ci 17827c2aad20Sopenharmony_ci *value = (num << left_shift_bits) >> right_shift_bits; 17837c2aad20Sopenharmony_ci 17847c2aad20Sopenharmony_ci return 0; 17857c2aad20Sopenharmony_ci} 17867c2aad20Sopenharmony_ci 17877c2aad20Sopenharmony_cistatic int btf_dump_bitfield_check_zero(struct btf_dump *d, 17887c2aad20Sopenharmony_ci const struct btf_type *t, 17897c2aad20Sopenharmony_ci const void *data, 17907c2aad20Sopenharmony_ci __u8 bits_offset, 17917c2aad20Sopenharmony_ci __u8 bit_sz) 17927c2aad20Sopenharmony_ci{ 17937c2aad20Sopenharmony_ci __u64 check_num; 17947c2aad20Sopenharmony_ci int err; 17957c2aad20Sopenharmony_ci 17967c2aad20Sopenharmony_ci err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz, &check_num); 17977c2aad20Sopenharmony_ci if (err) 17987c2aad20Sopenharmony_ci return err; 17997c2aad20Sopenharmony_ci if (check_num == 0) 18007c2aad20Sopenharmony_ci return -ENODATA; 18017c2aad20Sopenharmony_ci return 0; 18027c2aad20Sopenharmony_ci} 18037c2aad20Sopenharmony_ci 18047c2aad20Sopenharmony_cistatic int btf_dump_bitfield_data(struct btf_dump *d, 18057c2aad20Sopenharmony_ci const struct btf_type *t, 18067c2aad20Sopenharmony_ci const void *data, 18077c2aad20Sopenharmony_ci __u8 bits_offset, 18087c2aad20Sopenharmony_ci __u8 bit_sz) 18097c2aad20Sopenharmony_ci{ 18107c2aad20Sopenharmony_ci __u64 print_num; 18117c2aad20Sopenharmony_ci int err; 18127c2aad20Sopenharmony_ci 18137c2aad20Sopenharmony_ci err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz, &print_num); 18147c2aad20Sopenharmony_ci if (err) 18157c2aad20Sopenharmony_ci return err; 18167c2aad20Sopenharmony_ci 18177c2aad20Sopenharmony_ci btf_dump_type_values(d, "0x%llx", (unsigned long long)print_num); 18187c2aad20Sopenharmony_ci 18197c2aad20Sopenharmony_ci return 0; 18207c2aad20Sopenharmony_ci} 18217c2aad20Sopenharmony_ci 18227c2aad20Sopenharmony_ci/* ints, floats and ptrs */ 18237c2aad20Sopenharmony_cistatic int btf_dump_base_type_check_zero(struct btf_dump *d, 18247c2aad20Sopenharmony_ci const struct btf_type *t, 18257c2aad20Sopenharmony_ci __u32 id, 18267c2aad20Sopenharmony_ci const void *data) 18277c2aad20Sopenharmony_ci{ 18287c2aad20Sopenharmony_ci static __u8 bytecmp[16] = {}; 18297c2aad20Sopenharmony_ci int nr_bytes; 18307c2aad20Sopenharmony_ci 18317c2aad20Sopenharmony_ci /* For pointer types, pointer size is not defined on a per-type basis. 18327c2aad20Sopenharmony_ci * On dump creation however, we store the pointer size. 18337c2aad20Sopenharmony_ci */ 18347c2aad20Sopenharmony_ci if (btf_kind(t) == BTF_KIND_PTR) 18357c2aad20Sopenharmony_ci nr_bytes = d->ptr_sz; 18367c2aad20Sopenharmony_ci else 18377c2aad20Sopenharmony_ci nr_bytes = t->size; 18387c2aad20Sopenharmony_ci 18397c2aad20Sopenharmony_ci if (nr_bytes < 1 || nr_bytes > 16) { 18407c2aad20Sopenharmony_ci pr_warn("unexpected size %d for id [%u]\n", nr_bytes, id); 18417c2aad20Sopenharmony_ci return -EINVAL; 18427c2aad20Sopenharmony_ci } 18437c2aad20Sopenharmony_ci 18447c2aad20Sopenharmony_ci if (memcmp(data, bytecmp, nr_bytes) == 0) 18457c2aad20Sopenharmony_ci return -ENODATA; 18467c2aad20Sopenharmony_ci return 0; 18477c2aad20Sopenharmony_ci} 18487c2aad20Sopenharmony_ci 18497c2aad20Sopenharmony_cistatic bool ptr_is_aligned(const struct btf *btf, __u32 type_id, 18507c2aad20Sopenharmony_ci const void *data) 18517c2aad20Sopenharmony_ci{ 18527c2aad20Sopenharmony_ci int alignment = btf__align_of(btf, type_id); 18537c2aad20Sopenharmony_ci 18547c2aad20Sopenharmony_ci if (alignment == 0) 18557c2aad20Sopenharmony_ci return false; 18567c2aad20Sopenharmony_ci 18577c2aad20Sopenharmony_ci return ((uintptr_t)data) % alignment == 0; 18587c2aad20Sopenharmony_ci} 18597c2aad20Sopenharmony_ci 18607c2aad20Sopenharmony_cistatic int btf_dump_int_data(struct btf_dump *d, 18617c2aad20Sopenharmony_ci const struct btf_type *t, 18627c2aad20Sopenharmony_ci __u32 type_id, 18637c2aad20Sopenharmony_ci const void *data, 18647c2aad20Sopenharmony_ci __u8 bits_offset) 18657c2aad20Sopenharmony_ci{ 18667c2aad20Sopenharmony_ci __u8 encoding = btf_int_encoding(t); 18677c2aad20Sopenharmony_ci bool sign = encoding & BTF_INT_SIGNED; 18687c2aad20Sopenharmony_ci char buf[16] __attribute__((aligned(16))); 18697c2aad20Sopenharmony_ci int sz = t->size; 18707c2aad20Sopenharmony_ci 18717c2aad20Sopenharmony_ci if (sz == 0 || sz > sizeof(buf)) { 18727c2aad20Sopenharmony_ci pr_warn("unexpected size %d for id [%u]\n", sz, type_id); 18737c2aad20Sopenharmony_ci return -EINVAL; 18747c2aad20Sopenharmony_ci } 18757c2aad20Sopenharmony_ci 18767c2aad20Sopenharmony_ci /* handle packed int data - accesses of integers not aligned on 18777c2aad20Sopenharmony_ci * int boundaries can cause problems on some platforms. 18787c2aad20Sopenharmony_ci */ 18797c2aad20Sopenharmony_ci if (!ptr_is_aligned(d->btf, type_id, data)) { 18807c2aad20Sopenharmony_ci memcpy(buf, data, sz); 18817c2aad20Sopenharmony_ci data = buf; 18827c2aad20Sopenharmony_ci } 18837c2aad20Sopenharmony_ci 18847c2aad20Sopenharmony_ci switch (sz) { 18857c2aad20Sopenharmony_ci case 16: { 18867c2aad20Sopenharmony_ci const __u64 *ints = data; 18877c2aad20Sopenharmony_ci __u64 lsi, msi; 18887c2aad20Sopenharmony_ci 18897c2aad20Sopenharmony_ci /* avoid use of __int128 as some 32-bit platforms do not 18907c2aad20Sopenharmony_ci * support it. 18917c2aad20Sopenharmony_ci */ 18927c2aad20Sopenharmony_ci#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 18937c2aad20Sopenharmony_ci lsi = ints[0]; 18947c2aad20Sopenharmony_ci msi = ints[1]; 18957c2aad20Sopenharmony_ci#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 18967c2aad20Sopenharmony_ci lsi = ints[1]; 18977c2aad20Sopenharmony_ci msi = ints[0]; 18987c2aad20Sopenharmony_ci#else 18997c2aad20Sopenharmony_ci# error "Unrecognized __BYTE_ORDER__" 19007c2aad20Sopenharmony_ci#endif 19017c2aad20Sopenharmony_ci if (msi == 0) 19027c2aad20Sopenharmony_ci btf_dump_type_values(d, "0x%llx", (unsigned long long)lsi); 19037c2aad20Sopenharmony_ci else 19047c2aad20Sopenharmony_ci btf_dump_type_values(d, "0x%llx%016llx", (unsigned long long)msi, 19057c2aad20Sopenharmony_ci (unsigned long long)lsi); 19067c2aad20Sopenharmony_ci break; 19077c2aad20Sopenharmony_ci } 19087c2aad20Sopenharmony_ci case 8: 19097c2aad20Sopenharmony_ci if (sign) 19107c2aad20Sopenharmony_ci btf_dump_type_values(d, "%lld", *(long long *)data); 19117c2aad20Sopenharmony_ci else 19127c2aad20Sopenharmony_ci btf_dump_type_values(d, "%llu", *(unsigned long long *)data); 19137c2aad20Sopenharmony_ci break; 19147c2aad20Sopenharmony_ci case 4: 19157c2aad20Sopenharmony_ci if (sign) 19167c2aad20Sopenharmony_ci btf_dump_type_values(d, "%d", *(__s32 *)data); 19177c2aad20Sopenharmony_ci else 19187c2aad20Sopenharmony_ci btf_dump_type_values(d, "%u", *(__u32 *)data); 19197c2aad20Sopenharmony_ci break; 19207c2aad20Sopenharmony_ci case 2: 19217c2aad20Sopenharmony_ci if (sign) 19227c2aad20Sopenharmony_ci btf_dump_type_values(d, "%d", *(__s16 *)data); 19237c2aad20Sopenharmony_ci else 19247c2aad20Sopenharmony_ci btf_dump_type_values(d, "%u", *(__u16 *)data); 19257c2aad20Sopenharmony_ci break; 19267c2aad20Sopenharmony_ci case 1: 19277c2aad20Sopenharmony_ci if (d->typed_dump->is_array_char) { 19287c2aad20Sopenharmony_ci /* check for null terminator */ 19297c2aad20Sopenharmony_ci if (d->typed_dump->is_array_terminated) 19307c2aad20Sopenharmony_ci break; 19317c2aad20Sopenharmony_ci if (*(char *)data == '\0') { 19327c2aad20Sopenharmony_ci d->typed_dump->is_array_terminated = true; 19337c2aad20Sopenharmony_ci break; 19347c2aad20Sopenharmony_ci } 19357c2aad20Sopenharmony_ci if (isprint(*(char *)data)) { 19367c2aad20Sopenharmony_ci btf_dump_type_values(d, "'%c'", *(char *)data); 19377c2aad20Sopenharmony_ci break; 19387c2aad20Sopenharmony_ci } 19397c2aad20Sopenharmony_ci } 19407c2aad20Sopenharmony_ci if (sign) 19417c2aad20Sopenharmony_ci btf_dump_type_values(d, "%d", *(__s8 *)data); 19427c2aad20Sopenharmony_ci else 19437c2aad20Sopenharmony_ci btf_dump_type_values(d, "%u", *(__u8 *)data); 19447c2aad20Sopenharmony_ci break; 19457c2aad20Sopenharmony_ci default: 19467c2aad20Sopenharmony_ci pr_warn("unexpected sz %d for id [%u]\n", sz, type_id); 19477c2aad20Sopenharmony_ci return -EINVAL; 19487c2aad20Sopenharmony_ci } 19497c2aad20Sopenharmony_ci return 0; 19507c2aad20Sopenharmony_ci} 19517c2aad20Sopenharmony_ci 19527c2aad20Sopenharmony_ciunion float_data { 19537c2aad20Sopenharmony_ci long double ld; 19547c2aad20Sopenharmony_ci double d; 19557c2aad20Sopenharmony_ci float f; 19567c2aad20Sopenharmony_ci}; 19577c2aad20Sopenharmony_ci 19587c2aad20Sopenharmony_cistatic int btf_dump_float_data(struct btf_dump *d, 19597c2aad20Sopenharmony_ci const struct btf_type *t, 19607c2aad20Sopenharmony_ci __u32 type_id, 19617c2aad20Sopenharmony_ci const void *data) 19627c2aad20Sopenharmony_ci{ 19637c2aad20Sopenharmony_ci const union float_data *flp = data; 19647c2aad20Sopenharmony_ci union float_data fl; 19657c2aad20Sopenharmony_ci int sz = t->size; 19667c2aad20Sopenharmony_ci 19677c2aad20Sopenharmony_ci /* handle unaligned data; copy to local union */ 19687c2aad20Sopenharmony_ci if (!ptr_is_aligned(d->btf, type_id, data)) { 19697c2aad20Sopenharmony_ci memcpy(&fl, data, sz); 19707c2aad20Sopenharmony_ci flp = &fl; 19717c2aad20Sopenharmony_ci } 19727c2aad20Sopenharmony_ci 19737c2aad20Sopenharmony_ci switch (sz) { 19747c2aad20Sopenharmony_ci case 16: 19757c2aad20Sopenharmony_ci btf_dump_type_values(d, "%Lf", flp->ld); 19767c2aad20Sopenharmony_ci break; 19777c2aad20Sopenharmony_ci case 8: 19787c2aad20Sopenharmony_ci btf_dump_type_values(d, "%lf", flp->d); 19797c2aad20Sopenharmony_ci break; 19807c2aad20Sopenharmony_ci case 4: 19817c2aad20Sopenharmony_ci btf_dump_type_values(d, "%f", flp->f); 19827c2aad20Sopenharmony_ci break; 19837c2aad20Sopenharmony_ci default: 19847c2aad20Sopenharmony_ci pr_warn("unexpected size %d for id [%u]\n", sz, type_id); 19857c2aad20Sopenharmony_ci return -EINVAL; 19867c2aad20Sopenharmony_ci } 19877c2aad20Sopenharmony_ci return 0; 19887c2aad20Sopenharmony_ci} 19897c2aad20Sopenharmony_ci 19907c2aad20Sopenharmony_cistatic int btf_dump_var_data(struct btf_dump *d, 19917c2aad20Sopenharmony_ci const struct btf_type *v, 19927c2aad20Sopenharmony_ci __u32 id, 19937c2aad20Sopenharmony_ci const void *data) 19947c2aad20Sopenharmony_ci{ 19957c2aad20Sopenharmony_ci enum btf_func_linkage linkage = btf_var(v)->linkage; 19967c2aad20Sopenharmony_ci const struct btf_type *t; 19977c2aad20Sopenharmony_ci const char *l; 19987c2aad20Sopenharmony_ci __u32 type_id; 19997c2aad20Sopenharmony_ci 20007c2aad20Sopenharmony_ci switch (linkage) { 20017c2aad20Sopenharmony_ci case BTF_FUNC_STATIC: 20027c2aad20Sopenharmony_ci l = "static "; 20037c2aad20Sopenharmony_ci break; 20047c2aad20Sopenharmony_ci case BTF_FUNC_EXTERN: 20057c2aad20Sopenharmony_ci l = "extern "; 20067c2aad20Sopenharmony_ci break; 20077c2aad20Sopenharmony_ci case BTF_FUNC_GLOBAL: 20087c2aad20Sopenharmony_ci default: 20097c2aad20Sopenharmony_ci l = ""; 20107c2aad20Sopenharmony_ci break; 20117c2aad20Sopenharmony_ci } 20127c2aad20Sopenharmony_ci 20137c2aad20Sopenharmony_ci /* format of output here is [linkage] [type] [varname] = (type)value, 20147c2aad20Sopenharmony_ci * for example "static int cpu_profile_flip = (int)1" 20157c2aad20Sopenharmony_ci */ 20167c2aad20Sopenharmony_ci btf_dump_printf(d, "%s", l); 20177c2aad20Sopenharmony_ci type_id = v->type; 20187c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, type_id); 20197c2aad20Sopenharmony_ci btf_dump_emit_type_cast(d, type_id, false); 20207c2aad20Sopenharmony_ci btf_dump_printf(d, " %s = ", btf_name_of(d, v->name_off)); 20217c2aad20Sopenharmony_ci return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0); 20227c2aad20Sopenharmony_ci} 20237c2aad20Sopenharmony_ci 20247c2aad20Sopenharmony_cistatic int btf_dump_array_data(struct btf_dump *d, 20257c2aad20Sopenharmony_ci const struct btf_type *t, 20267c2aad20Sopenharmony_ci __u32 id, 20277c2aad20Sopenharmony_ci const void *data) 20287c2aad20Sopenharmony_ci{ 20297c2aad20Sopenharmony_ci const struct btf_array *array = btf_array(t); 20307c2aad20Sopenharmony_ci const struct btf_type *elem_type; 20317c2aad20Sopenharmony_ci __u32 i, elem_type_id; 20327c2aad20Sopenharmony_ci __s64 elem_size; 20337c2aad20Sopenharmony_ci bool is_array_member; 20347c2aad20Sopenharmony_ci 20357c2aad20Sopenharmony_ci elem_type_id = array->type; 20367c2aad20Sopenharmony_ci elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL); 20377c2aad20Sopenharmony_ci elem_size = btf__resolve_size(d->btf, elem_type_id); 20387c2aad20Sopenharmony_ci if (elem_size <= 0) { 20397c2aad20Sopenharmony_ci pr_warn("unexpected elem size %zd for array type [%u]\n", 20407c2aad20Sopenharmony_ci (ssize_t)elem_size, id); 20417c2aad20Sopenharmony_ci return -EINVAL; 20427c2aad20Sopenharmony_ci } 20437c2aad20Sopenharmony_ci 20447c2aad20Sopenharmony_ci if (btf_is_int(elem_type)) { 20457c2aad20Sopenharmony_ci /* 20467c2aad20Sopenharmony_ci * BTF_INT_CHAR encoding never seems to be set for 20477c2aad20Sopenharmony_ci * char arrays, so if size is 1 and element is 20487c2aad20Sopenharmony_ci * printable as a char, we'll do that. 20497c2aad20Sopenharmony_ci */ 20507c2aad20Sopenharmony_ci if (elem_size == 1) 20517c2aad20Sopenharmony_ci d->typed_dump->is_array_char = true; 20527c2aad20Sopenharmony_ci } 20537c2aad20Sopenharmony_ci 20547c2aad20Sopenharmony_ci /* note that we increment depth before calling btf_dump_print() below; 20557c2aad20Sopenharmony_ci * this is intentional. btf_dump_data_newline() will not print a 20567c2aad20Sopenharmony_ci * newline for depth 0 (since this leaves us with trailing newlines 20577c2aad20Sopenharmony_ci * at the end of typed display), so depth is incremented first. 20587c2aad20Sopenharmony_ci * For similar reasons, we decrement depth before showing the closing 20597c2aad20Sopenharmony_ci * parenthesis. 20607c2aad20Sopenharmony_ci */ 20617c2aad20Sopenharmony_ci d->typed_dump->depth++; 20627c2aad20Sopenharmony_ci btf_dump_printf(d, "[%s", btf_dump_data_newline(d)); 20637c2aad20Sopenharmony_ci 20647c2aad20Sopenharmony_ci /* may be a multidimensional array, so store current "is array member" 20657c2aad20Sopenharmony_ci * status so we can restore it correctly later. 20667c2aad20Sopenharmony_ci */ 20677c2aad20Sopenharmony_ci is_array_member = d->typed_dump->is_array_member; 20687c2aad20Sopenharmony_ci d->typed_dump->is_array_member = true; 20697c2aad20Sopenharmony_ci for (i = 0; i < array->nelems; i++, data += elem_size) { 20707c2aad20Sopenharmony_ci if (d->typed_dump->is_array_terminated) 20717c2aad20Sopenharmony_ci break; 20727c2aad20Sopenharmony_ci btf_dump_dump_type_data(d, NULL, elem_type, elem_type_id, data, 0, 0); 20737c2aad20Sopenharmony_ci } 20747c2aad20Sopenharmony_ci d->typed_dump->is_array_member = is_array_member; 20757c2aad20Sopenharmony_ci d->typed_dump->depth--; 20767c2aad20Sopenharmony_ci btf_dump_data_pfx(d); 20777c2aad20Sopenharmony_ci btf_dump_type_values(d, "]"); 20787c2aad20Sopenharmony_ci 20797c2aad20Sopenharmony_ci return 0; 20807c2aad20Sopenharmony_ci} 20817c2aad20Sopenharmony_ci 20827c2aad20Sopenharmony_cistatic int btf_dump_struct_data(struct btf_dump *d, 20837c2aad20Sopenharmony_ci const struct btf_type *t, 20847c2aad20Sopenharmony_ci __u32 id, 20857c2aad20Sopenharmony_ci const void *data) 20867c2aad20Sopenharmony_ci{ 20877c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 20887c2aad20Sopenharmony_ci __u16 n = btf_vlen(t); 20897c2aad20Sopenharmony_ci int i, err = 0; 20907c2aad20Sopenharmony_ci 20917c2aad20Sopenharmony_ci /* note that we increment depth before calling btf_dump_print() below; 20927c2aad20Sopenharmony_ci * this is intentional. btf_dump_data_newline() will not print a 20937c2aad20Sopenharmony_ci * newline for depth 0 (since this leaves us with trailing newlines 20947c2aad20Sopenharmony_ci * at the end of typed display), so depth is incremented first. 20957c2aad20Sopenharmony_ci * For similar reasons, we decrement depth before showing the closing 20967c2aad20Sopenharmony_ci * parenthesis. 20977c2aad20Sopenharmony_ci */ 20987c2aad20Sopenharmony_ci d->typed_dump->depth++; 20997c2aad20Sopenharmony_ci btf_dump_printf(d, "{%s", btf_dump_data_newline(d)); 21007c2aad20Sopenharmony_ci 21017c2aad20Sopenharmony_ci for (i = 0; i < n; i++, m++) { 21027c2aad20Sopenharmony_ci const struct btf_type *mtype; 21037c2aad20Sopenharmony_ci const char *mname; 21047c2aad20Sopenharmony_ci __u32 moffset; 21057c2aad20Sopenharmony_ci __u8 bit_sz; 21067c2aad20Sopenharmony_ci 21077c2aad20Sopenharmony_ci mtype = btf__type_by_id(d->btf, m->type); 21087c2aad20Sopenharmony_ci mname = btf_name_of(d, m->name_off); 21097c2aad20Sopenharmony_ci moffset = btf_member_bit_offset(t, i); 21107c2aad20Sopenharmony_ci 21117c2aad20Sopenharmony_ci bit_sz = btf_member_bitfield_size(t, i); 21127c2aad20Sopenharmony_ci err = btf_dump_dump_type_data(d, mname, mtype, m->type, data + moffset / 8, 21137c2aad20Sopenharmony_ci moffset % 8, bit_sz); 21147c2aad20Sopenharmony_ci if (err < 0) 21157c2aad20Sopenharmony_ci return err; 21167c2aad20Sopenharmony_ci } 21177c2aad20Sopenharmony_ci d->typed_dump->depth--; 21187c2aad20Sopenharmony_ci btf_dump_data_pfx(d); 21197c2aad20Sopenharmony_ci btf_dump_type_values(d, "}"); 21207c2aad20Sopenharmony_ci return err; 21217c2aad20Sopenharmony_ci} 21227c2aad20Sopenharmony_ci 21237c2aad20Sopenharmony_ciunion ptr_data { 21247c2aad20Sopenharmony_ci unsigned int p; 21257c2aad20Sopenharmony_ci unsigned long long lp; 21267c2aad20Sopenharmony_ci}; 21277c2aad20Sopenharmony_ci 21287c2aad20Sopenharmony_cistatic int btf_dump_ptr_data(struct btf_dump *d, 21297c2aad20Sopenharmony_ci const struct btf_type *t, 21307c2aad20Sopenharmony_ci __u32 id, 21317c2aad20Sopenharmony_ci const void *data) 21327c2aad20Sopenharmony_ci{ 21337c2aad20Sopenharmony_ci if (ptr_is_aligned(d->btf, id, data) && d->ptr_sz == sizeof(void *)) { 21347c2aad20Sopenharmony_ci btf_dump_type_values(d, "%p", *(void **)data); 21357c2aad20Sopenharmony_ci } else { 21367c2aad20Sopenharmony_ci union ptr_data pt; 21377c2aad20Sopenharmony_ci 21387c2aad20Sopenharmony_ci memcpy(&pt, data, d->ptr_sz); 21397c2aad20Sopenharmony_ci if (d->ptr_sz == 4) 21407c2aad20Sopenharmony_ci btf_dump_type_values(d, "0x%x", pt.p); 21417c2aad20Sopenharmony_ci else 21427c2aad20Sopenharmony_ci btf_dump_type_values(d, "0x%llx", pt.lp); 21437c2aad20Sopenharmony_ci } 21447c2aad20Sopenharmony_ci return 0; 21457c2aad20Sopenharmony_ci} 21467c2aad20Sopenharmony_ci 21477c2aad20Sopenharmony_cistatic int btf_dump_get_enum_value(struct btf_dump *d, 21487c2aad20Sopenharmony_ci const struct btf_type *t, 21497c2aad20Sopenharmony_ci const void *data, 21507c2aad20Sopenharmony_ci __u32 id, 21517c2aad20Sopenharmony_ci __s64 *value) 21527c2aad20Sopenharmony_ci{ 21537c2aad20Sopenharmony_ci bool is_signed = btf_kflag(t); 21547c2aad20Sopenharmony_ci 21557c2aad20Sopenharmony_ci if (!ptr_is_aligned(d->btf, id, data)) { 21567c2aad20Sopenharmony_ci __u64 val; 21577c2aad20Sopenharmony_ci int err; 21587c2aad20Sopenharmony_ci 21597c2aad20Sopenharmony_ci err = btf_dump_get_bitfield_value(d, t, data, 0, 0, &val); 21607c2aad20Sopenharmony_ci if (err) 21617c2aad20Sopenharmony_ci return err; 21627c2aad20Sopenharmony_ci *value = (__s64)val; 21637c2aad20Sopenharmony_ci return 0; 21647c2aad20Sopenharmony_ci } 21657c2aad20Sopenharmony_ci 21667c2aad20Sopenharmony_ci switch (t->size) { 21677c2aad20Sopenharmony_ci case 8: 21687c2aad20Sopenharmony_ci *value = *(__s64 *)data; 21697c2aad20Sopenharmony_ci return 0; 21707c2aad20Sopenharmony_ci case 4: 21717c2aad20Sopenharmony_ci *value = is_signed ? (__s64)*(__s32 *)data : *(__u32 *)data; 21727c2aad20Sopenharmony_ci return 0; 21737c2aad20Sopenharmony_ci case 2: 21747c2aad20Sopenharmony_ci *value = is_signed ? *(__s16 *)data : *(__u16 *)data; 21757c2aad20Sopenharmony_ci return 0; 21767c2aad20Sopenharmony_ci case 1: 21777c2aad20Sopenharmony_ci *value = is_signed ? *(__s8 *)data : *(__u8 *)data; 21787c2aad20Sopenharmony_ci return 0; 21797c2aad20Sopenharmony_ci default: 21807c2aad20Sopenharmony_ci pr_warn("unexpected size %d for enum, id:[%u]\n", t->size, id); 21817c2aad20Sopenharmony_ci return -EINVAL; 21827c2aad20Sopenharmony_ci } 21837c2aad20Sopenharmony_ci} 21847c2aad20Sopenharmony_ci 21857c2aad20Sopenharmony_cistatic int btf_dump_enum_data(struct btf_dump *d, 21867c2aad20Sopenharmony_ci const struct btf_type *t, 21877c2aad20Sopenharmony_ci __u32 id, 21887c2aad20Sopenharmony_ci const void *data) 21897c2aad20Sopenharmony_ci{ 21907c2aad20Sopenharmony_ci bool is_signed; 21917c2aad20Sopenharmony_ci __s64 value; 21927c2aad20Sopenharmony_ci int i, err; 21937c2aad20Sopenharmony_ci 21947c2aad20Sopenharmony_ci err = btf_dump_get_enum_value(d, t, data, id, &value); 21957c2aad20Sopenharmony_ci if (err) 21967c2aad20Sopenharmony_ci return err; 21977c2aad20Sopenharmony_ci 21987c2aad20Sopenharmony_ci is_signed = btf_kflag(t); 21997c2aad20Sopenharmony_ci if (btf_is_enum(t)) { 22007c2aad20Sopenharmony_ci const struct btf_enum *e; 22017c2aad20Sopenharmony_ci 22027c2aad20Sopenharmony_ci for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) { 22037c2aad20Sopenharmony_ci if (value != e->val) 22047c2aad20Sopenharmony_ci continue; 22057c2aad20Sopenharmony_ci btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off)); 22067c2aad20Sopenharmony_ci return 0; 22077c2aad20Sopenharmony_ci } 22087c2aad20Sopenharmony_ci 22097c2aad20Sopenharmony_ci btf_dump_type_values(d, is_signed ? "%d" : "%u", value); 22107c2aad20Sopenharmony_ci } else { 22117c2aad20Sopenharmony_ci const struct btf_enum64 *e; 22127c2aad20Sopenharmony_ci 22137c2aad20Sopenharmony_ci for (i = 0, e = btf_enum64(t); i < btf_vlen(t); i++, e++) { 22147c2aad20Sopenharmony_ci if (value != btf_enum64_value(e)) 22157c2aad20Sopenharmony_ci continue; 22167c2aad20Sopenharmony_ci btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off)); 22177c2aad20Sopenharmony_ci return 0; 22187c2aad20Sopenharmony_ci } 22197c2aad20Sopenharmony_ci 22207c2aad20Sopenharmony_ci btf_dump_type_values(d, is_signed ? "%lldLL" : "%lluULL", 22217c2aad20Sopenharmony_ci (unsigned long long)value); 22227c2aad20Sopenharmony_ci } 22237c2aad20Sopenharmony_ci return 0; 22247c2aad20Sopenharmony_ci} 22257c2aad20Sopenharmony_ci 22267c2aad20Sopenharmony_cistatic int btf_dump_datasec_data(struct btf_dump *d, 22277c2aad20Sopenharmony_ci const struct btf_type *t, 22287c2aad20Sopenharmony_ci __u32 id, 22297c2aad20Sopenharmony_ci const void *data) 22307c2aad20Sopenharmony_ci{ 22317c2aad20Sopenharmony_ci const struct btf_var_secinfo *vsi; 22327c2aad20Sopenharmony_ci const struct btf_type *var; 22337c2aad20Sopenharmony_ci __u32 i; 22347c2aad20Sopenharmony_ci int err; 22357c2aad20Sopenharmony_ci 22367c2aad20Sopenharmony_ci btf_dump_type_values(d, "SEC(\"%s\") ", btf_name_of(d, t->name_off)); 22377c2aad20Sopenharmony_ci 22387c2aad20Sopenharmony_ci for (i = 0, vsi = btf_var_secinfos(t); i < btf_vlen(t); i++, vsi++) { 22397c2aad20Sopenharmony_ci var = btf__type_by_id(d->btf, vsi->type); 22407c2aad20Sopenharmony_ci err = btf_dump_dump_type_data(d, NULL, var, vsi->type, data + vsi->offset, 0, 0); 22417c2aad20Sopenharmony_ci if (err < 0) 22427c2aad20Sopenharmony_ci return err; 22437c2aad20Sopenharmony_ci btf_dump_printf(d, ";"); 22447c2aad20Sopenharmony_ci } 22457c2aad20Sopenharmony_ci return 0; 22467c2aad20Sopenharmony_ci} 22477c2aad20Sopenharmony_ci 22487c2aad20Sopenharmony_ci/* return size of type, or if base type overflows, return -E2BIG. */ 22497c2aad20Sopenharmony_cistatic int btf_dump_type_data_check_overflow(struct btf_dump *d, 22507c2aad20Sopenharmony_ci const struct btf_type *t, 22517c2aad20Sopenharmony_ci __u32 id, 22527c2aad20Sopenharmony_ci const void *data, 22537c2aad20Sopenharmony_ci __u8 bits_offset, 22547c2aad20Sopenharmony_ci __u8 bit_sz) 22557c2aad20Sopenharmony_ci{ 22567c2aad20Sopenharmony_ci __s64 size; 22577c2aad20Sopenharmony_ci 22587c2aad20Sopenharmony_ci if (bit_sz) { 22597c2aad20Sopenharmony_ci /* bits_offset is at most 7. bit_sz is at most 128. */ 22607c2aad20Sopenharmony_ci __u8 nr_bytes = (bits_offset + bit_sz + 7) / 8; 22617c2aad20Sopenharmony_ci 22627c2aad20Sopenharmony_ci /* When bit_sz is non zero, it is called from 22637c2aad20Sopenharmony_ci * btf_dump_struct_data() where it only cares about 22647c2aad20Sopenharmony_ci * negative error value. 22657c2aad20Sopenharmony_ci * Return nr_bytes in success case to make it 22667c2aad20Sopenharmony_ci * consistent as the regular integer case below. 22677c2aad20Sopenharmony_ci */ 22687c2aad20Sopenharmony_ci return data + nr_bytes > d->typed_dump->data_end ? -E2BIG : nr_bytes; 22697c2aad20Sopenharmony_ci } 22707c2aad20Sopenharmony_ci 22717c2aad20Sopenharmony_ci size = btf__resolve_size(d->btf, id); 22727c2aad20Sopenharmony_ci 22737c2aad20Sopenharmony_ci if (size < 0 || size >= INT_MAX) { 22747c2aad20Sopenharmony_ci pr_warn("unexpected size [%zu] for id [%u]\n", 22757c2aad20Sopenharmony_ci (size_t)size, id); 22767c2aad20Sopenharmony_ci return -EINVAL; 22777c2aad20Sopenharmony_ci } 22787c2aad20Sopenharmony_ci 22797c2aad20Sopenharmony_ci /* Only do overflow checking for base types; we do not want to 22807c2aad20Sopenharmony_ci * avoid showing part of a struct, union or array, even if we 22817c2aad20Sopenharmony_ci * do not have enough data to show the full object. By 22827c2aad20Sopenharmony_ci * restricting overflow checking to base types we can ensure 22837c2aad20Sopenharmony_ci * that partial display succeeds, while avoiding overflowing 22847c2aad20Sopenharmony_ci * and using bogus data for display. 22857c2aad20Sopenharmony_ci */ 22867c2aad20Sopenharmony_ci t = skip_mods_and_typedefs(d->btf, id, NULL); 22877c2aad20Sopenharmony_ci if (!t) { 22887c2aad20Sopenharmony_ci pr_warn("unexpected error skipping mods/typedefs for id [%u]\n", 22897c2aad20Sopenharmony_ci id); 22907c2aad20Sopenharmony_ci return -EINVAL; 22917c2aad20Sopenharmony_ci } 22927c2aad20Sopenharmony_ci 22937c2aad20Sopenharmony_ci switch (btf_kind(t)) { 22947c2aad20Sopenharmony_ci case BTF_KIND_INT: 22957c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 22967c2aad20Sopenharmony_ci case BTF_KIND_PTR: 22977c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 22987c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 22997c2aad20Sopenharmony_ci if (data + bits_offset / 8 + size > d->typed_dump->data_end) 23007c2aad20Sopenharmony_ci return -E2BIG; 23017c2aad20Sopenharmony_ci break; 23027c2aad20Sopenharmony_ci default: 23037c2aad20Sopenharmony_ci break; 23047c2aad20Sopenharmony_ci } 23057c2aad20Sopenharmony_ci return (int)size; 23067c2aad20Sopenharmony_ci} 23077c2aad20Sopenharmony_ci 23087c2aad20Sopenharmony_cistatic int btf_dump_type_data_check_zero(struct btf_dump *d, 23097c2aad20Sopenharmony_ci const struct btf_type *t, 23107c2aad20Sopenharmony_ci __u32 id, 23117c2aad20Sopenharmony_ci const void *data, 23127c2aad20Sopenharmony_ci __u8 bits_offset, 23137c2aad20Sopenharmony_ci __u8 bit_sz) 23147c2aad20Sopenharmony_ci{ 23157c2aad20Sopenharmony_ci __s64 value; 23167c2aad20Sopenharmony_ci int i, err; 23177c2aad20Sopenharmony_ci 23187c2aad20Sopenharmony_ci /* toplevel exceptions; we show zero values if 23197c2aad20Sopenharmony_ci * - we ask for them (emit_zeros) 23207c2aad20Sopenharmony_ci * - if we are at top-level so we see "struct empty { }" 23217c2aad20Sopenharmony_ci * - or if we are an array member and the array is non-empty and 23227c2aad20Sopenharmony_ci * not a char array; we don't want to be in a situation where we 23237c2aad20Sopenharmony_ci * have an integer array 0, 1, 0, 1 and only show non-zero values. 23247c2aad20Sopenharmony_ci * If the array contains zeroes only, or is a char array starting 23257c2aad20Sopenharmony_ci * with a '\0', the array-level check_zero() will prevent showing it; 23267c2aad20Sopenharmony_ci * we are concerned with determining zero value at the array member 23277c2aad20Sopenharmony_ci * level here. 23287c2aad20Sopenharmony_ci */ 23297c2aad20Sopenharmony_ci if (d->typed_dump->emit_zeroes || d->typed_dump->depth == 0 || 23307c2aad20Sopenharmony_ci (d->typed_dump->is_array_member && 23317c2aad20Sopenharmony_ci !d->typed_dump->is_array_char)) 23327c2aad20Sopenharmony_ci return 0; 23337c2aad20Sopenharmony_ci 23347c2aad20Sopenharmony_ci t = skip_mods_and_typedefs(d->btf, id, NULL); 23357c2aad20Sopenharmony_ci 23367c2aad20Sopenharmony_ci switch (btf_kind(t)) { 23377c2aad20Sopenharmony_ci case BTF_KIND_INT: 23387c2aad20Sopenharmony_ci if (bit_sz) 23397c2aad20Sopenharmony_ci return btf_dump_bitfield_check_zero(d, t, data, bits_offset, bit_sz); 23407c2aad20Sopenharmony_ci return btf_dump_base_type_check_zero(d, t, id, data); 23417c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 23427c2aad20Sopenharmony_ci case BTF_KIND_PTR: 23437c2aad20Sopenharmony_ci return btf_dump_base_type_check_zero(d, t, id, data); 23447c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: { 23457c2aad20Sopenharmony_ci const struct btf_array *array = btf_array(t); 23467c2aad20Sopenharmony_ci const struct btf_type *elem_type; 23477c2aad20Sopenharmony_ci __u32 elem_type_id, elem_size; 23487c2aad20Sopenharmony_ci bool ischar; 23497c2aad20Sopenharmony_ci 23507c2aad20Sopenharmony_ci elem_type_id = array->type; 23517c2aad20Sopenharmony_ci elem_size = btf__resolve_size(d->btf, elem_type_id); 23527c2aad20Sopenharmony_ci elem_type = skip_mods_and_typedefs(d->btf, elem_type_id, NULL); 23537c2aad20Sopenharmony_ci 23547c2aad20Sopenharmony_ci ischar = btf_is_int(elem_type) && elem_size == 1; 23557c2aad20Sopenharmony_ci 23567c2aad20Sopenharmony_ci /* check all elements; if _any_ element is nonzero, all 23577c2aad20Sopenharmony_ci * of array is displayed. We make an exception however 23587c2aad20Sopenharmony_ci * for char arrays where the first element is 0; these 23597c2aad20Sopenharmony_ci * are considered zeroed also, even if later elements are 23607c2aad20Sopenharmony_ci * non-zero because the string is terminated. 23617c2aad20Sopenharmony_ci */ 23627c2aad20Sopenharmony_ci for (i = 0; i < array->nelems; i++) { 23637c2aad20Sopenharmony_ci if (i == 0 && ischar && *(char *)data == 0) 23647c2aad20Sopenharmony_ci return -ENODATA; 23657c2aad20Sopenharmony_ci err = btf_dump_type_data_check_zero(d, elem_type, 23667c2aad20Sopenharmony_ci elem_type_id, 23677c2aad20Sopenharmony_ci data + 23687c2aad20Sopenharmony_ci (i * elem_size), 23697c2aad20Sopenharmony_ci bits_offset, 0); 23707c2aad20Sopenharmony_ci if (err != -ENODATA) 23717c2aad20Sopenharmony_ci return err; 23727c2aad20Sopenharmony_ci } 23737c2aad20Sopenharmony_ci return -ENODATA; 23747c2aad20Sopenharmony_ci } 23757c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 23767c2aad20Sopenharmony_ci case BTF_KIND_UNION: { 23777c2aad20Sopenharmony_ci const struct btf_member *m = btf_members(t); 23787c2aad20Sopenharmony_ci __u16 n = btf_vlen(t); 23797c2aad20Sopenharmony_ci 23807c2aad20Sopenharmony_ci /* if any struct/union member is non-zero, the struct/union 23817c2aad20Sopenharmony_ci * is considered non-zero and dumped. 23827c2aad20Sopenharmony_ci */ 23837c2aad20Sopenharmony_ci for (i = 0; i < n; i++, m++) { 23847c2aad20Sopenharmony_ci const struct btf_type *mtype; 23857c2aad20Sopenharmony_ci __u32 moffset; 23867c2aad20Sopenharmony_ci 23877c2aad20Sopenharmony_ci mtype = btf__type_by_id(d->btf, m->type); 23887c2aad20Sopenharmony_ci moffset = btf_member_bit_offset(t, i); 23897c2aad20Sopenharmony_ci 23907c2aad20Sopenharmony_ci /* btf_int_bits() does not store member bitfield size; 23917c2aad20Sopenharmony_ci * bitfield size needs to be stored here so int display 23927c2aad20Sopenharmony_ci * of member can retrieve it. 23937c2aad20Sopenharmony_ci */ 23947c2aad20Sopenharmony_ci bit_sz = btf_member_bitfield_size(t, i); 23957c2aad20Sopenharmony_ci err = btf_dump_type_data_check_zero(d, mtype, m->type, data + moffset / 8, 23967c2aad20Sopenharmony_ci moffset % 8, bit_sz); 23977c2aad20Sopenharmony_ci if (err != ENODATA) 23987c2aad20Sopenharmony_ci return err; 23997c2aad20Sopenharmony_ci } 24007c2aad20Sopenharmony_ci return -ENODATA; 24017c2aad20Sopenharmony_ci } 24027c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 24037c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 24047c2aad20Sopenharmony_ci err = btf_dump_get_enum_value(d, t, data, id, &value); 24057c2aad20Sopenharmony_ci if (err) 24067c2aad20Sopenharmony_ci return err; 24077c2aad20Sopenharmony_ci if (value == 0) 24087c2aad20Sopenharmony_ci return -ENODATA; 24097c2aad20Sopenharmony_ci return 0; 24107c2aad20Sopenharmony_ci default: 24117c2aad20Sopenharmony_ci return 0; 24127c2aad20Sopenharmony_ci } 24137c2aad20Sopenharmony_ci} 24147c2aad20Sopenharmony_ci 24157c2aad20Sopenharmony_ci/* returns size of data dumped, or error. */ 24167c2aad20Sopenharmony_cistatic int btf_dump_dump_type_data(struct btf_dump *d, 24177c2aad20Sopenharmony_ci const char *fname, 24187c2aad20Sopenharmony_ci const struct btf_type *t, 24197c2aad20Sopenharmony_ci __u32 id, 24207c2aad20Sopenharmony_ci const void *data, 24217c2aad20Sopenharmony_ci __u8 bits_offset, 24227c2aad20Sopenharmony_ci __u8 bit_sz) 24237c2aad20Sopenharmony_ci{ 24247c2aad20Sopenharmony_ci int size, err = 0; 24257c2aad20Sopenharmony_ci 24267c2aad20Sopenharmony_ci size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset, bit_sz); 24277c2aad20Sopenharmony_ci if (size < 0) 24287c2aad20Sopenharmony_ci return size; 24297c2aad20Sopenharmony_ci err = btf_dump_type_data_check_zero(d, t, id, data, bits_offset, bit_sz); 24307c2aad20Sopenharmony_ci if (err) { 24317c2aad20Sopenharmony_ci /* zeroed data is expected and not an error, so simply skip 24327c2aad20Sopenharmony_ci * dumping such data. Record other errors however. 24337c2aad20Sopenharmony_ci */ 24347c2aad20Sopenharmony_ci if (err == -ENODATA) 24357c2aad20Sopenharmony_ci return size; 24367c2aad20Sopenharmony_ci return err; 24377c2aad20Sopenharmony_ci } 24387c2aad20Sopenharmony_ci btf_dump_data_pfx(d); 24397c2aad20Sopenharmony_ci 24407c2aad20Sopenharmony_ci if (!d->typed_dump->skip_names) { 24417c2aad20Sopenharmony_ci if (fname && strlen(fname) > 0) 24427c2aad20Sopenharmony_ci btf_dump_printf(d, ".%s = ", fname); 24437c2aad20Sopenharmony_ci btf_dump_emit_type_cast(d, id, true); 24447c2aad20Sopenharmony_ci } 24457c2aad20Sopenharmony_ci 24467c2aad20Sopenharmony_ci t = skip_mods_and_typedefs(d->btf, id, NULL); 24477c2aad20Sopenharmony_ci 24487c2aad20Sopenharmony_ci switch (btf_kind(t)) { 24497c2aad20Sopenharmony_ci case BTF_KIND_UNKN: 24507c2aad20Sopenharmony_ci case BTF_KIND_FWD: 24517c2aad20Sopenharmony_ci case BTF_KIND_FUNC: 24527c2aad20Sopenharmony_ci case BTF_KIND_FUNC_PROTO: 24537c2aad20Sopenharmony_ci case BTF_KIND_DECL_TAG: 24547c2aad20Sopenharmony_ci err = btf_dump_unsupported_data(d, t, id); 24557c2aad20Sopenharmony_ci break; 24567c2aad20Sopenharmony_ci case BTF_KIND_INT: 24577c2aad20Sopenharmony_ci if (bit_sz) 24587c2aad20Sopenharmony_ci err = btf_dump_bitfield_data(d, t, data, bits_offset, bit_sz); 24597c2aad20Sopenharmony_ci else 24607c2aad20Sopenharmony_ci err = btf_dump_int_data(d, t, id, data, bits_offset); 24617c2aad20Sopenharmony_ci break; 24627c2aad20Sopenharmony_ci case BTF_KIND_FLOAT: 24637c2aad20Sopenharmony_ci err = btf_dump_float_data(d, t, id, data); 24647c2aad20Sopenharmony_ci break; 24657c2aad20Sopenharmony_ci case BTF_KIND_PTR: 24667c2aad20Sopenharmony_ci err = btf_dump_ptr_data(d, t, id, data); 24677c2aad20Sopenharmony_ci break; 24687c2aad20Sopenharmony_ci case BTF_KIND_ARRAY: 24697c2aad20Sopenharmony_ci err = btf_dump_array_data(d, t, id, data); 24707c2aad20Sopenharmony_ci break; 24717c2aad20Sopenharmony_ci case BTF_KIND_STRUCT: 24727c2aad20Sopenharmony_ci case BTF_KIND_UNION: 24737c2aad20Sopenharmony_ci err = btf_dump_struct_data(d, t, id, data); 24747c2aad20Sopenharmony_ci break; 24757c2aad20Sopenharmony_ci case BTF_KIND_ENUM: 24767c2aad20Sopenharmony_ci case BTF_KIND_ENUM64: 24777c2aad20Sopenharmony_ci /* handle bitfield and int enum values */ 24787c2aad20Sopenharmony_ci if (bit_sz) { 24797c2aad20Sopenharmony_ci __u64 print_num; 24807c2aad20Sopenharmony_ci __s64 enum_val; 24817c2aad20Sopenharmony_ci 24827c2aad20Sopenharmony_ci err = btf_dump_get_bitfield_value(d, t, data, bits_offset, bit_sz, 24837c2aad20Sopenharmony_ci &print_num); 24847c2aad20Sopenharmony_ci if (err) 24857c2aad20Sopenharmony_ci break; 24867c2aad20Sopenharmony_ci enum_val = (__s64)print_num; 24877c2aad20Sopenharmony_ci err = btf_dump_enum_data(d, t, id, &enum_val); 24887c2aad20Sopenharmony_ci } else 24897c2aad20Sopenharmony_ci err = btf_dump_enum_data(d, t, id, data); 24907c2aad20Sopenharmony_ci break; 24917c2aad20Sopenharmony_ci case BTF_KIND_VAR: 24927c2aad20Sopenharmony_ci err = btf_dump_var_data(d, t, id, data); 24937c2aad20Sopenharmony_ci break; 24947c2aad20Sopenharmony_ci case BTF_KIND_DATASEC: 24957c2aad20Sopenharmony_ci err = btf_dump_datasec_data(d, t, id, data); 24967c2aad20Sopenharmony_ci break; 24977c2aad20Sopenharmony_ci default: 24987c2aad20Sopenharmony_ci pr_warn("unexpected kind [%u] for id [%u]\n", 24997c2aad20Sopenharmony_ci BTF_INFO_KIND(t->info), id); 25007c2aad20Sopenharmony_ci return -EINVAL; 25017c2aad20Sopenharmony_ci } 25027c2aad20Sopenharmony_ci if (err < 0) 25037c2aad20Sopenharmony_ci return err; 25047c2aad20Sopenharmony_ci return size; 25057c2aad20Sopenharmony_ci} 25067c2aad20Sopenharmony_ci 25077c2aad20Sopenharmony_ciint btf_dump__dump_type_data(struct btf_dump *d, __u32 id, 25087c2aad20Sopenharmony_ci const void *data, size_t data_sz, 25097c2aad20Sopenharmony_ci const struct btf_dump_type_data_opts *opts) 25107c2aad20Sopenharmony_ci{ 25117c2aad20Sopenharmony_ci struct btf_dump_data typed_dump = {}; 25127c2aad20Sopenharmony_ci const struct btf_type *t; 25137c2aad20Sopenharmony_ci int ret; 25147c2aad20Sopenharmony_ci 25157c2aad20Sopenharmony_ci if (!OPTS_VALID(opts, btf_dump_type_data_opts)) 25167c2aad20Sopenharmony_ci return libbpf_err(-EINVAL); 25177c2aad20Sopenharmony_ci 25187c2aad20Sopenharmony_ci t = btf__type_by_id(d->btf, id); 25197c2aad20Sopenharmony_ci if (!t) 25207c2aad20Sopenharmony_ci return libbpf_err(-ENOENT); 25217c2aad20Sopenharmony_ci 25227c2aad20Sopenharmony_ci d->typed_dump = &typed_dump; 25237c2aad20Sopenharmony_ci d->typed_dump->data_end = data + data_sz; 25247c2aad20Sopenharmony_ci d->typed_dump->indent_lvl = OPTS_GET(opts, indent_level, 0); 25257c2aad20Sopenharmony_ci 25267c2aad20Sopenharmony_ci /* default indent string is a tab */ 25277c2aad20Sopenharmony_ci if (!OPTS_GET(opts, indent_str, NULL)) 25287c2aad20Sopenharmony_ci d->typed_dump->indent_str[0] = '\t'; 25297c2aad20Sopenharmony_ci else 25307c2aad20Sopenharmony_ci libbpf_strlcpy(d->typed_dump->indent_str, opts->indent_str, 25317c2aad20Sopenharmony_ci sizeof(d->typed_dump->indent_str)); 25327c2aad20Sopenharmony_ci 25337c2aad20Sopenharmony_ci d->typed_dump->compact = OPTS_GET(opts, compact, false); 25347c2aad20Sopenharmony_ci d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false); 25357c2aad20Sopenharmony_ci d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false); 25367c2aad20Sopenharmony_ci 25377c2aad20Sopenharmony_ci ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0); 25387c2aad20Sopenharmony_ci 25397c2aad20Sopenharmony_ci d->typed_dump = NULL; 25407c2aad20Sopenharmony_ci 25417c2aad20Sopenharmony_ci return libbpf_err(ret); 25427c2aad20Sopenharmony_ci} 2543