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 cilium#1162

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
  • Loading branch information
lmb committed Nov 7, 2023
1 parent ada76cf commit f448f22
Show file tree
Hide file tree
Showing 6 changed files with 477 additions and 282 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
196 changes: 105 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,122 @@ 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() {
elfSectionDefs = append(elfSectionDefs, []libbpfElfSectionDef{
// 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},
// Deprecated in 450b167fb9be ("libbpf: clean up SEC() handling")
{"socket/", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, ignoreExtra},
{"sk_lookup/", sys.BPF_PROG_TYPE_SK_LOOKUP, sys.BPF_SK_LOOKUP, _SEC_NONE},
{"xdp/", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_ATTACHABLE_OPT | ignoreExtra},
{"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},
{"sk_skb/stream_verdict/", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_VERDICT, _SEC_ATTACHABLE_OPT | ignoreExtra},
{"sk_skb/", sys.BPF_PROG_TYPE_SK_SKB, 0, ignoreExtra},
}...)
}

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, ""
}

func matchSectionName(sectionName, pattern string) (extra string, found bool) {
if strings.HasSuffix(pattern, "/") {
if !strings.HasPrefix(sectionName, pattern) {
return "", false
}

return sectionName[len(pattern):], true
}

if !strings.HasSuffix(pattern, "+") {
return "", sectionName == pattern
}

// '+' means either the prefix verbatim, or prefix plus extra data
// delimited by a forward slash.
prefix := pattern[:len(pattern)-1]
if !strings.HasPrefix(sectionName, prefix) {
return "", false
}

extra = sectionName[len(prefix):]
if len(extra) > 0 {
if extra[0] != '/' {
return "", false
}

extra = extra[1:]
}

return extra, true
}

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

Expand Down

0 comments on commit f448f22

Please sign in to comment.