 470d3ee057
			
		
	
	470d3ee057
	
	
	
		
			
			The fuzzer is broken and it breaks OSS-Fuzz according to #7288. Signed-off-by: Kazuyoshi Kato <katokazu@amazon.com>
		
			
				
	
	
		
			722 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			722 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |