btf: Add BTF support
This patch introduces BPF Type Format (BTF). BTF (BPF Type Format) is the meta data format which describes the data types of BPF program/map. Hence, it basically focus on the C programming language which the modern BPF is primary using. The first use case is to provide a generic pretty print capability for a BPF map. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
81466af0d4
commit
68645f7fac
|
@ -34,8 +34,8 @@ find_package(ZLIB REQUIRED)
|
|||
_set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}${CMAKE_INSTALL_PREFIX}/${__LIB}" "libdir")
|
||||
|
||||
set(dwarves_LIB_SRCS dwarves.c dwarves_fprintf.c gobuffer strings
|
||||
ctf_encoder.c ctf_loader.c libctf.c dwarf_loader.c
|
||||
dutil.c elf_symtab.c rbtree.c)
|
||||
ctf_encoder.c ctf_loader.c libctf.c btf_encoder.c libbtf.c
|
||||
dwarf_loader.c dutil.c elf_symtab.c rbtree.c)
|
||||
add_library(dwarves SHARED ${dwarves_LIB_SRCS})
|
||||
set_target_properties(dwarves PROPERTIES VERSION 1.0.0 SOVERSION 1)
|
||||
set_target_properties(dwarves PROPERTIES INTERFACE_LINK_LIBRARIES "")
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef _UAPI__LINUX_BTF_H__
|
||||
#define _UAPI__LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_VERSION 1
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
__u32 hdr_len;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 type_len; /* length of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x0000ffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x0000ffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
struct btf_type {
|
||||
__u32 name_off;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-27: kind (e.g. int, ptr, array...etc)
|
||||
* bits 28-31: unused
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
|
||||
#define BTF_KIND_UNKN 0 /* Unknown */
|
||||
#define BTF_KIND_INT 1 /* Integer */
|
||||
#define BTF_KIND_PTR 2 /* Pointer */
|
||||
#define BTF_KIND_ARRAY 3 /* Array */
|
||||
#define BTF_KIND_STRUCT 4 /* Struct */
|
||||
#define BTF_KIND_UNION 5 /* Union */
|
||||
#define BTF_KIND_ENUM 6 /* Enumeration */
|
||||
#define BTF_KIND_FWD 7 /* Forward */
|
||||
#define BTF_KIND_TYPEDEF 8 /* Typedef */
|
||||
#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
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL & 0x00ff0000)) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x0000ffff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED (1 << 0)
|
||||
#define BTF_INT_CHAR (1 << 1)
|
||||
#define BTF_INT_BOOL (1 << 2)
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name_off;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
__u32 offset; /* offset in bits */
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
|
@ -0,0 +1,174 @@
|
|||
#include "dwarves.h"
|
||||
#include "libbtf.h"
|
||||
#include "btf.h"
|
||||
#include "hash.h"
|
||||
#include "elf_symtab.h"
|
||||
#include "btf_encoder.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static int tag__check_id_drift(const struct tag *tag,
|
||||
uint32_t core_id, uint32_t btf_type_id)
|
||||
{
|
||||
if (btf_type_id != core_id) {
|
||||
fprintf(stderr, "%s: %s id drift, core_id: %u, btf_type_id: %u\n",
|
||||
__func__, dwarf_tag_name(tag->tag),
|
||||
core_id, btf_type_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t structure_type__encode(struct btf *btf, struct tag *tag)
|
||||
{
|
||||
struct type *type = tag__type(tag);
|
||||
struct class_member *pos;
|
||||
int32_t type_id;
|
||||
uint8_t kind;
|
||||
|
||||
kind = (tag->tag == DW_TAG_union_type) ?
|
||||
BTF_KIND_UNION : BTF_KIND_STRUCT;
|
||||
type_id = btf__add_struct(btf, kind, type->namespace.name,
|
||||
type->size, type->nr_members);
|
||||
if (type_id < 0)
|
||||
return type_id;
|
||||
|
||||
type__for_each_data_member(type, pos)
|
||||
if (btf__add_member(btf, pos->name, pos->tag.type,
|
||||
pos->bit_offset))
|
||||
return -1;
|
||||
|
||||
return type_id;
|
||||
}
|
||||
|
||||
static uint32_t array_type__nelems(struct tag *tag)
|
||||
{
|
||||
int i;
|
||||
uint32_t nelem = 1;
|
||||
struct array_type *array = tag__array_type(tag);
|
||||
|
||||
for (i = array->dimensions - 1; i >= 0; --i)
|
||||
nelem *= array->nr_entries[i];
|
||||
|
||||
return nelem;
|
||||
}
|
||||
|
||||
static int32_t enumeration_type__encode(struct btf *btf, struct tag *tag)
|
||||
{
|
||||
struct type *etype = tag__type(tag);
|
||||
struct enumerator *pos;
|
||||
int32_t type_id;
|
||||
|
||||
type_id = btf__add_enum(btf, etype->namespace.name,
|
||||
etype->size, etype->nr_members);
|
||||
if (type_id < 0)
|
||||
return type_id;
|
||||
|
||||
type__for_each_enumerator(etype, pos)
|
||||
if (btf__add_enum_val(btf, pos->name, pos->value))
|
||||
return -1;
|
||||
|
||||
return type_id;
|
||||
}
|
||||
|
||||
static int tag__encode_btf(struct tag *tag, uint32_t core_id, struct btf *btf,
|
||||
uint32_t array_index_id)
|
||||
{
|
||||
switch (tag->tag) {
|
||||
case DW_TAG_base_type:
|
||||
return btf__add_base_type(btf, tag__base_type(tag));
|
||||
case DW_TAG_const_type:
|
||||
return btf__add_ref_type(btf, BTF_KIND_CONST, tag->type, 0);
|
||||
case DW_TAG_pointer_type:
|
||||
return btf__add_ref_type(btf, BTF_KIND_PTR, tag->type, 0);
|
||||
case DW_TAG_restrict_type:
|
||||
return btf__add_ref_type(btf, BTF_KIND_RESTRICT, tag->type, 0);
|
||||
case DW_TAG_volatile_type:
|
||||
return btf__add_ref_type(btf, BTF_KIND_VOLATILE, tag->type, 0);
|
||||
case DW_TAG_typedef:
|
||||
return btf__add_ref_type(btf, BTF_KIND_TYPEDEF, tag->type,
|
||||
tag__namespace(tag)->name);
|
||||
case DW_TAG_structure_type:
|
||||
case DW_TAG_union_type:
|
||||
case DW_TAG_class_type:
|
||||
if (tag__type(tag)->declaration)
|
||||
return btf__add_ref_type(btf, BTF_KIND_FWD, 0,
|
||||
tag__namespace(tag)->name);
|
||||
else
|
||||
return structure_type__encode(btf, tag);
|
||||
case DW_TAG_array_type:
|
||||
return btf__add_array(btf, tag->type, array_index_id,
|
||||
/*TODO: Encode one dimension
|
||||
* at a time.
|
||||
*/
|
||||
array_type__nelems(tag));
|
||||
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);
|
||||
default:
|
||||
fprintf(stderr, "Unsupported DW_TAG_%s(0x%x)\n",
|
||||
dwarf_tag_name(tag->tag), tag->tag);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Its in the DWARF loader, we have to find a better handoff
|
||||
* mechanizm...
|
||||
*/
|
||||
extern struct strings *strings;
|
||||
|
||||
int cu__encode_btf(struct cu *cu, int verbose)
|
||||
{
|
||||
struct btf *btf = btf__new(cu->filename, cu->elf);
|
||||
struct tag *pos;
|
||||
uint32_t core_id, array_index_id;
|
||||
uint16_t id;
|
||||
int err;
|
||||
|
||||
btf_verbose = verbose;
|
||||
|
||||
if (btf == NULL)
|
||||
return -1;
|
||||
|
||||
btf__set_strings(btf, &strings->gb);
|
||||
|
||||
/* cu__find_base_type_by_name() takes "uint16_t *id" */
|
||||
if (!cu__find_base_type_by_name(cu, "int", &id))
|
||||
id = cu->types_table.nr_entries;
|
||||
array_index_id = id;
|
||||
|
||||
cu__for_each_type(cu, core_id, pos) {
|
||||
int32_t btf_type_id = tag__encode_btf(pos, core_id, btf,
|
||||
array_index_id);
|
||||
|
||||
if (btf_type_id < 0 ||
|
||||
tag__check_id_drift(pos, core_id, btf_type_id)) {
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
id = btf_type_id;
|
||||
}
|
||||
|
||||
if (array_index_id == cu->types_table.nr_entries) {
|
||||
struct base_type bt = {};
|
||||
|
||||
bt.name = 0;
|
||||
bt.bit_size = 32;
|
||||
btf__add_base_type(btf, &bt);
|
||||
}
|
||||
|
||||
err = btf__encode(btf, 0);
|
||||
|
||||
out:
|
||||
btf__free(btf);
|
||||
if (err)
|
||||
fprintf(stderr, "Failed to encode BTF\n");
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef _BTF_ENCODER_H_
|
||||
#define _BTF_ENCODER_H_ 1
|
||||
|
||||
struct cu;
|
||||
|
||||
int cu__encode_btf(struct cu *cu, int verbose);
|
||||
|
||||
#endif /* _BTF_ENCODER_H_ */
|
|
@ -0,0 +1,518 @@
|
|||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "libbtf.h"
|
||||
#include "btf.h"
|
||||
#include "dutil.h"
|
||||
#include "gobuffer.h"
|
||||
#include "dwarves.h"
|
||||
|
||||
#define BTF_INFO_ENCODE(kind, root, vlen) \
|
||||
((!!(root) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
|
||||
#define BTF_INT_ENCODE(encoding, bits_offset, nr_bits) \
|
||||
((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
|
||||
|
||||
struct btf_int_type {
|
||||
struct btf_type type;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
struct btf_enum_type {
|
||||
struct btf_type type;
|
||||
struct btf_enum btf_enum;
|
||||
};
|
||||
|
||||
struct btf_array_type {
|
||||
struct btf_type type;
|
||||
struct btf_array array;
|
||||
};
|
||||
|
||||
uint8_t btf_verbose;
|
||||
|
||||
struct btf *btf__new(const char *filename, Elf *elf)
|
||||
{
|
||||
struct btf *btf = zalloc(sizeof(*btf));
|
||||
|
||||
if (!btf)
|
||||
return NULL;
|
||||
|
||||
btf->in_fd = -1;
|
||||
btf->filename = strdup(filename);
|
||||
if (btf->filename == NULL)
|
||||
goto errout;
|
||||
|
||||
if (elf != NULL) {
|
||||
btf->elf = elf;
|
||||
} else {
|
||||
btf->in_fd = open(filename, O_RDONLY);
|
||||
if (btf->in_fd < 0)
|
||||
goto errout;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
fprintf(stderr, "%s: cannot set libelf version.\n",
|
||||
__func__);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
btf->elf = elf_begin(btf->in_fd, ELF_C_READ_MMAP, NULL);
|
||||
if (!btf->elf) {
|
||||
fprintf(stderr, "%s: cannot read %s ELF file.\n",
|
||||
__func__, filename);
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
if (gelf_getehdr(btf->elf, &btf->ehdr) == NULL) {
|
||||
fprintf(stderr, "%s: cannot get elf header.\n", __func__);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
switch (btf->ehdr.e_ident[EI_CLASS]) {
|
||||
case ELFCLASS32: btf->wordsize = 4; break;
|
||||
case ELFCLASS64: btf->wordsize = 8; break;
|
||||
default: btf->wordsize = 0; break;
|
||||
}
|
||||
|
||||
return btf;
|
||||
|
||||
errout:
|
||||
btf__free(btf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void btf__free(struct btf *btf)
|
||||
{
|
||||
if (!btf)
|
||||
return;
|
||||
|
||||
if (btf->in_fd != -1) {
|
||||
close(btf->in_fd);
|
||||
if (btf->elf)
|
||||
elf_end(btf->elf);
|
||||
}
|
||||
|
||||
__gobuffer__delete(&btf->types);
|
||||
free(btf->filename);
|
||||
free(btf->data);
|
||||
free(btf);
|
||||
}
|
||||
|
||||
static void *btf__nohdr_data(struct btf *btf)
|
||||
{
|
||||
return btf->hdr + 1;
|
||||
}
|
||||
|
||||
void btf__set_strings(struct btf *btf, struct gobuffer *strings)
|
||||
{
|
||||
btf->strings = strings;
|
||||
}
|
||||
|
||||
#define BITS_PER_BYTE 8
|
||||
#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
|
||||
#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
|
||||
#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
|
||||
#define BITS_ROUNDUP_BYTES(bits) (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
|
||||
|
||||
static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
||||
[BTF_KIND_UNKN] = "UNKNOWN",
|
||||
[BTF_KIND_INT] = "INT",
|
||||
[BTF_KIND_PTR] = "PTR",
|
||||
[BTF_KIND_ARRAY] = "ARRAY",
|
||||
[BTF_KIND_STRUCT] = "STRUCT",
|
||||
[BTF_KIND_UNION] = "UNION",
|
||||
[BTF_KIND_ENUM] = "ENUM",
|
||||
[BTF_KIND_FWD] = "FWD",
|
||||
[BTF_KIND_TYPEDEF] = "TYPEDEF",
|
||||
[BTF_KIND_VOLATILE] = "VOLATILE",
|
||||
[BTF_KIND_CONST] = "CONST",
|
||||
[BTF_KIND_RESTRICT] = "RESTRICT",
|
||||
};
|
||||
|
||||
static const char *btf__name_in_gobuf(const struct btf *btf,
|
||||
uint32_t offset)
|
||||
{
|
||||
if (!offset)
|
||||
return "(anon)";
|
||||
else
|
||||
return &btf->strings->entries[offset];
|
||||
}
|
||||
|
||||
static const char * btf__int_encoding_str(uint8_t encoding)
|
||||
{
|
||||
if (encoding == 0)
|
||||
return "(none)";
|
||||
else if (encoding == BTF_INT_SIGNED)
|
||||
return "SIGNED";
|
||||
else if (encoding == BTF_INT_CHAR)
|
||||
return "CHAR";
|
||||
else if (encoding == BTF_INT_BOOL)
|
||||
return "BOOL";
|
||||
else
|
||||
return "UNKN";
|
||||
}
|
||||
|
||||
__attribute ((format (printf, 4, 5)))
|
||||
static void btf__log_type(const struct btf *btf, const struct btf_type *t,
|
||||
bool err, const char *fmt, ...)
|
||||
{
|
||||
uint8_t kind;
|
||||
FILE *out;
|
||||
|
||||
if (!btf_verbose && !err)
|
||||
return;
|
||||
|
||||
kind = BTF_INFO_KIND(t->info);
|
||||
out = err ? stderr : stdout;
|
||||
|
||||
fprintf(out, "[%u] %s %s",
|
||||
btf->type_index, btf_kind_str[kind],
|
||||
btf__name_in_gobuf(btf, t->name_off));
|
||||
|
||||
if (fmt && *fmt) {
|
||||
va_list ap;
|
||||
|
||||
fprintf(out, " ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
__attribute ((format (printf, 4, 5)))
|
||||
static void btf_log_member(const struct btf *btf,
|
||||
const struct btf_member *member,
|
||||
bool err, const char *fmt, ...)
|
||||
{
|
||||
FILE *out;
|
||||
|
||||
if (!btf_verbose && !err)
|
||||
return;
|
||||
|
||||
out = err ? stderr : stdout;
|
||||
|
||||
fprintf(out, "\t%s type_id=%u bits_offset=%u",
|
||||
btf__name_in_gobuf(btf, member->name_off),
|
||||
member->type, member->offset);
|
||||
|
||||
if (fmt && *fmt) {
|
||||
va_list ap;
|
||||
|
||||
fprintf(out, " ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
int32_t btf__add_base_type(struct btf *btf, const struct base_type *bt)
|
||||
{
|
||||
struct btf_int_type int_type;
|
||||
struct btf_type *t = &int_type.type;
|
||||
uint8_t encoding;
|
||||
|
||||
t->name_off = bt->name;
|
||||
t->info = BTF_INFO_ENCODE(BTF_KIND_INT, 0, 0);
|
||||
t->size = BITS_ROUNDUP_BYTES(bt->bit_size);
|
||||
if (bt->is_signed) {
|
||||
encoding = BTF_INT_SIGNED;
|
||||
} else if (bt->is_bool) {
|
||||
encoding = BTF_INT_BOOL;
|
||||
} else if (bt->float_type) {
|
||||
fprintf(stderr, "float_type is not supported\n");
|
||||
return -1;
|
||||
}
|
||||
int_type.data = BTF_INT_ENCODE(encoding, 0, bt->bit_size);
|
||||
|
||||
++btf->type_index;
|
||||
if (gobuffer__add(&btf->types, &int_type, sizeof(int_type)) >= 0) {
|
||||
btf__log_type(btf, t, false,
|
||||
"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,
|
||||
"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),
|
||||
btf__int_encoding_str(BTF_INT_ENCODING(int_type.data)));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t btf__add_ref_type(struct btf *btf, uint16_t kind, uint32_t type,
|
||||
uint32_t name)
|
||||
{
|
||||
struct btf_type t;
|
||||
|
||||
t.name_off = name;
|
||||
t.info = BTF_INFO_ENCODE(kind, 0, 0);
|
||||
t.type = type;
|
||||
|
||||
++btf->type_index;
|
||||
if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) {
|
||||
btf__log_type(btf, &t, false, "type_id=%u", t.type);
|
||||
return btf->type_index;
|
||||
} else {
|
||||
btf__log_type(btf, &t, true,
|
||||
"type_id=%u Error in adding gobuffer",
|
||||
t.type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t btf__add_array(struct btf *btf, uint32_t type, uint32_t index_type,
|
||||
uint32_t nelems)
|
||||
{
|
||||
struct btf_array_type array_type;
|
||||
struct btf_type *t = &array_type.type;
|
||||
struct btf_array *array = &array_type.array;
|
||||
|
||||
t->name_off = 0;
|
||||
t->info = BTF_INFO_ENCODE(BTF_KIND_ARRAY, 0, 0);
|
||||
t->size = 0;
|
||||
|
||||
array->type = type;
|
||||
array->index_type = index_type;
|
||||
array->nelems = nelems;
|
||||
|
||||
++btf->type_index;
|
||||
if (gobuffer__add(&btf->types, &array_type, sizeof(array_type)) >= 0) {
|
||||
btf__log_type(btf, t, false,
|
||||
"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,
|
||||
"type_id=%u index_type_id=%u nr_elems=%u Error in adding gobuffer",
|
||||
array->type, array->index_type, array->nelems);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int btf__add_member(struct btf *btf, uint32_t name, uint32_t type,
|
||||
uint32_t offset)
|
||||
{
|
||||
struct btf_member member = {
|
||||
.name_off = name,
|
||||
.type = type,
|
||||
.offset = offset,
|
||||
};
|
||||
|
||||
if (gobuffer__add(&btf->types, &member, sizeof(member)) >= 0) {
|
||||
btf_log_member(btf, &member, false, NULL);
|
||||
return 0;
|
||||
} else {
|
||||
btf_log_member(btf, &member, true, "Error in adding gobuffer");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t btf__add_struct(struct btf *btf, uint8_t kind, uint32_t name,
|
||||
uint32_t size, uint16_t nr_members)
|
||||
{
|
||||
struct btf_type t;
|
||||
|
||||
t.name_off = name;
|
||||
t.info = BTF_INFO_ENCODE(kind, 0, nr_members);
|
||||
t.size = size;
|
||||
|
||||
++btf->type_index;
|
||||
if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) {
|
||||
btf__log_type(btf, &t, false, "size=%u vlen=%u", t.size,
|
||||
BTF_INFO_VLEN(t.info));
|
||||
return btf->type_index;
|
||||
} else {
|
||||
btf__log_type(btf, &t, true,
|
||||
"size=%u vlen=%u Error in adding gobuffer",
|
||||
t.size, BTF_INFO_VLEN(t.info));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t btf__add_enum(struct btf *btf, uint32_t name, uint32_t bit_size,
|
||||
uint16_t nr_entries)
|
||||
{
|
||||
struct btf_type t;
|
||||
|
||||
t.name_off = name;
|
||||
t.info = BTF_INFO_ENCODE(BTF_KIND_ENUM, 0, nr_entries);
|
||||
t.size = BITS_ROUNDUP_BYTES(bit_size);
|
||||
|
||||
++btf->type_index;
|
||||
if (gobuffer__add(&btf->types, &t, sizeof(t)) >= 0) {
|
||||
btf__log_type(btf, &t, false, "size=%u vlen=%u",
|
||||
t.size, BTF_INFO_VLEN(t.info));
|
||||
return btf->type_index;
|
||||
} else {
|
||||
btf__log_type(btf, &t, true,
|
||||
"size=%u vlen=%u Error in adding gobuffer",
|
||||
t.size, BTF_INFO_VLEN(t.info));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int btf__add_enum_val(struct btf *btf, uint32_t name, int32_t value)
|
||||
{
|
||||
struct btf_enum e = {
|
||||
.name_off = name,
|
||||
.val = value,
|
||||
};
|
||||
|
||||
if (gobuffer__add(&btf->types, &e, sizeof(e)) < 0) {
|
||||
fprintf(stderr, "\t%s val=%d Error in adding gobuffer\n",
|
||||
btf__name_in_gobuf(btf, e.name_off), e.val);
|
||||
return -1;
|
||||
} else if (btf_verbose)
|
||||
printf("\t%s val=%d\n", btf__name_in_gobuf(btf, e.name_off),
|
||||
e.val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf__write_elf(struct btf *btf)
|
||||
{
|
||||
GElf_Shdr shdr_mem, *shdr;
|
||||
GElf_Ehdr ehdr_mem, *ehdr;
|
||||
Elf_Data *btf_elf = NULL;
|
||||
Elf_Scn *scn = NULL;
|
||||
Elf *elf = NULL;
|
||||
int fd, err = -1;
|
||||
size_t strndx;
|
||||
|
||||
fd = open(btf->filename, O_RDWR);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open %s\n", btf->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
fprintf(stderr, "Cannot set libelf version.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
elf = elf_begin(fd, ELF_C_RDWR, NULL);
|
||||
if (elf == NULL) {
|
||||
fprintf(stderr, "Cannot update ELF file.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
elf_flagelf(elf, ELF_C_SET, ELF_F_DIRTY);
|
||||
|
||||
ehdr = gelf_getehdr(elf, &ehdr_mem);
|
||||
if (ehdr == NULL) {
|
||||
fprintf(stderr, "%s: elf_getehdr failed.\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* First we look if there was already a .BTF section to overwrite.
|
||||
*/
|
||||
|
||||
elf_getshdrstrndx(elf, &strndx);
|
||||
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
||||
shdr = gelf_getshdr(scn, &shdr_mem);
|
||||
if (shdr == NULL)
|
||||
continue;
|
||||
char *secname = elf_strptr(elf, strndx, shdr->sh_name);
|
||||
if (strcmp(secname, ".BTF") == 0) {
|
||||
btf_elf = elf_getdata(scn, btf_elf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (btf_elf) {
|
||||
/* Exisiting .BTF section found */
|
||||
btf_elf->d_buf = btf->data;
|
||||
btf_elf->d_size = btf->size;
|
||||
elf_flagdata(btf_elf, ELF_C_SET, ELF_F_DIRTY);
|
||||
|
||||
if (elf_update(elf, ELF_C_NULL) >= 0 &&
|
||||
elf_update(elf, ELF_C_WRITE) >= 0)
|
||||
err = 0;
|
||||
} else {
|
||||
const char *llvm_objcopy;
|
||||
char tmp_fn[PATH_MAX];
|
||||
char cmd[PATH_MAX];
|
||||
|
||||
llvm_objcopy = getenv("LLVM_OBJCOPY");
|
||||
if (!llvm_objcopy)
|
||||
llvm_objcopy = "llvm-objcopy";
|
||||
|
||||
/* Use objcopy to add a .BTF section */
|
||||
snprintf(tmp_fn, sizeof(tmp_fn), "%s.btf", btf->filename);
|
||||
close(fd);
|
||||
fd = creat(tmp_fn, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "%s: open(%s) failed!\n", __func__,
|
||||
tmp_fn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "%s --add-section .BTF=%s %s",
|
||||
llvm_objcopy, tmp_fn, btf->filename);
|
||||
|
||||
if (write(fd, btf->data, btf->size) == btf->size &&
|
||||
!system(cmd))
|
||||
err = 0;
|
||||
|
||||
unlink(tmp_fn);
|
||||
}
|
||||
|
||||
out:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
if (elf)
|
||||
elf_end(elf);
|
||||
return err;
|
||||
}
|
||||
|
||||
int btf__encode(struct btf *btf, uint8_t flags)
|
||||
{
|
||||
struct btf_header *hdr;
|
||||
|
||||
/* Empty file, nothing to do, so... done! */
|
||||
if (gobuffer__size(&btf->types) == 0)
|
||||
return 0;
|
||||
|
||||
btf->size = sizeof(*hdr) +
|
||||
(gobuffer__size(&btf->types) +
|
||||
gobuffer__size(btf->strings));
|
||||
btf->data = zalloc(btf->size);
|
||||
|
||||
if (btf->data == NULL) {
|
||||
fprintf(stderr, "%s: malloc failed!\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = btf->hdr;
|
||||
hdr->magic = BTF_MAGIC;
|
||||
hdr->version = 1;
|
||||
hdr->flags = 0;
|
||||
hdr->hdr_len = sizeof(*hdr);
|
||||
|
||||
hdr->type_off = 0;
|
||||
hdr->type_len = gobuffer__size(&btf->types);
|
||||
hdr->str_off = hdr->type_len;
|
||||
hdr->str_len = gobuffer__size(btf->strings);
|
||||
|
||||
gobuffer__copy(&btf->types, btf__nohdr_data(btf) + hdr->type_off);
|
||||
gobuffer__copy(btf->strings, btf__nohdr_data(btf) + hdr->str_off);
|
||||
|
||||
*(char *)(btf__nohdr_data(btf) + hdr->str_off) = '\0';
|
||||
|
||||
return btf__write_elf(btf);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef _LIBBTF_H
|
||||
#define _LIBBTF_H
|
||||
|
||||
#include "gobuffer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct btf {
|
||||
union {
|
||||
struct btf_header *hdr;
|
||||
void *data;
|
||||
};
|
||||
void *priv;
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
struct gobuffer types;
|
||||
struct gobuffer *strings;
|
||||
char *filename;
|
||||
size_t size;
|
||||
int swapped;
|
||||
int in_fd;
|
||||
uint8_t wordsize;
|
||||
uint32_t type_index;
|
||||
};
|
||||
|
||||
extern uint8_t btf_verbose;
|
||||
#define btf_verbose_log(fmt, ...) { if (btf_verbose) printf(fmt, __VA_ARGS__); }
|
||||
|
||||
struct base_type;
|
||||
|
||||
struct btf *btf__new(const char *filename, Elf *elf);
|
||||
void btf__free(struct btf *btf);
|
||||
|
||||
int32_t btf__add_base_type(struct btf *btf, const struct base_type *bt);
|
||||
int32_t btf__add_ref_type(struct btf *btf, uint16_t kind, uint32_t type,
|
||||
uint32_t name);
|
||||
int btf__add_member(struct btf *btf, uint32_t name, uint32_t type,
|
||||
uint32_t bit_offset);
|
||||
int32_t btf__add_struct(struct btf *btf, uint8_t kind, uint32_t name,
|
||||
uint32_t size, uint16_t nr_members);
|
||||
int32_t btf__add_array(struct btf *btf, uint32_t type, uint32_t index_type,
|
||||
uint32_t nelems);
|
||||
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);
|
||||
void btf__set_strings(struct btf *btf, struct gobuffer *strings);
|
||||
int btf__encode(struct btf *btf, uint8_t flags);
|
||||
|
||||
#endif /* _LIBBTF_H */
|
13
pahole.c
13
pahole.c
|
@ -21,7 +21,9 @@
|
|||
#include "dwarves.h"
|
||||
#include "dutil.h"
|
||||
#include "ctf_encoder.h"
|
||||
#include "btf_encoder.h"
|
||||
|
||||
static bool btf_encode;
|
||||
static bool ctf_encode;
|
||||
static bool first_obj_only;
|
||||
|
||||
|
@ -964,6 +966,11 @@ static const struct argp_option pahole__options[] = {
|
|||
.key = ARGP_hex_fmt,
|
||||
.doc = "Print offsets and sizes in hexadecimal",
|
||||
},
|
||||
{
|
||||
.name = "btf_encode",
|
||||
.key = 'J',
|
||||
.doc = "Encode as BTF",
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
}
|
||||
|
@ -995,6 +1002,7 @@ static error_t pahole__options_parser(int key, char *arg,
|
|||
conf_load.extra_dbg_info = 1; break;
|
||||
case 'i': find_containers = 1;
|
||||
class_name = arg; break;
|
||||
case 'J': btf_encode = 1; break;
|
||||
case 'l': conf.show_first_biggest_size_base_type_member = 1; break;
|
||||
case 'M': conf.show_only_data_members = 1; break;
|
||||
case 'm': stats_formatter = nr_methods_formatter; break;
|
||||
|
@ -1105,6 +1113,11 @@ static enum load_steal_kind pahole_stealer(struct cu *cu,
|
|||
if (!cu__filter(cu))
|
||||
goto filter_it;
|
||||
|
||||
if (btf_encode) {
|
||||
cu__encode_btf(cu, global_verbose);
|
||||
goto dump_and_stop;
|
||||
}
|
||||
|
||||
if (ctf_encode) {
|
||||
cu__encode_ctf(cu, global_verbose);
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue