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

prep work for BTF marshaling #791

Merged
merged 5 commits into from Sep 20, 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
59 changes: 28 additions & 31 deletions btf/core.go
Expand Up @@ -156,16 +156,17 @@ func (k coreKind) String() string {
}
}

// CORERelocate calculates the difference in types between local and target.
// CORERelocate calculates changes needed to adjust eBPF instructions for differences
// in types.
//
// Returns a list of fixups which can be applied to instructions to make them
// match the target type(s).
//
// Fixups are returned in the order of relos, e.g. fixup[i] is the solution
// for relos[i].
func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, error) {
if local.byteOrder != target.byteOrder {
return nil, fmt.Errorf("can't relocate %s against %s", local.byteOrder, target.byteOrder)
func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([]COREFixup, error) {
if bo != target.byteOrder {
return nil, fmt.Errorf("can't relocate %s against %s", bo, target.byteOrder)
}

type reloGroup struct {
Expand All @@ -185,15 +186,14 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor)
}

id, err := local.TypeID(relo.typ)
if err != nil {
return nil, fmt.Errorf("%s: %w", relo.kind, err)
}

result[i] = COREFixup{
kind: relo.kind,
local: uint32(id),
target: uint32(id),
kind: relo.kind,
local: uint32(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: uint32(relo.id),
}
continue
}
Expand All @@ -214,7 +214,7 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
}

targets := target.namedTypes[newEssentialName(localTypeName)]
fixups, err := coreCalculateFixups(local, target, localType, targets, group.relos)
fixups, err := coreCalculateFixups(group.relos, target, targets, bo)
if err != nil {
return nil, fmt.Errorf("relocate %s: %w", localType, err)
}
Expand All @@ -230,18 +230,13 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
var errAmbiguousRelocation = errors.New("ambiguous relocation")
var errImpossibleRelocation = errors.New("impossible relocation")

// coreCalculateFixups calculates the fixups for the given relocations using
// the "best" target.
// coreCalculateFixups finds the target type that best matches all relocations.
//
// All relos must target the same type.
//
// The best target is determined by scoring: the less poisoning we have to do
// the better the target is.
func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type, relos []*CORERelocation) ([]COREFixup, error) {
localID, err := localSpec.TypeID(local)
if err != nil {
return nil, fmt.Errorf("local type ID: %w", err)
}
local = Copy(local, UnderlyingType)

func coreCalculateFixups(relos []*CORERelocation, targetSpec *Spec, targets []Type, bo binary.ByteOrder) ([]COREFixup, error) {
bestScore := len(relos)
var bestFixups []COREFixup
for i := range targets {
Expand All @@ -254,7 +249,7 @@ func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type
score := 0 // lower is better
fixups := make([]COREFixup, 0, len(relos))
for _, relo := range relos {
fixup, err := coreCalculateFixup(localSpec.byteOrder, local, localID, target, targetID, relo)
fixup, err := coreCalculateFixup(relo, target, targetID, bo)
if err != nil {
return nil, fmt.Errorf("target %s: %w", target, err)
}
Expand Down Expand Up @@ -305,7 +300,7 @@ func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type

// coreCalculateFixup calculates the fixup for a single local type, target type
// and relocation.
func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID, target Type, targetID TypeID, relo *CORERelocation) (COREFixup, error) {
func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo binary.ByteOrder) (COREFixup, error) {
fixup := func(local, target uint32) (COREFixup, error) {
return COREFixup{kind: relo.kind, local: local, target: target}, nil
}
Expand All @@ -320,6 +315,8 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,
}
zero := COREFixup{}

local := Copy(relo.typ, UnderlyingType)

switch relo.kind {
case reloTypeIDTarget, reloTypeSize, reloTypeExists:
if len(relo.accessor) > 1 || relo.accessor[0] != 0 {
Expand All @@ -339,7 +336,7 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,
return fixup(1, 1)

case reloTypeIDTarget:
return fixup(uint32(localID), uint32(targetID))
return fixup(uint32(relo.id), uint32(targetID))

case reloTypeSize:
localSize, err := Sizeof(local)
Expand Down Expand Up @@ -427,7 +424,7 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,

case reloFieldLShiftU64:
var target uint32
if byteOrder == binary.LittleEndian {
if bo == binary.LittleEndian {
targetSize, err := targetField.sizeBits()
if err != nil {
return zero, err
Expand Down Expand Up @@ -858,7 +855,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
depth = 0
)

for ; l != nil && t != nil; l, t = localTs.shift(), targetTs.shift() {
for ; l != nil && t != nil; l, t = localTs.Shift(), targetTs.Shift() {
if depth >= maxTypeDepth {
return errors.New("types are nested too deep")
}
Expand All @@ -876,8 +873,8 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {

case *Pointer, *Array:
depth++
localType.walk(&localTs)
targetType.walk(&targetTs)
walkType(localType, localTs.Push)
walkType(targetType, targetTs.Push)

case *FuncProto:
tv := targetType.(*FuncProto)
Expand All @@ -886,8 +883,8 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
}

depth++
localType.walk(&localTs)
targetType.walk(&targetTs)
walkType(localType, localTs.Push)
walkType(targetType, targetTs.Push)

default:
return fmt.Errorf("unsupported type %T", localType)
Expand Down
2 changes: 1 addition & 1 deletion btf/core_test.go
Expand Up @@ -546,7 +546,7 @@ func TestCORERelocation(t *testing.T) {
relos = append(relos, reloInfo.relo)
}

fixups, err := CORERelocate(spec, spec, relos)
fixups, err := CORERelocate(relos, spec, spec.byteOrder)
if want := errs[name]; want != nil {
if !errors.Is(err, want) {
t.Fatal("Expected", want, "got", err)
Expand Down
4 changes: 4 additions & 0 deletions btf/ext_info.go
Expand Up @@ -605,9 +605,12 @@ type bpfCORERelo struct {
}

type CORERelocation struct {
// The local type of the relocation, stripped of typedefs and qualifiers.
typ Type
accessor coreAccessor
kind coreKind
// The ID of the local type in the source BTF.
id TypeID
}

func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
Expand Down Expand Up @@ -641,6 +644,7 @@ func newRelocationInfo(relo bpfCORERelo, ts types, strings *stringTable) (*coreR
typ,
accessor,
relo.Kind,
relo.TypeID,
},
asm.RawInstructionOffset(relo.InsnOff),
}, nil
Expand Down
56 changes: 56 additions & 0 deletions btf/traversal.go
@@ -0,0 +1,56 @@
package btf

import "fmt"

// walkType calls fn on each child of typ.
func walkType(typ Type, fn func(*Type)) {
ti-mo marked this conversation as resolved.
Show resolved Hide resolved
// Explicitly type switch on the most common types to allow the inliner to
// do its work. This avoids allocating intermediate slices from walk() on
// the heap.
switch v := typ.(type) {
case *Void, *Int, *Enum, *Fwd, *Float:
// No children to traverse.
case *Pointer:
fn(&v.Target)
case *Array:
fn(&v.Index)
fn(&v.Type)
case *Struct:
for i := range v.Members {
fn(&v.Members[i].Type)
}
case *Union:
for i := range v.Members {
fn(&v.Members[i].Type)
}
case *Typedef:
fn(&v.Type)
case *Volatile:
fn(&v.Type)
case *Const:
fn(&v.Type)
case *Restrict:
fn(&v.Type)
case *Func:
fn(&v.Type)
case *FuncProto:
fn(&v.Return)
for i := range v.Params {
fn(&v.Params[i].Type)
}
case *Var:
fn(&v.Type)
case *Datasec:
for i := range v.Vars {
fn(&v.Vars[i].Type)
}
case *declTag:
fn(&v.Type)
case *typeTag:
fn(&v.Type)
case *cycle:
// cycle has children, but we ignore them deliberately.
default:
panic(fmt.Sprintf("don't know how to walk Type %T", v))
}
}