mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-20 17:28:40 +00:00
acc333c40b
When GTS is running in a container runtime which has configured CPU or memory limits or under an init system that uses cgroups to impose CPU and memory limits the values the Go runtime sees for GOMAXPROCS and GOMEMLIMIT are still based on the host resources, not the cgroup. At least for the throttling middlewares which use GOMAXPROCS to configure their queue size, this can result in GTS running with values too big compared to the resources that will actuall be available to it. This introduces 2 dependencies which can pick up resource contraints from the current cgroup and tune the Go runtime accordingly. This should result in the different queues being appropriately sized and in general more predictable performance. These dependencies are a no-op on non-Linux systems or if running in a cgroup that doesn't set a limit on CPU or memory. The automatic tuning of GOMEMLIMIT can be disabled by either explicitly setting GOMEMLIMIT yourself or by setting AUTOMEMLIMIT=off. The automatic tuning of GOMAXPROCS can similarly be counteracted by setting GOMAXPROCS yourself.
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
|
|
}
|