mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-20 17:28:40 +00:00
57dc742c76
Bumps [github.com/KimMachineGun/automemlimit](https://github.com/KimMachineGun/automemlimit) from 0.2.4 to 0.2.5. - [Release notes](https://github.com/KimMachineGun/automemlimit/releases) - [Commits](https://github.com/KimMachineGun/automemlimit/compare/v0.2.4...v0.2.5) --- updated-dependencies: - dependency-name: github.com/KimMachineGun/automemlimit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1213 lines
26 KiB
Go
1213 lines
26 KiB
Go
package btf
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/cilium/ebpf/asm"
|
|
)
|
|
|
|
const maxTypeDepth = 32
|
|
|
|
// TypeID identifies a type in a BTF section.
|
|
type TypeID uint32
|
|
|
|
// Type represents a type described by BTF.
|
|
type Type interface {
|
|
// Type can be formatted using the %s and %v verbs. %s outputs only the
|
|
// identity of the type, without any detail. %v outputs additional detail.
|
|
//
|
|
// Use the '+' flag to include the address of the type.
|
|
//
|
|
// Use the width to specify how many levels of detail to output, for example
|
|
// %1v will output detail for the root type and a short description of its
|
|
// children. %2v would output details of the root type and its children
|
|
// as well as a short description of the grandchildren.
|
|
fmt.Formatter
|
|
|
|
// Name of the type, empty for anonymous types and types that cannot
|
|
// carry a name, like Void and Pointer.
|
|
TypeName() string
|
|
|
|
// Make a copy of the type, without copying Type members.
|
|
copy() Type
|
|
|
|
// Enumerate all nested Types. Repeated calls must visit nested
|
|
// types in the same order.
|
|
walk(*typeDeque)
|
|
}
|
|
|
|
var (
|
|
_ Type = (*Int)(nil)
|
|
_ Type = (*Struct)(nil)
|
|
_ Type = (*Union)(nil)
|
|
_ Type = (*Enum)(nil)
|
|
_ Type = (*Fwd)(nil)
|
|
_ Type = (*Func)(nil)
|
|
_ Type = (*Typedef)(nil)
|
|
_ Type = (*Var)(nil)
|
|
_ Type = (*Datasec)(nil)
|
|
_ Type = (*Float)(nil)
|
|
)
|
|
|
|
// types is a list of Type.
|
|
//
|
|
// The order determines the ID of a type.
|
|
type types []Type
|
|
|
|
func (ts types) ByID(id TypeID) (Type, error) {
|
|
if int(id) > len(ts) {
|
|
return nil, fmt.Errorf("type ID %d: %w", id, ErrNotFound)
|
|
}
|
|
return ts[id], nil
|
|
}
|
|
|
|
// Void is the unit type of BTF.
|
|
type Void struct{}
|
|
|
|
func (v *Void) Format(fs fmt.State, verb rune) { formatType(fs, verb, v) }
|
|
func (v *Void) TypeName() string { return "" }
|
|
func (v *Void) size() uint32 { return 0 }
|
|
func (v *Void) copy() Type { return (*Void)(nil) }
|
|
func (v *Void) walk(*typeDeque) {}
|
|
|
|
type IntEncoding byte
|
|
|
|
const (
|
|
Signed IntEncoding = 1 << iota
|
|
Char
|
|
Bool
|
|
)
|
|
|
|
func (ie IntEncoding) IsSigned() bool {
|
|
return ie&Signed != 0
|
|
}
|
|
|
|
func (ie IntEncoding) IsChar() bool {
|
|
return ie&Char != 0
|
|
}
|
|
|
|
func (ie IntEncoding) IsBool() bool {
|
|
return ie&Bool != 0
|
|
}
|
|
|
|
func (ie IntEncoding) String() string {
|
|
switch {
|
|
case ie.IsChar() && ie.IsSigned():
|
|
return "char"
|
|
case ie.IsChar() && !ie.IsSigned():
|
|
return "uchar"
|
|
case ie.IsBool():
|
|
return "bool"
|
|
case ie.IsSigned():
|
|
return "signed"
|
|
default:
|
|
return "unsigned"
|
|
}
|
|
}
|
|
|
|
// Int is an integer of a given length.
|
|
//
|
|
// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int
|
|
type Int struct {
|
|
Name string
|
|
|
|
// The size of the integer in bytes.
|
|
Size uint32
|
|
Encoding IntEncoding
|
|
}
|
|
|
|
func (i *Int) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, i, i.Encoding, "size=", i.Size*8)
|
|
}
|
|
|
|
func (i *Int) TypeName() string { return i.Name }
|
|
func (i *Int) size() uint32 { return i.Size }
|
|
func (i *Int) walk(*typeDeque) {}
|
|
func (i *Int) copy() Type {
|
|
cpy := *i
|
|
return &cpy
|
|
}
|
|
|
|
// Pointer is a pointer to another type.
|
|
type Pointer struct {
|
|
Target Type
|
|
}
|
|
|
|
func (p *Pointer) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, p, "target=", p.Target)
|
|
}
|
|
|
|
func (p *Pointer) TypeName() string { return "" }
|
|
func (p *Pointer) size() uint32 { return 8 }
|
|
func (p *Pointer) walk(tdq *typeDeque) { tdq.push(&p.Target) }
|
|
func (p *Pointer) copy() Type {
|
|
cpy := *p
|
|
return &cpy
|
|
}
|
|
|
|
// Array is an array with a fixed number of elements.
|
|
type Array struct {
|
|
Index Type
|
|
Type Type
|
|
Nelems uint32
|
|
}
|
|
|
|
func (arr *Array) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, arr, "index=", arr.Index, "type=", arr.Type, "n=", arr.Nelems)
|
|
}
|
|
|
|
func (arr *Array) TypeName() string { return "" }
|
|
|
|
func (arr *Array) walk(tdq *typeDeque) {
|
|
tdq.push(&arr.Index)
|
|
tdq.push(&arr.Type)
|
|
}
|
|
|
|
func (arr *Array) copy() Type {
|
|
cpy := *arr
|
|
return &cpy
|
|
}
|
|
|
|
// Struct is a compound type of consecutive members.
|
|
type Struct struct {
|
|
Name string
|
|
// The size of the struct including padding, in bytes
|
|
Size uint32
|
|
Members []Member
|
|
}
|
|
|
|
func (s *Struct) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, s, "fields=", len(s.Members))
|
|
}
|
|
|
|
func (s *Struct) TypeName() string { return s.Name }
|
|
|
|
func (s *Struct) size() uint32 { return s.Size }
|
|
|
|
func (s *Struct) walk(tdq *typeDeque) {
|
|
for i := range s.Members {
|
|
tdq.push(&s.Members[i].Type)
|
|
}
|
|
}
|
|
|
|
func (s *Struct) copy() Type {
|
|
cpy := *s
|
|
cpy.Members = copyMembers(s.Members)
|
|
return &cpy
|
|
}
|
|
|
|
func (s *Struct) members() []Member {
|
|
return s.Members
|
|
}
|
|
|
|
// Union is a compound type where members occupy the same memory.
|
|
type Union struct {
|
|
Name string
|
|
// The size of the union including padding, in bytes.
|
|
Size uint32
|
|
Members []Member
|
|
}
|
|
|
|
func (u *Union) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, u, "fields=", len(u.Members))
|
|
}
|
|
|
|
func (u *Union) TypeName() string { return u.Name }
|
|
|
|
func (u *Union) size() uint32 { return u.Size }
|
|
|
|
func (u *Union) walk(tdq *typeDeque) {
|
|
for i := range u.Members {
|
|
tdq.push(&u.Members[i].Type)
|
|
}
|
|
}
|
|
|
|
func (u *Union) copy() Type {
|
|
cpy := *u
|
|
cpy.Members = copyMembers(u.Members)
|
|
return &cpy
|
|
}
|
|
|
|
func (u *Union) members() []Member {
|
|
return u.Members
|
|
}
|
|
|
|
func copyMembers(orig []Member) []Member {
|
|
cpy := make([]Member, len(orig))
|
|
copy(cpy, orig)
|
|
return cpy
|
|
}
|
|
|
|
type composite interface {
|
|
members() []Member
|
|
}
|
|
|
|
var (
|
|
_ composite = (*Struct)(nil)
|
|
_ composite = (*Union)(nil)
|
|
)
|
|
|
|
// A value in bits.
|
|
type Bits uint32
|
|
|
|
// Bytes converts a bit value into bytes.
|
|
func (b Bits) Bytes() uint32 {
|
|
return uint32(b / 8)
|
|
}
|
|
|
|
// Member is part of a Struct or Union.
|
|
//
|
|
// It is not a valid Type.
|
|
type Member struct {
|
|
Name string
|
|
Type Type
|
|
Offset Bits
|
|
BitfieldSize Bits
|
|
}
|
|
|
|
// Enum lists possible values.
|
|
type Enum struct {
|
|
Name string
|
|
// Size of the enum value in bytes.
|
|
Size uint32
|
|
Values []EnumValue
|
|
}
|
|
|
|
func (e *Enum) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, e, "size=", e.Size, "values=", len(e.Values))
|
|
}
|
|
|
|
func (e *Enum) TypeName() string { return e.Name }
|
|
|
|
// EnumValue is part of an Enum
|
|
//
|
|
// Is is not a valid Type
|
|
type EnumValue struct {
|
|
Name string
|
|
Value int32
|
|
}
|
|
|
|
func (e *Enum) size() uint32 { return e.Size }
|
|
func (e *Enum) walk(*typeDeque) {}
|
|
func (e *Enum) copy() Type {
|
|
cpy := *e
|
|
cpy.Values = make([]EnumValue, len(e.Values))
|
|
copy(cpy.Values, e.Values)
|
|
return &cpy
|
|
}
|
|
|
|
// FwdKind is the type of forward declaration.
|
|
type FwdKind int
|
|
|
|
// Valid types of forward declaration.
|
|
const (
|
|
FwdStruct FwdKind = iota
|
|
FwdUnion
|
|
)
|
|
|
|
func (fk FwdKind) String() string {
|
|
switch fk {
|
|
case FwdStruct:
|
|
return "struct"
|
|
case FwdUnion:
|
|
return "union"
|
|
default:
|
|
return fmt.Sprintf("%T(%d)", fk, int(fk))
|
|
}
|
|
}
|
|
|
|
// Fwd is a forward declaration of a Type.
|
|
type Fwd struct {
|
|
Name string
|
|
Kind FwdKind
|
|
}
|
|
|
|
func (f *Fwd) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, f, f.Kind)
|
|
}
|
|
|
|
func (f *Fwd) TypeName() string { return f.Name }
|
|
|
|
func (f *Fwd) walk(*typeDeque) {}
|
|
func (f *Fwd) copy() Type {
|
|
cpy := *f
|
|
return &cpy
|
|
}
|
|
|
|
// Typedef is an alias of a Type.
|
|
type Typedef struct {
|
|
Name string
|
|
Type Type
|
|
}
|
|
|
|
func (td *Typedef) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, td, td.Type)
|
|
}
|
|
|
|
func (td *Typedef) TypeName() string { return td.Name }
|
|
|
|
func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) }
|
|
func (td *Typedef) copy() Type {
|
|
cpy := *td
|
|
return &cpy
|
|
}
|
|
|
|
// Volatile is a qualifier.
|
|
type Volatile struct {
|
|
Type Type
|
|
}
|
|
|
|
func (v *Volatile) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, v, v.Type)
|
|
}
|
|
|
|
func (v *Volatile) TypeName() string { return "" }
|
|
|
|
func (v *Volatile) qualify() Type { return v.Type }
|
|
func (v *Volatile) walk(tdq *typeDeque) { tdq.push(&v.Type) }
|
|
func (v *Volatile) copy() Type {
|
|
cpy := *v
|
|
return &cpy
|
|
}
|
|
|
|
// Const is a qualifier.
|
|
type Const struct {
|
|
Type Type
|
|
}
|
|
|
|
func (c *Const) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, c, c.Type)
|
|
}
|
|
|
|
func (c *Const) TypeName() string { return "" }
|
|
|
|
func (c *Const) qualify() Type { return c.Type }
|
|
func (c *Const) walk(tdq *typeDeque) { tdq.push(&c.Type) }
|
|
func (c *Const) copy() Type {
|
|
cpy := *c
|
|
return &cpy
|
|
}
|
|
|
|
// Restrict is a qualifier.
|
|
type Restrict struct {
|
|
Type Type
|
|
}
|
|
|
|
func (r *Restrict) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, r, r.Type)
|
|
}
|
|
|
|
func (r *Restrict) TypeName() string { return "" }
|
|
|
|
func (r *Restrict) qualify() Type { return r.Type }
|
|
func (r *Restrict) walk(tdq *typeDeque) { tdq.push(&r.Type) }
|
|
func (r *Restrict) copy() Type {
|
|
cpy := *r
|
|
return &cpy
|
|
}
|
|
|
|
// Func is a function definition.
|
|
type Func struct {
|
|
Name string
|
|
Type Type
|
|
Linkage FuncLinkage
|
|
}
|
|
|
|
func FuncMetadata(ins *asm.Instruction) *Func {
|
|
fn, _ := ins.Metadata.Get(funcInfoMeta{}).(*Func)
|
|
return fn
|
|
}
|
|
|
|
func (f *Func) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, f, f.Linkage, "proto=", f.Type)
|
|
}
|
|
|
|
func (f *Func) TypeName() string { return f.Name }
|
|
|
|
func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) }
|
|
func (f *Func) copy() Type {
|
|
cpy := *f
|
|
return &cpy
|
|
}
|
|
|
|
// FuncProto is a function declaration.
|
|
type FuncProto struct {
|
|
Return Type
|
|
Params []FuncParam
|
|
}
|
|
|
|
func (fp *FuncProto) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, fp, "args=", len(fp.Params), "return=", fp.Return)
|
|
}
|
|
|
|
func (fp *FuncProto) TypeName() string { return "" }
|
|
|
|
func (fp *FuncProto) walk(tdq *typeDeque) {
|
|
tdq.push(&fp.Return)
|
|
for i := range fp.Params {
|
|
tdq.push(&fp.Params[i].Type)
|
|
}
|
|
}
|
|
|
|
func (fp *FuncProto) copy() Type {
|
|
cpy := *fp
|
|
cpy.Params = make([]FuncParam, len(fp.Params))
|
|
copy(cpy.Params, fp.Params)
|
|
return &cpy
|
|
}
|
|
|
|
type FuncParam struct {
|
|
Name string
|
|
Type Type
|
|
}
|
|
|
|
// Var is a global variable.
|
|
type Var struct {
|
|
Name string
|
|
Type Type
|
|
Linkage VarLinkage
|
|
}
|
|
|
|
func (v *Var) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, v, v.Linkage)
|
|
}
|
|
|
|
func (v *Var) TypeName() string { return v.Name }
|
|
|
|
func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) }
|
|
func (v *Var) copy() Type {
|
|
cpy := *v
|
|
return &cpy
|
|
}
|
|
|
|
// Datasec is a global program section containing data.
|
|
type Datasec struct {
|
|
Name string
|
|
Size uint32
|
|
Vars []VarSecinfo
|
|
}
|
|
|
|
func (ds *Datasec) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, ds)
|
|
}
|
|
|
|
func (ds *Datasec) TypeName() string { return ds.Name }
|
|
|
|
func (ds *Datasec) size() uint32 { return ds.Size }
|
|
|
|
func (ds *Datasec) walk(tdq *typeDeque) {
|
|
for i := range ds.Vars {
|
|
tdq.push(&ds.Vars[i].Type)
|
|
}
|
|
}
|
|
|
|
func (ds *Datasec) copy() Type {
|
|
cpy := *ds
|
|
cpy.Vars = make([]VarSecinfo, len(ds.Vars))
|
|
copy(cpy.Vars, ds.Vars)
|
|
return &cpy
|
|
}
|
|
|
|
// VarSecinfo describes variable in a Datasec.
|
|
//
|
|
// It is not a valid Type.
|
|
type VarSecinfo struct {
|
|
Type Type
|
|
Offset uint32
|
|
Size uint32
|
|
}
|
|
|
|
// Float is a float of a given length.
|
|
type Float struct {
|
|
Name string
|
|
|
|
// The size of the float in bytes.
|
|
Size uint32
|
|
}
|
|
|
|
func (f *Float) Format(fs fmt.State, verb rune) {
|
|
formatType(fs, verb, f, "size=", f.Size*8)
|
|
}
|
|
|
|
func (f *Float) TypeName() string { return f.Name }
|
|
func (f *Float) size() uint32 { return f.Size }
|
|
func (f *Float) walk(*typeDeque) {}
|
|
func (f *Float) copy() Type {
|
|
cpy := *f
|
|
return &cpy
|
|
}
|
|
|
|
// cycle is a type which had to be elided since it exceeded maxTypeDepth.
|
|
type cycle struct {
|
|
root Type
|
|
}
|
|
|
|
func (c *cycle) ID() TypeID { return math.MaxUint32 }
|
|
func (c *cycle) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, "root=", c.root) }
|
|
func (c *cycle) TypeName() string { return "" }
|
|
func (c *cycle) walk(*typeDeque) {}
|
|
func (c *cycle) copy() Type {
|
|
cpy := *c
|
|
return &cpy
|
|
}
|
|
|
|
type sizer interface {
|
|
size() uint32
|
|
}
|
|
|
|
var (
|
|
_ sizer = (*Int)(nil)
|
|
_ sizer = (*Pointer)(nil)
|
|
_ sizer = (*Struct)(nil)
|
|
_ sizer = (*Union)(nil)
|
|
_ sizer = (*Enum)(nil)
|
|
_ sizer = (*Datasec)(nil)
|
|
)
|
|
|
|
type qualifier interface {
|
|
qualify() Type
|
|
}
|
|
|
|
var (
|
|
_ qualifier = (*Const)(nil)
|
|
_ qualifier = (*Restrict)(nil)
|
|
_ qualifier = (*Volatile)(nil)
|
|
)
|
|
|
|
// Sizeof returns the size of a type in bytes.
|
|
//
|
|
// Returns an error if the size can't be computed.
|
|
func Sizeof(typ Type) (int, error) {
|
|
var (
|
|
n = int64(1)
|
|
elem int64
|
|
)
|
|
|
|
for i := 0; i < maxTypeDepth; i++ {
|
|
switch v := typ.(type) {
|
|
case *Array:
|
|
if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
|
|
return 0, fmt.Errorf("type %s: overflow", typ)
|
|
}
|
|
|
|
// Arrays may be of zero length, which allows
|
|
// n to be zero as well.
|
|
n *= int64(v.Nelems)
|
|
typ = v.Type
|
|
continue
|
|
|
|
case sizer:
|
|
elem = int64(v.size())
|
|
|
|
case *Typedef:
|
|
typ = v.Type
|
|
continue
|
|
|
|
case qualifier:
|
|
typ = v.qualify()
|
|
continue
|
|
|
|
default:
|
|
return 0, fmt.Errorf("unsized type %T", typ)
|
|
}
|
|
|
|
if n > 0 && elem > math.MaxInt64/n {
|
|
return 0, fmt.Errorf("type %s: overflow", typ)
|
|
}
|
|
|
|
size := n * elem
|
|
if int64(int(size)) != size {
|
|
return 0, fmt.Errorf("type %s: overflow", typ)
|
|
}
|
|
|
|
return int(size), nil
|
|
}
|
|
|
|
return 0, fmt.Errorf("type %s: exceeded type depth", typ)
|
|
}
|
|
|
|
// alignof returns the alignment of a type.
|
|
//
|
|
// Currently only supports the subset of types necessary for bitfield relocations.
|
|
func alignof(typ Type) (int, error) {
|
|
switch t := UnderlyingType(typ).(type) {
|
|
case *Enum:
|
|
return int(t.size()), nil
|
|
case *Int:
|
|
return int(t.Size), nil
|
|
default:
|
|
return 0, fmt.Errorf("can't calculate alignment of %T", t)
|
|
}
|
|
}
|
|
|
|
// Transformer modifies a given Type and returns the result.
|
|
//
|
|
// For example, UnderlyingType removes any qualifiers or typedefs from a type.
|
|
// See the example on Copy for how to use a transform.
|
|
type Transformer func(Type) Type
|
|
|
|
// Copy a Type recursively.
|
|
//
|
|
// typ may form a cycle. If transform is not nil, it is called with the
|
|
// to be copied type, and the returned value is copied instead.
|
|
func Copy(typ Type, transform Transformer) Type {
|
|
copies := make(copier)
|
|
copies.copy(&typ, transform)
|
|
return typ
|
|
}
|
|
|
|
// copy a slice of Types recursively.
|
|
//
|
|
// See Copy for the semantics.
|
|
func copyTypes(types []Type, transform Transformer) []Type {
|
|
result := make([]Type, len(types))
|
|
copy(result, types)
|
|
|
|
copies := make(copier)
|
|
for i := range result {
|
|
copies.copy(&result[i], transform)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
type copier map[Type]Type
|
|
|
|
func (c copier) copy(typ *Type, transform Transformer) {
|
|
var work typeDeque
|
|
for t := typ; t != nil; t = work.pop() {
|
|
// *t is the identity of the type.
|
|
if cpy := c[*t]; cpy != nil {
|
|
*t = cpy
|
|
continue
|
|
}
|
|
|
|
var cpy Type
|
|
if transform != nil {
|
|
cpy = transform(*t).copy()
|
|
} else {
|
|
cpy = (*t).copy()
|
|
}
|
|
|
|
c[*t] = cpy
|
|
*t = cpy
|
|
|
|
// Mark any nested types for copying.
|
|
cpy.walk(&work)
|
|
}
|
|
}
|
|
|
|
// typeDeque keeps track of pointers to types which still
|
|
// need to be visited.
|
|
type typeDeque struct {
|
|
types []*Type
|
|
read, write uint64
|
|
mask uint64
|
|
}
|
|
|
|
func (dq *typeDeque) empty() bool {
|
|
return dq.read == dq.write
|
|
}
|
|
|
|
// push adds a type to the stack.
|
|
func (dq *typeDeque) push(t *Type) {
|
|
if dq.write-dq.read < uint64(len(dq.types)) {
|
|
dq.types[dq.write&dq.mask] = t
|
|
dq.write++
|
|
return
|
|
}
|
|
|
|
new := len(dq.types) * 2
|
|
if new == 0 {
|
|
new = 8
|
|
}
|
|
|
|
types := make([]*Type, new)
|
|
pivot := dq.read & dq.mask
|
|
n := copy(types, dq.types[pivot:])
|
|
n += copy(types[n:], dq.types[:pivot])
|
|
types[n] = t
|
|
|
|
dq.types = types
|
|
dq.mask = uint64(new) - 1
|
|
dq.read, dq.write = 0, uint64(n+1)
|
|
}
|
|
|
|
// shift returns the first element or null.
|
|
func (dq *typeDeque) shift() *Type {
|
|
if dq.empty() {
|
|
return nil
|
|
}
|
|
|
|
index := dq.read & dq.mask
|
|
t := dq.types[index]
|
|
dq.types[index] = nil
|
|
dq.read++
|
|
return t
|
|
}
|
|
|
|
// pop returns the last element or null.
|
|
func (dq *typeDeque) pop() *Type {
|
|
if dq.empty() {
|
|
return nil
|
|
}
|
|
|
|
dq.write--
|
|
index := dq.write & dq.mask
|
|
t := dq.types[index]
|
|
dq.types[index] = nil
|
|
return t
|
|
}
|
|
|
|
// all returns all elements.
|
|
//
|
|
// The deque is empty after calling this method.
|
|
func (dq *typeDeque) all() []*Type {
|
|
length := dq.write - dq.read
|
|
types := make([]*Type, 0, length)
|
|
for t := dq.shift(); t != nil; t = dq.shift() {
|
|
types = append(types, t)
|
|
}
|
|
return types
|
|
}
|
|
|
|
// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns
|
|
// it into a graph of Types connected via pointers.
|
|
//
|
|
// If baseTypes are provided, then the raw types are
|
|
// considered to be of a split BTF (e.g., a kernel module).
|
|
//
|
|
// Returns a slice of types indexed by TypeID. Since BTF ignores compilation
|
|
// units, multiple types may share the same name. A Type may form a cyclic graph
|
|
// by pointing at itself.
|
|
func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTable) ([]Type, error) {
|
|
types := make([]Type, 0, len(rawTypes)+1) // +1 for Void added to base types
|
|
|
|
typeIDOffset := TypeID(1) // Void is TypeID(0), so the rest starts from TypeID(1)
|
|
|
|
if baseTypes == nil {
|
|
// Void is defined to always be type ID 0, and is thus omitted from BTF.
|
|
types = append(types, (*Void)(nil))
|
|
} else {
|
|
// For split BTF, the next ID is max base BTF type ID + 1
|
|
typeIDOffset = TypeID(len(baseTypes))
|
|
}
|
|
|
|
type fixupDef struct {
|
|
id TypeID
|
|
typ *Type
|
|
}
|
|
|
|
var fixups []fixupDef
|
|
fixup := func(id TypeID, typ *Type) {
|
|
if id < TypeID(len(baseTypes)) {
|
|
*typ = baseTypes[id]
|
|
return
|
|
}
|
|
|
|
idx := id
|
|
if baseTypes != nil {
|
|
idx = id - TypeID(len(baseTypes))
|
|
}
|
|
if idx < TypeID(len(types)) {
|
|
// We've already inflated this type, fix it up immediately.
|
|
*typ = types[idx]
|
|
return
|
|
}
|
|
fixups = append(fixups, fixupDef{id, typ})
|
|
}
|
|
|
|
type assertion struct {
|
|
typ *Type
|
|
want reflect.Type
|
|
}
|
|
|
|
var assertions []assertion
|
|
assert := func(typ *Type, want reflect.Type) error {
|
|
if *typ != nil {
|
|
// The type has already been fixed up, check the type immediately.
|
|
if reflect.TypeOf(*typ) != want {
|
|
return fmt.Errorf("expected %s, got %T", want, *typ)
|
|
}
|
|
return nil
|
|
}
|
|
assertions = append(assertions, assertion{typ, want})
|
|
return nil
|
|
}
|
|
|
|
type bitfieldFixupDef struct {
|
|
id TypeID
|
|
m *Member
|
|
}
|
|
|
|
var (
|
|
legacyBitfields = make(map[TypeID][2]Bits) // offset, size
|
|
bitfieldFixups []bitfieldFixupDef
|
|
)
|
|
convertMembers := func(raw []btfMember, kindFlag bool) ([]Member, error) {
|
|
// NB: The fixup below relies on pre-allocating this array to
|
|
// work, since otherwise append might re-allocate members.
|
|
members := make([]Member, 0, len(raw))
|
|
for i, btfMember := range raw {
|
|
name, err := rawStrings.Lookup(btfMember.NameOff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
|
|
}
|
|
|
|
members = append(members, Member{
|
|
Name: name,
|
|
Offset: Bits(btfMember.Offset),
|
|
})
|
|
|
|
m := &members[i]
|
|
fixup(raw[i].Type, &m.Type)
|
|
|
|
if kindFlag {
|
|
m.BitfieldSize = Bits(btfMember.Offset >> 24)
|
|
m.Offset &= 0xffffff
|
|
// We ignore legacy bitfield definitions if the current composite
|
|
// is a new-style bitfield. This is kind of safe since offset and
|
|
// size on the type of the member must be zero if kindFlat is set
|
|
// according to spec.
|
|
continue
|
|
}
|
|
|
|
// This may be a legacy bitfield, try to fix it up.
|
|
data, ok := legacyBitfields[raw[i].Type]
|
|
if ok {
|
|
// Bingo!
|
|
m.Offset += data[0]
|
|
m.BitfieldSize = data[1]
|
|
continue
|
|
}
|
|
|
|
if m.Type != nil {
|
|
// We couldn't find a legacy bitfield, but we know that the member's
|
|
// type has already been inflated. Hence we know that it can't be
|
|
// a legacy bitfield and there is nothing left to do.
|
|
continue
|
|
}
|
|
|
|
// We don't have fixup data, and the type we're pointing
|
|
// at hasn't been inflated yet. No choice but to defer
|
|
// the fixup.
|
|
bitfieldFixups = append(bitfieldFixups, bitfieldFixupDef{
|
|
raw[i].Type,
|
|
m,
|
|
})
|
|
}
|
|
return members, nil
|
|
}
|
|
|
|
for i, raw := range rawTypes {
|
|
var (
|
|
id = typeIDOffset + TypeID(i)
|
|
typ Type
|
|
)
|
|
|
|
name, err := rawStrings.Lookup(raw.NameOff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get name for type id %d: %w", id, err)
|
|
}
|
|
|
|
switch raw.Kind() {
|
|
case kindInt:
|
|
size := raw.Size()
|
|
bi := raw.data.(*btfInt)
|
|
if bi.Offset() > 0 || bi.Bits().Bytes() != size {
|
|
legacyBitfields[id] = [2]Bits{bi.Offset(), bi.Bits()}
|
|
}
|
|
typ = &Int{name, raw.Size(), bi.Encoding()}
|
|
|
|
case kindPointer:
|
|
ptr := &Pointer{nil}
|
|
fixup(raw.Type(), &ptr.Target)
|
|
typ = ptr
|
|
|
|
case kindArray:
|
|
btfArr := raw.data.(*btfArray)
|
|
arr := &Array{nil, nil, btfArr.Nelems}
|
|
fixup(btfArr.IndexType, &arr.Index)
|
|
fixup(btfArr.Type, &arr.Type)
|
|
typ = arr
|
|
|
|
case kindStruct:
|
|
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
|
|
}
|
|
typ = &Struct{name, raw.Size(), members}
|
|
|
|
case kindUnion:
|
|
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
|
|
}
|
|
typ = &Union{name, raw.Size(), members}
|
|
|
|
case kindEnum:
|
|
rawvals := raw.data.([]btfEnum)
|
|
vals := make([]EnumValue, 0, len(rawvals))
|
|
for i, btfVal := range rawvals {
|
|
name, err := rawStrings.Lookup(btfVal.NameOff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get name for enum value %d: %s", i, err)
|
|
}
|
|
vals = append(vals, EnumValue{
|
|
Name: name,
|
|
Value: btfVal.Val,
|
|
})
|
|
}
|
|
typ = &Enum{name, raw.Size(), vals}
|
|
|
|
case kindForward:
|
|
if raw.KindFlag() {
|
|
typ = &Fwd{name, FwdUnion}
|
|
} else {
|
|
typ = &Fwd{name, FwdStruct}
|
|
}
|
|
|
|
case kindTypedef:
|
|
typedef := &Typedef{name, nil}
|
|
fixup(raw.Type(), &typedef.Type)
|
|
typ = typedef
|
|
|
|
case kindVolatile:
|
|
volatile := &Volatile{nil}
|
|
fixup(raw.Type(), &volatile.Type)
|
|
typ = volatile
|
|
|
|
case kindConst:
|
|
cnst := &Const{nil}
|
|
fixup(raw.Type(), &cnst.Type)
|
|
typ = cnst
|
|
|
|
case kindRestrict:
|
|
restrict := &Restrict{nil}
|
|
fixup(raw.Type(), &restrict.Type)
|
|
typ = restrict
|
|
|
|
case kindFunc:
|
|
fn := &Func{name, nil, raw.Linkage()}
|
|
fixup(raw.Type(), &fn.Type)
|
|
if err := assert(&fn.Type, reflect.TypeOf((*FuncProto)(nil))); err != nil {
|
|
return nil, err
|
|
}
|
|
typ = fn
|
|
|
|
case kindFuncProto:
|
|
rawparams := raw.data.([]btfParam)
|
|
params := make([]FuncParam, 0, len(rawparams))
|
|
for i, param := range rawparams {
|
|
name, err := rawStrings.Lookup(param.NameOff)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err)
|
|
}
|
|
params = append(params, FuncParam{
|
|
Name: name,
|
|
})
|
|
}
|
|
for i := range params {
|
|
fixup(rawparams[i].Type, ¶ms[i].Type)
|
|
}
|
|
|
|
fp := &FuncProto{nil, params}
|
|
fixup(raw.Type(), &fp.Return)
|
|
typ = fp
|
|
|
|
case kindVar:
|
|
variable := raw.data.(*btfVariable)
|
|
v := &Var{name, nil, VarLinkage(variable.Linkage)}
|
|
fixup(raw.Type(), &v.Type)
|
|
typ = v
|
|
|
|
case kindDatasec:
|
|
btfVars := raw.data.([]btfVarSecinfo)
|
|
vars := make([]VarSecinfo, 0, len(btfVars))
|
|
for _, btfVar := range btfVars {
|
|
vars = append(vars, VarSecinfo{
|
|
Offset: btfVar.Offset,
|
|
Size: btfVar.Size,
|
|
})
|
|
}
|
|
for i := range vars {
|
|
fixup(btfVars[i].Type, &vars[i].Type)
|
|
if err := assert(&vars[i].Type, reflect.TypeOf((*Var)(nil))); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
typ = &Datasec{name, raw.SizeType, vars}
|
|
|
|
case kindFloat:
|
|
typ = &Float{name, raw.Size()}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
|
|
}
|
|
|
|
types = append(types, typ)
|
|
}
|
|
|
|
for _, fixup := range fixups {
|
|
i := int(fixup.id)
|
|
if i >= len(types)+len(baseTypes) {
|
|
return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id)
|
|
}
|
|
if i < len(baseTypes) {
|
|
return nil, fmt.Errorf("fixup for base type id %d is not expected", i)
|
|
}
|
|
|
|
*fixup.typ = types[i-len(baseTypes)]
|
|
}
|
|
|
|
for _, bitfieldFixup := range bitfieldFixups {
|
|
if bitfieldFixup.id < TypeID(len(baseTypes)) {
|
|
return nil, fmt.Errorf("bitfield fixup from split to base types is not expected")
|
|
}
|
|
|
|
data, ok := legacyBitfields[bitfieldFixup.id]
|
|
if ok {
|
|
// This is indeed a legacy bitfield, fix it up.
|
|
bitfieldFixup.m.Offset += data[0]
|
|
bitfieldFixup.m.BitfieldSize = data[1]
|
|
}
|
|
}
|
|
|
|
for _, assertion := range assertions {
|
|
if reflect.TypeOf(*assertion.typ) != assertion.want {
|
|
return nil, fmt.Errorf("expected %s, got %T", assertion.want, *assertion.typ)
|
|
}
|
|
}
|
|
|
|
return types, nil
|
|
}
|
|
|
|
// essentialName represents the name of a BTF type stripped of any flavor
|
|
// suffixes after a ___ delimiter.
|
|
type essentialName string
|
|
|
|
// newEssentialName returns name without a ___ suffix.
|
|
//
|
|
// CO-RE has the concept of 'struct flavors', which are used to deal with
|
|
// changes in kernel data structures. Anything after three underscores
|
|
// in a type name is ignored for the purpose of finding a candidate type
|
|
// in the kernel's BTF.
|
|
func newEssentialName(name string) essentialName {
|
|
if name == "" {
|
|
return ""
|
|
}
|
|
lastIdx := strings.LastIndex(name, "___")
|
|
if lastIdx > 0 {
|
|
return essentialName(name[:lastIdx])
|
|
}
|
|
return essentialName(name)
|
|
}
|
|
|
|
// UnderlyingType skips qualifiers and Typedefs.
|
|
func UnderlyingType(typ Type) Type {
|
|
result := typ
|
|
for depth := 0; depth <= maxTypeDepth; depth++ {
|
|
switch v := (result).(type) {
|
|
case qualifier:
|
|
result = v.qualify()
|
|
case *Typedef:
|
|
result = v.Type
|
|
default:
|
|
return result
|
|
}
|
|
}
|
|
return &cycle{typ}
|
|
}
|
|
|
|
type formatState struct {
|
|
fmt.State
|
|
depth int
|
|
}
|
|
|
|
// formattableType is a subset of Type, to ease unit testing of formatType.
|
|
type formattableType interface {
|
|
fmt.Formatter
|
|
TypeName() string
|
|
}
|
|
|
|
// formatType formats a type in a canonical form.
|
|
//
|
|
// Handles cyclical types by only printing cycles up to a certain depth. Elements
|
|
// in extra are separated by spaces unless the preceding element is a string
|
|
// ending in '='.
|
|
func formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) {
|
|
if verb != 'v' && verb != 's' {
|
|
fmt.Fprintf(f, "{UNRECOGNIZED: %c}", verb)
|
|
return
|
|
}
|
|
|
|
// This is the same as %T, but elides the package name. Assumes that
|
|
// formattableType is implemented by a pointer receiver.
|
|
goTypeName := reflect.TypeOf(t).Elem().Name()
|
|
_, _ = io.WriteString(f, goTypeName)
|
|
|
|
if name := t.TypeName(); name != "" {
|
|
// Output BTF type name if present.
|
|
fmt.Fprintf(f, ":%q", name)
|
|
}
|
|
|
|
if f.Flag('+') {
|
|
// Output address if requested.
|
|
fmt.Fprintf(f, ":%#p", t)
|
|
}
|
|
|
|
if verb == 's' {
|
|
// %s omits details.
|
|
return
|
|
}
|
|
|
|
var depth int
|
|
if ps, ok := f.(*formatState); ok {
|
|
depth = ps.depth
|
|
f = ps.State
|
|
}
|
|
|
|
maxDepth, ok := f.Width()
|
|
if !ok {
|
|
maxDepth = 0
|
|
}
|
|
|
|
if depth > maxDepth {
|
|
// We've reached the maximum depth. This avoids infinite recursion even
|
|
// for cyclical types.
|
|
return
|
|
}
|
|
|
|
if len(extra) == 0 {
|
|
return
|
|
}
|
|
|
|
wantSpace := false
|
|
_, _ = io.WriteString(f, "[")
|
|
for _, arg := range extra {
|
|
if wantSpace {
|
|
_, _ = io.WriteString(f, " ")
|
|
}
|
|
|
|
switch v := arg.(type) {
|
|
case string:
|
|
_, _ = io.WriteString(f, v)
|
|
wantSpace = len(v) > 0 && v[len(v)-1] != '='
|
|
continue
|
|
|
|
case formattableType:
|
|
v.Format(&formatState{f, depth + 1}, verb)
|
|
|
|
default:
|
|
fmt.Fprint(f, arg)
|
|
}
|
|
|
|
wantSpace = true
|
|
}
|
|
_, _ = io.WriteString(f, "]")
|
|
}
|