diff --git a/info.go b/info.go index 4318540a3..aa84883af 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,34 @@ 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 +} + +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 a3c31d3e2..d7599fc61 100644 --- a/info_test.go +++ b/info_test.go @@ -510,3 +510,116 @@ 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") + } + } +} + +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) + } + } +} 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