From c79d9231cf313beac37ff5db3ff85665f7e41b4c Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 3 Oct 2024 21:01:45 +0800 Subject: [PATCH 1/2] info: expose ksym info by ProgramInfo Exposes additional metadata fields from `bpf_prog_info` to `ProgramInfo`: - nr_jited_ksyms - jited_ksyms Meanwhile, expose ksym addresses from ProgramInfo. `bpf_prog_info` supports ksym info since commit [bpf: get kernel symbol addresses via syscall](https://github.com/torvalds/linux/commit/dbecd7388476aedeb66389febea84d5450d28773). Signed-off-by: Leon Hwang --- info.go | 20 +++++++++++++++++++ info_test.go | 36 +++++++++++++++++++++++++++++++++++ internal/cmd/gentypes/main.go | 1 + internal/sys/types.go | 2 +- 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/info.go b/info.go index 4318540a3..3bf9b7b0a 100644 --- a/info.go +++ b/info.go @@ -204,6 +204,8 @@ type ProgramInfo struct { numLineInfos uint32 funcInfos []byte numFuncInfos uint32 + ksymInfos []uint64 + numKsymInfos uint32 } func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { @@ -281,6 +283,14 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { makeSecondCall = true } + if info.NrJitedKsyms > 0 { + pi.ksymInfos = make([]uint64, info.NrJitedKsyms) + info2.JitedKsyms = sys.NewPointer(unsafe.Pointer(&pi.ksymInfos[0])) + info2.NrJitedKsyms = info.NrJitedKsyms + pi.numKsymInfos = info.NrJitedKsyms + makeSecondCall = true + } + if makeSecondCall { if err := sys.ObjInfo(fd, &info2); err != nil { return nil, err @@ -508,6 +518,16 @@ func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) { return pi.verifiedInstructions, pi.verifiedInstructions > 0 } +// KsymAddrs returns the ksym addresses of bpf programs, including subprograms, +// of current bpf program. The addresses are same as the ones in /proc/kallsyms. +// +// Available from 4.18. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) KsymAddrs() ([]uint64, bool) { + return pi.ksymInfos, pi.numKsymInfos > 0 +} + func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) if err != nil { diff --git a/info_test.go b/info_test.go index a3c31d3e2..0f6eadaa1 100644 --- a/info_test.go +++ b/info_test.go @@ -510,3 +510,39 @@ func TestZero(t *testing.T) { qt.Assert(t, qt.IsTrue(zero(&inul))) qt.Assert(t, qt.IsFalse(zero(&ione))) } + +func TestProgInfoKsym(t *testing.T) { + testutils.SkipOnOldKernel(t, "4.18", "Program ksym addresses") + + spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf")) + if err != nil { + t.Fatal(err) + } + + var obj struct { + Main *Program `ebpf:"xdp_prog"` + } + + err = spec.LoadAndAssign(&obj, nil) + testutils.SkipIfNotSupported(t, err) + if err != nil { + t.Fatal(err) + } + defer obj.Main.Close() + + info, err := obj.Main.Info() + if err != nil { + t.Fatal(err) + } + + expectedNumKsymInfos := uint32(5) + if info.numKsymInfos != expectedNumKsymInfos { + t.Errorf("expected %d ksym infos, got %d", expectedNumKsymInfos, info.numKsymInfos) + } + + for _, ksym := range info.ksymInfos { + if ksym == 0 { + t.Errorf("expected non-zero ksym address") + } + } +} diff --git a/internal/cmd/gentypes/main.go b/internal/cmd/gentypes/main.go index 7b693ca5d..11f285943 100644 --- a/internal/cmd/gentypes/main.go +++ b/internal/cmd/gentypes/main.go @@ -200,6 +200,7 @@ import ( replace(pointer, "xlated_prog_insns"), replace(pointer, "map_ids"), replace(pointer, "line_info"), + replace(pointer, "jited_ksyms"), replace(pointer, "func_info"), replace(btfID, "btf_id", "attach_btf_obj_id"), replace(typeID, "attach_btf_id"), diff --git a/internal/sys/types.go b/internal/sys/types.go index d46164ae8..19b287a5c 100644 --- a/internal/sys/types.go +++ b/internal/sys/types.go @@ -733,7 +733,7 @@ type ProgInfo struct { NetnsIno uint64 NrJitedKsyms uint32 NrJitedFuncLens uint32 - JitedKsyms uint64 + JitedKsyms Pointer JitedFuncLens uint64 BtfId BTFID FuncInfoRecSize uint32 From 8a66787d58992eaf96ffc831ed7f91f23882f1cd Mon Sep 17 00:00:00 2001 From: Leon Hwang Date: Thu, 3 Oct 2024 21:52:28 +0800 Subject: [PATCH 2/2] info: expose func info from ProgramInfo `bpf_prog_info` supports func info since commit [bpf: Introduce bpf_func_info](https://github.com/torvalds/linux/commit/838e96904ff3fc6c30e5ebbc611474669856e3c0). Signed-off-by: Leon Hwang --- info.go | 18 ++++++++++++ info_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/info.go b/info.go index 3bf9b7b0a..aa84883af 100644 --- a/info.go +++ b/info.go @@ -528,6 +528,24 @@ func (pi *ProgramInfo) KsymAddrs() ([]uint64, bool) { return pi.ksymInfos, pi.numKsymInfos > 0 } +type ProgramFunctionInfo = sys.FuncInfo + +// FuncInfos returns the function information of bpf programs, including +// subprograms, of current bpf program. +// +// Available from 5.0. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) FuncInfos() ([]ProgramFunctionInfo, bool) { + if pi.numFuncInfos == 0 { + return nil, false + } + + ptr := (*ProgramFunctionInfo)(unsafe.Pointer(&pi.funcInfos[0])) + funcs := unsafe.Slice(ptr, int(pi.numFuncInfos)) + return funcs, true +} + func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) if err != nil { diff --git a/info_test.go b/info_test.go index 0f6eadaa1..d7599fc61 100644 --- a/info_test.go +++ b/info_test.go @@ -546,3 +546,80 @@ func TestProgInfoKsym(t *testing.T) { } } } + +func TestProgInfoFuncInfo(t *testing.T) { + testutils.SkipOnOldKernel(t, "5.0", "Program func info") + + spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf")) + if err != nil { + t.Fatal(err) + } + + var obj struct { + Main *Program `ebpf:"xdp_prog"` + } + + err = spec.LoadAndAssign(&obj, nil) + testutils.SkipIfNotSupported(t, err) + if err != nil { + t.Fatal(err) + } + defer obj.Main.Close() + + info, err := obj.Main.Info() + if err != nil { + t.Fatal(err) + } + + funcs, ok := info.FuncInfos() + if !ok { + t.Fatalf("expected function info to be available") + } + + expectedNumFuncInfos := uint32(5) + if info.numFuncInfos != expectedNumFuncInfos { + t.Fatalf("expected %d ksym infos, got %d", expectedNumFuncInfos, info.numFuncInfos) + } + + handle, err := obj.Main.Handle() + if err != nil { + t.Fatalf("failed to get program handle: %v", err) + } + defer handle.Close() + + btfSpec, err := handle.Spec(nil) + if err != nil { + t.Errorf("failed to get BTF spec: %v", err) + return + } + + expectedFuncInfo := map[string]bool{ + "xdp_prog": false, + "static_fn": false, + "global_fn": false, + "global_fn2": false, + "global_fn3": false, + } + + for _, funcInfo := range funcs { + btfInfo, err := btfSpec.TypeByID(sys.TypeID(funcInfo.TypeId)) + if err != nil { + t.Errorf("failed to get BTF type info: %v", err) + return + } + + funcBtfInfo, ok := btfInfo.(*btf.Func) + if !ok { + t.Errorf("expected BTF type to be a function, got %T", btfInfo) + return + } + + expectedFuncInfo[funcBtfInfo.Name] = true + } + + for fn, found := range expectedFuncInfo { + if !found { + t.Errorf("func %q not found", fn) + } + } +}