gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ext_info.go (20137B)


      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 }