Skip to content

Commit

Permalink
btf: add CO-RE read test that fails unless relocations were correctly…
Browse files Browse the repository at this point in the history
… applied

The linker rework in 9d739bd caused a piece of code that appended CORERelos
to the entrypoint instruction stream to be forgotten. The existing CO-RE tests
were all written in a way where the BPF programs would return 0 (success) if
no CO-RE relocations were applied at all.

This caused CO-RE relocations against functions (subprogs) that were only
included by a bpf2bpf call to be skipped.

This patch adds a broken test program that returns non-zero if left unaltered,
and requires CO-RE relocation against another BTF blob to return 0.
Additionally, the checks are performed from a non-inlined subprogram to ensure
CO-RE relocations against subprogs work.

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed Mar 7, 2022
1 parent 9853f79 commit b605a83
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 2 deletions.
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
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()

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)
}

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

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)
}
})
}
})
}
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);
}

0 comments on commit b605a83

Please sign in to comment.