Skip to content

Commit

Permalink
program: retrieve xlated Instructions from Program
Browse files Browse the repository at this point in the history
Allow introspecting instructions after they have been translated by and
loaded into the kernel.

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed Jan 17, 2022
1 parent 23c9fb3 commit 9c06a48
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 4 deletions.
20 changes: 20 additions & 0 deletions asm/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,26 @@ func (ins Instruction) Size() uint64 {
// Instructions is an eBPF program.
type Instructions []Instruction

// Unmarshal unmarshals an Instructions from a binary instruction stream.
func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error {
var offset uint64
for {
var ins Instruction
n, err := ins.Unmarshal(r, bo)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return fmt.Errorf("offset %d: %w", offset, err)
}

*insns = append(*insns, ins)
offset += n
}

return nil
}

// Name returns the name of the function insns belongs to, if any.
func (insns Instructions) Name() string {
if len(insns) == 0 {
Expand Down
42 changes: 40 additions & 2 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ebpf

import (
"bufio"
"bytes"
"encoding/hex"
"errors"
"fmt"
Expand All @@ -12,6 +13,7 @@ import (
"time"
"unsafe"

"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/sys"
Expand Down Expand Up @@ -95,7 +97,8 @@ type ProgramInfo struct {
btf btf.ID
stats *programStats

maps []MapID
maps []MapID
insns []byte
}

func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
Expand Down Expand Up @@ -129,7 +132,13 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0]))
}

if info.NrMapIds > 0 {
if info.XlatedProgLen > 0 {
pi.insns = make([]byte, info.XlatedProgLen)
info2.XlatedProgLen = info.XlatedProgLen
info2.XlatedProgInsns = sys.NewPointer(unsafe.Pointer(&pi.insns[0]))
}

if info.NrMapIds > 0 || info.XlatedProgLen > 0 {
if err := sys.ObjInfo(fd, &info2); err != nil {
return nil, err
}
Expand Down Expand Up @@ -199,6 +208,35 @@ func (pi *ProgramInfo) Runtime() (time.Duration, bool) {
return time.Duration(0), false
}

// Instructions returns the 'xlated' instruction stream of the program
// after it has been verified and rewritten by the kernel. These instructions
// cannot be loaded back into the kernel as-is, this is mainly used for
// inspecting loaded programs for troubleshooting, dumping, etc.
//
// For example, map accesses are made to reference their kernel map IDs,
// not the FDs they had when the program was inserted.
//
// The first instruction is marked as a symbol using the Program's name.
//
// Available from 4.13. Requires CAP_BPF or equivalent.
func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
// If the calling process is not BPF-capable, the field will be empty.
if len(pi.insns) == 0 {
return nil, fmt.Errorf("getting xlated instructions: %w", os.ErrPermission)
}

r := bytes.NewReader(pi.insns)
var insns asm.Instructions
if err := insns.Unmarshal(r, internal.NativeEndian); err != nil {
return nil, fmt.Errorf("unmarshaling instructions: %w", err)
}

// Tag the first instruction with the name of the program, if available.
insns[0] = insns[0].Sym(pi.Name)

return insns, nil
}

// MapIDs returns the maps related to the program.
//
// Available from 4.15.
Expand Down
6 changes: 5 additions & 1 deletion internal/cmd/gentypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ import (
}{
{
"ProgInfo", "bpf_prog_info",
[]patch{replace(objName, "name"), replace(pointer, "map_ids")},
[]patch{
replace(objName, "name"),
replace(pointer, "xlated_prog_insns"),
replace(pointer, "map_ids"),
},
},
{
"MapInfo", "bpf_map_info",
Expand Down
2 changes: 1 addition & 1 deletion internal/sys/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions prog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,61 @@ func TestProgramBindMap(t *testing.T) {
}
}

func TestProgramInstructions(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.13", "BPF_OBJ_GET_INFO_BY_FD")

arr := createArray(t)
defer arr.Close()

info, err := arr.Info()
if err != nil {
t.Fatal(err)
}

id, ok := info.ID()
if !ok {
t.Fatal("Can't determine map ID")
}

name := "test_prog"

spec := &ProgramSpec{
Type: SocketFilter,
Name: name,
Instructions: asm.Instructions{
asm.LoadImm(asm.R0, -1, asm.DWord).Sym(name),
asm.LoadMapPtr(asm.R1, arr.FD()),
asm.Mov.Imm32(asm.R0, 0),
asm.Return(),
},
License: "MIT",
}

prog, err := NewProgram(spec)
if err != nil {
t.Fatal(err)
}
defer prog.Close()

// Replace the map's fd in spec with the map's ID,
// which is what the xlated instructions reflect.
spec.Instructions[1].Constant = int64(id)

pi, err := prog.Info()
if err != nil {
t.Fatal(err)
}

insns, err := pi.Instructions()
if err != nil {
t.Fatal(err)
}

if diff := cmp.Diff(insns, spec.Instructions); diff != "" {
t.Fatal(diff)
}
}

type testReaderAt struct {
file *os.File
read bool
Expand Down

0 comments on commit 9c06a48

Please sign in to comment.