...

Source file src/github.com/cilium/ebpf/btf/ext_info.go

Documentation: github.com/cilium/ebpf/btf

     1  package btf
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"sort"
    11  
    12  	"github.com/cilium/ebpf/asm"
    13  	"github.com/cilium/ebpf/internal"
    14  )
    15  
    16  // ExtInfos contains ELF section metadata.
    17  type ExtInfos struct {
    18  	// The slices are sorted by offset in ascending order.
    19  	funcInfos       map[string][]funcInfo
    20  	lineInfos       map[string][]lineInfo
    21  	relocationInfos map[string][]coreRelocationInfo
    22  }
    23  
    24  // loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF.
    25  //
    26  // Returns an error wrapping ErrNotFound if no ext infos are present.
    27  func loadExtInfosFromELF(file *internal.SafeELFFile, ts types, strings *stringTable) (*ExtInfos, error) {
    28  	section := file.Section(".BTF.ext")
    29  	if section == nil {
    30  		return nil, fmt.Errorf("btf ext infos: %w", ErrNotFound)
    31  	}
    32  
    33  	if section.ReaderAt == nil {
    34  		return nil, fmt.Errorf("compressed ext_info is not supported")
    35  	}
    36  
    37  	return loadExtInfos(section.ReaderAt, file.ByteOrder, ts, strings)
    38  }
    39  
    40  // loadExtInfos parses bare ext infos.
    41  func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, ts types, strings *stringTable) (*ExtInfos, error) {
    42  	// Open unbuffered section reader. binary.Read() calls io.ReadFull on
    43  	// the header structs, resulting in one syscall per header.
    44  	headerRd := io.NewSectionReader(r, 0, math.MaxInt64)
    45  	extHeader, err := parseBTFExtHeader(headerRd, bo)
    46  	if err != nil {
    47  		return nil, fmt.Errorf("parsing BTF extension header: %w", err)
    48  	}
    49  
    50  	coreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("parsing BTF CO-RE header: %w", err)
    53  	}
    54  
    55  	buf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen))
    56  	btfFuncInfos, err := parseFuncInfos(buf, bo, strings)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("parsing BTF function info: %w", err)
    59  	}
    60  
    61  	funcInfos := make(map[string][]funcInfo, len(btfFuncInfos))
    62  	for section, bfis := range btfFuncInfos {
    63  		funcInfos[section], err = newFuncInfos(bfis, ts)
    64  		if err != nil {
    65  			return nil, fmt.Errorf("section %s: func infos: %w", section, err)
    66  		}
    67  	}
    68  
    69  	buf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen))
    70  	btfLineInfos, err := parseLineInfos(buf, bo, strings)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("parsing BTF line info: %w", err)
    73  	}
    74  
    75  	lineInfos := make(map[string][]lineInfo, len(btfLineInfos))
    76  	for section, blis := range btfLineInfos {
    77  		lineInfos[section], err = newLineInfos(blis, strings)
    78  		if err != nil {
    79  			return nil, fmt.Errorf("section %s: line infos: %w", section, err)
    80  		}
    81  	}
    82  
    83  	if coreHeader == nil || coreHeader.COREReloLen == 0 {
    84  		return &ExtInfos{funcInfos, lineInfos, nil}, nil
    85  	}
    86  
    87  	var btfCORERelos map[string][]bpfCORERelo
    88  	buf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen))
    89  	btfCORERelos, err = parseCORERelos(buf, bo, strings)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err)
    92  	}
    93  
    94  	coreRelos := make(map[string][]coreRelocationInfo, len(btfCORERelos))
    95  	for section, brs := range btfCORERelos {
    96  		coreRelos[section], err = newRelocationInfos(brs, ts, strings)
    97  		if err != nil {
    98  			return nil, fmt.Errorf("section %s: CO-RE relocations: %w", section, err)
    99  		}
   100  	}
   101  
   102  	return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
   103  }
   104  
   105  type funcInfoMeta struct{}
   106  type coreRelocationMeta struct{}
   107  
   108  // Assign per-section metadata from BTF to a section's instructions.
   109  func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
   110  	funcInfos := ei.funcInfos[section]
   111  	lineInfos := ei.lineInfos[section]
   112  	reloInfos := ei.relocationInfos[section]
   113  
   114  	iter := insns.Iterate()
   115  	for iter.Next() {
   116  		if len(funcInfos) > 0 && funcInfos[0].offset == iter.Offset {
   117  			iter.Ins.Metadata.Set(funcInfoMeta{}, funcInfos[0].fn)
   118  			funcInfos = funcInfos[1:]
   119  		}
   120  
   121  		if len(lineInfos) > 0 && lineInfos[0].offset == iter.Offset {
   122  			*iter.Ins = iter.Ins.WithSource(lineInfos[0].line)
   123  			lineInfos = lineInfos[1:]
   124  		}
   125  
   126  		if len(reloInfos) > 0 && reloInfos[0].offset == iter.Offset {
   127  			iter.Ins.Metadata.Set(coreRelocationMeta{}, reloInfos[0].relo)
   128  			reloInfos = reloInfos[1:]
   129  		}
   130  	}
   131  }
   132  
   133  // MarshalExtInfos encodes function and line info embedded in insns into kernel
   134  // wire format.
   135  func MarshalExtInfos(insns asm.Instructions, typeID func(Type) (TypeID, error)) (funcInfos, lineInfos []byte, _ error) {
   136  	iter := insns.Iterate()
   137  	var fiBuf, liBuf bytes.Buffer
   138  	for iter.Next() {
   139  		if fn := FuncMetadata(iter.Ins); fn != nil {
   140  			fi := &funcInfo{
   141  				fn:     fn,
   142  				offset: iter.Offset,
   143  			}
   144  			if err := fi.marshal(&fiBuf, typeID); err != nil {
   145  				return nil, nil, fmt.Errorf("write func info: %w", err)
   146  			}
   147  		}
   148  
   149  		if line, ok := iter.Ins.Source().(*Line); ok {
   150  			li := &lineInfo{
   151  				line:   line,
   152  				offset: iter.Offset,
   153  			}
   154  			if err := li.marshal(&liBuf); err != nil {
   155  				return nil, nil, fmt.Errorf("write line info: %w", err)
   156  			}
   157  		}
   158  	}
   159  	return fiBuf.Bytes(), liBuf.Bytes(), nil
   160  }
   161  
   162  // btfExtHeader is found at the start of the .BTF.ext section.
   163  type btfExtHeader struct {
   164  	Magic   uint16
   165  	Version uint8
   166  	Flags   uint8
   167  
   168  	// HdrLen is larger than the size of struct btfExtHeader when it is
   169  	// immediately followed by a btfExtCOREHeader.
   170  	HdrLen uint32
   171  
   172  	FuncInfoOff uint32
   173  	FuncInfoLen uint32
   174  	LineInfoOff uint32
   175  	LineInfoLen uint32
   176  }
   177  
   178  // parseBTFExtHeader parses the header of the .BTF.ext section.
   179  func parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) {
   180  	var header btfExtHeader
   181  	if err := binary.Read(r, bo, &header); err != nil {
   182  		return nil, fmt.Errorf("can't read header: %v", err)
   183  	}
   184  
   185  	if header.Magic != btfMagic {
   186  		return nil, fmt.Errorf("incorrect magic value %v", header.Magic)
   187  	}
   188  
   189  	if header.Version != 1 {
   190  		return nil, fmt.Errorf("unexpected version %v", header.Version)
   191  	}
   192  
   193  	if header.Flags != 0 {
   194  		return nil, fmt.Errorf("unsupported flags %v", header.Flags)
   195  	}
   196  
   197  	if int64(header.HdrLen) < int64(binary.Size(&header)) {
   198  		return nil, fmt.Errorf("header length shorter than btfExtHeader size")
   199  	}
   200  
   201  	return &header, nil
   202  }
   203  
   204  // funcInfoStart returns the offset from the beginning of the .BTF.ext section
   205  // to the start of its func_info entries.
   206  func (h *btfExtHeader) funcInfoStart() int64 {
   207  	return int64(h.HdrLen + h.FuncInfoOff)
   208  }
   209  
   210  // lineInfoStart returns the offset from the beginning of the .BTF.ext section
   211  // to the start of its line_info entries.
   212  func (h *btfExtHeader) lineInfoStart() int64 {
   213  	return int64(h.HdrLen + h.LineInfoOff)
   214  }
   215  
   216  // coreReloStart returns the offset from the beginning of the .BTF.ext section
   217  // to the start of its CO-RE relocation entries.
   218  func (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 {
   219  	return int64(h.HdrLen + ch.COREReloOff)
   220  }
   221  
   222  // btfExtCOREHeader is found right after the btfExtHeader when its HdrLen
   223  // field is larger than its size.
   224  type btfExtCOREHeader struct {
   225  	COREReloOff uint32
   226  	COREReloLen uint32
   227  }
   228  
   229  // parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional
   230  // header bytes are present, extHeader.HdrLen will be larger than the struct,
   231  // indicating the presence of a CO-RE extension header.
   232  func parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) {
   233  	extHdrSize := int64(binary.Size(&extHeader))
   234  	remainder := int64(extHeader.HdrLen) - extHdrSize
   235  
   236  	if remainder == 0 {
   237  		return nil, nil
   238  	}
   239  
   240  	var coreHeader btfExtCOREHeader
   241  	if err := binary.Read(r, bo, &coreHeader); err != nil {
   242  		return nil, fmt.Errorf("can't read header: %v", err)
   243  	}
   244  
   245  	return &coreHeader, nil
   246  }
   247  
   248  type btfExtInfoSec struct {
   249  	SecNameOff uint32
   250  	NumInfo    uint32
   251  }
   252  
   253  // parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext,
   254  // appearing within func_info and line_info sub-sections.
   255  // These headers appear once for each program section in the ELF and are
   256  // followed by one or more func/line_info records for the section.
   257  func parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) {
   258  	var infoHeader btfExtInfoSec
   259  	if err := binary.Read(r, bo, &infoHeader); err != nil {
   260  		return "", nil, fmt.Errorf("read ext info header: %w", err)
   261  	}
   262  
   263  	secName, err := strings.Lookup(infoHeader.SecNameOff)
   264  	if err != nil {
   265  		return "", nil, fmt.Errorf("get section name: %w", err)
   266  	}
   267  	if secName == "" {
   268  		return "", nil, fmt.Errorf("extinfo header refers to empty section name")
   269  	}
   270  
   271  	if infoHeader.NumInfo == 0 {
   272  		return "", nil, fmt.Errorf("section %s has zero records", secName)
   273  	}
   274  
   275  	return secName, &infoHeader, nil
   276  }
   277  
   278  // parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos
   279  // or line_infos segment that describes the length of all extInfoRecords in
   280  // that segment.
   281  func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
   282  	const maxRecordSize = 256
   283  
   284  	var recordSize uint32
   285  	if err := binary.Read(r, bo, &recordSize); err != nil {
   286  		return 0, fmt.Errorf("can't read record size: %v", err)
   287  	}
   288  
   289  	if recordSize < 4 {
   290  		// Need at least InsnOff worth of bytes per record.
   291  		return 0, errors.New("record size too short")
   292  	}
   293  	if recordSize > maxRecordSize {
   294  		return 0, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize)
   295  	}
   296  
   297  	return recordSize, nil
   298  }
   299  
   300  // The size of a FuncInfo in BTF wire format.
   301  var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))
   302  
   303  type funcInfo struct {
   304  	fn     *Func
   305  	offset asm.RawInstructionOffset
   306  }
   307  
   308  type bpfFuncInfo struct {
   309  	// Instruction offset of the function within an ELF section.
   310  	InsnOff uint32
   311  	TypeID  TypeID
   312  }
   313  
   314  func newFuncInfo(fi bpfFuncInfo, ts types) (*funcInfo, error) {
   315  	typ, err := ts.ByID(fi.TypeID)
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  
   320  	fn, ok := typ.(*Func)
   321  	if !ok {
   322  		return nil, fmt.Errorf("type ID %d is a %T, but expected a Func", fi.TypeID, typ)
   323  	}
   324  
   325  	// C doesn't have anonymous functions, but check just in case.
   326  	if fn.Name == "" {
   327  		return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID)
   328  	}
   329  
   330  	return &funcInfo{
   331  		fn,
   332  		asm.RawInstructionOffset(fi.InsnOff),
   333  	}, nil
   334  }
   335  
   336  func newFuncInfos(bfis []bpfFuncInfo, ts types) ([]funcInfo, error) {
   337  	fis := make([]funcInfo, 0, len(bfis))
   338  	for _, bfi := range bfis {
   339  		fi, err := newFuncInfo(bfi, ts)
   340  		if err != nil {
   341  			return nil, fmt.Errorf("offset %d: %w", bfi.InsnOff, err)
   342  		}
   343  		fis = append(fis, *fi)
   344  	}
   345  	sort.Slice(fis, func(i, j int) bool {
   346  		return fis[i].offset <= fis[j].offset
   347  	})
   348  	return fis, nil
   349  }
   350  
   351  // marshal into the BTF wire format.
   352  func (fi *funcInfo) marshal(w io.Writer, typeID func(Type) (TypeID, error)) error {
   353  	id, err := typeID(fi.fn)
   354  	if err != nil {
   355  		return err
   356  	}
   357  	bfi := bpfFuncInfo{
   358  		InsnOff: uint32(fi.offset),
   359  		TypeID:  id,
   360  	}
   361  	return binary.Write(w, internal.NativeEndian, &bfi)
   362  }
   363  
   364  // parseLineInfos parses a func_info sub-section within .BTF.ext ito a map of
   365  // func infos indexed by section name.
   366  func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) {
   367  	recordSize, err := parseExtInfoRecordSize(r, bo)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  
   372  	result := make(map[string][]bpfFuncInfo)
   373  	for {
   374  		secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
   375  		if errors.Is(err, io.EOF) {
   376  			return result, nil
   377  		}
   378  		if err != nil {
   379  			return nil, err
   380  		}
   381  
   382  		records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
   383  		if err != nil {
   384  			return nil, fmt.Errorf("section %v: %w", secName, err)
   385  		}
   386  
   387  		result[secName] = records
   388  	}
   389  }
   390  
   391  // parseFuncInfoRecords parses a stream of func_infos into a funcInfos.
   392  // These records appear after a btf_ext_info_sec header in the func_info
   393  // sub-section of .BTF.ext.
   394  func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfFuncInfo, error) {
   395  	var out []bpfFuncInfo
   396  	var fi bpfFuncInfo
   397  
   398  	if exp, got := FuncInfoSize, recordSize; exp != got {
   399  		// BTF blob's record size is longer than we know how to parse.
   400  		return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got)
   401  	}
   402  
   403  	for i := uint32(0); i < recordNum; i++ {
   404  		if err := binary.Read(r, bo, &fi); err != nil {
   405  			return nil, fmt.Errorf("can't read function info: %v", err)
   406  		}
   407  
   408  		if fi.InsnOff%asm.InstructionSize != 0 {
   409  			return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff)
   410  		}
   411  
   412  		// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
   413  		// Convert as early as possible.
   414  		fi.InsnOff /= asm.InstructionSize
   415  
   416  		out = append(out, fi)
   417  	}
   418  
   419  	return out, nil
   420  }
   421  
   422  var LineInfoSize = uint32(binary.Size(bpfLineInfo{}))
   423  
   424  // Line represents the location and contents of a single line of source
   425  // code a BPF ELF was compiled from.
   426  type Line struct {
   427  	fileName   string
   428  	line       string
   429  	lineNumber uint32
   430  	lineColumn uint32
   431  
   432  	// TODO: We should get rid of the fields below, but for that we need to be
   433  	// able to write BTF.
   434  
   435  	fileNameOff uint32
   436  	lineOff     uint32
   437  }
   438  
   439  func (li *Line) FileName() string {
   440  	return li.fileName
   441  }
   442  
   443  func (li *Line) Line() string {
   444  	return li.line
   445  }
   446  
   447  func (li *Line) LineNumber() uint32 {
   448  	return li.lineNumber
   449  }
   450  
   451  func (li *Line) LineColumn() uint32 {
   452  	return li.lineColumn
   453  }
   454  
   455  func (li *Line) String() string {
   456  	return li.line
   457  }
   458  
   459  type lineInfo struct {
   460  	line   *Line
   461  	offset asm.RawInstructionOffset
   462  }
   463  
   464  // Constants for the format of bpfLineInfo.LineCol.
   465  const (
   466  	bpfLineShift = 10
   467  	bpfLineMax   = (1 << (32 - bpfLineShift)) - 1
   468  	bpfColumnMax = (1 << bpfLineShift) - 1
   469  )
   470  
   471  type bpfLineInfo struct {
   472  	// Instruction offset of the line within the whole instruction stream, in instructions.
   473  	InsnOff     uint32
   474  	FileNameOff uint32
   475  	LineOff     uint32
   476  	LineCol     uint32
   477  }
   478  
   479  func newLineInfo(li bpfLineInfo, strings *stringTable) (*lineInfo, error) {
   480  	line, err := strings.Lookup(li.LineOff)
   481  	if err != nil {
   482  		return nil, fmt.Errorf("lookup of line: %w", err)
   483  	}
   484  
   485  	fileName, err := strings.Lookup(li.FileNameOff)
   486  	if err != nil {
   487  		return nil, fmt.Errorf("lookup of filename: %w", err)
   488  	}
   489  
   490  	lineNumber := li.LineCol >> bpfLineShift
   491  	lineColumn := li.LineCol & bpfColumnMax
   492  
   493  	return &lineInfo{
   494  		&Line{
   495  			fileName,
   496  			line,
   497  			lineNumber,
   498  			lineColumn,
   499  			li.FileNameOff,
   500  			li.LineOff,
   501  		},
   502  		asm.RawInstructionOffset(li.InsnOff),
   503  	}, nil
   504  }
   505  
   506  func newLineInfos(blis []bpfLineInfo, strings *stringTable) ([]lineInfo, error) {
   507  	lis := make([]lineInfo, 0, len(blis))
   508  	for _, bli := range blis {
   509  		li, err := newLineInfo(bli, strings)
   510  		if err != nil {
   511  			return nil, fmt.Errorf("offset %d: %w", bli.InsnOff, err)
   512  		}
   513  		lis = append(lis, *li)
   514  	}
   515  	sort.Slice(lis, func(i, j int) bool {
   516  		return lis[i].offset <= lis[j].offset
   517  	})
   518  	return lis, nil
   519  }
   520  
   521  // marshal writes the binary representation of the LineInfo to w.
   522  func (li *lineInfo) marshal(w io.Writer) error {
   523  	line := li.line
   524  	if line.lineNumber > bpfLineMax {
   525  		return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax)
   526  	}
   527  
   528  	if line.lineColumn > bpfColumnMax {
   529  		return fmt.Errorf("column %d exceeds %d", line.lineColumn, bpfColumnMax)
   530  	}
   531  
   532  	bli := bpfLineInfo{
   533  		uint32(li.offset),
   534  		line.fileNameOff,
   535  		line.lineOff,
   536  		(line.lineNumber << bpfLineShift) | line.lineColumn,
   537  	}
   538  	return binary.Write(w, internal.NativeEndian, &bli)
   539  }
   540  
   541  // parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of
   542  // line infos indexed by section name.
   543  func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) {
   544  	recordSize, err := parseExtInfoRecordSize(r, bo)
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	result := make(map[string][]bpfLineInfo)
   550  	for {
   551  		secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
   552  		if errors.Is(err, io.EOF) {
   553  			return result, nil
   554  		}
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  
   559  		records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
   560  		if err != nil {
   561  			return nil, fmt.Errorf("section %v: %w", secName, err)
   562  		}
   563  
   564  		result[secName] = records
   565  	}
   566  }
   567  
   568  // parseLineInfoRecords parses a stream of line_infos into a lineInfos.
   569  // These records appear after a btf_ext_info_sec header in the line_info
   570  // sub-section of .BTF.ext.
   571  func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfLineInfo, error) {
   572  	var out []bpfLineInfo
   573  	var li bpfLineInfo
   574  
   575  	if exp, got := uint32(binary.Size(li)), recordSize; exp != got {
   576  		// BTF blob's record size is longer than we know how to parse.
   577  		return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got)
   578  	}
   579  
   580  	for i := uint32(0); i < recordNum; i++ {
   581  		if err := binary.Read(r, bo, &li); err != nil {
   582  			return nil, fmt.Errorf("can't read line info: %v", err)
   583  		}
   584  
   585  		if li.InsnOff%asm.InstructionSize != 0 {
   586  			return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff)
   587  		}
   588  
   589  		// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
   590  		// Convert as early as possible.
   591  		li.InsnOff /= asm.InstructionSize
   592  
   593  		out = append(out, li)
   594  	}
   595  
   596  	return out, nil
   597  }
   598  
   599  // bpfCORERelo matches the kernel's struct bpf_core_relo.
   600  type bpfCORERelo struct {
   601  	InsnOff      uint32
   602  	TypeID       TypeID
   603  	AccessStrOff uint32
   604  	Kind         coreKind
   605  }
   606  
   607  type CORERelocation struct {
   608  	typ      Type
   609  	accessor coreAccessor
   610  	kind     coreKind
   611  }
   612  
   613  func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
   614  	relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation)
   615  	return relo
   616  }
   617  
   618  type coreRelocationInfo struct {
   619  	relo   *CORERelocation
   620  	offset asm.RawInstructionOffset
   621  }
   622  
   623  func newRelocationInfo(relo bpfCORERelo, ts types, strings *stringTable) (*coreRelocationInfo, error) {
   624  	typ, err := ts.ByID(relo.TypeID)
   625  	if err != nil {
   626  		return nil, err
   627  	}
   628  
   629  	accessorStr, err := strings.Lookup(relo.AccessStrOff)
   630  	if err != nil {
   631  		return nil, err
   632  	}
   633  
   634  	accessor, err := parseCOREAccessor(accessorStr)
   635  	if err != nil {
   636  		return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
   637  	}
   638  
   639  	return &coreRelocationInfo{
   640  		&CORERelocation{
   641  			typ,
   642  			accessor,
   643  			relo.Kind,
   644  		},
   645  		asm.RawInstructionOffset(relo.InsnOff),
   646  	}, nil
   647  }
   648  
   649  func newRelocationInfos(brs []bpfCORERelo, ts types, strings *stringTable) ([]coreRelocationInfo, error) {
   650  	rs := make([]coreRelocationInfo, 0, len(brs))
   651  	for _, br := range brs {
   652  		relo, err := newRelocationInfo(br, ts, strings)
   653  		if err != nil {
   654  			return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err)
   655  		}
   656  		rs = append(rs, *relo)
   657  	}
   658  	sort.Slice(rs, func(i, j int) bool {
   659  		return rs[i].offset < rs[j].offset
   660  	})
   661  	return rs, nil
   662  }
   663  
   664  var extInfoReloSize = binary.Size(bpfCORERelo{})
   665  
   666  // parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of
   667  // CO-RE relocations indexed by section name.
   668  func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) {
   669  	recordSize, err := parseExtInfoRecordSize(r, bo)
   670  	if err != nil {
   671  		return nil, err
   672  	}
   673  
   674  	if recordSize != uint32(extInfoReloSize) {
   675  		return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize)
   676  	}
   677  
   678  	result := make(map[string][]bpfCORERelo)
   679  	for {
   680  		secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
   681  		if errors.Is(err, io.EOF) {
   682  			return result, nil
   683  		}
   684  		if err != nil {
   685  			return nil, err
   686  		}
   687  
   688  		records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo)
   689  		if err != nil {
   690  			return nil, fmt.Errorf("section %v: %w", secName, err)
   691  		}
   692  
   693  		result[secName] = records
   694  	}
   695  }
   696  
   697  // parseCOREReloRecords parses a stream of CO-RE relocation entries into a
   698  // coreRelos. These records appear after a btf_ext_info_sec header in the
   699  // core_relos sub-section of .BTF.ext.
   700  func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) {
   701  	var out []bpfCORERelo
   702  
   703  	var relo bpfCORERelo
   704  	for i := uint32(0); i < recordNum; i++ {
   705  		if err := binary.Read(r, bo, &relo); err != nil {
   706  			return nil, fmt.Errorf("can't read CO-RE relocation: %v", err)
   707  		}
   708  
   709  		if relo.InsnOff%asm.InstructionSize != 0 {
   710  			return nil, fmt.Errorf("offset %v is not aligned with instruction size", relo.InsnOff)
   711  		}
   712  
   713  		// ELF tracks offset in bytes, the kernel expects raw BPF instructions.
   714  		// Convert as early as possible.
   715  		relo.InsnOff /= asm.InstructionSize
   716  
   717  		out = append(out, relo)
   718  	}
   719  
   720  	return out, nil
   721  }
   722  

View as plain text