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 <yhs@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexei Starovoitov <ast@fb.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
8630ce4042
commit
3aa3fd506e
15
btf.h
15
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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
125
libbtf.c
125
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;
|
||||
|
|
3
libbtf.h
3
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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue