123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- package btf
- import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "math"
- "sort"
- "github.com/cilium/ebpf/asm"
- "github.com/cilium/ebpf/internal"
- )
- // ExtInfos contains ELF section metadata.
- type ExtInfos struct {
- // The slices are sorted by offset in ascending order.
- funcInfos map[string][]funcInfo
- lineInfos map[string][]lineInfo
- relocationInfos map[string][]coreRelocationInfo
- }
- // loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF.
- //
- // Returns an error wrapping ErrNotFound if no ext infos are present.
- func loadExtInfosFromELF(file *internal.SafeELFFile, ts types, strings *stringTable) (*ExtInfos, error) {
- section := file.Section(".BTF.ext")
- if section == nil {
- return nil, fmt.Errorf("btf ext infos: %w", ErrNotFound)
- }
- if section.ReaderAt == nil {
- return nil, fmt.Errorf("compressed ext_info is not supported")
- }
- return loadExtInfos(section.ReaderAt, file.ByteOrder, ts, strings)
- }
- // loadExtInfos parses bare ext infos.
- func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, ts types, strings *stringTable) (*ExtInfos, error) {
- // Open unbuffered section reader. binary.Read() calls io.ReadFull on
- // the header structs, resulting in one syscall per header.
- headerRd := io.NewSectionReader(r, 0, math.MaxInt64)
- extHeader, err := parseBTFExtHeader(headerRd, bo)
- if err != nil {
- return nil, fmt.Errorf("parsing BTF extension header: %w", err)
- }
- coreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader)
- if err != nil {
- return nil, fmt.Errorf("parsing BTF CO-RE header: %w", err)
- }
- buf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen))
- btfFuncInfos, err := parseFuncInfos(buf, bo, strings)
- if err != nil {
- return nil, fmt.Errorf("parsing BTF function info: %w", err)
- }
- funcInfos := make(map[string][]funcInfo, len(btfFuncInfos))
- for section, bfis := range btfFuncInfos {
- funcInfos[section], err = newFuncInfos(bfis, ts)
- if err != nil {
- return nil, fmt.Errorf("section %s: func infos: %w", section, err)
- }
- }
- buf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen))
- btfLineInfos, err := parseLineInfos(buf, bo, strings)
- if err != nil {
- return nil, fmt.Errorf("parsing BTF line info: %w", err)
- }
- lineInfos := make(map[string][]lineInfo, len(btfLineInfos))
- for section, blis := range btfLineInfos {
- lineInfos[section], err = newLineInfos(blis, strings)
- if err != nil {
- return nil, fmt.Errorf("section %s: line infos: %w", section, err)
- }
- }
- if coreHeader == nil || coreHeader.COREReloLen == 0 {
- return &ExtInfos{funcInfos, lineInfos, nil}, nil
- }
- var btfCORERelos map[string][]bpfCORERelo
- buf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen))
- btfCORERelos, err = parseCORERelos(buf, bo, strings)
- if err != nil {
- return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err)
- }
- coreRelos := make(map[string][]coreRelocationInfo, len(btfCORERelos))
- for section, brs := range btfCORERelos {
- coreRelos[section], err = newRelocationInfos(brs, ts, strings)
- if err != nil {
- return nil, fmt.Errorf("section %s: CO-RE relocations: %w", section, err)
- }
- }
- return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
- }
- type funcInfoMeta struct{}
- type coreRelocationMeta struct{}
- // Assign per-section metadata from BTF to a section's instructions.
- func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
- funcInfos := ei.funcInfos[section]
- lineInfos := ei.lineInfos[section]
- reloInfos := ei.relocationInfos[section]
- iter := insns.Iterate()
- for iter.Next() {
- if len(funcInfos) > 0 && funcInfos[0].offset == iter.Offset {
- iter.Ins.Metadata.Set(funcInfoMeta{}, funcInfos[0].fn)
- funcInfos = funcInfos[1:]
- }
- if len(lineInfos) > 0 && lineInfos[0].offset == iter.Offset {
- *iter.Ins = iter.Ins.WithSource(lineInfos[0].line)
- lineInfos = lineInfos[1:]
- }
- if len(reloInfos) > 0 && reloInfos[0].offset == iter.Offset {
- iter.Ins.Metadata.Set(coreRelocationMeta{}, reloInfos[0].relo)
- reloInfos = reloInfos[1:]
- }
- }
- }
- // MarshalExtInfos encodes function and line info embedded in insns into kernel
- // wire format.
- func MarshalExtInfos(insns asm.Instructions, typeID func(Type) (TypeID, error)) (funcInfos, lineInfos []byte, _ error) {
- iter := insns.Iterate()
- var fiBuf, liBuf bytes.Buffer
- for iter.Next() {
- if fn := FuncMetadata(iter.Ins); fn != nil {
- fi := &funcInfo{
- fn: fn,
- offset: iter.Offset,
- }
- if err := fi.marshal(&fiBuf, typeID); err != nil {
- return nil, nil, fmt.Errorf("write func info: %w", err)
- }
- }
- if line, ok := iter.Ins.Source().(*Line); ok {
- li := &lineInfo{
- line: line,
- offset: iter.Offset,
- }
- if err := li.marshal(&liBuf); err != nil {
- return nil, nil, fmt.Errorf("write line info: %w", err)
- }
- }
- }
- return fiBuf.Bytes(), liBuf.Bytes(), nil
- }
- // btfExtHeader is found at the start of the .BTF.ext section.
- type btfExtHeader struct {
- Magic uint16
- Version uint8
- Flags uint8
- // HdrLen is larger than the size of struct btfExtHeader when it is
- // immediately followed by a btfExtCOREHeader.
- HdrLen uint32
- FuncInfoOff uint32
- FuncInfoLen uint32
- LineInfoOff uint32
- LineInfoLen uint32
- }
- // parseBTFExtHeader parses the header of the .BTF.ext section.
- func parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) {
- var header btfExtHeader
- if err := binary.Read(r, bo, &header); err != nil {
- return nil, fmt.Errorf("can't read header: %v", err)
- }
- if header.Magic != btfMagic {
- return nil, fmt.Errorf("incorrect magic value %v", header.Magic)
- }
- if header.Version != 1 {
- return nil, fmt.Errorf("unexpected version %v", header.Version)
- }
- if header.Flags != 0 {
- return nil, fmt.Errorf("unsupported flags %v", header.Flags)
- }
- if int64(header.HdrLen) < int64(binary.Size(&header)) {
- return nil, fmt.Errorf("header length shorter than btfExtHeader size")
- }
- return &header, nil
- }
- // funcInfoStart returns the offset from the beginning of the .BTF.ext section
- // to the start of its func_info entries.
- func (h *btfExtHeader) funcInfoStart() int64 {
- return int64(h.HdrLen + h.FuncInfoOff)
- }
- // lineInfoStart returns the offset from the beginning of the .BTF.ext section
- // to the start of its line_info entries.
- func (h *btfExtHeader) lineInfoStart() int64 {
- return int64(h.HdrLen + h.LineInfoOff)
- }
- // coreReloStart returns the offset from the beginning of the .BTF.ext section
- // to the start of its CO-RE relocation entries.
- func (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 {
- return int64(h.HdrLen + ch.COREReloOff)
- }
- // btfExtCOREHeader is found right after the btfExtHeader when its HdrLen
- // field is larger than its size.
- type btfExtCOREHeader struct {
- COREReloOff uint32
- COREReloLen uint32
- }
- // parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional
- // header bytes are present, extHeader.HdrLen will be larger than the struct,
- // indicating the presence of a CO-RE extension header.
- func parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) {
- extHdrSize := int64(binary.Size(&extHeader))
- remainder := int64(extHeader.HdrLen) - extHdrSize
- if remainder == 0 {
- return nil, nil
- }
- var coreHeader btfExtCOREHeader
- if err := binary.Read(r, bo, &coreHeader); err != nil {
- return nil, fmt.Errorf("can't read header: %v", err)
- }
- return &coreHeader, nil
- }
- type btfExtInfoSec struct {
- SecNameOff uint32
- NumInfo uint32
- }
- // parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext,
- // appearing within func_info and line_info sub-sections.
- // These headers appear once for each program section in the ELF and are
- // followed by one or more func/line_info records for the section.
- func parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) {
- var infoHeader btfExtInfoSec
- if err := binary.Read(r, bo, &infoHeader); err != nil {
- return "", nil, fmt.Errorf("read ext info header: %w", err)
- }
- secName, err := strings.Lookup(infoHeader.SecNameOff)
- if err != nil {
- return "", nil, fmt.Errorf("get section name: %w", err)
- }
- if secName == "" {
- return "", nil, fmt.Errorf("extinfo header refers to empty section name")
- }
- if infoHeader.NumInfo == 0 {
- return "", nil, fmt.Errorf("section %s has zero records", secName)
- }
- return secName, &infoHeader, nil
- }
- // parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos
- // or line_infos segment that describes the length of all extInfoRecords in
- // that segment.
- func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
- const maxRecordSize = 256
- var recordSize uint32
- if err := binary.Read(r, bo, &recordSize); err != nil {
- return 0, fmt.Errorf("can't read record size: %v", err)
- }
- if recordSize < 4 {
- // Need at least InsnOff worth of bytes per record.
- return 0, errors.New("record size too short")
- }
- if recordSize > maxRecordSize {
- return 0, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize)
- }
- return recordSize, nil
- }
- // The size of a FuncInfo in BTF wire format.
- var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))
- type funcInfo struct {
- fn *Func
- offset asm.RawInstructionOffset
- }
- type bpfFuncInfo struct {
- // Instruction offset of the function within an ELF section.
- InsnOff uint32
- TypeID TypeID
- }
- func newFuncInfo(fi bpfFuncInfo, ts types) (*funcInfo, error) {
- typ, err := ts.ByID(fi.TypeID)
- if err != nil {
- return nil, err
- }
- fn, ok := typ.(*Func)
- if !ok {
- return nil, fmt.Errorf("type ID %d is a %T, but expected a Func", fi.TypeID, typ)
- }
- // C doesn't have anonymous functions, but check just in case.
- if fn.Name == "" {
- return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID)
- }
- return &funcInfo{
- fn,
- asm.RawInstructionOffset(fi.InsnOff),
- }, nil
- }
- func newFuncInfos(bfis []bpfFuncInfo, ts types) ([]funcInfo, error) {
- fis := make([]funcInfo, 0, len(bfis))
- for _, bfi := range bfis {
- fi, err := newFuncInfo(bfi, ts)
- if err != nil {
- return nil, fmt.Errorf("offset %d: %w", bfi.InsnOff, err)
- }
- fis = append(fis, *fi)
- }
- sort.Slice(fis, func(i, j int) bool {
- return fis[i].offset <= fis[j].offset
- })
- return fis, nil
- }
- // marshal into the BTF wire format.
- func (fi *funcInfo) marshal(w io.Writer, typeID func(Type) (TypeID, error)) error {
- id, err := typeID(fi.fn)
- if err != nil {
- return err
- }
- bfi := bpfFuncInfo{
- InsnOff: uint32(fi.offset),
- TypeID: id,
- }
- return binary.Write(w, internal.NativeEndian, &bfi)
- }
- // parseLineInfos parses a func_info sub-section within .BTF.ext ito a map of
- // func infos indexed by section name.
- func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) {
- recordSize, err := parseExtInfoRecordSize(r, bo)
- if err != nil {
- return nil, err
- }
- result := make(map[string][]bpfFuncInfo)
- for {
- secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
- if errors.Is(err, io.EOF) {
- return result, nil
- }
- if err != nil {
- return nil, err
- }
- records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
- if err != nil {
- return nil, fmt.Errorf("section %v: %w", secName, err)
- }
- result[secName] = records
- }
- }
- // 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) {
- var out []bpfFuncInfo
- var fi bpfFuncInfo
- if exp, got := FuncInfoSize, recordSize; exp != got {
- // BTF blob's record size is longer than we know how to parse.
- return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got)
- }
- for i := uint32(0); i < recordNum; i++ {
- if err := binary.Read(r, bo, &fi); err != nil {
- 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)
- }
- // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
- // Convert as early as possible.
- fi.InsnOff /= asm.InstructionSize
- out = append(out, fi)
- }
- return out, nil
- }
- var LineInfoSize = uint32(binary.Size(bpfLineInfo{}))
- // Line represents the location and contents of a single line of source
- // code a BPF ELF was compiled from.
- type Line struct {
- fileName string
- line string
- lineNumber uint32
- lineColumn uint32
- // TODO: We should get rid of the fields below, but for that we need to be
- // able to write BTF.
- fileNameOff uint32
- lineOff uint32
- }
- func (li *Line) FileName() string {
- return li.fileName
- }
- func (li *Line) Line() string {
- return li.line
- }
- func (li *Line) LineNumber() uint32 {
- return li.lineNumber
- }
- func (li *Line) LineColumn() uint32 {
- return li.lineColumn
- }
- func (li *Line) String() string {
- return li.line
- }
- type lineInfo struct {
- line *Line
- offset asm.RawInstructionOffset
- }
- // Constants for the format of bpfLineInfo.LineCol.
- const (
- bpfLineShift = 10
- bpfLineMax = (1 << (32 - bpfLineShift)) - 1
- bpfColumnMax = (1 << bpfLineShift) - 1
- )
- type bpfLineInfo struct {
- // Instruction offset of the line within the whole instruction stream, in instructions.
- InsnOff uint32
- FileNameOff uint32
- LineOff uint32
- LineCol uint32
- }
- func newLineInfo(li bpfLineInfo, strings *stringTable) (*lineInfo, error) {
- line, err := strings.Lookup(li.LineOff)
- if err != nil {
- return nil, fmt.Errorf("lookup of line: %w", err)
- }
- fileName, err := strings.Lookup(li.FileNameOff)
- if err != nil {
- return nil, fmt.Errorf("lookup of filename: %w", err)
- }
- lineNumber := li.LineCol >> bpfLineShift
- lineColumn := li.LineCol & bpfColumnMax
- return &lineInfo{
- &Line{
- fileName,
- line,
- lineNumber,
- lineColumn,
- li.FileNameOff,
- li.LineOff,
- },
- asm.RawInstructionOffset(li.InsnOff),
- }, nil
- }
- func newLineInfos(blis []bpfLineInfo, strings *stringTable) ([]lineInfo, error) {
- lis := make([]lineInfo, 0, len(blis))
- for _, bli := range blis {
- li, err := newLineInfo(bli, strings)
- if err != nil {
- return nil, fmt.Errorf("offset %d: %w", bli.InsnOff, err)
- }
- lis = append(lis, *li)
- }
- sort.Slice(lis, func(i, j int) bool {
- return lis[i].offset <= lis[j].offset
- })
- return lis, nil
- }
- // marshal writes the binary representation of the LineInfo to w.
- func (li *lineInfo) marshal(w io.Writer) error {
- line := li.line
- if line.lineNumber > bpfLineMax {
- return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax)
- }
- if line.lineColumn > bpfColumnMax {
- return fmt.Errorf("column %d exceeds %d", line.lineColumn, bpfColumnMax)
- }
- bli := bpfLineInfo{
- uint32(li.offset),
- line.fileNameOff,
- line.lineOff,
- (line.lineNumber << bpfLineShift) | line.lineColumn,
- }
- return binary.Write(w, internal.NativeEndian, &bli)
- }
- // parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of
- // line infos indexed by section name.
- func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) {
- recordSize, err := parseExtInfoRecordSize(r, bo)
- if err != nil {
- return nil, err
- }
- result := make(map[string][]bpfLineInfo)
- for {
- secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
- if errors.Is(err, io.EOF) {
- return result, nil
- }
- if err != nil {
- return nil, err
- }
- records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
- if err != nil {
- return nil, fmt.Errorf("section %v: %w", secName, err)
- }
- result[secName] = records
- }
- }
- // 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) {
- var out []bpfLineInfo
- var li bpfLineInfo
- if exp, got := uint32(binary.Size(li)), recordSize; exp != got {
- // BTF blob's record size is longer than we know how to parse.
- return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got)
- }
- for i := uint32(0); i < recordNum; i++ {
- if err := binary.Read(r, bo, &li); err != nil {
- 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)
- }
- // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
- // Convert as early as possible.
- li.InsnOff /= asm.InstructionSize
- out = append(out, li)
- }
- return out, nil
- }
- // bpfCORERelo matches the kernel's struct bpf_core_relo.
- type bpfCORERelo struct {
- InsnOff uint32
- TypeID TypeID
- AccessStrOff uint32
- Kind coreKind
- }
- type CORERelocation struct {
- typ Type
- accessor coreAccessor
- kind coreKind
- }
- func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
- relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation)
- return relo
- }
- type coreRelocationInfo struct {
- relo *CORERelocation
- offset asm.RawInstructionOffset
- }
- func newRelocationInfo(relo bpfCORERelo, ts types, strings *stringTable) (*coreRelocationInfo, error) {
- typ, err := ts.ByID(relo.TypeID)
- if err != nil {
- return nil, err
- }
- accessorStr, err := strings.Lookup(relo.AccessStrOff)
- if err != nil {
- return nil, err
- }
- accessor, err := parseCOREAccessor(accessorStr)
- if err != nil {
- return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
- }
- return &coreRelocationInfo{
- &CORERelocation{
- typ,
- accessor,
- relo.Kind,
- },
- asm.RawInstructionOffset(relo.InsnOff),
- }, nil
- }
- func newRelocationInfos(brs []bpfCORERelo, ts types, strings *stringTable) ([]coreRelocationInfo, error) {
- rs := make([]coreRelocationInfo, 0, len(brs))
- for _, br := range brs {
- relo, err := newRelocationInfo(br, ts, strings)
- if err != nil {
- return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err)
- }
- rs = append(rs, *relo)
- }
- sort.Slice(rs, func(i, j int) bool {
- return rs[i].offset < rs[j].offset
- })
- return rs, nil
- }
- var extInfoReloSize = binary.Size(bpfCORERelo{})
- // parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of
- // CO-RE relocations indexed by section name.
- func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) {
- recordSize, err := parseExtInfoRecordSize(r, bo)
- if err != nil {
- return nil, err
- }
- if recordSize != uint32(extInfoReloSize) {
- return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize)
- }
- result := make(map[string][]bpfCORERelo)
- for {
- secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
- if errors.Is(err, io.EOF) {
- return result, nil
- }
- if err != nil {
- return nil, err
- }
- records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo)
- if err != nil {
- return nil, fmt.Errorf("section %v: %w", secName, err)
- }
- result[secName] = records
- }
- }
- // parseCOREReloRecords parses a stream of CO-RE relocation entries into a
- // coreRelos. These records appear after a btf_ext_info_sec header in the
- // core_relos sub-section of .BTF.ext.
- func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) {
- var out []bpfCORERelo
- var relo bpfCORERelo
- for i := uint32(0); i < recordNum; i++ {
- if err := binary.Read(r, bo, &relo); err != nil {
- return nil, fmt.Errorf("can't read CO-RE relocation: %v", err)
- }
- if relo.InsnOff%asm.InstructionSize != 0 {
- return nil, fmt.Errorf("offset %v is not aligned with instruction size", relo.InsnOff)
- }
- // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
- // Convert as early as possible.
- relo.InsnOff /= asm.InstructionSize
- out = append(out, relo)
- }
- return out, nil
- }
|