From 3aa3fd506e6c8293e6f15798db84886cd77a7b3b Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Tue, 18 Dec 2018 14:09:42 -0800 Subject: [PATCH] btf: add func_proto support Two new btf kinds, BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO, have been added in kernel since https://patchwork.ozlabs.org/cover/1000176/ to support better func introspection. Currently, for a DW_TAG_subroutine_type dwarf type, a simple "void *" is generated instead of real subroutine type. This patch teaches pahole to generate BTF_KIND_FUNC_PROTO properly. After this patch, pahole should have complete type coverage for C frontend with types a bpf program cares. For example, $ cat t1.c typedef int __int32; struct t1 { int a1; int (*f1)(char p1, __int32 p2); } g1; $ cat t2.c typedef int __int32; struct t2 { int a2; int (*f2)(char q1, __int32 q2, ...); int (*f3)(); } g2; int main() { return 0; } $ gcc -O2 -o t1 -g t1.c t2.c $ pahole -JV t1 File t1: [1] TYPEDEF __int32 type_id=2 [2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED [3] STRUCT t1 kind_flag=0 size=16 vlen=2 a1 type_id=2 bits_offset=0 f1 type_id=6 bits_offset=64 [4] FUNC_PROTO (anon) return=2 args=(5 (anon), 1 (anon)) [5] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none) [6] PTR (anon) type_id=4 [7] TYPEDEF __int32 type_id=8 [8] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED [9] STRUCT t2 kind_flag=0 size=24 vlen=3 a2 type_id=8 bits_offset=0 f2 type_id=12 bits_offset=64 f3 type_id=14 bits_offset=128 [10] FUNC_PROTO (anon) return=8 args=(11 (anon), 7 (anon), vararg) [11] INT char size=1 bit_offset=0 nr_bits=8 encoding=(none) [12] PTR (anon) type_id=10 [13] FUNC_PROTO (anon) return=8 args=(vararg) [14] PTR (anon) type_id=13 $ In the above example, type [4], [10] and [13] represent the func_proto types. BTF_KIND_FUNC, which represents a real subprogram, is not generated in this patch and will be considered later. Signed-off-by: Yonghong Song Acked-by: Martin KaFai Lau Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Signed-off-by: Arnaldo Carvalho de Melo --- btf.h | 15 +++++- btf_encoder.c | 5 +- libbtf.c | 125 ++++++++++++++++++++++++++++++++++++++++++++------ libbtf.h | 3 ++ 4 files changed, 128 insertions(+), 20 deletions(-) diff --git a/btf.h b/btf.h index 488ea2b..d7ca45c 100644 --- a/btf.h +++ b/btf.h @@ -67,8 +67,10 @@ struct btf_type { #define BTF_KIND_VOLATILE 9 /* Volatile */ #define BTF_KIND_CONST 10 /* Const */ #define BTF_KIND_RESTRICT 11 /* Restrict */ -#define BTF_KIND_MAX 11 -#define NR_BTF_KINDS 12 +#define BTF_KIND_FUNC 12 /* Function */ +#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */ +#define BTF_KIND_MAX 13 +#define NR_BTF_KINDS 14 /* For some specific BTF_KIND, "struct btf_type" is immediately * followed by extra data. @@ -126,4 +128,13 @@ struct btf_member { #define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24) #define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff) +/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". + * The exact number of btf_param is stored in the vlen (of the + * info in "struct btf_type"). + */ +struct btf_param { + __u32 name_off; + __u32 type; +}; + #endif /* _UAPI__LINUX_BTF_H__ */ diff --git a/btf_encoder.c b/btf_encoder.c index 91ef95c..cb378b2 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -170,10 +170,7 @@ static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf *btf, case DW_TAG_enumeration_type: return enumeration_type__encode(btf, tag); case DW_TAG_subroutine_type: - /* A dummy void * to avoid a shift in btf->type_index */ - btf_verbose_log("Filling unsupported DW_TAG_%s(0x%x) with void *\n", - dwarf_tag_name(tag->tag), tag->tag); - return btf__add_ref_type(btf, BTF_KIND_PTR, 0, 0, false); + return btf__add_func_proto(btf, tag__ftype(tag), type_id_off); default: fprintf(stderr, "Unsupported DW_TAG_%s(0x%x)\n", dwarf_tag_name(tag->tag), tag->tag); diff --git a/libbtf.c b/libbtf.c index af97deb..04cf6a3 100644 --- a/libbtf.c +++ b/libbtf.c @@ -142,6 +142,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = { [BTF_KIND_VOLATILE] = "VOLATILE", [BTF_KIND_CONST] = "CONST", [BTF_KIND_RESTRICT] = "RESTRICT", + [BTF_KIND_FUNC] = "FUNC", + [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", }; static const char *btf__name_in_gobuf(const struct btf *btf, @@ -167,9 +169,9 @@ static const char * btf__int_encoding_str(uint8_t encoding) return "UNKN"; } -__attribute ((format (printf, 4, 5))) +__attribute ((format (printf, 5, 6))) static void btf__log_type(const struct btf *btf, const struct btf_type *t, - bool err, const char *fmt, ...) + bool err, bool output_cr, const char *fmt, ...) { uint8_t kind; FILE *out; @@ -193,7 +195,8 @@ static void btf__log_type(const struct btf *btf, const struct btf_type *t, va_end(ap); } - fprintf(out, "\n"); + if (output_cr) + fprintf(out, "\n"); } __attribute ((format (printf, 5, 6))) @@ -232,6 +235,36 @@ static void btf_log_member(const struct btf *btf, fprintf(out, "\n"); } +__attribute ((format (printf, 6, 7))) +static void btf_log_func_param(const struct btf *btf, + uint32_t name_off, uint32_t type, + bool err, bool is_last_param, + const char *fmt, ...) +{ + FILE *out; + + if (!btf_verbose && !err) + return; + + out = err ? stderr : stdout; + + if (is_last_param && !type) + fprintf(out, "vararg)\n"); + else + fprintf(out, "%u %s%s", type, + btf__name_in_gobuf(btf, name_off), + is_last_param ? ")\n" : ", "); + + if (fmt && *fmt) { + va_list ap; + + fprintf(out, " "); + va_start(ap, fmt); + vfprintf(out, fmt, ap); + va_end(ap); + } +} + int32_t btf__add_base_type(struct btf *btf, const struct base_type *bt) { struct btf_int_type int_type; @@ -253,14 +286,14 @@ int32_t btf__add_base_type(struct btf *btf, const struct base_type *bt) ++btf->type_index; if (gobuffer__add(&btf->types, &int_type, sizeof(int_type)) >= 0) { - btf__log_type(btf, t, false, + btf__log_type(btf, t, false, true, "size=%u bit_offset=%u nr_bits=%u encoding=%s", t->size, BTF_INT_OFFSET(int_type.data), BTF_INT_BITS(int_type.data), btf__int_encoding_str(BTF_INT_ENCODING(int_type.data))); return btf->type_index; } else { - btf__log_type(btf, t, true, + btf__log_type(btf, t, true, true, "size=%u bit_offset=%u nr_bits=%u encoding=%s Error in adding gobuffer", t->size, BTF_INT_OFFSET(int_type.data), BTF_INT_BITS(int_type.data), @@ -281,13 +314,13 @@ int32_t btf__add_ref_type(struct btf *btf, uint16_t kind, uint32_t type, ++btf->type_index; if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) { if (kind == BTF_KIND_FWD) - btf__log_type(btf, &t, false, "%s", + btf__log_type(btf, &t, false, true, "%s", kind_flag ? "union" : "struct"); else - btf__log_type(btf, &t, false, "type_id=%u", t.type); + btf__log_type(btf, &t, false, true, "type_id=%u", t.type); return btf->type_index; } else { - btf__log_type(btf, &t, true, + btf__log_type(btf, &t, true, true, "kind_flag=%d type_id=%u Error in adding gobuffer", kind_flag, t.type); return -1; @@ -311,12 +344,12 @@ int32_t btf__add_array(struct btf *btf, uint32_t type, uint32_t index_type, ++btf->type_index; if (gobuffer__add(&btf->types, &array_type, sizeof(array_type)) >= 0) { - btf__log_type(btf, t, false, + btf__log_type(btf, t, false, true, "type_id=%u index_type_id=%u nr_elems=%u", array->type, array->index_type, array->nelems); return btf->type_index; } else { - btf__log_type(btf, t, true, + btf__log_type(btf, t, true, true, "type_id=%u index_type_id=%u nr_elems=%u Error in adding gobuffer", array->type, array->index_type, array->nelems); return -1; @@ -352,11 +385,11 @@ int32_t btf__add_struct(struct btf *btf, uint8_t kind, uint32_t name, ++btf->type_index; if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) { - btf__log_type(btf, &t, false, "kind_flag=%d size=%u vlen=%u", + btf__log_type(btf, &t, false, true, "kind_flag=%d size=%u vlen=%u", kind_flag, t.size, BTF_INFO_VLEN(t.info)); return btf->type_index; } else { - btf__log_type(btf, &t, true, + btf__log_type(btf, &t, true, true, "kind_flag=%d size=%u vlen=%u Error in adding gobuffer", kind_flag, t.size, BTF_INFO_VLEN(t.info)); return -1; @@ -374,11 +407,11 @@ int32_t btf__add_enum(struct btf *btf, uint32_t name, uint32_t bit_size, ++btf->type_index; if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) { - btf__log_type(btf, &t, false, "size=%u vlen=%u", + btf__log_type(btf, &t, false, true, "size=%u vlen=%u", t.size, BTF_INFO_VLEN(t.info)); return btf->type_index; } else { - btf__log_type(btf, &t, true, + btf__log_type(btf, &t, true, true, "size=%u vlen=%u Error in adding gobuffer", t.size, BTF_INFO_VLEN(t.info)); return -1; @@ -403,6 +436,70 @@ int btf__add_enum_val(struct btf *btf, uint32_t name, int32_t value) return 0; } +static int32_t btf__add_func_proto_param(struct btf *btf, uint32_t name, + uint32_t type, bool is_last_param) +{ + struct btf_param param; + + param.name_off = name; + param.type = type; + + if (gobuffer__add(&btf->types, ¶m, sizeof(param)) >= 0) { + btf_log_func_param(btf, name, type, false, is_last_param, NULL); + return 0; + } else { + btf_log_func_param(btf, name, type, true, is_last_param, + "Error in adding gobuffer"); + return -1; + } +} + +int32_t btf__add_func_proto(struct btf *btf, struct ftype *ftype, + uint32_t type_id_off) +{ + uint16_t nr_params, param_idx; + struct parameter *param; + struct btf_type t; + int32_t type_id; + + /* add btf_type for func_proto */ + nr_params = ftype->nr_parms + (ftype->unspec_parms ? 1 : 0); + + t.name_off = 0; + t.info = BTF_INFO_ENCODE(BTF_KIND_FUNC_PROTO, 0, nr_params); + t.type = type_id_off + ftype->tag.type; + + ++btf->type_index; + if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) { + btf__log_type(btf, &t, false, false, "return=%u args=(%s", + t.type, !nr_params ? "void)\n" : ""); + type_id = btf->type_index; + } else { + btf__log_type(btf, &t, true, true, + "return=%u vlen=%u Error in adding gobuffer", + t.type, BTF_INFO_VLEN(t.info)); + return -1; + } + + /* add parameters */ + param_idx = 0; + ftype__for_each_parameter(ftype, param) { + ++param_idx; + if (btf__add_func_proto_param(btf, param->name, + type_id_off + param->tag.type, + param_idx == nr_params)) + return -1; + } + + ++param_idx; + if (ftype->unspec_parms) + if (btf__add_func_proto_param(btf, 0, 0, + param_idx == nr_params)) + return -1; + + return type_id; +} + static int btf__write_elf(struct btf *btf) { GElf_Shdr shdr_mem, *shdr; diff --git a/libbtf.h b/libbtf.h index 13ecb42..28c6bcb 100644 --- a/libbtf.h +++ b/libbtf.h @@ -29,6 +29,7 @@ extern uint8_t btf_verbose; #define btf_verbose_log(fmt, ...) { if (btf_verbose) printf(fmt, __VA_ARGS__); } struct base_type; +struct ftype; struct btf *btf__new(const char *filename, Elf *elf); void btf__free(struct btf *btf); @@ -45,6 +46,8 @@ int32_t btf__add_array(struct btf *btf, uint32_t type, uint32_t index_type, int32_t btf__add_enum(struct btf *btf, uint32_t name, uint32_t size, uint16_t nr_entries); int btf__add_enum_val(struct btf *btf, uint32_t name, int32_t value); +int32_t btf__add_func_proto(struct btf *btf, struct ftype *ftype, + uint32_t type_id_off); void btf__set_strings(struct btf *btf, struct gobuffer *strings); int btf__encode(struct btf *btf, uint8_t flags);