Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

btf: fix IntEncoding #797

Merged
merged 1 commit into from Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 21 additions & 10 deletions btf/format.go
Expand Up @@ -119,7 +119,7 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
var err error
switch v := skipQualifiers(typ).(type) {
case *Int:
gf.writeIntLit(v)
err = gf.writeIntLit(v)

case *Enum:
if !v.Signed {
Expand Down Expand Up @@ -166,19 +166,30 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
return nil
}

func (gf *GoFormatter) writeIntLit(i *Int) {
// NB: Encoding.IsChar is ignored.
if i.Encoding.IsBool() && i.Size == 1 {
gf.w.WriteString("bool")
return
}

func (gf *GoFormatter) writeIntLit(i *Int) error {
bits := i.Size * 8
if i.Encoding.IsSigned() {
switch i.Encoding {
case Bool:
if i.Size != 1 {
return fmt.Errorf("bool with size %d", i.Size)
}
gf.w.WriteString("bool")
case Signed:
fmt.Fprintf(&gf.w, "int%d", bits)
} else {
case Char:
if i.Size != 1 {
return fmt.Errorf("char with size %d", i.Size)
}
// BTF doesn't have a way to specify the signedness of a char. Assume
// we are dealing with unsigned, since this works nicely with []byte
// in Go code.
fallthrough
case Unsigned:
fmt.Fprintf(&gf.w, "uint%d", bits)
default:
return fmt.Errorf("can't encode %s", i.Encoding)
}
return nil
}

func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
Expand Down
34 changes: 22 additions & 12 deletions btf/format_test.go
Expand Up @@ -15,10 +15,7 @@ func TestGoTypeDeclaration(t *testing.T) {
}{
{&Int{Size: 1}, "type t uint8"},
{&Int{Size: 1, Encoding: Bool}, "type t bool"},
{&Int{Size: 2, Encoding: Bool}, "type t uint16"},
{&Int{Size: 1, Encoding: Char}, "type t uint8"},
{&Int{Size: 1, Encoding: Char | Signed}, "type t int8"},
{&Int{Size: 2, Encoding: Char}, "type t uint16"},
{&Int{Size: 2, Encoding: Signed}, "type t int16"},
{&Int{Size: 4, Encoding: Signed}, "type t int32"},
{&Int{Size: 8}, "type t uint64"},
Expand Down Expand Up @@ -223,15 +220,28 @@ func TestGoTypeDeclarationCycle(t *testing.T) {
}

func TestRejectBogusTypes(t *testing.T) {
var gf GoFormatter
_, err := gf.TypeDeclaration("t", &Struct{
Size: 1,
Members: []Member{
{Name: "foo", Type: &Int{Size: 2}, Offset: 0},
},
})
if err == nil {
t.Fatal("TypeDeclaration does not reject bogus struct")
tests := []struct {
typ Type
}{
{&Struct{
Size: 1,
Members: []Member{
{Name: "foo", Type: &Int{Size: 2}, Offset: 0},
},
}},
{&Int{Size: 2, Encoding: Bool}},
{&Int{Size: 1, Encoding: Char | Signed}},
{&Int{Size: 2, Encoding: Char}},
}
for _, test := range tests {
t.Run(fmt.Sprint(test.typ), func(t *testing.T) {
var gf GoFormatter

_, err := gf.TypeDeclaration("t", test.typ)
if err == nil {
t.Fatal("TypeDeclaration does not reject bogus type")
}
})
}
}

Expand Down
37 changes: 15 additions & 22 deletions btf/types.go
Expand Up @@ -77,36 +77,29 @@ func (v *Void) copy() Type { return (*Void)(nil) }

type IntEncoding byte

// Valid IntEncodings.
//
// These may look like they are flags, but they aren't.
const (
Signed IntEncoding = 1 << iota
Char
Bool
Unsigned IntEncoding = 0
Signed IntEncoding = 1
Char IntEncoding = 2
Bool IntEncoding = 4
)

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():
switch ie {
case Char:
// NB: There is no way to determine signedness for char.
return "char"
case ie.IsChar() && !ie.IsSigned():
return "uchar"
case ie.IsBool():
case Bool:
return "bool"
case ie.IsSigned():
case Signed:
return "signed"
default:
case Unsigned:
return "unsigned"
default:
return fmt.Sprintf("IntEncoding(%d)", byte(ie))
}
}

Expand Down