Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 0 additions & 80 deletions asm/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,45 +233,13 @@ func (ins *Instruction) AssociateMap(m FDer) error {
return nil
}

// RewriteMapPtr changes an instruction to use a new map fd.
//
// Returns an error if the instruction doesn't load a map.
//
// Deprecated: use AssociateMap instead. If you cannot provide a Map,
// wrap an fd in a type implementing FDer.
func (ins *Instruction) RewriteMapPtr(fd int) error {
if !ins.IsLoadFromMap() {
return errors.New("not a load from a map")
}

ins.encodeMapFD(fd)

return nil
}

func (ins *Instruction) encodeMapFD(fd int) {
// Preserve the offset value for direct map loads.
offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
rawFd := uint64(uint32(fd))
ins.Constant = int64(offset | rawFd)
}

// MapPtr returns the map fd for this instruction.
//
// The result is undefined if the instruction is not a load from a map,
// see IsLoadFromMap.
//
// Deprecated: use Map() instead.
func (ins *Instruction) MapPtr() int {
// If there is a map associated with the instruction, return its FD.
if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
return fd.(FDer).FD()
}

// Fall back to the fd stored in the Constant field
return ins.mapFd()
}

// mapFd returns the map file descriptor stored in the 32 least significant
// bits of ins' Constant field.
func (ins *Instruction) mapFd() int {
Expand Down Expand Up @@ -486,13 +454,6 @@ func (ins Instruction) WithSymbol(name string) Instruction {
return ins
}

// Sym creates a symbol.
//
// Deprecated: use WithSymbol instead.
func (ins Instruction) Sym(name string) Instruction {
return ins.WithSymbol(name)
}

// Symbol returns the value ins has been marked with using WithSymbol,
// otherwise returns an empty string. A symbol is often an Instruction
// at the start of a function body.
Expand Down Expand Up @@ -632,39 +593,6 @@ func (insns Instructions) AssociateMap(symbol string, m FDer) error {
return nil
}

// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
//
// Returns ErrUnreferencedSymbol if the symbol isn't used.
//
// Deprecated: use AssociateMap instead.
func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
if symbol == "" {
return errors.New("empty symbol")
}

var found bool
for i := range insns {
ins := &insns[i]
if ins.Reference() != symbol {
continue
}

if !ins.IsLoadFromMap() {
return errors.New("not a load from a map")
}

ins.encodeMapFD(fd)

found = true
}

if !found {
return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
}

return nil
}

// SymbolOffsets returns the set of symbols and their offset in
// the instructions.
func (insns Instructions) SymbolOffsets() (map[string]int, error) {
Expand Down Expand Up @@ -968,11 +896,3 @@ func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, erro
return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
}
}

// IsUnreferencedSymbol returns true if err was caused by
// an unreferenced symbol.
//
// Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
func IsUnreferencedSymbol(err error) bool {
return errors.Is(err, ErrUnreferencedSymbol)
}
58 changes: 0 additions & 58 deletions asm/instruction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"math"
Expand Down Expand Up @@ -97,36 +96,6 @@ func TestSignedJump(t *testing.T) {
}
}

func TestInstructionRewriteMapConstant(t *testing.T) {
ins := LoadMapValue(R0, 123, 321)

qt.Assert(t, qt.Equals(ins.MapPtr(), 123))
qt.Assert(t, qt.Equals(ins.mapOffset(), 321))

qt.Assert(t, qt.IsNil(ins.RewriteMapPtr(-1)))
qt.Assert(t, qt.Equals(ins.MapPtr(), -1))

qt.Assert(t, qt.IsNil(ins.RewriteMapPtr(1)))
qt.Assert(t, qt.Equals(ins.MapPtr(), 1))

// mapOffset should be unchanged after rewriting the pointer.
qt.Assert(t, qt.Equals(ins.mapOffset(), 321))

qt.Assert(t, qt.IsNil(ins.RewriteMapOffset(123)))
qt.Assert(t, qt.Equals(ins.mapOffset(), 123))

// MapPtr should be unchanged.
qt.Assert(t, qt.Equals(ins.MapPtr(), 1))

ins = Mov.Imm(R1, 32)
if err := ins.RewriteMapPtr(1); err == nil {
t.Error("RewriteMapPtr rewriting bogus instruction")
}
if err := ins.RewriteMapOffset(1); err == nil {
t.Error("RewriteMapOffset rewriting bogus instruction")
}
}

func TestInstructionLoadMapValue(t *testing.T) {
ins := LoadMapValue(R0, 1, 123)
if !ins.IsLoadFromMap() {
Expand All @@ -140,33 +109,6 @@ func TestInstructionLoadMapValue(t *testing.T) {
}
}

func TestInstructionsRewriteMapPtr(t *testing.T) {
insns := Instructions{
LoadMapPtr(R1, 0).WithReference("good"),
Return(),
}

if err := insns.RewriteMapPtr("good", 1); err != nil {
t.Fatal(err)
}

if insns[0].Constant != 1 {
t.Error("Constant should be 1, have", insns[0].Constant)
}

if err := insns.RewriteMapPtr("good", 2); err != nil {
t.Fatal(err)
}

if insns[0].Constant != 2 {
t.Error("Constant should be 2, have", insns[0].Constant)
}

if err := insns.RewriteMapPtr("bad", 1); !errors.Is(err, ErrUnreferencedSymbol) {
t.Error("Rewriting unreferenced map doesn't return appropriate error")
}
}

func TestInstructionWithMetadata(t *testing.T) {
ins := LoadImm(R0, 123, DWord).WithSymbol("abc")
ins2 := LoadImm(R0, 567, DWord).WithMetadata(ins.Metadata)
Expand Down
92 changes: 0 additions & 92 deletions collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"slices"
"strings"

"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/kallsyms"
Expand Down Expand Up @@ -95,97 +94,6 @@ func copyMapOfSpecs[T interface{ Copy() T }](m map[string]T) map[string]T {
return cpy
}

// RewriteMaps replaces all references to specific maps.
//
// Use this function to use pre-existing maps instead of creating new ones
// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps.
//
// Returns an error if a named map isn't used in at least one program.
//
// Deprecated: Pass CollectionOptions.MapReplacements when loading the Collection
// instead.
func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
for symbol, m := range maps {
// have we seen a program that uses this symbol / map
seen := false
for progName, progSpec := range cs.Programs {
err := progSpec.Instructions.AssociateMap(symbol, m)

switch {
case err == nil:
seen = true

case errors.Is(err, asm.ErrUnreferencedSymbol):
// Not all programs need to use the map

default:
return fmt.Errorf("program %s: %w", progName, err)
}
}

if !seen {
return fmt.Errorf("map %s not referenced by any programs", symbol)
}

// Prevent NewCollection from creating rewritten maps
delete(cs.Maps, symbol)
}

return nil
}

// MissingConstantsError is returned by [CollectionSpec.RewriteConstants].
type MissingConstantsError struct {
// The constants missing from .rodata.
Constants []string
}

func (m *MissingConstantsError) Error() string {
return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", "))
}

// RewriteConstants replaces the value of multiple constants.
//
// The constant must be defined like so in the C program:
//
// volatile const type foobar;
// volatile const type foobar = default;
//
// Replacement values must be of the same length as the C sizeof(type).
// If necessary, they are marshalled according to the same rules as
// map values.
//
// From Linux 5.5 the verifier will use constants to eliminate dead code.
//
// Returns an error wrapping [MissingConstantsError] if a constant doesn't exist.
//
// Deprecated: Use [CollectionSpec.Variables] to interact with constants instead.
// RewriteConstants is now a wrapper around the VariableSpec API.
func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
var missing []string
for n, c := range consts {
v, ok := cs.Variables[n]
if !ok {
missing = append(missing, n)
continue
}

if !v.Constant() {
return fmt.Errorf("variable %s is not a constant", n)
}

if err := v.Set(c); err != nil {
return fmt.Errorf("rewriting constant %s: %w", n, err)
}
}

if len(missing) != 0 {
return fmt.Errorf("rewrite constants: %w", &MissingConstantsError{Constants: missing})
}

return nil
}

// Assign the contents of a CollectionSpec to a struct.
//
// This function is a shortcut to manually checking the presence
Expand Down
43 changes: 0 additions & 43 deletions collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,49 +126,6 @@ var loadKeyFromMapProgramSpec = &ProgramSpec{
},
}

func TestCollectionSpecRewriteMaps(t *testing.T) {
cs := &CollectionSpec{
Maps: map[string]*MapSpec{
"test-map": {
Type: Array,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
},
},
Programs: map[string]*ProgramSpec{
"test-prog": loadKeyFromMapProgramSpec.Copy(),
},
}

// Override the map with another one
newMap := mustNewMap(t, cs.Maps["test-map"], nil)

err := newMap.Put(uint32(0), uint32(2))
if err != nil {
t.Fatal(err)
}

err = cs.RewriteMaps(map[string]*Map{
"test-map": newMap,
})
if err != nil {
t.Fatal(err)
}

if cs.Maps["test-map"] != nil {
t.Error("RewriteMaps doesn't remove map from CollectionSpec.Maps")
}

coll := mustNewCollection(t, cs, nil)

ret := mustRun(t, coll.Programs["test-prog"], nil)

if ret != 2 {
t.Fatal("new / override map not used")
}
}

func TestCollectionSpecMapReplacements(t *testing.T) {
cs := &CollectionSpec{
Maps: map[string]*MapSpec{
Expand Down
31 changes: 4 additions & 27 deletions elf_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,33 +242,14 @@ func TestLoadCollectionSpec(t *testing.T) {
t.Fatal("Can't parse ELF:", err)
}

err = have.RewriteConstants(map[string]interface{}{
"arg": uint32(1),
"arg2": uint32(2),
})
if err != nil {
t.Fatal("Can't rewrite constant:", err)
}

err = have.RewriteConstants(map[string]interface{}{
"totallyBogus": uint32(1),
"totallyBogus2": uint32(2),
})
if err == nil {
t.Error("Rewriting a bogus constant doesn't fail")
}

var mErr *MissingConstantsError
if !errors.As(err, &mErr) {
t.Fatal("Error doesn't wrap MissingConstantsError:", err)
}
qt.Assert(t, qt.ContentEquals(mErr.Constants, []string{"totallyBogus", "totallyBogus2"}))

qt.Assert(t, qt.Equals(have.Maps["perf_event_array"].ValueSize, 0))
qt.Assert(t, qt.IsNotNil(have.Maps["perf_event_array"].Value))

qt.Assert(t, qt.CmpEquals(have, coll, csCmpOpts))

qt.Assert(t, qt.IsNil(have.Variables["arg"].Set(uint32(1))))
qt.Assert(t, qt.IsNil(have.Variables["arg2"].Set(uint32(2))))

if have.ByteOrder != internal.NativeEndian {
return
}
Expand Down Expand Up @@ -361,11 +342,7 @@ func TestFreezeRodata(t *testing.T) {
Program *Program `ebpf:"freeze_rodata"`
}

if err := spec.RewriteConstants(map[string]interface{}{
"ret": uint32(1),
}); err != nil {
t.Fatal(err)
}
qt.Assert(t, qt.IsNil(spec.Variables["ret"].Set(uint32(1))))

mustLoadAndAssign(t, spec, &obj, nil)
obj.Program.Close()
Expand Down
Loading
Loading