Revision b9098e6b25f97b4af51ebec3c24f5cf6ad48551f authored by Gilberto Bertin on 29 February 2024, 07:36:40 UTC, committed by Martynas Pumputis on 29 February 2024, 15:47:00 UTC
currently some functions in policy.h reference POLICY_MAP, assuming it's always defined. This prevents including this header in a context where the POLICY_MAP is not defined. To overcome this, remove all the POLICY_MAP references from these functions and always pass the map explicitly in the caller. No functional changes are introduced. Signed-off-by: Gilberto Bertin <jibi@cilium.io>
1 parent d7dba5e
alignchecker.go
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
package alignchecker
import (
"encoding/binary"
"fmt"
"reflect"
"github.com/cilium/ebpf/btf"
)
// CheckStructAlignments checks whether size and offsets match of the given
// C and Go structs which are listed in the given toCheck map (C type name =>
// Go type).
//
// C struct layout is extracted from the given ELF object file's BTF info.
//
// To find a matching C struct field, a Go field has to be tagged with
// `align:"field_name_in_c_struct". In the case of unnamed union field, such
// union fields can be referred with special tags - `align:"$union0"`,
// `align:"$union1"`, etc.
func CheckStructAlignments(pathToObj string, toCheck map[string][]any, checkOffsets bool) error {
spec, err := btf.LoadSpec(pathToObj)
if err != nil {
return fmt.Errorf("cannot parse BTF debug info %s: %s", pathToObj, err)
}
structInfo, err := getStructInfosFromBTF(spec, toCheck)
if err != nil {
return fmt.Errorf("cannot extract struct info from BTF %s: %s", pathToObj, err)
}
for cName, goStructs := range toCheck {
if err := check(cName, goStructs, structInfo, checkOffsets); err != nil {
return err
}
}
return nil
}
type structInfo struct {
size uint32
fieldOffsets map[string]uint32
}
func getStructInfosFromBTF(types *btf.Spec, toCheck map[string][]any) (map[string]*structInfo, error) {
structs := make(map[string]*structInfo)
for name := range toCheck {
t, err := types.AnyTypeByName(name)
if err != nil {
return nil, fmt.Errorf("looking up type %s by name: %w", name, err)
}
si, err := getStructInfoFromBTF(t)
if err != nil {
return nil, err
}
structs[name] = si
}
return structs, nil
}
func getStructInfoFromBTF(t btf.Type) (*structInfo, error) {
switch typ := t.(type) {
case *btf.Typedef:
// Resolve Typedefs to their target types.
return getStructInfoFromBTF(typ.Type)
case *btf.Int:
return &structInfo{
size: typ.Size,
fieldOffsets: nil,
}, nil
case *btf.Struct:
return &structInfo{
size: typ.Size,
fieldOffsets: memberOffsets(typ.Members),
}, nil
case *btf.Union:
return &structInfo{
size: typ.Size,
fieldOffsets: memberOffsets(typ.Members),
}, nil
}
return nil, fmt.Errorf("unsupported type: %s", t)
}
func dotConcat(x, y string) string {
dot := ""
if x != "" && y != "" {
dot = "."
}
return x + dot + y
}
func _memberOffsets(members []btf.Member, offsets map[string]uint32, currOffset uint32, prefix string) {
anonUnions := 0
anonStructs := 0
for _, member := range members {
memberName := member.Name
if memberName == "" {
if _, ok := member.Type.(*btf.Union); ok {
memberName = fmt.Sprintf("$union%d", anonUnions)
anonUnions++
} else if _, ok := member.Type.(*btf.Struct); ok {
memberName = fmt.Sprintf("$struct%d", anonStructs)
anonStructs++
}
}
fullName := dotConcat(prefix, memberName)
offset := uint32(member.Offset.Bytes())
if typ, ok := member.Type.(*btf.Union); ok {
_memberOffsets(typ.Members, offsets, currOffset+offset, fullName)
} else if typ, ok := member.Type.(*btf.Struct); ok {
_memberOffsets(typ.Members, offsets, currOffset+offset, fullName)
}
offsets[fullName] = currOffset + offset
}
}
func memberOffsets(members []btf.Member) map[string]uint32 {
offsets := make(map[string]uint32, len(members))
_memberOffsets(members, offsets, 0, "")
return offsets
}
func check(name string, toCheck []any, structs map[string]*structInfo, checkOffsets bool) error {
for _, i := range toCheck {
c, found := structs[name]
if !found {
return fmt.Errorf("could not find C struct %s", name)
}
g := reflect.TypeOf(i)
if g == nil {
return fmt.Errorf("nil interface passed for type %s", name)
}
// Input type must be a struct.
if g.Kind() != reflect.Struct {
return fmt.Errorf("type %s is not a struct", name)
}
if bs, rs := binary.Size(i), int(g.Size()); bs != rs {
return fmt.Errorf("type %s's binary.Size (%d) does not equal its unsafe.Sizeof (%d) size (struct with implicit trailing padding?)", g.Name(), bs, rs)
}
if c.size != uint32(g.Size()) {
return fmt.Errorf("%s(%d) size does not match %s(%d)", g, g.Size(),
name, c.size)
}
if !checkOffsets {
continue
}
for i := 0; i < g.NumField(); i++ {
fieldName := g.Field(i).Tag.Get("align")
// Ignore fields without `align` struct tag
if fieldName == "" {
continue
}
goOffset := uint32(g.Field(i).Offset)
if cOffset, ok := c.fieldOffsets[fieldName]; !ok {
return fmt.Errorf("%s.%s does not match any field (should match %s.%s) [debug=%v]",
g, g.Field(i).Name, name, fieldName, c.fieldOffsets)
} else if goOffset != cOffset {
return fmt.Errorf("%s.%s offset(%d) does not match %s.%s(%d) [debug=%v]",
g, g.Field(i).Name, goOffset, name, fieldName, cOffset, c.fieldOffsets)
}
}
}
return nil
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...