github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/link/internal/ld/dwarf_test.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ld
     6  
     7  import (
     8  	objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
     9  	"debug/dwarf"
    10  	"internal/testenv"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"reflect"
    16  	"runtime"
    17  	"testing"
    18  )
    19  
    20  func TestRuntimeTypeDIEs(t *testing.T) {
    21  	testenv.MustHaveGoBuild(t)
    22  
    23  	if runtime.GOOS == "plan9" {
    24  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
    25  	}
    26  
    27  	dir, err := ioutil.TempDir("", "TestRuntimeTypeDIEs")
    28  	if err != nil {
    29  		t.Fatalf("could not create directory: %v", err)
    30  	}
    31  	defer os.RemoveAll(dir)
    32  
    33  	f := gobuild(t, dir, `package main; func main() { }`)
    34  	defer f.Close()
    35  
    36  	dwarf, err := f.DWARF()
    37  	if err != nil {
    38  		t.Fatalf("error reading DWARF: %v", err)
    39  	}
    40  
    41  	want := map[string]bool{
    42  		"runtime._type":         true,
    43  		"runtime.arraytype":     true,
    44  		"runtime.chantype":      true,
    45  		"runtime.functype":      true,
    46  		"runtime.maptype":       true,
    47  		"runtime.ptrtype":       true,
    48  		"runtime.slicetype":     true,
    49  		"runtime.structtype":    true,
    50  		"runtime.interfacetype": true,
    51  		"runtime.itab":          true,
    52  		"runtime.imethod":       true,
    53  	}
    54  
    55  	found := findTypes(t, dwarf, want)
    56  	if len(found) != len(want) {
    57  		t.Errorf("found %v, want %v", found, want)
    58  	}
    59  }
    60  
    61  func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
    62  	found = make(map[string]bool)
    63  	rdr := dw.Reader()
    64  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
    65  		if err != nil {
    66  			t.Fatalf("error reading DWARF: %v", err)
    67  		}
    68  		switch entry.Tag {
    69  		case dwarf.TagTypedef:
    70  			if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
    71  				found[name] = true
    72  			}
    73  		}
    74  	}
    75  	return
    76  }
    77  
    78  func gobuild(t *testing.T, dir string, testfile string) *objfilepkg.File {
    79  	src := filepath.Join(dir, "test.go")
    80  	dst := filepath.Join(dir, "out")
    81  
    82  	if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", dst, src)
    87  	if b, err := cmd.CombinedOutput(); err != nil {
    88  		t.Logf("build: %s\n", b)
    89  		t.Fatalf("build error: %v", err)
    90  	}
    91  
    92  	f, err := objfilepkg.Open(dst)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	return f
    97  }
    98  
    99  func TestEmbeddedStructMarker(t *testing.T) {
   100  	testenv.MustHaveGoBuild(t)
   101  
   102  	if runtime.GOOS == "plan9" {
   103  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   104  	}
   105  
   106  	const prog = `
   107  package main
   108  
   109  import "fmt"
   110  
   111  type Foo struct { v int }
   112  type Bar struct {
   113  	Foo
   114  	name string
   115  }
   116  type Baz struct {
   117  	*Foo
   118  	name string
   119  }
   120  
   121  func main() {
   122  	bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
   123  	baz := Baz{ Foo: &bar.Foo, name: "123" }
   124  	fmt.Println(bar, baz)
   125  }`
   126  
   127  	want := map[string]map[string]bool{
   128  		"main.Foo": map[string]bool{"v": false},
   129  		"main.Bar": map[string]bool{"Foo": true, "name": false},
   130  		"main.Baz": map[string]bool{"Foo": true, "name": false},
   131  	}
   132  
   133  	dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
   134  	if err != nil {
   135  		t.Fatalf("could not create directory: %v", err)
   136  	}
   137  	defer os.RemoveAll(dir)
   138  
   139  	f := gobuild(t, dir, prog)
   140  
   141  	defer f.Close()
   142  
   143  	d, err := f.DWARF()
   144  	if err != nil {
   145  		t.Fatalf("error reading DWARF: %v", err)
   146  	}
   147  
   148  	rdr := d.Reader()
   149  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   150  		if err != nil {
   151  			t.Fatalf("error reading DWARF: %v", err)
   152  		}
   153  		switch entry.Tag {
   154  		case dwarf.TagStructType:
   155  			name := entry.Val(dwarf.AttrName).(string)
   156  			wantMembers := want[name]
   157  			if wantMembers == nil {
   158  				continue
   159  			}
   160  			gotMembers, err := findMembers(rdr)
   161  			if err != nil {
   162  				t.Fatalf("error reading DWARF: %v", err)
   163  			}
   164  
   165  			if !reflect.DeepEqual(gotMembers, wantMembers) {
   166  				t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
   167  			}
   168  			delete(want, name)
   169  		}
   170  	}
   171  	if len(want) != 0 {
   172  		t.Errorf("failed to check all expected types: missing types = %+v", want)
   173  	}
   174  }
   175  
   176  func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
   177  	memberEmbedded := map[string]bool{}
   178  	// TODO(hyangah): define in debug/dwarf package
   179  	const goEmbeddedStruct = dwarf.Attr(0x2903)
   180  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		switch entry.Tag {
   185  		case dwarf.TagMember:
   186  			name := entry.Val(dwarf.AttrName).(string)
   187  			embedded := entry.Val(goEmbeddedStruct).(bool)
   188  			memberEmbedded[name] = embedded
   189  		case 0:
   190  			return memberEmbedded, nil
   191  		}
   192  	}
   193  	return memberEmbedded, nil
   194  }
   195  
   196  func TestSizes(t *testing.T) {
   197  	if runtime.GOOS == "plan9" {
   198  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   199  	}
   200  
   201  	// DWARF sizes should never be -1.
   202  	// See issue #21097
   203  	const prog = `
   204  package main
   205  var x func()
   206  var y [4]func()
   207  func main() {
   208  	x = nil
   209  	y[0] = nil
   210  }
   211  `
   212  	dir, err := ioutil.TempDir("", "TestSizes")
   213  	if err != nil {
   214  		t.Fatalf("could not create directory: %v", err)
   215  	}
   216  	defer os.RemoveAll(dir)
   217  	f := gobuild(t, dir, prog)
   218  	defer f.Close()
   219  	d, err := f.DWARF()
   220  	if err != nil {
   221  		t.Fatalf("error reading DWARF: %v", err)
   222  	}
   223  	rdr := d.Reader()
   224  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   225  		if err != nil {
   226  			t.Fatalf("error reading DWARF: %v", err)
   227  		}
   228  		switch entry.Tag {
   229  		case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
   230  		default:
   231  			continue
   232  		}
   233  		typ, err := d.Type(entry.Offset)
   234  		if err != nil {
   235  			t.Fatalf("can't read type: %v", err)
   236  		}
   237  		if typ.Size() < 0 {
   238  			t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
   239  		}
   240  	}
   241  }
   242  
   243  func TestFieldOverlap(t *testing.T) {
   244  	if runtime.GOOS == "plan9" {
   245  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   246  	}
   247  
   248  	// This test grew out of issue 21094, where specific sudog<T> DWARF types
   249  	// had elem fields set to values instead of pointers.
   250  	const prog = `
   251  package main
   252  
   253  var c chan string
   254  
   255  func main() {
   256  	c <- "foo"
   257  }
   258  `
   259  	dir, err := ioutil.TempDir("", "TestFieldOverlap")
   260  	if err != nil {
   261  		t.Fatalf("could not create directory: %v", err)
   262  	}
   263  	defer os.RemoveAll(dir)
   264  
   265  	f := gobuild(t, dir, prog)
   266  	defer f.Close()
   267  
   268  	d, err := f.DWARF()
   269  	if err != nil {
   270  		t.Fatalf("error reading DWARF: %v", err)
   271  	}
   272  
   273  	rdr := d.Reader()
   274  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   275  		if err != nil {
   276  			t.Fatalf("error reading DWARF: %v", err)
   277  		}
   278  		if entry.Tag != dwarf.TagStructType {
   279  			continue
   280  		}
   281  		typ, err := d.Type(entry.Offset)
   282  		if err != nil {
   283  			t.Fatalf("can't read type: %v", err)
   284  		}
   285  		s := typ.(*dwarf.StructType)
   286  		for i := 0; i < len(s.Field); i++ {
   287  			end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
   288  			var limit int64
   289  			if i == len(s.Field)-1 {
   290  				limit = s.Size()
   291  			} else {
   292  				limit = s.Field[i+1].ByteOffset
   293  			}
   294  			if end > limit {
   295  				name := entry.Val(dwarf.AttrName).(string)
   296  				t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
   297  			}
   298  		}
   299  	}
   300  }
   301  
   302  func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
   303  	testenv.MustHaveGoBuild(t)
   304  
   305  	if runtime.GOOS == "plan9" {
   306  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   307  	}
   308  
   309  	const prog = `
   310  package main
   311  
   312  func main() {
   313  	var i int
   314  	i = i
   315  }
   316  `
   317  	dir, err := ioutil.TempDir("", "TestVarDeclCoords")
   318  	if err != nil {
   319  		t.Fatalf("could not create directory: %v", err)
   320  	}
   321  	defer os.RemoveAll(dir)
   322  
   323  	f := gobuild(t, dir, prog)
   324  
   325  	d, err := f.DWARF()
   326  	if err != nil {
   327  		t.Fatalf("error reading DWARF: %v", err)
   328  	}
   329  
   330  	rdr := d.Reader()
   331  	var iEntry *dwarf.Entry
   332  	var pEntry *dwarf.Entry
   333  	foundMain := false
   334  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   335  		if err != nil {
   336  			t.Fatalf("error reading DWARF: %v", err)
   337  		}
   338  		if entry.Tag == dwarf.TagSubprogram && entry.Val(dwarf.AttrName).(string) == "main.main" {
   339  			foundMain = true
   340  			pEntry = entry
   341  			continue
   342  		}
   343  		if !foundMain {
   344  			continue
   345  		}
   346  		if entry.Tag == dwarf.TagSubprogram {
   347  			t.Fatalf("didn't find DW_TAG_variable for i in main.main")
   348  		}
   349  		if foundMain && entry.Tag == dwarf.TagVariable && entry.Val(dwarf.AttrName).(string) == "i" {
   350  			iEntry = entry
   351  			break
   352  		}
   353  	}
   354  
   355  	line := iEntry.Val(dwarf.AttrDeclLine)
   356  	if line == nil || line.(int64) != 5 {
   357  		t.Errorf("DW_AT_decl_line for i is %v, want 5", line)
   358  	}
   359  
   360  	file := pEntry.Val(dwarf.AttrDeclFile)
   361  	if file == nil || file.(int64) != 1 {
   362  		t.Errorf("DW_AT_decl_file for main is %v, want 1", file)
   363  	}
   364  }