github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/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  	intdwarf "cmd/internal/dwarf"
     9  	objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
    10  	"debug/dwarf"
    11  	"errors"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"io/ioutil"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"reflect"
    19  	"runtime"
    20  	"strconv"
    21  	"strings"
    22  	"testing"
    23  )
    24  
    25  const (
    26  	DefaultOpt = "-gcflags="
    27  	NoOpt      = "-gcflags=-l -N"
    28  	OptInl4    = "-gcflags=-l=4"
    29  	OptAllInl4 = "-gcflags=all=-l=4"
    30  )
    31  
    32  func TestRuntimeTypesPresent(t *testing.T) {
    33  	t.Parallel()
    34  	testenv.MustHaveGoBuild(t)
    35  
    36  	if runtime.GOOS == "plan9" {
    37  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
    38  	}
    39  
    40  	dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent")
    41  	if err != nil {
    42  		t.Fatalf("could not create directory: %v", err)
    43  	}
    44  	defer os.RemoveAll(dir)
    45  
    46  	f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
    47  	defer f.Close()
    48  
    49  	dwarf, err := f.DWARF()
    50  	if err != nil {
    51  		t.Fatalf("error reading DWARF: %v", err)
    52  	}
    53  
    54  	want := map[string]bool{
    55  		"runtime._type":         true,
    56  		"runtime.arraytype":     true,
    57  		"runtime.chantype":      true,
    58  		"runtime.functype":      true,
    59  		"runtime.maptype":       true,
    60  		"runtime.ptrtype":       true,
    61  		"runtime.slicetype":     true,
    62  		"runtime.structtype":    true,
    63  		"runtime.interfacetype": true,
    64  		"runtime.itab":          true,
    65  		"runtime.imethod":       true,
    66  	}
    67  
    68  	found := findTypes(t, dwarf, want)
    69  	if len(found) != len(want) {
    70  		t.Errorf("found %v, want %v", found, want)
    71  	}
    72  }
    73  
    74  func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
    75  	found = make(map[string]bool)
    76  	rdr := dw.Reader()
    77  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
    78  		if err != nil {
    79  			t.Fatalf("error reading DWARF: %v", err)
    80  		}
    81  		switch entry.Tag {
    82  		case dwarf.TagTypedef:
    83  			if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
    84  				found[name] = true
    85  			}
    86  		}
    87  	}
    88  	return
    89  }
    90  
    91  type builtFile struct {
    92  	*objfilepkg.File
    93  	path string
    94  }
    95  
    96  func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
    97  	src := filepath.Join(dir, "test.go")
    98  	dst := filepath.Join(dir, "out.exe")
    99  
   100  	if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
   101  		t.Fatal(err)
   102  	}
   103  
   104  	cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
   105  	if b, err := cmd.CombinedOutput(); err != nil {
   106  		t.Logf("build: %s\n", b)
   107  		t.Fatalf("build error: %v", err)
   108  	}
   109  
   110  	f, err := objfilepkg.Open(dst)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	return &builtFile{f, dst}
   115  }
   116  
   117  func envWithGoPathSet(gp string) []string {
   118  	env := os.Environ()
   119  	for i := 0; i < len(env); i++ {
   120  		if strings.HasPrefix(env[i], "GOPATH=") {
   121  			env[i] = "GOPATH=" + gp
   122  			return env
   123  		}
   124  	}
   125  	env = append(env, "GOPATH="+gp)
   126  	return env
   127  }
   128  
   129  // Similar to gobuild() above, but runs off a separate GOPATH environment
   130  
   131  func gobuildTestdata(t *testing.T, tdir string, gopathdir string, packtobuild string, gcflags string) *builtFile {
   132  	dst := filepath.Join(tdir, "out.exe")
   133  
   134  	// Run a build with an updated GOPATH
   135  	cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, packtobuild)
   136  	cmd.Env = envWithGoPathSet(gopathdir)
   137  	if b, err := cmd.CombinedOutput(); err != nil {
   138  		t.Logf("build: %s\n", b)
   139  		t.Fatalf("build error: %v", err)
   140  	}
   141  
   142  	f, err := objfilepkg.Open(dst)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	return &builtFile{f, dst}
   147  }
   148  
   149  func TestEmbeddedStructMarker(t *testing.T) {
   150  	t.Parallel()
   151  	testenv.MustHaveGoBuild(t)
   152  
   153  	if runtime.GOOS == "plan9" {
   154  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   155  	}
   156  
   157  	const prog = `
   158  package main
   159  
   160  import "fmt"
   161  
   162  type Foo struct { v int }
   163  type Bar struct {
   164  	Foo
   165  	name string
   166  }
   167  type Baz struct {
   168  	*Foo
   169  	name string
   170  }
   171  
   172  func main() {
   173  	bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
   174  	baz := Baz{ Foo: &bar.Foo, name: "123" }
   175  	fmt.Println(bar, baz)
   176  }`
   177  
   178  	want := map[string]map[string]bool{
   179  		"main.Foo": map[string]bool{"v": false},
   180  		"main.Bar": map[string]bool{"Foo": true, "name": false},
   181  		"main.Baz": map[string]bool{"Foo": true, "name": false},
   182  	}
   183  
   184  	dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker")
   185  	if err != nil {
   186  		t.Fatalf("could not create directory: %v", err)
   187  	}
   188  	defer os.RemoveAll(dir)
   189  
   190  	f := gobuild(t, dir, prog, NoOpt)
   191  
   192  	defer f.Close()
   193  
   194  	d, err := f.DWARF()
   195  	if err != nil {
   196  		t.Fatalf("error reading DWARF: %v", err)
   197  	}
   198  
   199  	rdr := d.Reader()
   200  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   201  		if err != nil {
   202  			t.Fatalf("error reading DWARF: %v", err)
   203  		}
   204  		switch entry.Tag {
   205  		case dwarf.TagStructType:
   206  			name := entry.Val(dwarf.AttrName).(string)
   207  			wantMembers := want[name]
   208  			if wantMembers == nil {
   209  				continue
   210  			}
   211  			gotMembers, err := findMembers(rdr)
   212  			if err != nil {
   213  				t.Fatalf("error reading DWARF: %v", err)
   214  			}
   215  
   216  			if !reflect.DeepEqual(gotMembers, wantMembers) {
   217  				t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
   218  			}
   219  			delete(want, name)
   220  		}
   221  	}
   222  	if len(want) != 0 {
   223  		t.Errorf("failed to check all expected types: missing types = %+v", want)
   224  	}
   225  }
   226  
   227  func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
   228  	memberEmbedded := map[string]bool{}
   229  	// TODO(hyangah): define in debug/dwarf package
   230  	const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
   231  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  		switch entry.Tag {
   236  		case dwarf.TagMember:
   237  			name := entry.Val(dwarf.AttrName).(string)
   238  			embedded := entry.Val(goEmbeddedStruct).(bool)
   239  			memberEmbedded[name] = embedded
   240  		case 0:
   241  			return memberEmbedded, nil
   242  		}
   243  	}
   244  	return memberEmbedded, nil
   245  }
   246  
   247  func TestSizes(t *testing.T) {
   248  	if runtime.GOOS == "plan9" {
   249  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   250  	}
   251  	t.Parallel()
   252  
   253  	// DWARF sizes should never be -1.
   254  	// See issue #21097
   255  	const prog = `
   256  package main
   257  var x func()
   258  var y [4]func()
   259  func main() {
   260  	x = nil
   261  	y[0] = nil
   262  }
   263  `
   264  	dir, err := ioutil.TempDir("", "TestSizes")
   265  	if err != nil {
   266  		t.Fatalf("could not create directory: %v", err)
   267  	}
   268  	defer os.RemoveAll(dir)
   269  	f := gobuild(t, dir, prog, NoOpt)
   270  	defer f.Close()
   271  	d, err := f.DWARF()
   272  	if err != nil {
   273  		t.Fatalf("error reading DWARF: %v", err)
   274  	}
   275  	rdr := d.Reader()
   276  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   277  		if err != nil {
   278  			t.Fatalf("error reading DWARF: %v", err)
   279  		}
   280  		switch entry.Tag {
   281  		case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
   282  		default:
   283  			continue
   284  		}
   285  		typ, err := d.Type(entry.Offset)
   286  		if err != nil {
   287  			t.Fatalf("can't read type: %v", err)
   288  		}
   289  		if typ.Size() < 0 {
   290  			t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
   291  		}
   292  	}
   293  }
   294  
   295  func TestFieldOverlap(t *testing.T) {
   296  	if runtime.GOOS == "plan9" {
   297  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   298  	}
   299  	t.Parallel()
   300  
   301  	// This test grew out of issue 21094, where specific sudog<T> DWARF types
   302  	// had elem fields set to values instead of pointers.
   303  	const prog = `
   304  package main
   305  
   306  var c chan string
   307  
   308  func main() {
   309  	c <- "foo"
   310  }
   311  `
   312  	dir, err := ioutil.TempDir("", "TestFieldOverlap")
   313  	if err != nil {
   314  		t.Fatalf("could not create directory: %v", err)
   315  	}
   316  	defer os.RemoveAll(dir)
   317  
   318  	f := gobuild(t, dir, prog, NoOpt)
   319  	defer f.Close()
   320  
   321  	d, err := f.DWARF()
   322  	if err != nil {
   323  		t.Fatalf("error reading DWARF: %v", err)
   324  	}
   325  
   326  	rdr := d.Reader()
   327  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   328  		if err != nil {
   329  			t.Fatalf("error reading DWARF: %v", err)
   330  		}
   331  		if entry.Tag != dwarf.TagStructType {
   332  			continue
   333  		}
   334  		typ, err := d.Type(entry.Offset)
   335  		if err != nil {
   336  			t.Fatalf("can't read type: %v", err)
   337  		}
   338  		s := typ.(*dwarf.StructType)
   339  		for i := 0; i < len(s.Field); i++ {
   340  			end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
   341  			var limit int64
   342  			if i == len(s.Field)-1 {
   343  				limit = s.Size()
   344  			} else {
   345  				limit = s.Field[i+1].ByteOffset
   346  			}
   347  			if end > limit {
   348  				name := entry.Val(dwarf.AttrName).(string)
   349  				t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
   350  			}
   351  		}
   352  	}
   353  }
   354  
   355  func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile int, expectLine int, directive string) {
   356  	t.Parallel()
   357  
   358  	prog := fmt.Sprintf("package main\n\nfunc main() {\n%s\nvar i int\ni = i\n}\n", directive)
   359  
   360  	dir, err := ioutil.TempDir("", testpoint)
   361  	if err != nil {
   362  		t.Fatalf("could not create directory: %v", err)
   363  	}
   364  	defer os.RemoveAll(dir)
   365  
   366  	f := gobuild(t, dir, prog, NoOpt)
   367  
   368  	d, err := f.DWARF()
   369  	if err != nil {
   370  		t.Fatalf("error reading DWARF: %v", err)
   371  	}
   372  
   373  	rdr := d.Reader()
   374  	ex := examiner{}
   375  	if err := ex.populate(rdr); err != nil {
   376  		t.Fatalf("error reading DWARF: %v", err)
   377  	}
   378  
   379  	// Locate the main.main DIE
   380  	mains := ex.Named("main.main")
   381  	if len(mains) == 0 {
   382  		t.Fatalf("unable to locate DIE for main.main")
   383  	}
   384  	if len(mains) != 1 {
   385  		t.Fatalf("more than one main.main DIE")
   386  	}
   387  	maindie := mains[0]
   388  
   389  	// Vet the main.main DIE
   390  	if maindie.Tag != dwarf.TagSubprogram {
   391  		t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
   392  	}
   393  
   394  	// Walk main's children and select variable "i".
   395  	mainIdx := ex.idxFromOffset(maindie.Offset)
   396  	childDies := ex.Children(mainIdx)
   397  	var iEntry *dwarf.Entry
   398  	for _, child := range childDies {
   399  		if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
   400  			iEntry = child
   401  			break
   402  		}
   403  	}
   404  	if iEntry == nil {
   405  		t.Fatalf("didn't find DW_TAG_variable for i in main.main")
   406  	}
   407  
   408  	// Verify line/file attributes.
   409  	line := iEntry.Val(dwarf.AttrDeclLine)
   410  	if line == nil || line.(int64) != int64(expectLine) {
   411  		t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine)
   412  	}
   413  
   414  	file := maindie.Val(dwarf.AttrDeclFile)
   415  	if file == nil || file.(int64) != 1 {
   416  		t.Errorf("DW_AT_decl_file for main is %v, want %d", file, expectFile)
   417  	}
   418  }
   419  
   420  func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
   421  	testenv.MustHaveGoBuild(t)
   422  
   423  	if runtime.GOOS == "plan9" {
   424  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   425  	}
   426  
   427  	varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", 1, 5, "")
   428  }
   429  
   430  func TestVarDeclCoordsWithLineDirective(t *testing.T) {
   431  	testenv.MustHaveGoBuild(t)
   432  
   433  	if runtime.GOOS == "plan9" {
   434  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   435  	}
   436  
   437  	varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
   438  		2, 200, "//line /foobar.go:200")
   439  }
   440  
   441  // Helper class for supporting queries on DIEs within a DWARF .debug_info
   442  // section. Invoke the populate() method below passing in a dwarf.Reader,
   443  // which will read in all DIEs and keep track of parent/child
   444  // relationships. Queries can then be made to ask for DIEs by name or
   445  // by offset. This will hopefully reduce boilerplate for future test
   446  // writing.
   447  
   448  type examiner struct {
   449  	dies        []*dwarf.Entry
   450  	idxByOffset map[dwarf.Offset]int
   451  	kids        map[int][]int
   452  	parent      map[int]int
   453  	byname      map[string][]int
   454  }
   455  
   456  // Populate the examiner using the DIEs read from rdr.
   457  func (ex *examiner) populate(rdr *dwarf.Reader) error {
   458  	ex.idxByOffset = make(map[dwarf.Offset]int)
   459  	ex.kids = make(map[int][]int)
   460  	ex.parent = make(map[int]int)
   461  	ex.byname = make(map[string][]int)
   462  	var nesting []int
   463  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   464  		if err != nil {
   465  			return err
   466  		}
   467  		if entry.Tag == 0 {
   468  			// terminator
   469  			if len(nesting) == 0 {
   470  				return errors.New("nesting stack underflow")
   471  			}
   472  			nesting = nesting[:len(nesting)-1]
   473  			continue
   474  		}
   475  		idx := len(ex.dies)
   476  		ex.dies = append(ex.dies, entry)
   477  		if _, found := ex.idxByOffset[entry.Offset]; found {
   478  			return errors.New("DIE clash on offset")
   479  		}
   480  		ex.idxByOffset[entry.Offset] = idx
   481  		if name, ok := entry.Val(dwarf.AttrName).(string); ok {
   482  			ex.byname[name] = append(ex.byname[name], idx)
   483  		}
   484  		if len(nesting) > 0 {
   485  			parent := nesting[len(nesting)-1]
   486  			ex.kids[parent] = append(ex.kids[parent], idx)
   487  			ex.parent[idx] = parent
   488  		}
   489  		if entry.Children {
   490  			nesting = append(nesting, idx)
   491  		}
   492  	}
   493  	if len(nesting) > 0 {
   494  		return errors.New("unterminated child sequence")
   495  	}
   496  	return nil
   497  }
   498  
   499  func indent(ilevel int) {
   500  	for i := 0; i < ilevel; i++ {
   501  		fmt.Printf("  ")
   502  	}
   503  }
   504  
   505  // For debugging new tests
   506  func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error {
   507  	if idx >= len(ex.dies) {
   508  		msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx)
   509  		return errors.New(msg)
   510  	}
   511  	entry := ex.dies[idx]
   512  	indent(ilevel)
   513  	fmt.Printf("0x%x: %v\n", idx, entry.Tag)
   514  	for _, f := range entry.Field {
   515  		indent(ilevel)
   516  		fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
   517  	}
   518  	if dumpKids {
   519  		ksl := ex.kids[idx]
   520  		for _, k := range ksl {
   521  			ex.dumpEntry(k, true, ilevel+2)
   522  		}
   523  	}
   524  	return nil
   525  }
   526  
   527  // Given a DIE offset, return the previously read dwarf.Entry, or nil
   528  func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry {
   529  	if idx, found := ex.idxByOffset[off]; found && idx != -1 {
   530  		return ex.entryFromIdx(idx)
   531  	}
   532  	return nil
   533  }
   534  
   535  // Return the ID that examiner uses to refer to the DIE at offset off
   536  func (ex *examiner) idxFromOffset(off dwarf.Offset) int {
   537  	if idx, found := ex.idxByOffset[off]; found {
   538  		return idx
   539  	}
   540  	return -1
   541  }
   542  
   543  // Return the dwarf.Entry pointer for the DIE with id 'idx'
   544  func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry {
   545  	if idx >= len(ex.dies) || idx < 0 {
   546  		return nil
   547  	}
   548  	return ex.dies[idx]
   549  }
   550  
   551  // Returns a list of child entries for a die with ID 'idx'
   552  func (ex *examiner) Children(idx int) []*dwarf.Entry {
   553  	sl := ex.kids[idx]
   554  	ret := make([]*dwarf.Entry, len(sl))
   555  	for i, k := range sl {
   556  		ret[i] = ex.entryFromIdx(k)
   557  	}
   558  	return ret
   559  }
   560  
   561  // Returns parent DIE for DIE 'idx', or nil if the DIE is top level
   562  func (ex *examiner) Parent(idx int) *dwarf.Entry {
   563  	p, found := ex.parent[idx]
   564  	if !found {
   565  		return nil
   566  	}
   567  	return ex.entryFromIdx(p)
   568  }
   569  
   570  // Return a list of all DIEs with name 'name'. When searching for DIEs
   571  // by name, keep in mind that the returned results will include child
   572  // DIEs such as params/variables. For example, asking for all DIEs named
   573  // "p" for even a small program will give you 400-500 entries.
   574  func (ex *examiner) Named(name string) []*dwarf.Entry {
   575  	sl := ex.byname[name]
   576  	ret := make([]*dwarf.Entry, len(sl))
   577  	for i, k := range sl {
   578  		ret[i] = ex.entryFromIdx(k)
   579  	}
   580  	return ret
   581  }
   582  
   583  func TestInlinedRoutineRecords(t *testing.T) {
   584  	testenv.MustHaveGoBuild(t)
   585  
   586  	if runtime.GOOS == "plan9" {
   587  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   588  	}
   589  	if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
   590  		t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
   591  	}
   592  
   593  	t.Parallel()
   594  
   595  	const prog = `
   596  package main
   597  
   598  var G int
   599  
   600  func noinline(x int) int {
   601  	defer func() { G += x }()
   602  	return x
   603  }
   604  
   605  func cand(x, y int) int {
   606  	return noinline(x+y) ^ (y - x)
   607  }
   608  
   609  func main() {
   610      x := cand(G*G,G|7%G)
   611      G = x
   612  }
   613  `
   614  	dir, err := ioutil.TempDir("", "TestInlinedRoutineRecords")
   615  	if err != nil {
   616  		t.Fatalf("could not create directory: %v", err)
   617  	}
   618  	defer os.RemoveAll(dir)
   619  
   620  	// Note: this is a build with "-l=4", as opposed to "-l -N". The
   621  	// test is intended to verify DWARF that is only generated when
   622  	// the inliner is active. We're only going to look at the DWARF for
   623  	// main.main, however, hence we build with "-gcflags=-l=4" as opposed
   624  	// to "-gcflags=all=-l=4".
   625  	f := gobuild(t, dir, prog, OptInl4)
   626  
   627  	d, err := f.DWARF()
   628  	if err != nil {
   629  		t.Fatalf("error reading DWARF: %v", err)
   630  	}
   631  
   632  	// The inlined subroutines we expect to visit
   633  	expectedInl := []string{"main.cand"}
   634  
   635  	rdr := d.Reader()
   636  	ex := examiner{}
   637  	if err := ex.populate(rdr); err != nil {
   638  		t.Fatalf("error reading DWARF: %v", err)
   639  	}
   640  
   641  	// Locate the main.main DIE
   642  	mains := ex.Named("main.main")
   643  	if len(mains) == 0 {
   644  		t.Fatalf("unable to locate DIE for main.main")
   645  	}
   646  	if len(mains) != 1 {
   647  		t.Fatalf("more than one main.main DIE")
   648  	}
   649  	maindie := mains[0]
   650  
   651  	// Vet the main.main DIE
   652  	if maindie.Tag != dwarf.TagSubprogram {
   653  		t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
   654  	}
   655  
   656  	// Walk main's children and pick out the inlined subroutines
   657  	mainIdx := ex.idxFromOffset(maindie.Offset)
   658  	childDies := ex.Children(mainIdx)
   659  	exCount := 0
   660  	for _, child := range childDies {
   661  		if child.Tag == dwarf.TagInlinedSubroutine {
   662  			// Found an inlined subroutine, locate abstract origin.
   663  			ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   664  			if !originOK {
   665  				t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
   666  			}
   667  			originDIE := ex.entryFromOffset(ooff)
   668  			if originDIE == nil {
   669  				t.Fatalf("can't locate origin DIE at off %v", ooff)
   670  			}
   671  
   672  			// Walk the children of the abstract subroutine. We expect
   673  			// to see child variables there, even if (perhaps due to
   674  			// optimization) there are no references to them from the
   675  			// inlined subroutine DIE.
   676  			absFcnIdx := ex.idxFromOffset(ooff)
   677  			absFcnChildDies := ex.Children(absFcnIdx)
   678  			if len(absFcnChildDies) != 2 {
   679  				t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
   680  			}
   681  			formalCount := 0
   682  			for _, absChild := range absFcnChildDies {
   683  				if absChild.Tag == dwarf.TagFormalParameter {
   684  					formalCount += 1
   685  					continue
   686  				}
   687  				t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
   688  			}
   689  			if formalCount != 2 {
   690  				t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
   691  			}
   692  
   693  			if exCount >= len(expectedInl) {
   694  				t.Fatalf("too many inlined subroutines found in main.main")
   695  			}
   696  
   697  			// Name should check out.
   698  			expected := expectedInl[exCount]
   699  			if name, ok := originDIE.Val(dwarf.AttrName).(string); ok {
   700  				if name != expected {
   701  					t.Fatalf("expected inlined routine %s got %s", name, expected)
   702  				}
   703  			}
   704  			exCount++
   705  
   706  			omap := make(map[dwarf.Offset]bool)
   707  
   708  			// Walk the child variables of the inlined routine. Each
   709  			// of them should have a distinct abstract origin-- if two
   710  			// vars point to the same origin things are definitely broken.
   711  			inlIdx := ex.idxFromOffset(child.Offset)
   712  			inlChildDies := ex.Children(inlIdx)
   713  			for _, k := range inlChildDies {
   714  				ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   715  				if !originOK {
   716  					t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
   717  				}
   718  				if _, found := omap[ooff]; found {
   719  					t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
   720  				}
   721  				omap[ooff] = true
   722  			}
   723  		}
   724  	}
   725  	if exCount != len(expectedInl) {
   726  		t.Fatalf("not enough inlined subroutines found in main.main")
   727  	}
   728  }
   729  
   730  func abstractOriginSanity(t *testing.T, gopathdir string, flags string) {
   731  	t.Parallel()
   732  
   733  	dir, err := ioutil.TempDir("", "TestAbstractOriginSanity")
   734  	if err != nil {
   735  		t.Fatalf("could not create directory: %v", err)
   736  	}
   737  	defer os.RemoveAll(dir)
   738  
   739  	// Build with inlining, to exercise DWARF inlining support.
   740  	f := gobuildTestdata(t, dir, gopathdir, "main", flags)
   741  
   742  	d, err := f.DWARF()
   743  	if err != nil {
   744  		t.Fatalf("error reading DWARF: %v", err)
   745  	}
   746  	rdr := d.Reader()
   747  	ex := examiner{}
   748  	if err := ex.populate(rdr); err != nil {
   749  		t.Fatalf("error reading DWARF: %v", err)
   750  	}
   751  
   752  	// Make a pass through all DIEs looking for abstract origin
   753  	// references.
   754  	abscount := 0
   755  	for i, die := range ex.dies {
   756  		// Does it have an abstract origin?
   757  		ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   758  		if !originOK {
   759  			continue
   760  		}
   761  
   762  		// All abstract origin references should be resolvable.
   763  		abscount += 1
   764  		originDIE := ex.entryFromOffset(ooff)
   765  		if originDIE == nil {
   766  			ex.dumpEntry(i, false, 0)
   767  			t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
   768  		}
   769  
   770  		// Suppose that DIE X has parameter/variable children {K1,
   771  		// K2, ... KN}. If X has an abstract origin of A, then for
   772  		// each KJ, the abstract origin of KJ should be a child of A.
   773  		// Note that this same rule doesn't hold for non-variable DIEs.
   774  		pidx := ex.idxFromOffset(die.Offset)
   775  		if pidx < 0 {
   776  			t.Fatalf("can't locate DIE id")
   777  		}
   778  		kids := ex.Children(pidx)
   779  		for _, kid := range kids {
   780  			if kid.Tag != dwarf.TagVariable &&
   781  				kid.Tag != dwarf.TagFormalParameter {
   782  				continue
   783  			}
   784  			kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   785  			if !originOK {
   786  				continue
   787  			}
   788  			childOriginDIE := ex.entryFromOffset(kooff)
   789  			if childOriginDIE == nil {
   790  				ex.dumpEntry(i, false, 0)
   791  				t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
   792  			}
   793  			coidx := ex.idxFromOffset(childOriginDIE.Offset)
   794  			childOriginParent := ex.Parent(coidx)
   795  			if childOriginParent != originDIE {
   796  				ex.dumpEntry(i, false, 0)
   797  				t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
   798  			}
   799  		}
   800  	}
   801  	if abscount == 0 {
   802  		t.Fatalf("no abstract origin refs found, something is wrong")
   803  	}
   804  }
   805  
   806  func TestAbstractOriginSanity(t *testing.T) {
   807  	testenv.MustHaveGoBuild(t)
   808  
   809  	if testing.Short() {
   810  		t.Skip("skipping test in short mode.")
   811  	}
   812  
   813  	if runtime.GOOS == "plan9" {
   814  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   815  	}
   816  	if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
   817  		t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
   818  	}
   819  
   820  	if wd, err := os.Getwd(); err == nil {
   821  		gopathdir := filepath.Join(wd, "testdata", "httptest")
   822  		abstractOriginSanity(t, gopathdir, OptAllInl4)
   823  	} else {
   824  		t.Fatalf("os.Getwd() failed %v", err)
   825  	}
   826  }
   827  
   828  func TestAbstractOriginSanityIssue25459(t *testing.T) {
   829  	testenv.MustHaveGoBuild(t)
   830  
   831  	if runtime.GOOS == "plan9" {
   832  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   833  	}
   834  	if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
   835  		t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
   836  	}
   837  	if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" {
   838  		t.Skip("skipping on not-amd64 not-x86; location lists not supported")
   839  	}
   840  
   841  	if wd, err := os.Getwd(); err == nil {
   842  		gopathdir := filepath.Join(wd, "testdata", "issue25459")
   843  		abstractOriginSanity(t, gopathdir, DefaultOpt)
   844  	} else {
   845  		t.Fatalf("os.Getwd() failed %v", err)
   846  	}
   847  }
   848  
   849  func TestAbstractOriginSanityIssue26237(t *testing.T) {
   850  	testenv.MustHaveGoBuild(t)
   851  
   852  	if runtime.GOOS == "plan9" {
   853  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   854  	}
   855  	if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
   856  		t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
   857  	}
   858  	if wd, err := os.Getwd(); err == nil {
   859  		gopathdir := filepath.Join(wd, "testdata", "issue26237")
   860  		abstractOriginSanity(t, gopathdir, DefaultOpt)
   861  	} else {
   862  		t.Fatalf("os.Getwd() failed %v", err)
   863  	}
   864  }
   865  
   866  func TestRuntimeTypeAttrInternal(t *testing.T) {
   867  	testenv.MustHaveGoBuild(t)
   868  
   869  	if runtime.GOOS == "plan9" {
   870  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   871  	}
   872  
   873  	if runtime.GOOS == "windows" && runtime.GOARCH == "arm" {
   874  		t.Skip("skipping on windows/arm; test is incompatible with relocatable binaries")
   875  	}
   876  
   877  	testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
   878  }
   879  
   880  // External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732)
   881  func TestRuntimeTypeAttrExternal(t *testing.T) {
   882  	testenv.MustHaveGoBuild(t)
   883  	testenv.MustHaveCGO(t)
   884  
   885  	if runtime.GOOS == "plan9" {
   886  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   887  	}
   888  
   889  	// Explicitly test external linking, for dsymutil compatibility on Darwin.
   890  	if runtime.GOARCH == "ppc64" {
   891  		t.Skip("-linkmode=external not supported on ppc64")
   892  	}
   893  	testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
   894  }
   895  
   896  func testRuntimeTypeAttr(t *testing.T, flags string) {
   897  	t.Parallel()
   898  
   899  	const prog = `
   900  package main
   901  
   902  import "unsafe"
   903  
   904  type X struct{ _ int }
   905  
   906  func main() {
   907  	var x interface{} = &X{}
   908  	p := *(*uintptr)(unsafe.Pointer(&x))
   909  	print(p)
   910  }
   911  `
   912  	dir, err := ioutil.TempDir("", "TestRuntimeType")
   913  	if err != nil {
   914  		t.Fatalf("could not create directory: %v", err)
   915  	}
   916  	defer os.RemoveAll(dir)
   917  
   918  	f := gobuild(t, dir, prog, flags)
   919  	out, err := exec.Command(f.path).CombinedOutput()
   920  	if err != nil {
   921  		t.Fatalf("could not run test program: %v", err)
   922  	}
   923  	addr, err := strconv.ParseUint(string(out), 10, 64)
   924  	if err != nil {
   925  		t.Fatalf("could not parse type address from program output %q: %v", out, err)
   926  	}
   927  
   928  	symbols, err := f.Symbols()
   929  	if err != nil {
   930  		t.Fatalf("error reading symbols: %v", err)
   931  	}
   932  	var types *objfilepkg.Sym
   933  	for _, sym := range symbols {
   934  		if sym.Name == "runtime.types" {
   935  			types = &sym
   936  			break
   937  		}
   938  	}
   939  	if types == nil {
   940  		t.Fatal("couldn't find runtime.types in symbols")
   941  	}
   942  
   943  	d, err := f.DWARF()
   944  	if err != nil {
   945  		t.Fatalf("error reading DWARF: %v", err)
   946  	}
   947  
   948  	rdr := d.Reader()
   949  	ex := examiner{}
   950  	if err := ex.populate(rdr); err != nil {
   951  		t.Fatalf("error reading DWARF: %v", err)
   952  	}
   953  	dies := ex.Named("*main.X")
   954  	if len(dies) != 1 {
   955  		t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
   956  	}
   957  	rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
   958  	if rtAttr == nil {
   959  		t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
   960  	}
   961  
   962  	if rtAttr.(uint64)+types.Addr != addr {
   963  		t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
   964  	}
   965  }
   966  
   967  func TestIssue27614(t *testing.T) {
   968  	// Type references in debug_info should always use the DW_TAG_typedef_type
   969  	// for the type, when that's generated.
   970  
   971  	testenv.MustHaveGoBuild(t)
   972  
   973  	if runtime.GOOS == "plan9" {
   974  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   975  	}
   976  
   977  	t.Parallel()
   978  
   979  	dir, err := ioutil.TempDir("", "go-build")
   980  	if err != nil {
   981  		t.Fatal(err)
   982  	}
   983  	defer os.RemoveAll(dir)
   984  
   985  	const prog = `package main
   986  
   987  import "fmt"
   988  
   989  type astruct struct {
   990  	X int
   991  }
   992  
   993  type bstruct struct {
   994  	X float32
   995  }
   996  
   997  var globalptr *astruct
   998  var globalvar astruct
   999  var bvar0, bvar1, bvar2 bstruct
  1000  
  1001  func main() {
  1002  	fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
  1003  }
  1004  `
  1005  
  1006  	f := gobuild(t, dir, prog, NoOpt)
  1007  
  1008  	defer f.Close()
  1009  
  1010  	data, err := f.DWARF()
  1011  	if err != nil {
  1012  		t.Fatal(err)
  1013  	}
  1014  
  1015  	rdr := data.Reader()
  1016  
  1017  	var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
  1018  	var globalptrDIE, globalvarDIE *dwarf.Entry
  1019  	var bvarDIE [3]*dwarf.Entry
  1020  
  1021  	for {
  1022  		e, err := rdr.Next()
  1023  		if err != nil {
  1024  			t.Fatal(err)
  1025  		}
  1026  		if e == nil {
  1027  			break
  1028  		}
  1029  
  1030  		name, _ := e.Val(dwarf.AttrName).(string)
  1031  
  1032  		switch e.Tag {
  1033  		case dwarf.TagTypedef:
  1034  			switch name {
  1035  			case "main.astruct":
  1036  				astructTypeDIE = e
  1037  			case "main.bstruct":
  1038  				bstructTypeDIE = e
  1039  			}
  1040  		case dwarf.TagPointerType:
  1041  			if name == "*main.astruct" {
  1042  				ptrastructTypeDIE = e
  1043  			}
  1044  		case dwarf.TagVariable:
  1045  			switch name {
  1046  			case "main.globalptr":
  1047  				globalptrDIE = e
  1048  			case "main.globalvar":
  1049  				globalvarDIE = e
  1050  			default:
  1051  				const bvarprefix = "main.bvar"
  1052  				if strings.HasPrefix(name, bvarprefix) {
  1053  					i, _ := strconv.Atoi(name[len(bvarprefix):])
  1054  					bvarDIE[i] = e
  1055  				}
  1056  			}
  1057  		}
  1058  	}
  1059  
  1060  	typedieof := func(e *dwarf.Entry) dwarf.Offset {
  1061  		return e.Val(dwarf.AttrType).(dwarf.Offset)
  1062  	}
  1063  
  1064  	if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
  1065  		t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
  1066  	}
  1067  
  1068  	if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
  1069  		t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
  1070  	}
  1071  
  1072  	if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
  1073  		t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
  1074  	}
  1075  
  1076  	for i := range bvarDIE {
  1077  		if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
  1078  			t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
  1079  		}
  1080  	}
  1081  }
  1082  
  1083  func TestStaticTmp(t *testing.T) {
  1084  	// Checks that statictmp variables do not appear in debug_info or the
  1085  	// symbol table.
  1086  	// Also checks that statictmp variables do not collide with user defined
  1087  	// variables (issue #25113)
  1088  
  1089  	testenv.MustHaveGoBuild(t)
  1090  
  1091  	if runtime.GOOS == "plan9" {
  1092  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1093  	}
  1094  
  1095  	t.Parallel()
  1096  
  1097  	dir, err := ioutil.TempDir("", "go-build")
  1098  	if err != nil {
  1099  		t.Fatal(err)
  1100  	}
  1101  	defer os.RemoveAll(dir)
  1102  
  1103  	const prog = `package main
  1104  
  1105  var stmp_0 string
  1106  var a []int
  1107  
  1108  func init() {
  1109  	a = []int{ 7 }
  1110  }
  1111  
  1112  func main() {
  1113  	println(a[0])
  1114  }
  1115  `
  1116  
  1117  	f := gobuild(t, dir, prog, NoOpt)
  1118  
  1119  	defer f.Close()
  1120  
  1121  	d, err := f.DWARF()
  1122  	if err != nil {
  1123  		t.Fatalf("error reading DWARF: %v", err)
  1124  	}
  1125  
  1126  	rdr := d.Reader()
  1127  	for {
  1128  		e, err := rdr.Next()
  1129  		if err != nil {
  1130  			t.Fatal(err)
  1131  		}
  1132  		if e == nil {
  1133  			break
  1134  		}
  1135  		if e.Tag != dwarf.TagVariable {
  1136  			continue
  1137  		}
  1138  		name, ok := e.Val(dwarf.AttrName).(string)
  1139  		if !ok {
  1140  			continue
  1141  		}
  1142  		if strings.Contains(name, "stmp") {
  1143  			t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
  1144  		}
  1145  	}
  1146  
  1147  	syms, err := f.Symbols()
  1148  	if err != nil {
  1149  		t.Fatalf("error reading symbols: %v", err)
  1150  	}
  1151  	for _, sym := range syms {
  1152  		if strings.Contains(sym.Name, "stmp") {
  1153  			t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
  1154  		}
  1155  	}
  1156  }