Skip to content

Commit

Permalink
elf: generate ELF section patterns from libbpf
Browse files Browse the repository at this point in the history
We've historically relied on manually pulling in updated ELF
section names from libbpf. This has led to errors creeping in as
well as being constantly out of sync.

Fix this by using a small awk script to pull the section
definitions out of libbpf.c. We already rely on awk to update
function names in the awk package so this isn't a new requirement.

It turns out that we've strayed from libbpf in a couple of places.
This commit adds compatibility behaviour to avoid outward visible
changes for the section names we have tests for.

The old generate-btf target is repurposed to do the generation
and therefore renamed to update-kernel-deps.

Fixes #1162

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
  • Loading branch information
lmb authored and ti-mo committed Nov 10, 2023
1 parent 7e286e7 commit 0acd95c
Show file tree
Hide file tree
Showing 12 changed files with 514 additions and 292 deletions.
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ testdata/loader-%-eb.elf: testdata/loader.c
$(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@
$(STRIP) -g $@

.PHONY: generate-btf
generate-btf: KERNEL_VERSION?=6.6
generate-btf:
.PHONY: update-kernel-deps
update-kernel-deps: KERNEL_VERSION?=6.6
update-kernel-deps:
$(eval TMP := $(shell mktemp -d))
curl -fL https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/tools/lib/bpf/libbpf.c?h=v$(KERNEL_VERSION) -o "$(TMP)/libbpf.c"
"$(CURDIR)/internal/cmd/gensections.awk" "$(TMP)/libbpf.c" | gofmt > "$(CURDIR)/elf_sections.go"
curl -fL "$(CI_KERNEL_URL)/linux-$(KERNEL_VERSION)-amd64.tgz" -o "$(TMP)/linux.tgz"
tar xvf "$(TMP)/linux.tgz" -C "$(TMP)" --strip-components=2 ./boot/vmlinuz ./lib/modules
/lib/modules/$(shell uname -r)/build/scripts/extract-vmlinux "$(TMP)/vmlinuz" > "$(TMP)/vmlinux"
Expand Down
10 changes: 8 additions & 2 deletions attachtype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion btf/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ func TestCORERelocation(t *testing.T) {
}

for section := range extInfos.funcInfos {
name := strings.TrimPrefix(section, "socket_filter/")
name := strings.TrimPrefix(section, "socket/")
t.Run(name, func(t *testing.T) {
var relos []*CORERelocation
for _, reloInfo := range extInfos.relocationInfos[section].infos {
Expand Down
Binary file modified btf/testdata/relocs-eb.elf
Binary file not shown.
Binary file modified btf/testdata/relocs-el.elf
Binary file not shown.
12 changes: 6 additions & 6 deletions btf/testdata/relocs.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ typedef union u u_t;
} \
})

__section("socket_filter/type_ids") int type_ids() {
__section("socket/type_ids") int type_ids() {
local_id_not_zero(int);
local_id_not_zero(struct { int frob; });
local_id_not_zero(enum {FRAP});
Expand Down Expand Up @@ -97,7 +97,7 @@ __section("socket_filter/type_ids") int type_ids() {
} \
})

__section("socket_filter/types") int types() {
__section("socket/types") int types() {
type_exists(struct s);
type_exists(s_t);
type_exists(const s_t);
Expand Down Expand Up @@ -142,7 +142,7 @@ __section("socket_filter/types") int types() {
} \
})

__section("socket_filter/enums") int enums() {
__section("socket/enums") int enums() {
enum_value_exists(enum e, ONE);
enum_value_exists(volatile enum e, ONE);
enum_value_exists(const enum e, ONE);
Expand Down Expand Up @@ -195,7 +195,7 @@ __section("socket_filter/enums") int enums() {
} \
})

__section("socket_filter/fields") int fields() {
__section("socket/fields") int fields() {
field_exists((struct s){}._1);
field_exists((s_t){}._2);
field_exists((union u){}._1);
Expand Down Expand Up @@ -262,10 +262,10 @@ struct ambiguous___flavour {
int _2;
};

__section("socket_filter/err_ambiguous") int err_ambiguous() {
__section("socket/err_ambiguous") int err_ambiguous() {
return bpf_core_type_id_kernel(struct ambiguous);
}

__section("socket_filter/err_ambiguous_flavour") int err_ambiguous_flavour() {
__section("socket/err_ambiguous_flavour") int err_ambiguous_flavour() {
return bpf_core_type_id_kernel(struct ambiguous___flavour);
}
180 changes: 89 additions & 91 deletions elf_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/sys"
"github.com/cilium/ebpf/internal/unix"
)

Expand Down Expand Up @@ -1181,109 +1182,106 @@ func (ec *elfCode) loadKsymsSection() error {
return nil
}

type libbpfElfSectionDef struct {
pattern string
programType sys.ProgType
attachType sys.AttachType
flags libbpfElfSectionFlag
}

type libbpfElfSectionFlag uint32

// The values correspond to enum sec_def_flags in libbpf.
const (
_SEC_NONE libbpfElfSectionFlag = 0

_SEC_EXP_ATTACH_OPT libbpfElfSectionFlag = 1 << (iota - 1)
_SEC_ATTACHABLE
_SEC_ATTACH_BTF
_SEC_SLEEPABLE
_SEC_XDP_FRAGS
_SEC_USDT

// Ignore any present extra in order to preserve backwards compatibility
// with earlier versions of the library.
ignoreExtra

_SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT
)

func init() {
// Compatibility with older versions of the library.
// We prepend libbpf definitions since they contain a prefix match
// for "xdp".
elfSectionDefs = append([]libbpfElfSectionDef{
{"xdp.frags/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS | ignoreExtra},
{"xdp.frags_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS},
{"xdp_devmap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, 0},
{"xdp.frags_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS},
{"xdp_cpumap/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, 0},
// This has been in the library since the beginning of time. Not sure
// where it came from.
{"seccomp", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE},
}, elfSectionDefs...)
}

func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
types := []struct {
prefix string
progType ProgramType
attachType AttachType
progFlags uint32
}{
// Please update the types from libbpf.c and follow the order of it.
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c
{"socket", SocketFilter, AttachNone, 0},
{"sk_reuseport/migrate", SkReuseport, AttachSkReuseportSelectOrMigrate, 0},
{"sk_reuseport", SkReuseport, AttachSkReuseportSelect, 0},
{"kprobe/", Kprobe, AttachNone, 0},
{"uprobe/", Kprobe, AttachNone, 0},
{"kretprobe/", Kprobe, AttachNone, 0},
{"uretprobe/", Kprobe, AttachNone, 0},
{"tc", SchedCLS, AttachNone, 0},
{"classifier", SchedCLS, AttachNone, 0},
{"action", SchedACT, AttachNone, 0},
{"tracepoint/", TracePoint, AttachNone, 0},
{"tp/", TracePoint, AttachNone, 0},
{"raw_tracepoint/", RawTracepoint, AttachNone, 0},
{"raw_tp/", RawTracepoint, AttachNone, 0},
{"raw_tracepoint.w/", RawTracepointWritable, AttachNone, 0},
{"raw_tp.w/", RawTracepointWritable, AttachNone, 0},
{"tp_btf/", Tracing, AttachTraceRawTp, 0},
{"fentry/", Tracing, AttachTraceFEntry, 0},
{"fmod_ret/", Tracing, AttachModifyReturn, 0},
{"fexit/", Tracing, AttachTraceFExit, 0},
{"fentry.s/", Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE},
{"fmod_ret.s/", Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE},
{"fexit.s/", Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE},
{"freplace/", Extension, AttachNone, 0},
{"lsm/", LSM, AttachLSMMac, 0},
{"lsm.s/", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE},
{"iter/", Tracing, AttachTraceIter, 0},
{"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE},
{"syscall", Syscall, AttachNone, 0},
{"xdp.frags_devmap/", XDP, AttachXDPDevMap, unix.BPF_F_XDP_HAS_FRAGS},
{"xdp_devmap/", XDP, AttachXDPDevMap, 0},
{"xdp.frags_cpumap/", XDP, AttachXDPCPUMap, unix.BPF_F_XDP_HAS_FRAGS},
{"xdp_cpumap/", XDP, AttachXDPCPUMap, 0},
{"xdp.frags", XDP, AttachNone, unix.BPF_F_XDP_HAS_FRAGS},
{"xdp", XDP, AttachNone, 0},
{"perf_event", PerfEvent, AttachNone, 0},
{"lwt_in", LWTIn, AttachNone, 0},
{"lwt_out", LWTOut, AttachNone, 0},
{"lwt_xmit", LWTXmit, AttachNone, 0},
{"lwt_seg6local", LWTSeg6Local, AttachNone, 0},
{"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, 0},
{"cgroup_skb/egress", CGroupSKB, AttachCGroupInetEgress, 0},
{"cgroup/skb", CGroupSKB, AttachNone, 0},
{"cgroup/sock_create", CGroupSock, AttachCGroupInetSockCreate, 0},
{"cgroup/sock_release", CGroupSock, AttachCgroupInetSockRelease, 0},
{"cgroup/sock", CGroupSock, AttachCGroupInetSockCreate, 0},
{"cgroup/post_bind4", CGroupSock, AttachCGroupInet4PostBind, 0},
{"cgroup/post_bind6", CGroupSock, AttachCGroupInet6PostBind, 0},
{"cgroup/dev", CGroupDevice, AttachCGroupDevice, 0},
{"sockops", SockOps, AttachCGroupSockOps, 0},
{"sk_skb/stream_parser", SkSKB, AttachSkSKBStreamParser, 0},
{"sk_skb/stream_verdict", SkSKB, AttachSkSKBStreamVerdict, 0},
{"sk_skb", SkSKB, AttachNone, 0},
{"sk_msg", SkMsg, AttachSkMsgVerdict, 0},
{"lirc_mode2", LircMode2, AttachLircMode2, 0},
{"flow_dissector", FlowDissector, AttachFlowDissector, 0},
{"cgroup/bind4", CGroupSockAddr, AttachCGroupInet4Bind, 0},
{"cgroup/bind6", CGroupSockAddr, AttachCGroupInet6Bind, 0},
{"cgroup/connect4", CGroupSockAddr, AttachCGroupInet4Connect, 0},
{"cgroup/connect6", CGroupSockAddr, AttachCGroupInet6Connect, 0},
{"cgroup/sendmsg4", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0},
{"cgroup/sendmsg6", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0},
{"cgroup/recvmsg4", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0},
{"cgroup/recvmsg6", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0},
{"cgroup/getpeername4", CGroupSockAddr, AttachCgroupInet4GetPeername, 0},
{"cgroup/getpeername6", CGroupSockAddr, AttachCgroupInet6GetPeername, 0},
{"cgroup/getsockname4", CGroupSockAddr, AttachCgroupInet4GetSockname, 0},
{"cgroup/getsockname6", CGroupSockAddr, AttachCgroupInet6GetSockname, 0},
{"cgroup/sysctl", CGroupSysctl, AttachCGroupSysctl, 0},
{"cgroup/getsockopt", CGroupSockopt, AttachCGroupGetsockopt, 0},
{"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0},
{"struct_ops+", StructOps, AttachNone, 0},
{"sk_lookup/", SkLookup, AttachSkLookup, 0},
{"seccomp", SocketFilter, AttachNone, 0},
{"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0},
{"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0},
// Document all prefixes in docs/ebpf/concepts/elf-sections.md.
}
// Skip optional program marking for now.
sectionName = strings.TrimPrefix(sectionName, "?")

for _, t := range types {
if !strings.HasPrefix(sectionName, t.prefix) {
for _, t := range elfSectionDefs {
extra, ok := matchSectionName(sectionName, t.pattern)
if !ok {
continue
}

if !strings.HasSuffix(t.prefix, "/") {
return t.progType, t.attachType, t.progFlags, ""
programType := ProgramType(t.programType)
attachType := AttachType(t.attachType)

var flags uint32
if t.flags&_SEC_SLEEPABLE > 0 {
flags |= unix.BPF_F_SLEEPABLE
}
if t.flags&_SEC_XDP_FRAGS > 0 {
flags |= unix.BPF_F_XDP_HAS_FRAGS
}
if t.flags&_SEC_EXP_ATTACH_OPT > 0 {
if programType == XDP {
// The library doesn't yet have code to fallback to not specifying
// attach type. Only do this for XDP since we've enforced correct
// attach type for all other program types.
attachType = AttachNone
}
}
if t.flags&ignoreExtra > 0 {
extra = ""
}

return t.progType, t.attachType, t.progFlags, sectionName[len(t.prefix):]
return programType, attachType, flags, extra
}

return UnspecifiedProgram, AttachNone, 0, ""
}

// matchSectionName checks a section name against a pattern.
//
// It's behaviour mirrors that of libbpf's sec_def_matches.
func matchSectionName(sectionName, pattern string) (extra string, found bool) {
have, extra, found := strings.Cut(sectionName, "/")
want := strings.TrimRight(pattern, "+/")

if strings.HasSuffix(pattern, "/") {
// Section name must have a slash and extra may be empty.
return extra, have == want && found
} else if strings.HasSuffix(pattern, "+") {
// Section name may have a slash and extra may be empty.
return extra, have == want
}

// Section name must have a prefix. extra is ignored.
return "", strings.HasPrefix(sectionName, pattern)
}

func (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) {
rels := make(map[uint64]elf.Symbol)

Expand Down
Loading

0 comments on commit 0acd95c

Please sign in to comment.