summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/internal/btf/ext_info.go')
-rw-r--r--vendor/github.com/cilium/ebpf/internal/btf/ext_info.go281
1 files changed, 281 insertions, 0 deletions
diff --git a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
new file mode 100644
index 000000000..6a21b6bda
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
@@ -0,0 +1,281 @@
+package btf
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+
+ "github.com/cilium/ebpf/asm"
+ "github.com/cilium/ebpf/internal"
+)
+
+type btfExtHeader struct {
+ Magic uint16
+ Version uint8
+ Flags uint8
+ HdrLen uint32
+
+ FuncInfoOff uint32
+ FuncInfoLen uint32
+ LineInfoOff uint32
+ LineInfoLen uint32
+}
+
+type btfExtCoreHeader struct {
+ CoreReloOff uint32
+ CoreReloLen uint32
+}
+
+func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, coreRelos map[string]bpfCoreRelos, err error) {
+ var header btfExtHeader
+ var coreHeader btfExtCoreHeader
+ if err := binary.Read(r, bo, &header); err != nil {
+ return nil, nil, nil, fmt.Errorf("can't read header: %v", err)
+ }
+
+ if header.Magic != btfMagic {
+ return nil, nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
+ }
+
+ if header.Version != 1 {
+ return nil, nil, nil, fmt.Errorf("unexpected version %v", header.Version)
+ }
+
+ if header.Flags != 0 {
+ return nil, nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
+ }
+
+ remainder := int64(header.HdrLen) - int64(binary.Size(&header))
+ if remainder < 0 {
+ return nil, nil, nil, errors.New("header is too short")
+ }
+
+ coreHdrSize := int64(binary.Size(&coreHeader))
+ if remainder >= coreHdrSize {
+ if err := binary.Read(r, bo, &coreHeader); err != nil {
+ return nil, nil, nil, fmt.Errorf("can't read CO-RE relocation header: %v", err)
+ }
+ remainder -= coreHdrSize
+ }
+
+ // Of course, the .BTF.ext header has different semantics than the
+ // .BTF ext header. We need to ignore non-null values.
+ _, err = io.CopyN(ioutil.Discard, r, remainder)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("header padding: %v", err)
+ }
+
+ if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil {
+ return nil, nil, nil, fmt.Errorf("can't seek to function info section: %v", err)
+ }
+
+ buf := bufio.NewReader(io.LimitReader(r, int64(header.FuncInfoLen)))
+ funcInfo, err = parseExtInfo(buf, bo, strings)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("function info: %w", err)
+ }
+
+ if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil {
+ return nil, nil, nil, fmt.Errorf("can't seek to line info section: %v", err)
+ }
+
+ buf = bufio.NewReader(io.LimitReader(r, int64(header.LineInfoLen)))
+ lineInfo, err = parseExtInfo(buf, bo, strings)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("line info: %w", err)
+ }
+
+ if coreHeader.CoreReloOff > 0 && coreHeader.CoreReloLen > 0 {
+ if _, err := r.Seek(int64(header.HdrLen+coreHeader.CoreReloOff), io.SeekStart); err != nil {
+ return nil, nil, nil, fmt.Errorf("can't seek to CO-RE relocation section: %v", err)
+ }
+
+ coreRelos, err = parseExtInfoRelos(io.LimitReader(r, int64(coreHeader.CoreReloLen)), bo, strings)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("CO-RE relocation info: %w", err)
+ }
+ }
+
+ return funcInfo, lineInfo, coreRelos, nil
+}
+
+type btfExtInfoSec struct {
+ SecNameOff uint32
+ NumInfo uint32
+}
+
+type extInfoRecord struct {
+ InsnOff uint64
+ Opaque []byte
+}
+
+type extInfo struct {
+ recordSize uint32
+ records []extInfoRecord
+}
+
+func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
+ if other.recordSize != ei.recordSize {
+ return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
+ }
+
+ records := make([]extInfoRecord, 0, len(ei.records)+len(other.records))
+ records = append(records, ei.records...)
+ for _, info := range other.records {
+ records = append(records, extInfoRecord{
+ InsnOff: info.InsnOff + offset,
+ Opaque: info.Opaque,
+ })
+ }
+ return extInfo{ei.recordSize, records}, nil
+}
+
+func (ei extInfo) MarshalBinary() ([]byte, error) {
+ if len(ei.records) == 0 {
+ return nil, nil
+ }
+
+ buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records)))
+ for _, info := range ei.records {
+ // The kernel expects offsets in number of raw bpf instructions,
+ // while the ELF tracks it in bytes.
+ insnOff := uint32(info.InsnOff / asm.InstructionSize)
+ if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil {
+ return nil, fmt.Errorf("can't write instruction offset: %v", err)
+ }
+
+ buf.Write(info.Opaque)
+ }
+
+ return buf.Bytes(), nil
+}
+
+func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
+ const maxRecordSize = 256
+
+ var recordSize uint32
+ if err := binary.Read(r, bo, &recordSize); err != nil {
+ return nil, fmt.Errorf("can't read record size: %v", err)
+ }
+
+ if recordSize < 4 {
+ // Need at least insnOff
+ return nil, errors.New("record size too short")
+ }
+ if recordSize > maxRecordSize {
+ return nil, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize)
+ }
+
+ result := make(map[string]extInfo)
+ for {
+ secName, infoHeader, err := parseExtInfoHeader(r, bo, strings)
+ if errors.Is(err, io.EOF) {
+ return result, nil
+ }
+
+ var records []extInfoRecord
+ for i := uint32(0); i < infoHeader.NumInfo; i++ {
+ var byteOff uint32
+ if err := binary.Read(r, bo, &byteOff); err != nil {
+ return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err)
+ }
+
+ buf := make([]byte, int(recordSize-4))
+ if _, err := io.ReadFull(r, buf); err != nil {
+ return nil, fmt.Errorf("section %v: can't read record: %v", secName, err)
+ }
+
+ if byteOff%asm.InstructionSize != 0 {
+ return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
+ }
+
+ records = append(records, extInfoRecord{uint64(byteOff), buf})
+ }
+
+ result[secName] = extInfo{
+ recordSize,
+ records,
+ }
+ }
+}
+
+// bpfCoreRelo matches `struct bpf_core_relo` from the kernel
+type bpfCoreRelo struct {
+ InsnOff uint32
+ TypeID TypeID
+ AccessStrOff uint32
+ ReloKind coreReloKind
+}
+
+type bpfCoreRelos []bpfCoreRelo
+
+// append two slices of extInfoRelo to each other. The InsnOff of b are adjusted
+// by offset.
+func (r bpfCoreRelos) append(other bpfCoreRelos, offset uint64) bpfCoreRelos {
+ result := make([]bpfCoreRelo, 0, len(r)+len(other))
+ result = append(result, r...)
+ for _, relo := range other {
+ relo.InsnOff += uint32(offset)
+ result = append(result, relo)
+ }
+ return result
+}
+
+var extInfoReloSize = binary.Size(bpfCoreRelo{})
+
+func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]bpfCoreRelos, error) {
+ var recordSize uint32
+ if err := binary.Read(r, bo, &recordSize); err != nil {
+ return nil, fmt.Errorf("read record size: %v", err)
+ }
+
+ if recordSize != uint32(extInfoReloSize) {
+ return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize)
+ }
+
+ result := make(map[string]bpfCoreRelos)
+ for {
+ secName, infoHeader, err := parseExtInfoHeader(r, bo, strings)
+ if errors.Is(err, io.EOF) {
+ return result, nil
+ }
+
+ var relos []bpfCoreRelo
+ for i := uint32(0); i < infoHeader.NumInfo; i++ {
+ var relo bpfCoreRelo
+ if err := binary.Read(r, bo, &relo); err != nil {
+ return nil, fmt.Errorf("section %v: read record: %v", secName, err)
+ }
+
+ if relo.InsnOff%asm.InstructionSize != 0 {
+ return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, relo.InsnOff)
+ }
+
+ relos = append(relos, relo)
+ }
+
+ result[secName] = relos
+ }
+}
+
+func parseExtInfoHeader(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 infoHeader.NumInfo == 0 {
+ return "", nil, fmt.Errorf("section %s has zero records", secName)
+ }
+
+ return secName, &infoHeader, nil
+}