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:
Yonghong Song 2018-12-18 14:09:42 -08:00 committed by Arnaldo Carvalho de Melo
parent 8630ce4042
commit 3aa3fd506e
4 changed files with 128 additions and 20 deletions

15
btf.h
View File

@ -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__ */

View File

@ -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
View File

@ -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, &param, 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;

View File

@ -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);