Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute and test CO-RE relocations of bpf2bpf subprogs #590

Merged
merged 2 commits into from Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Expand Up @@ -35,7 +35,9 @@ TARGETS := \
testdata/map_spin_lock \
testdata/subprog_reloc \
testdata/fwd_decl \
internal/btf/testdata/relocs
internal/btf/testdata/relocs \
internal/btf/testdata/relocs_read \
internal/btf/testdata/relocs_read_tgt

.PHONY: all clean container-all container-shell generate

Expand Down
8 changes: 4 additions & 4 deletions internal/btf/btf.go
Expand Up @@ -843,11 +843,11 @@ func (p *Program) Spec() *Spec {
return p.spec
}

// Fixups returns the changes required to adjust the program to the target.
// CORERelocate returns the changes required to adjust the program to the target.
//
// Passing a nil target will relocate against the running kernel.
func (p *Program) Fixups(target *Spec) (COREFixups, error) {
if len(p.CoreRelos) == 0 {
func CORERelocate(local, target *Spec, relos CoreRelos) (COREFixups, error) {
if len(relos) == 0 {
return nil, nil
}

Expand All @@ -859,7 +859,7 @@ func (p *Program) Fixups(target *Spec) (COREFixups, error) {
}
}

return coreRelocate(p.spec, target, p.CoreRelos)
return coreRelocate(local, target, relos)
}

func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte {
Expand Down
61 changes: 60 additions & 1 deletion internal/btf/core_reloc_test.go
@@ -1,17 +1,19 @@
package btf_test

import (
"fmt"
"io"
"os"
"strings"
"testing"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/testutils"
)

func TestCoreRelocationLoad(t *testing.T) {
testutils.Files(t, testutils.Glob(t, "testdata/*-el.elf"), func(t *testing.T, file string) {
testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) {
fh, err := os.Open(file)
if err != nil {
t.Fatal(err)
Expand All @@ -23,6 +25,10 @@ func TestCoreRelocationLoad(t *testing.T) {
t.Fatal(err)
}

if spec.ByteOrder != internal.NativeEndian {
return
}

for _, progSpec := range spec.Programs {
t.Run(progSpec.Name, func(t *testing.T) {
if _, err := fh.Seek(0, io.SeekStart); err != nil {
Expand Down Expand Up @@ -60,3 +66,56 @@ func TestCoreRelocationLoad(t *testing.T) {
}
})
}

func TestCoreRelocationRead(t *testing.T) {
testutils.Files(t, testutils.Glob(t, "testdata/relocs_read-*.elf"), func(t *testing.T, file string) {
fh, err := os.Open(file)
if err != nil {
t.Fatal(err)
}
defer fh.Close()

spec, err := ebpf.LoadCollectionSpecFromReader(fh)
if err != nil {
t.Fatal(err)
}

if spec.ByteOrder != internal.NativeEndian {
return
}

tgt, err := os.Open(fmt.Sprintf("testdata/relocs_read_tgt-%s.elf", internal.ClangEndian))
if err != nil {
t.Fatal(err)
}
defer tgt.Close()

testutils.SkipOnOldKernel(t, "4.16", "bpf2bpf calls")

for _, progSpec := range spec.Programs {
t.Run(progSpec.Name, func(t *testing.T) {
if _, err := tgt.Seek(0, io.SeekStart); err != nil {
t.Fatal(err)
}

prog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{
TargetBTF: tgt,
})
if err != nil {
t.Fatal("Load program:", err)
}
defer prog.Close()

ret, _, err := prog.Test(make([]byte, 14))
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("Error when running:", err)
}

if ret != 0 {
t.Error("Assertion failed on line", ret)
}
})
}
})
}
2 changes: 1 addition & 1 deletion internal/btf/core_test.go
Expand Up @@ -509,7 +509,7 @@ func TestCoreRelocation(t *testing.T) {
t.Fatal("Retrieve program:", err)
}

relos, err := prog.Fixups(spec)
relos, err := CORERelocate(prog.Spec(), spec, prog.CoreRelos)
if want := errs[name]; want != nil {
if !errors.Is(err, want) {
t.Fatal("Expected", want, "got", err)
Expand Down
11 changes: 11 additions & 0 deletions internal/btf/ext_info.go
Expand Up @@ -447,6 +447,17 @@ type CoreRelo struct {

type CoreRelos []CoreRelo

// Offset adds offset to the instruction offset of all CoreRelos
// and returns the result.
func (cr CoreRelos) Offset(offset uint32) CoreRelos {
var relos CoreRelos
for _, relo := range cr {
relo.insnOff += offset
relos = append(relos, relo)
}
return relos
}

var extInfoReloSize = binary.Size(bpfCoreRelo{})

// parseCoreRelos parses a core_relos sub-section within .BTF.ext ito a map of
Expand Down
Binary file added internal/btf/testdata/relocs_read-eb.elf
Binary file not shown.
Binary file added internal/btf/testdata/relocs_read-el.elf
Binary file not shown.
37 changes: 37 additions & 0 deletions internal/btf/testdata/relocs_read.c
@@ -0,0 +1,37 @@
#include "../../../testdata/common.h"

#define core_access __builtin_preserve_access_index

// Struct with the members declared in the wrong order. Accesses need
// a successful CO-RE relocation against the type in relocs_read_tgt.c
// for the test below to pass.
struct s {
char b;
char a;
};

// Perform a read from a subprog to ensure CO-RE relocations
// occurring there are tracked and executed in the final linked program.
__attribute__((noinline)) int read_subprog() {
struct s foo = {
.a = 0,
.b = 1,
};

if (core_access(foo.a) == 0)
return __LINE__;

if (core_access(foo.b) == 1)
return __LINE__;

return 0;
}

__section("socket") int reads() {
int ret = read_subprog();
if (ret)
return ret;

return 0;
}

Binary file added internal/btf/testdata/relocs_read_tgt-eb.elf
Binary file not shown.
Binary file added internal/btf/testdata/relocs_read_tgt-el.elf
Binary file not shown.
16 changes: 16 additions & 0 deletions internal/btf/testdata/relocs_read_tgt.c
@@ -0,0 +1,16 @@
/*
This file exists to emit ELFs with specific BTF types to use as target BTF
in tests. It can be made redundant when btf.Spec can be handcrafted and
passed as a CO-RE target in the future.
*/

#define core_access __builtin_preserve_access_index

struct s {
char a;
char b;
};

int dummy() {
return core_access((struct s){}.a);
}
19 changes: 19 additions & 0 deletions linker.go
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/btf"
)

// The linker is responsible for resolving bpf-to-bpf calls between programs
Expand Down Expand Up @@ -80,6 +81,24 @@ func findReferences(progs map[string]*ProgramSpec) error {
return nil
}

// collectCoreRelos returns a list of CO-RE relocations of the layout's progs
// in order.
func collectCoreRelos(layout []reference) btf.CoreRelos {
ti-mo marked this conversation as resolved.
Show resolved Hide resolved
if len(layout) == 0 {
ti-mo marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

var relos btf.CoreRelos
for _, sym := range layout {
if sym.spec.BTF == nil {
continue
ti-mo marked this conversation as resolved.
Show resolved Hide resolved
}
relos = append(relos, sym.spec.BTF.CoreRelos.Offset(uint32(sym.offset))...)
}

return relos
}

// marshalFuncInfos returns the BTF func infos of all progs in order.
func marshalFuncInfos(layout []reference) ([]byte, error) {
if len(layout) == 0 {
Expand Down
3 changes: 2 additions & 1 deletion prog.go
Expand Up @@ -293,7 +293,8 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
var btfDisabled bool
var core btf.COREFixups
if spec.BTF != nil {
core, err = spec.BTF.Fixups(targetBTF)
relos := collectCoreRelos(layout)
core, err = btf.CORERelocate(spec.BTF.Spec(), targetBTF, relos)
if err != nil {
return nil, fmt.Errorf("CO-RE relocations: %w", err)
}
Expand Down