Skip to content

Commit

Permalink
btf,info: Fix bad instruction offset when parsing infos from kernel
Browse files Browse the repository at this point in the history
When BTF ext info is encoded in ELF the instruction offsets are in
bytes, but when you pass them to the kernel they must be instruction
indecies. Therefor we devide the offset by the instruction size in
the parsing logic for ELF.

This was missed during the initial implementation of reading back BTF
ext info from the kernel. This would cause an error when loading
back ext info which was not a multiple of the instruction size or
a bad instruction offset if it was.

This commit adds an argument to the parsing logic to specify where the
ext info is being read from. It also adds `LoadKernelLineInfos` and
`LoadKernelFuncInfos` to load the ext info from the kernel, using the
correct direction enum internaly. These are now used by the `info`
package.

Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
  • Loading branch information
dylandreimerink committed Oct 17, 2023
1 parent ed9bf3b commit 32db323
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 20 deletions.
82 changes: 64 additions & 18 deletions btf/ext_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,14 @@ func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
return recordSize, nil
}

// parseDir indicates from which source the BTF is parsed.
type parseDir int

const (
parseFromELF parseDir = iota
parseFromKernel
)

// FuncInfos contains a sorted list of func infos.
type FuncInfos struct {
infos []funcInfo
Expand Down Expand Up @@ -391,13 +399,30 @@ func newFuncInfos(bfis []bpfFuncInfo, spec *Spec) (FuncInfos, error) {
return fis, nil
}

// LoadFuncInfos parses btf func info in wire format.
// LoadFuncInfos parses btf ELF func info in wire format.
func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncInfos, error) {
fis, err := parseFuncInfoRecords(
reader,
bo,
FuncInfoSize,
recordNum,
parseFromELF,
)
if err != nil {
return FuncInfos{}, fmt.Errorf("parsing BTF func info: %w", err)
}

return newFuncInfos(fis, spec)
}

// LoadFuncInfos parses btf kernel func info in wire format.
func LoadKernelFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncInfos, error) {
fis, err := parseFuncInfoRecords(
reader,
bo,
FuncInfoSize,
recordNum,
parseFromKernel,
)
if err != nil {
return FuncInfos{}, fmt.Errorf("parsing BTF func info: %w", err)
Expand Down Expand Up @@ -441,7 +466,7 @@ func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map
return nil, err
}

records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo, parseFromELF)
if err != nil {
return nil, fmt.Errorf("section %v: %w", secName, err)
}
Expand All @@ -453,7 +478,7 @@ func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map
// parseFuncInfoRecords parses a stream of func_infos into a funcInfos.
// These records appear after a btf_ext_info_sec header in the func_info
// sub-section of .BTF.ext.
func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfFuncInfo, error) {
func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, dir parseDir) ([]bpfFuncInfo, error) {
var out []bpfFuncInfo
var fi bpfFuncInfo

Expand All @@ -467,13 +492,15 @@ func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r
return nil, fmt.Errorf("can't read function info: %v", err)
}

if fi.InsnOff%asm.InstructionSize != 0 {
return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff)
}
if dir == parseFromELF {
if fi.InsnOff%asm.InstructionSize != 0 {
return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff)
}

// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
// Convert as early as possible.
fi.InsnOff /= asm.InstructionSize
// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
// Convert as early as possible.
fi.InsnOff /= asm.InstructionSize
}

out = append(out, fi)
}
Expand Down Expand Up @@ -537,13 +564,30 @@ type bpfLineInfo struct {
LineCol uint32
}

// LoadLineInfos parses btf line info in wire format.
// LoadLineInfos parses btf ELF line info in wire format.
func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineInfos, error) {
lis, err := parseLineInfoRecords(
reader,
bo,
LineInfoSize,
recordNum,
parseFromELF,
)
if err != nil {
return LineInfos{}, fmt.Errorf("parsing BTF line info: %w", err)
}

return newLineInfos(lis, spec.strings)
}

// LoadKernelLineInfos parses btf kernel line info in wire format.
func LoadKernelLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineInfos, error) {
lis, err := parseLineInfoRecords(
reader,
bo,
LineInfoSize,
recordNum,
parseFromKernel,
)
if err != nil {
return LineInfos{}, fmt.Errorf("parsing BTF line info: %w", err)
Expand Down Expand Up @@ -649,7 +693,7 @@ func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map
return nil, err
}

records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo, parseFromELF)
if err != nil {
return nil, fmt.Errorf("section %v: %w", secName, err)
}
Expand All @@ -661,7 +705,7 @@ func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map
// parseLineInfoRecords parses a stream of line_infos into a lineInfos.
// These records appear after a btf_ext_info_sec header in the line_info
// sub-section of .BTF.ext.
func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfLineInfo, error) {
func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, dir parseDir) ([]bpfLineInfo, error) {
var out []bpfLineInfo
var li bpfLineInfo

Expand All @@ -675,13 +719,15 @@ func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r
return nil, fmt.Errorf("can't read line info: %v", err)
}

if li.InsnOff%asm.InstructionSize != 0 {
return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff)
}
if dir == parseFromELF {
if li.InsnOff%asm.InstructionSize != 0 {
return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff)
}

// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
// Convert as early as possible.
li.InsnOff /= asm.InstructionSize
// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
// Convert as early as possible.
li.InsnOff /= asm.InstructionSize
}

out = append(out, li)
}
Expand Down
4 changes: 2 additions & 2 deletions info.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return nil, fmt.Errorf("unable to get BTF spec: %w", err)
}

lineInfos, err := btf.LoadLineInfos(
lineInfos, err := btf.LoadKernelLineInfos(
bytes.NewReader(pi.lineInfos),
internal.NativeEndian,
pi.numLineInfos,
Expand All @@ -321,7 +321,7 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
return nil, fmt.Errorf("parse line info: %w", err)
}

funcInfos, err := btf.LoadFuncInfos(
funcInfos, err := btf.LoadKernelFuncInfos(
bytes.NewReader(pi.funcInfos),
internal.NativeEndian,
pi.numFuncInfos,
Expand Down

0 comments on commit 32db323

Please sign in to comment.