mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-06-02 21:39:25 +00:00
872 lines
18 KiB
Go
872 lines
18 KiB
Go
|
package btf
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
const maxTypeDepth = 32
|
||
|
|
||
|
// TypeID identifies a type in a BTF section.
|
||
|
type TypeID uint32
|
||
|
|
||
|
// ID implements part of the Type interface.
|
||
|
func (tid TypeID) ID() TypeID {
|
||
|
return tid
|
||
|
}
|
||
|
|
||
|
// Type represents a type described by BTF.
|
||
|
type Type interface {
|
||
|
ID() TypeID
|
||
|
|
||
|
String() 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)
|
||
|
}
|
||
|
|
||
|
// namedType is a type with a name.
|
||
|
//
|
||
|
// Most named types simply embed Name.
|
||
|
type namedType interface {
|
||
|
Type
|
||
|
name() string
|
||
|
}
|
||
|
|
||
|
// Name identifies a type.
|
||
|
//
|
||
|
// Anonymous types have an empty name.
|
||
|
type Name string
|
||
|
|
||
|
func (n Name) name() string {
|
||
|
return string(n)
|
||
|
}
|
||
|
|
||
|
// Void is the unit type of BTF.
|
||
|
type Void struct{}
|
||
|
|
||
|
func (v *Void) ID() TypeID { return 0 }
|
||
|
func (v *Void) String() string { return "void#0" }
|
||
|
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
|
||
|
)
|
||
|
|
||
|
// Int is an integer of a given length.
|
||
|
type Int struct {
|
||
|
TypeID
|
||
|
Name
|
||
|
|
||
|
// The size of the integer in bytes.
|
||
|
Size uint32
|
||
|
Encoding IntEncoding
|
||
|
// Offset is the starting bit offset. Currently always 0.
|
||
|
// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int
|
||
|
Offset uint32
|
||
|
Bits byte
|
||
|
}
|
||
|
|
||
|
var _ namedType = (*Int)(nil)
|
||
|
|
||
|
func (i *Int) String() string {
|
||
|
var s strings.Builder
|
||
|
|
||
|
switch {
|
||
|
case i.Encoding&Char != 0:
|
||
|
s.WriteString("char")
|
||
|
case i.Encoding&Bool != 0:
|
||
|
s.WriteString("bool")
|
||
|
default:
|
||
|
if i.Encoding&Signed == 0 {
|
||
|
s.WriteRune('u')
|
||
|
}
|
||
|
s.WriteString("int")
|
||
|
fmt.Fprintf(&s, "%d", i.Size*8)
|
||
|
}
|
||
|
|
||
|
fmt.Fprintf(&s, "#%d", i.TypeID)
|
||
|
|
||
|
if i.Bits > 0 {
|
||
|
fmt.Fprintf(&s, "[bits=%d]", i.Bits)
|
||
|
}
|
||
|
|
||
|
return s.String()
|
||
|
}
|
||
|
|
||
|
func (i *Int) size() uint32 { return i.Size }
|
||
|
func (i *Int) walk(*typeDeque) {}
|
||
|
func (i *Int) copy() Type {
|
||
|
cpy := *i
|
||
|
return &cpy
|
||
|
}
|
||
|
|
||
|
func (i *Int) isBitfield() bool {
|
||
|
return i.Offset > 0
|
||
|
}
|
||
|
|
||
|
// Pointer is a pointer to another type.
|
||
|
type Pointer struct {
|
||
|
TypeID
|
||
|
Target Type
|
||
|
}
|
||
|
|
||
|
func (p *Pointer) String() string {
|
||
|
return fmt.Sprintf("pointer#%d[target=#%d]", p.TypeID, p.Target.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Type Type
|
||
|
Nelems uint32
|
||
|
}
|
||
|
|
||
|
func (arr *Array) String() string {
|
||
|
return fmt.Sprintf("array#%d[type=#%d n=%d]", arr.TypeID, arr.Type.ID(), arr.Nelems)
|
||
|
}
|
||
|
|
||
|
func (arr *Array) walk(tdq *typeDeque) { tdq.push(&arr.Type) }
|
||
|
func (arr *Array) copy() Type {
|
||
|
cpy := *arr
|
||
|
return &cpy
|
||
|
}
|
||
|
|
||
|
// Struct is a compound type of consecutive members.
|
||
|
type Struct struct {
|
||
|
TypeID
|
||
|
Name
|
||
|
// The size of the struct including padding, in bytes
|
||
|
Size uint32
|
||
|
Members []Member
|
||
|
}
|
||
|
|
||
|
func (s *Struct) String() string {
|
||
|
return fmt.Sprintf("struct#%d[%q]", s.TypeID, 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 = make([]Member, len(s.Members))
|
||
|
copy(cpy.Members, 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 {
|
||
|
TypeID
|
||
|
Name
|
||
|
// The size of the union including padding, in bytes.
|
||
|
Size uint32
|
||
|
Members []Member
|
||
|
}
|
||
|
|
||
|
func (u *Union) String() string {
|
||
|
return fmt.Sprintf("union#%d[%q]", u.TypeID, 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 = make([]Member, len(u.Members))
|
||
|
copy(cpy.Members, u.Members)
|
||
|
return &cpy
|
||
|
}
|
||
|
|
||
|
func (u *Union) members() []Member {
|
||
|
return u.Members
|
||
|
}
|
||
|
|
||
|
type composite interface {
|
||
|
members() []Member
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
_ composite = (*Struct)(nil)
|
||
|
_ composite = (*Union)(nil)
|
||
|
)
|
||
|
|
||
|
// Member is part of a Struct or Union.
|
||
|
//
|
||
|
// It is not a valid Type.
|
||
|
type Member struct {
|
||
|
Name
|
||
|
Type Type
|
||
|
// Offset is the bit offset of this member
|
||
|
Offset uint32
|
||
|
BitfieldSize uint32
|
||
|
}
|
||
|
|
||
|
// Enum lists possible values.
|
||
|
type Enum struct {
|
||
|
TypeID
|
||
|
Name
|
||
|
Values []EnumValue
|
||
|
}
|
||
|
|
||
|
func (e *Enum) String() string {
|
||
|
return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name)
|
||
|
}
|
||
|
|
||
|
// EnumValue is part of an Enum
|
||
|
//
|
||
|
// Is is not a valid Type
|
||
|
type EnumValue struct {
|
||
|
Name
|
||
|
Value int32
|
||
|
}
|
||
|
|
||
|
func (e *Enum) size() uint32 { return 4 }
|
||
|
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 {
|
||
|
TypeID
|
||
|
Name
|
||
|
Kind FwdKind
|
||
|
}
|
||
|
|
||
|
func (f *Fwd) String() string {
|
||
|
return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, 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 {
|
||
|
TypeID
|
||
|
Name
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (td *Typedef) String() string {
|
||
|
return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (v *Volatile) String() string {
|
||
|
return fmt.Sprintf("volatile#%d[#%d]", v.TypeID, v.Type.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (c *Const) String() string {
|
||
|
return fmt.Sprintf("const#%d[#%d]", c.TypeID, c.Type.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (r *Restrict) String() string {
|
||
|
return fmt.Sprintf("restrict#%d[#%d]", r.TypeID, r.Type.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Name
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (f *Func) String() string {
|
||
|
return fmt.Sprintf("func#%d[%q proto=#%d]", f.TypeID, f.Name, f.Type.ID())
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
TypeID
|
||
|
Return Type
|
||
|
Params []FuncParam
|
||
|
}
|
||
|
|
||
|
func (fp *FuncProto) String() string {
|
||
|
var s strings.Builder
|
||
|
fmt.Fprintf(&s, "proto#%d[", fp.TypeID)
|
||
|
for _, param := range fp.Params {
|
||
|
fmt.Fprintf(&s, "%q=#%d, ", param.Name, param.Type.ID())
|
||
|
}
|
||
|
fmt.Fprintf(&s, "return=#%d]", fp.Return.ID())
|
||
|
return s.String()
|
||
|
}
|
||
|
|
||
|
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
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
// Var is a global variable.
|
||
|
type Var struct {
|
||
|
TypeID
|
||
|
Name
|
||
|
Type Type
|
||
|
}
|
||
|
|
||
|
func (v *Var) String() string {
|
||
|
// TODO: Linkage
|
||
|
return fmt.Sprintf("var#%d[%q]", v.TypeID, 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 {
|
||
|
TypeID
|
||
|
Name
|
||
|
Size uint32
|
||
|
Vars []VarSecinfo
|
||
|
}
|
||
|
|
||
|
func (ds *Datasec) String() string {
|
||
|
return fmt.Sprintf("section#%d[%q]", ds.TypeID, 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
|
||
|
}
|
||
|
|
||
|
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, errors.New("overflow")
|
||
|
}
|
||
|
|
||
|
// 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("unrecognized type %T", typ)
|
||
|
}
|
||
|
|
||
|
if n > 0 && elem > math.MaxInt64/n {
|
||
|
return 0, errors.New("overflow")
|
||
|
}
|
||
|
|
||
|
size := n * elem
|
||
|
if int64(int(size)) != size {
|
||
|
return 0, errors.New("overflow")
|
||
|
}
|
||
|
|
||
|
return int(size), nil
|
||
|
}
|
||
|
|
||
|
return 0, errors.New("exceeded type depth")
|
||
|
}
|
||
|
|
||
|
// copy a Type recursively.
|
||
|
//
|
||
|
// typ may form a cycle.
|
||
|
func copyType(typ Type) Type {
|
||
|
var (
|
||
|
copies = make(map[Type]Type)
|
||
|
work typeDeque
|
||
|
)
|
||
|
|
||
|
for t := &typ; t != nil; t = work.pop() {
|
||
|
// *t is the identity of the type.
|
||
|
if cpy := copies[*t]; cpy != nil {
|
||
|
*t = cpy
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
cpy := (*t).copy()
|
||
|
copies[*t] = cpy
|
||
|
*t = cpy
|
||
|
|
||
|
// Mark any nested types for copying.
|
||
|
cpy.walk(&work)
|
||
|
}
|
||
|
|
||
|
return typ
|
||
|
}
|
||
|
|
||
|
// typeDeque keeps track of pointers to types which still
|
||
|
// need to be visited.
|
||
|
type typeDeque struct {
|
||
|
types []*Type
|
||
|
read, write uint64
|
||
|
mask uint64
|
||
|
}
|
||
|
|
||
|
// 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.read == dq.write {
|
||
|
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.read == dq.write {
|
||
|
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.
|
||
|
//
|
||
|
// Returns a map of named types (so, where NameOff is non-zero) and 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, rawStrings stringTable) (types []Type, namedTypes map[string][]namedType, err error) {
|
||
|
type fixupDef struct {
|
||
|
id TypeID
|
||
|
expectedKind btfKind
|
||
|
typ *Type
|
||
|
}
|
||
|
|
||
|
var fixups []fixupDef
|
||
|
fixup := func(id TypeID, expectedKind btfKind, typ *Type) {
|
||
|
fixups = append(fixups, fixupDef{id, expectedKind, typ})
|
||
|
}
|
||
|
|
||
|
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.LookupName(btfMember.NameOff)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
|
||
|
}
|
||
|
m := Member{
|
||
|
Name: name,
|
||
|
Offset: btfMember.Offset,
|
||
|
}
|
||
|
if kindFlag {
|
||
|
m.BitfieldSize = btfMember.Offset >> 24
|
||
|
m.Offset &= 0xffffff
|
||
|
}
|
||
|
members = append(members, m)
|
||
|
}
|
||
|
for i := range members {
|
||
|
fixup(raw[i].Type, kindUnknown, &members[i].Type)
|
||
|
}
|
||
|
return members, nil
|
||
|
}
|
||
|
|
||
|
types = make([]Type, 0, len(rawTypes))
|
||
|
types = append(types, (*Void)(nil))
|
||
|
namedTypes = make(map[string][]namedType)
|
||
|
|
||
|
for i, raw := range rawTypes {
|
||
|
var (
|
||
|
// Void is defined to always be type ID 0, and is thus
|
||
|
// omitted from BTF.
|
||
|
id = TypeID(i + 1)
|
||
|
typ Type
|
||
|
)
|
||
|
|
||
|
name, err := rawStrings.LookupName(raw.NameOff)
|
||
|
if err != nil {
|
||
|
return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err)
|
||
|
}
|
||
|
|
||
|
switch raw.Kind() {
|
||
|
case kindInt:
|
||
|
encoding, offset, bits := intEncoding(*raw.data.(*uint32))
|
||
|
typ = &Int{id, name, raw.Size(), encoding, offset, bits}
|
||
|
|
||
|
case kindPointer:
|
||
|
ptr := &Pointer{id, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &ptr.Target)
|
||
|
typ = ptr
|
||
|
|
||
|
case kindArray:
|
||
|
btfArr := raw.data.(*btfArray)
|
||
|
|
||
|
// IndexType is unused according to btf.rst.
|
||
|
// Don't make it available right now.
|
||
|
arr := &Array{id, nil, btfArr.Nelems}
|
||
|
fixup(btfArr.Type, kindUnknown, &arr.Type)
|
||
|
typ = arr
|
||
|
|
||
|
case kindStruct:
|
||
|
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
||
|
if err != nil {
|
||
|
return nil, nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
|
||
|
}
|
||
|
typ = &Struct{id, name, raw.Size(), members}
|
||
|
|
||
|
case kindUnion:
|
||
|
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
||
|
if err != nil {
|
||
|
return nil, nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
|
||
|
}
|
||
|
typ = &Union{id, name, raw.Size(), members}
|
||
|
|
||
|
case kindEnum:
|
||
|
rawvals := raw.data.([]btfEnum)
|
||
|
vals := make([]EnumValue, 0, len(rawvals))
|
||
|
for i, btfVal := range rawvals {
|
||
|
name, err := rawStrings.LookupName(btfVal.NameOff)
|
||
|
if err != nil {
|
||
|
return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err)
|
||
|
}
|
||
|
vals = append(vals, EnumValue{
|
||
|
Name: name,
|
||
|
Value: btfVal.Val,
|
||
|
})
|
||
|
}
|
||
|
typ = &Enum{id, name, vals}
|
||
|
|
||
|
case kindForward:
|
||
|
if raw.KindFlag() {
|
||
|
typ = &Fwd{id, name, FwdUnion}
|
||
|
} else {
|
||
|
typ = &Fwd{id, name, FwdStruct}
|
||
|
}
|
||
|
|
||
|
case kindTypedef:
|
||
|
typedef := &Typedef{id, name, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &typedef.Type)
|
||
|
typ = typedef
|
||
|
|
||
|
case kindVolatile:
|
||
|
volatile := &Volatile{id, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &volatile.Type)
|
||
|
typ = volatile
|
||
|
|
||
|
case kindConst:
|
||
|
cnst := &Const{id, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &cnst.Type)
|
||
|
typ = cnst
|
||
|
|
||
|
case kindRestrict:
|
||
|
restrict := &Restrict{id, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &restrict.Type)
|
||
|
typ = restrict
|
||
|
|
||
|
case kindFunc:
|
||
|
fn := &Func{id, name, nil}
|
||
|
fixup(raw.Type(), kindFuncProto, &fn.Type)
|
||
|
typ = fn
|
||
|
|
||
|
case kindFuncProto:
|
||
|
rawparams := raw.data.([]btfParam)
|
||
|
params := make([]FuncParam, 0, len(rawparams))
|
||
|
for i, param := range rawparams {
|
||
|
name, err := rawStrings.LookupName(param.NameOff)
|
||
|
if err != nil {
|
||
|
return nil, 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, kindUnknown, ¶ms[i].Type)
|
||
|
}
|
||
|
|
||
|
fp := &FuncProto{id, nil, params}
|
||
|
fixup(raw.Type(), kindUnknown, &fp.Return)
|
||
|
typ = fp
|
||
|
|
||
|
case kindVar:
|
||
|
v := &Var{id, name, nil}
|
||
|
fixup(raw.Type(), kindUnknown, &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, kindVar, &vars[i].Type)
|
||
|
}
|
||
|
typ = &Datasec{id, name, raw.SizeType, vars}
|
||
|
|
||
|
default:
|
||
|
return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
|
||
|
}
|
||
|
|
||
|
types = append(types, typ)
|
||
|
|
||
|
if named, ok := typ.(namedType); ok {
|
||
|
if name := essentialName(named.name()); name != "" {
|
||
|
namedTypes[name] = append(namedTypes[name], named)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, fixup := range fixups {
|
||
|
i := int(fixup.id)
|
||
|
if i >= len(types) {
|
||
|
return nil, nil, fmt.Errorf("reference to invalid type id: %d", fixup.id)
|
||
|
}
|
||
|
|
||
|
// Default void (id 0) to unknown
|
||
|
rawKind := kindUnknown
|
||
|
if i > 0 {
|
||
|
rawKind = rawTypes[i-1].Kind()
|
||
|
}
|
||
|
|
||
|
if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected {
|
||
|
return nil, nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind)
|
||
|
}
|
||
|
|
||
|
*fixup.typ = types[i]
|
||
|
}
|
||
|
|
||
|
return types, namedTypes, nil
|
||
|
}
|
||
|
|
||
|
// essentialName returns name without a ___ suffix.
|
||
|
func essentialName(name string) string {
|
||
|
lastIdx := strings.LastIndex(name, "___")
|
||
|
if lastIdx > 0 {
|
||
|
return name[:lastIdx]
|
||
|
}
|
||
|
return name
|
||
|
}
|