diff --git a/CMakeLists.txt b/CMakeLists.txt index bb3e74b..179b308 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "") diff --git a/btf.h b/btf.h new file mode 100644 index 0000000..0b5ddbe --- /dev/null +++ b/btf.h @@ -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 + +#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__ */ diff --git a/btf_encoder.c b/btf_encoder.c new file mode 100644 index 0000000..bfdab27 --- /dev/null +++ b/btf_encoder.c @@ -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 + +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; +} diff --git a/btf_encoder.h b/btf_encoder.h new file mode 100644 index 0000000..a9d622f --- /dev/null +++ b/btf_encoder.h @@ -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_ */ diff --git a/libbtf.c b/libbtf.c new file mode 100644 index 0000000..3f49c0f --- /dev/null +++ b/libbtf.c @@ -0,0 +1,518 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libbtf.h b/libbtf.h new file mode 100644 index 0000000..02156fd --- /dev/null +++ b/libbtf.h @@ -0,0 +1,49 @@ +#ifndef _LIBBTF_H +#define _LIBBTF_H + +#include "gobuffer.h" + +#include + +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 */ diff --git a/pahole.c b/pahole.c index 9d506b1..55803c3 100644 --- a/pahole.c +++ b/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); /*