| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ld |
| |
| import ( |
| objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function |
| "debug/dwarf" |
| "internal/testenv" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "reflect" |
| "runtime" |
| "testing" |
| ) |
| |
| func TestRuntimeTypeDIEs(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| |
| if runtime.GOOS == "plan9" { |
| t.Skip("skipping on plan9; no DWARF symbol table in executables") |
| } |
| |
| dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs") |
| if err != nil { |
| t.Fatalf("could not create directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| |
| f := gobuild(t, dir, `package main; func main() { }`) |
| defer f.Close() |
| |
| dwarf, err := f.DWARF() |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| |
| want := map[string]bool{ |
| "runtime._type": true, |
| "runtime.arraytype": true, |
| "runtime.chantype": true, |
| "runtime.functype": true, |
| "runtime.maptype": true, |
| "runtime.ptrtype": true, |
| "runtime.slicetype": true, |
| "runtime.structtype": true, |
| "runtime.interfacetype": true, |
| "runtime.itab": true, |
| "runtime.imethod": true, |
| } |
| |
| found := findTypes(t, dwarf, want) |
| if len(found) != len(want) { |
| t.Errorf("found %v, want %v", found, want) |
| } |
| } |
| |
| func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { |
| found = make(map[string]bool) |
| rdr := dw.Reader() |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| switch entry.Tag { |
| case dwarf.TagTypedef: |
| if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { |
| found[name] = true |
| } |
| } |
| } |
| return |
| } |
| |
| func gobuild(t *testing.T, dir string, testfile string) *objfilepkg.File { |
| src := filepath.Join(dir, "test.go") |
| dst := filepath.Join(dir, "out") |
| |
| if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil { |
| t.Fatal(err) |
| } |
| |
| cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags", "-N -l", "-o", dst, src) |
| if b, err := cmd.CombinedOutput(); err != nil { |
| t.Logf("build: %s\n", b) |
| t.Fatalf("build error: %v", err) |
| } |
| |
| f, err := objfilepkg.Open(dst) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return f |
| } |
| |
| func TestEmbeddedStructMarker(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| |
| if runtime.GOOS == "plan9" { |
| t.Skip("skipping on plan9; no DWARF symbol table in executables") |
| } |
| |
| const prog = ` |
| package main |
| |
| import "fmt" |
| |
| type Foo struct { v int } |
| type Bar struct { |
| Foo |
| name string |
| } |
| type Baz struct { |
| *Foo |
| name string |
| } |
| |
| func main() { |
| bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} |
| baz := Baz{ Foo: &bar.Foo, name: "123" } |
| fmt.Println(bar, baz) |
| }` |
| |
| want := map[string]map[string]bool{ |
| "main.Foo": map[string]bool{"v": false}, |
| "main.Bar": map[string]bool{"Foo": true, "name": false}, |
| "main.Baz": map[string]bool{"Foo": true, "name": false}, |
| } |
| |
| dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") |
| if err != nil { |
| t.Fatalf("could not create directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| |
| f := gobuild(t, dir, prog) |
| |
| defer f.Close() |
| |
| d, err := f.DWARF() |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| |
| rdr := d.Reader() |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| switch entry.Tag { |
| case dwarf.TagStructType: |
| name := entry.Val(dwarf.AttrName).(string) |
| wantMembers := want[name] |
| if wantMembers == nil { |
| continue |
| } |
| gotMembers, err := findMembers(rdr) |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| |
| if !reflect.DeepEqual(gotMembers, wantMembers) { |
| t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) |
| } |
| delete(want, name) |
| } |
| } |
| if len(want) != 0 { |
| t.Errorf("failed to check all expected types: missing types = %+v", want) |
| } |
| } |
| |
| func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { |
| memberEmbedded := map[string]bool{} |
| // TODO(hyangah): define in debug/dwarf package |
| const goEmbeddedStruct = dwarf.Attr(0x2903) |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| return nil, err |
| } |
| switch entry.Tag { |
| case dwarf.TagMember: |
| name := entry.Val(dwarf.AttrName).(string) |
| embedded := entry.Val(goEmbeddedStruct).(bool) |
| memberEmbedded[name] = embedded |
| case 0: |
| return memberEmbedded, nil |
| } |
| } |
| return memberEmbedded, nil |
| } |
| |
| func TestSizes(t *testing.T) { |
| if runtime.GOOS == "plan9" { |
| t.Skip("skipping on plan9; no DWARF symbol table in executables") |
| } |
| |
| // DWARF sizes should never be -1. |
| // See issue #21097 |
| const prog = ` |
| package main |
| var x func() |
| var y [4]func() |
| func main() { |
| x = nil |
| y[0] = nil |
| } |
| ` |
| dir, err := ioutil.TempDir("", "TestSizes") |
| if err != nil { |
| t.Fatalf("could not create directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| f := gobuild(t, dir, prog) |
| defer f.Close() |
| d, err := f.DWARF() |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| rdr := d.Reader() |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| switch entry.Tag { |
| case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: |
| default: |
| continue |
| } |
| typ, err := d.Type(entry.Offset) |
| if err != nil { |
| t.Fatalf("can't read type: %v", err) |
| } |
| if typ.Size() < 0 { |
| t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ) |
| } |
| } |
| } |
| |
| func TestFieldOverlap(t *testing.T) { |
| if runtime.GOOS == "plan9" { |
| t.Skip("skipping on plan9; no DWARF symbol table in executables") |
| } |
| |
| // This test grew out of issue 21094, where specific sudog<T> DWARF types |
| // had elem fields set to values instead of pointers. |
| const prog = ` |
| package main |
| |
| var c chan string |
| |
| func main() { |
| c <- "foo" |
| } |
| ` |
| dir, err := ioutil.TempDir("", "TestFieldOverlap") |
| if err != nil { |
| t.Fatalf("could not create directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| |
| f := gobuild(t, dir, prog) |
| defer f.Close() |
| |
| d, err := f.DWARF() |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| |
| rdr := d.Reader() |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| if entry.Tag != dwarf.TagStructType { |
| continue |
| } |
| typ, err := d.Type(entry.Offset) |
| if err != nil { |
| t.Fatalf("can't read type: %v", err) |
| } |
| s := typ.(*dwarf.StructType) |
| for i := 0; i < len(s.Field); i++ { |
| end := s.Field[i].ByteOffset + s.Field[i].Type.Size() |
| var limit int64 |
| if i == len(s.Field)-1 { |
| limit = s.Size() |
| } else { |
| limit = s.Field[i+1].ByteOffset |
| } |
| if end > limit { |
| name := entry.Val(dwarf.AttrName).(string) |
| t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name) |
| } |
| } |
| } |
| } |
| |
| func TestVarDeclCoords(t *testing.T) { |
| testenv.MustHaveGoBuild(t) |
| |
| if runtime.GOOS == "plan9" { |
| t.Skip("skipping on plan9; no DWARF symbol table in executables") |
| } |
| |
| const prog = ` |
| package main |
| |
| func main() { |
| var i int |
| i = i |
| } |
| ` |
| dir, err := ioutil.TempDir("", "TestVarDeclCoords") |
| if err != nil { |
| t.Fatalf("could not create directory: %v", err) |
| } |
| defer os.RemoveAll(dir) |
| |
| f := gobuild(t, dir, prog) |
| |
| d, err := f.DWARF() |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| |
| rdr := d.Reader() |
| var iEntry *dwarf.Entry |
| foundMain := false |
| for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { |
| if err != nil { |
| t.Fatalf("error reading DWARF: %v", err) |
| } |
| if entry.Tag == dwarf.TagSubprogram && entry.Val(dwarf.AttrName).(string) == "main.main" { |
| foundMain = true |
| continue |
| } |
| if !foundMain { |
| continue |
| } |
| if entry.Tag == dwarf.TagSubprogram { |
| t.Fatalf("didn't find DW_TAG_variable for i in main.main") |
| } |
| if foundMain && entry.Tag == dwarf.TagVariable && entry.Val(dwarf.AttrName).(string) == "i" { |
| iEntry = entry |
| break |
| } |
| } |
| |
| line := iEntry.Val(dwarf.AttrDeclLine) |
| if line == nil || line.(int64) != 5 { |
| t.Errorf("DW_AT_decl_line for i is %v, want 5", line) |
| } |
| } |