diff --git a/btf/core.go b/btf/core.go index ded7d43d4..f19d56bb2 100644 --- a/btf/core.go +++ b/btf/core.go @@ -164,7 +164,7 @@ func (k coreKind) String() string { // // Fixups are returned in the order of relos, e.g. fixup[i] is the solution // for relos[i]. -func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([]COREFixup, error) { +func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder, typeID func(Type) (TypeID, error)) ([]COREFixup, error) { if target == nil { var err error target, _, err = kernelSpec() @@ -194,14 +194,15 @@ func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([ return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) } + id, err := typeID(relo.typ) + if err != nil { + return nil, fmt.Errorf("%s: get type id: %w", relo.kind, err) + } + result[i] = COREFixup{ - kind: relo.kind, - local: uint64(relo.id), - // NB: Using relo.id as the target here is incorrect, since - // it doesn't match the BTF we generate on the fly. This isn't - // too bad for now since there are no uses of the local type ID - // in the kernel, yet. - target: uint64(relo.id), + kind: relo.kind, + local: uint64(relo.id), + target: uint64(id), } continue } diff --git a/btf/core_reloc_test.go b/btf/core_reloc_test.go index 037d8419c..24d5540f7 100644 --- a/btf/core_reloc_test.go +++ b/btf/core_reloc_test.go @@ -30,6 +30,8 @@ func TestCORERelocationLoad(t *testing.T) { return } + testutils.SkipOnOldKernel(t, "4.18", "BTF") + for _, progSpec := range spec.Programs { t.Run(progSpec.Name, func(t *testing.T) { if _, err := fh.Seek(0, io.SeekStart); err != nil { diff --git a/btf/core_test.go b/btf/core_test.go index 75420ddca..fab618395 100644 --- a/btf/core_test.go +++ b/btf/core_test.go @@ -590,7 +590,7 @@ func TestCORERelocation(t *testing.T) { relos = append(relos, reloInfo.relo) } - fixups, err := CORERelocate(relos, spec, spec.byteOrder) + fixups, err := CORERelocate(relos, spec, spec.byteOrder, spec.TypeID) if want := errs[name]; want != nil { if !errors.Is(err, want) { t.Fatal("Expected", want, "got", err) @@ -737,7 +737,7 @@ func BenchmarkCORESkBuff(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - _, err = CORERelocate([]*CORERelocation{relo}, spec, spec.byteOrder) + _, err = CORERelocate([]*CORERelocation{relo}, spec, spec.byteOrder, spec.TypeID) if err != nil { b.Fatal(err) } diff --git a/btf/ext_info.go b/btf/ext_info.go index 3eae08b28..910c07e4d 100644 --- a/btf/ext_info.go +++ b/btf/ext_info.go @@ -144,11 +144,11 @@ func AssignMetadataToInstructions( // wire format. // // Returns ErrNotSupported if the kernel doesn't support BTF-associated programs. -func MarshalExtInfos(insns asm.Instructions) (_ *Handle, funcInfos, lineInfos []byte, _ error) { +func MarshalExtInfos(insns asm.Instructions, b *Builder) (funcInfos, lineInfos []byte, _ error) { // Bail out early if the kernel doesn't support Func(Proto). If this is the // case, func_info will also be unsupported. if err := haveProgBTF(); err != nil { - return nil, nil, nil, err + return nil, nil, err } iter := insns.Iterate() @@ -160,10 +160,9 @@ func MarshalExtInfos(insns asm.Instructions) (_ *Handle, funcInfos, lineInfos [] } } - return nil, nil, nil, nil + return nil, nil, nil marshal: - var b Builder var fiBuf, liBuf bytes.Buffer for { if fn := FuncMetadata(iter.Ins); fn != nil { @@ -171,8 +170,8 @@ marshal: fn: fn, offset: iter.Offset, } - if err := fi.marshal(&fiBuf, &b); err != nil { - return nil, nil, nil, fmt.Errorf("write func info: %w", err) + if err := fi.marshal(&fiBuf, b); err != nil { + return nil, nil, fmt.Errorf("write func info: %w", err) } } @@ -181,8 +180,8 @@ marshal: line: line, offset: iter.Offset, } - if err := li.marshal(&liBuf, &b); err != nil { - return nil, nil, nil, fmt.Errorf("write line info: %w", err) + if err := li.marshal(&liBuf, b); err != nil { + return nil, nil, fmt.Errorf("write line info: %w", err) } } @@ -191,8 +190,7 @@ marshal: } } - handle, err := NewHandle(&b) - return handle, fiBuf.Bytes(), liBuf.Bytes(), err + return fiBuf.Bytes(), liBuf.Bytes(), nil } // btfExtHeader is found at the start of the .BTF.ext section. diff --git a/btf/marshal.go b/btf/marshal.go index 0d093c665..cdaf08a6c 100644 --- a/btf/marshal.go +++ b/btf/marshal.go @@ -93,6 +93,11 @@ func NewBuilder(types []Type) (*Builder, error) { return b, nil } +// Empty returns true if [Add] has not been invoked on the builder. +func (b *Builder) Empty() bool { + return len(b.types) == 0 +} + // Add a Type and allocate a stable ID for it. // // Adding the identical Type multiple times is valid and will return the same ID. diff --git a/btf/testdata/relocs-eb.elf b/btf/testdata/relocs-eb.elf index 7af281b30..4fce1add7 100644 Binary files a/btf/testdata/relocs-eb.elf and b/btf/testdata/relocs-eb.elf differ diff --git a/btf/testdata/relocs-el.elf b/btf/testdata/relocs-el.elf index 6983abbee..e2d34ab95 100644 Binary files a/btf/testdata/relocs-el.elf and b/btf/testdata/relocs-el.elf differ diff --git a/btf/testdata/relocs.c b/btf/testdata/relocs.c index 766dc8158..92f83400f 100644 --- a/btf/testdata/relocs.c +++ b/btf/testdata/relocs.c @@ -29,13 +29,6 @@ union u { typedef union u u_t; -#define local_id_zero(expr) \ - ({ \ - if (bpf_core_type_id_local(expr) != 0) { \ - return __LINE__; \ - } \ - }) - #define local_id_not_zero(expr) \ ({ \ if (bpf_core_type_id_local(expr) == 0) { \ @@ -43,9 +36,9 @@ typedef union u u_t; } \ }) -#define target_and_local_id_match(expr) \ +#define target_and_local_id_dont_match(expr) \ ({ \ - if (bpf_core_type_id_kernel(expr) != bpf_core_type_id_local(expr)) { \ + if (bpf_core_type_id_kernel(expr) == bpf_core_type_id_local(expr)) { \ return __LINE__; \ } \ }) @@ -69,19 +62,23 @@ __section("socket_filter/type_ids") int type_ids() { local_id_not_zero(const u_t); local_id_not_zero(volatile u_t); + // In this context, target is the BTF generated by clang. local is + // generated on the fly by the library. There is a low chance that + // the order on both is the same, so we assert this to make sure that + // CO-RE uses the IDs from the dynamic BTF. // Qualifiers on types crash clang. - target_and_local_id_match(struct s); - target_and_local_id_match(s_t); - // target_and_local_id_match(const s_t); - // target_and_local_id_match(volatile s_t); - target_and_local_id_match(enum e); - target_and_local_id_match(e_t); - // target_and_local_id_match(const e_t); - // target_and_local_id_match(volatile e_t); - target_and_local_id_match(union u); - target_and_local_id_match(u_t); - // target_and_local_id_match(const u_t); - // target_and_local_id_match(volatile u_t); + target_and_local_id_dont_match(struct s); + target_and_local_id_dont_match(s_t); + // target_and_local_id_dont_match(const s_t); + // target_and_local_id_dont_match(volatile s_t); + target_and_local_id_dont_match(enum e); + target_and_local_id_dont_match(e_t); + // target_and_local_id_dont_match(const e_t); + // target_and_local_id_dont_match(volatile e_t); + target_and_local_id_dont_match(union u); + target_and_local_id_dont_match(u_t); + // target_and_local_id_dont_match(const u_t); + // target_and_local_id_dont_match(volatile u_t); return 0; } diff --git a/linker.go b/linker.go index b653b805e..38cc12536 100644 --- a/linker.go +++ b/linker.go @@ -106,7 +106,7 @@ func hasFunctionReferences(insns asm.Instructions) bool { // // Passing a nil target will relocate against the running kernel. insns are // modified in place. -func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOrder) error { +func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOrder, b *btf.Builder) error { var relos []*btf.CORERelocation var reloInsns []*asm.Instruction iter := insns.Iterate() @@ -125,7 +125,7 @@ func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOr bo = internal.NativeEndian } - fixups, err := btf.CORERelocate(relos, target, bo) + fixups, err := btf.CORERelocate(relos, target, bo, b.Add) if err != nil { return err } diff --git a/prog.go b/prog.go index 6d46a0422..0a58b9332 100644 --- a/prog.go +++ b/prog.go @@ -242,15 +242,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er insns := make(asm.Instructions, len(spec.Instructions)) copy(insns, spec.Instructions) - handle, fib, lib, err := btf.MarshalExtInfos(insns) + var b btf.Builder + fib, lib, err := btf.MarshalExtInfos(insns, &b) if err != nil && !errors.Is(err, btf.ErrNotSupported) { - return nil, fmt.Errorf("load ext_infos: %w", err) + return nil, fmt.Errorf("marshal ext_infos: %w", err) } - if handle != nil { - defer handle.Close() - - attr.ProgBtfFd = uint32(handle.FD()) - + if len(fib) > 0 || len(lib) > 0 { attr.FuncInfoRecSize = btf.FuncInfoSize attr.FuncInfoCnt = uint32(len(fib)) / btf.FuncInfoSize attr.FuncInfo = sys.NewSlicePointer(fib) @@ -260,10 +257,21 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er attr.LineInfo = sys.NewSlicePointer(lib) } - if err := applyRelocations(insns, opts.KernelTypes, spec.ByteOrder); err != nil { + if err := applyRelocations(insns, opts.KernelTypes, spec.ByteOrder, &b); err != nil { return nil, fmt.Errorf("apply CO-RE relocations: %w", err) } + var handle *btf.Handle + if !b.Empty() { + handle, err = btf.NewHandle(&b) + if err != nil { + return nil, fmt.Errorf("load BTF: %w", err) + } + defer handle.Close() + + attr.ProgBtfFd = uint32(handle.FD()) + } + kconfig, err := resolveKconfigReferences(insns) if err != nil { return nil, fmt.Errorf("resolve .kconfig: %w", err)