Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixup generated BTF for older kernels #2934

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ and this project adheres to
- [#2914](https://github.com/iovisor/bpftrace/pull/2914)
- Fix uprobe attachment across container boundary
- [#2662](https://github.com/iovisor/bpftrace/pull/2662)
- Fix generated BTF for older kernels
- [#2934](https://github.com/iovisor/bpftrace/pull/2934)
#### Docs
- Fix one-liner tutorial for systems with BTF
- [#2919](https://github.com/iovisor/bpftrace/pull/2919)
Expand Down
86 changes: 86 additions & 0 deletions src/bpfbytecode.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "bpfbytecode.h"

#include <bpf/btf.h>
#include <stdexcept>

namespace bpftrace {
Expand All @@ -25,4 +26,89 @@ const std::vector<uint8_t> &BpfBytecode::getSection(
return sections_.at(name);
}

// This is taken from libbpf (btf.c) and we need it to manually iterate BTF
// entries so that we can fix them up in place.
// Should go away once we let libbpf do the BTF fixup.
static int btf_type_size(const struct btf_type *t)
{
const int base_size = sizeof(struct btf_type);
__u16 vlen = btf_vlen(t);

switch (btf_kind(t))
{
case BTF_KIND_FWD:
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
case BTF_KIND_PTR:
case BTF_KIND_TYPEDEF:
case BTF_KIND_FUNC:
case BTF_KIND_FLOAT:
case BTF_KIND_TYPE_TAG:
return base_size;
case BTF_KIND_INT:
return base_size + sizeof(__u32);
case BTF_KIND_ENUM:
return base_size + vlen * sizeof(struct btf_enum);
case BTF_KIND_ENUM64:
return base_size + vlen * sizeof(struct btf_enum64);
case BTF_KIND_ARRAY:
return base_size + sizeof(struct btf_array);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
return base_size + vlen * sizeof(struct btf_member);
case BTF_KIND_FUNC_PROTO:
return base_size + vlen * sizeof(struct btf_param);
case BTF_KIND_VAR:
return base_size + sizeof(struct btf_var);
case BTF_KIND_DATASEC:
return base_size + vlen * sizeof(struct btf_var_secinfo);
case BTF_KIND_DECL_TAG:
return base_size + sizeof(struct btf_decl_tag);
default:
return -EINVAL;
}
}

// There are cases when BTF generated by LLVM needs to be patched. This is
// normally done by libbpf but only when loading via bpf_object is used. We're
// currently using direct loading using bpf_btf_load and bpf_prog_load so we
// need to mimic libbpf's behavior and do the patching manually.
// This should go away once we move to bpf_object-based loading.
ajor marked this conversation as resolved.
Show resolved Hide resolved
//
// Transformations done:
// - If running on a kernel not supporting BTF func entries with BTF_FUNC_GLOBAL
// linkage, change the linkage type to 0. We cannot do this directly in
// codegen b/c LLVM would optimize our functions away.
ajor marked this conversation as resolved.
Show resolved Hide resolved
void BpfBytecode::fixupBTF(BPFfeature &feature)
{
if (!hasSection(".BTF"))
return;
ajor marked this conversation as resolved.
Show resolved Hide resolved

auto &btfsec = sections_[".BTF"];
auto *btfhdr = reinterpret_cast<struct btf_header *>(btfsec.data());
auto *btf = btf__new(btfsec.data(), btfsec.size());

auto *types_start = btfsec.data() + sizeof(struct btf_header) +
btfhdr->type_off;
auto *types_end = types_start + btfhdr->type_len;

// Unfortunately, libbpf's btf__type_by_id returns a const pointer which
// doesn't allow modification. So, we must iterate the types manually.
auto *ptr = types_start;
while (ptr + sizeof(struct btf_type) <= types_end)
{
auto *btf_type = reinterpret_cast<struct btf_type *>(ptr);
ptr += btf_type_size(btf_type);

// Change linkage type to 0 if the kernel doesn't support BTF_FUNC_GLOBAL.
if (!feature.has_btf_func_global() &&
BTF_INFO_KIND(btf_type->info) == BTF_KIND_FUNC)
{
btf_type->info = BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0);
}
}
btf__free(btf);
}

} // namespace bpftrace
3 changes: 3 additions & 0 deletions src/bpfbytecode.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "bpffeature.h"
#include <cereal/access.hpp>
#include <cstdint>
#include <string>
Expand All @@ -26,6 +27,8 @@ class BpfBytecode
bool hasSection(const std::string &name) const;
const std::vector<uint8_t> &getSection(const std::string &name) const;

void fixupBTF(BPFfeature &feature);

private:
SectionMap sections_;

Expand Down
41 changes: 41 additions & 0 deletions src/bpffeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <bcc/bcc_syms.h>
#include <bcc/libbpf.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <cstddef>
#include <cstdio>
Expand Down Expand Up @@ -136,6 +137,25 @@ bool BPFfeature::try_load(enum libbpf::bpf_prog_type prog_type,
outfd);
}

bool BPFfeature::try_load_btf(const void* btf_data, size_t btf_size)
{
constexpr int log_size = 4096;
char log_buf[log_size] = {};
LIBBPF_OPTS(bpf_btf_load_opts,
btf_opts,
.log_buf = log_buf,
.log_level = 0,
.log_size = log_size, );

int fd = bpf_btf_load(btf_data, btf_size, &btf_opts);
if (fd >= 0)
{
close(fd);
return true;
}
return false;
}

bool BPFfeature::detect_helper(enum libbpf::bpf_func_id func_id,
enum libbpf::bpf_prog_type prog_type)
{
Expand Down Expand Up @@ -249,6 +269,27 @@ bool BPFfeature::has_btf(void)
return btf_.has_data();
}

bool BPFfeature::has_btf_func_global()
{
if (has_btf_func_global_.has_value())
return *has_btf_func_global_;

/* static void x(int a) {} */
Dismissed Show dismissed Hide dismissed
__u32 types[] = {
/* int */
BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
/* FUNC_PROTO */ /* [2] */
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
BTF_PARAM_ENC(7, 1),
/* FUNC x BTF_FUNC_GLOBAL */ /* [3] */
BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 2),
};

has_btf_func_global_ = std::make_optional<bool>(
try_load_btf(types, sizeof(types)));
return *has_btf_func_global_;
}

int BPFfeature::instruction_limit(void)
{
if (insns_limit_.has_value())
Expand Down
3 changes: 3 additions & 0 deletions src/bpffeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class BPFfeature
int instruction_limit();
bool has_loop();
bool has_btf();
bool has_btf_func_global();
bool has_map_batch();
bool has_d_path();
bool has_uprobe_refcnt();
Expand Down Expand Up @@ -142,6 +143,7 @@ class BPFfeature
std::optional<bool> has_raw_tp_special_;
std::optional<bool> has_prog_kfunc_;
std::optional<bool> has_module_btf_;
std::optional<bool> has_btf_func_global_;

private:
bool detect_map(libbpf::bpf_map_type map_type);
Expand All @@ -159,6 +161,7 @@ class BPFfeature
const char* name = nullptr,
std::optional<libbpf::bpf_attach_type> attach_type = std::nullopt,
int* outfd = nullptr);
bool try_load_btf(const void* btf_data, size_t btf_size);

BTF btf_ = BTF({ "vmlinux" });

Expand Down
1 change: 1 addition & 0 deletions src/bpftrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,7 @@ int BPFtrace::run(BpfBytecode bytecode)
return 1;

bytecode_ = std::move(bytecode);
bytecode_.fixupBTF(*feature_);

err = setup_output();
if (err)
Expand Down
11 changes: 11 additions & 0 deletions src/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
#include <unistd.h>
#include <unordered_set>

// Taken from libbpf
#define BTF_INFO_ENC(kind, kind_flag, vlen) \
((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen)&BTF_MAX_VLEN))
#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
BTF_INT_ENC(encoding, bits_offset, bits)
#define BTF_PARAM_ENC(name, type) (name), (type)

struct btf;
struct btf_type;

Expand Down
Loading