Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/link/internal/ld/dwarf_test.go

Documentation: cmd/link/internal/ld

     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  	"debug/pe"
    12  	"errors"
    13  	"fmt"
    14  	"internal/testenv"
    15  	"io"
    16  	"io/ioutil"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"reflect"
    21  	"runtime"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  	"testing"
    26  )
    27  
    28  const (
    29  	DefaultOpt = "-gcflags="
    30  	NoOpt      = "-gcflags=-l -N"
    31  	OptInl4    = "-gcflags=-l=4"
    32  	OptAllInl4 = "-gcflags=all=-l=4"
    33  )
    34  
    35  func TestRuntimeTypesPresent(t *testing.T) {
    36  	t.Parallel()
    37  	testenv.MustHaveGoBuild(t)
    38  
    39  	if runtime.GOOS == "plan9" {
    40  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
    41  	}
    42  
    43  	dir := t.TempDir()
    44  
    45  	f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
    46  	defer f.Close()
    47  
    48  	dwarf, err := f.DWARF()
    49  	if err != nil {
    50  		t.Fatalf("error reading DWARF: %v", err)
    51  	}
    52  
    53  	want := map[string]bool{
    54  		"runtime._type":         true,
    55  		"runtime.arraytype":     true,
    56  		"runtime.chantype":      true,
    57  		"runtime.functype":      true,
    58  		"runtime.maptype":       true,
    59  		"runtime.ptrtype":       true,
    60  		"runtime.slicetype":     true,
    61  		"runtime.structtype":    true,
    62  		"runtime.interfacetype": true,
    63  		"runtime.itab":          true,
    64  		"runtime.imethod":       true,
    65  	}
    66  
    67  	found := findTypes(t, dwarf, want)
    68  	if len(found) != len(want) {
    69  		t.Errorf("found %v, want %v", found, want)
    70  	}
    71  }
    72  
    73  func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
    74  	found = make(map[string]bool)
    75  	rdr := dw.Reader()
    76  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
    77  		if err != nil {
    78  			t.Fatalf("error reading DWARF: %v", err)
    79  		}
    80  		switch entry.Tag {
    81  		case dwarf.TagTypedef:
    82  			if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
    83  				found[name] = true
    84  			}
    85  		}
    86  	}
    87  	return
    88  }
    89  
    90  type builtFile struct {
    91  	*objfilepkg.File
    92  	path string
    93  }
    94  
    95  func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
    96  	src := filepath.Join(dir, "test.go")
    97  	dst := filepath.Join(dir, "out.exe")
    98  
    99  	if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil {
   100  		t.Fatal(err)
   101  	}
   102  
   103  	cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
   104  	if b, err := cmd.CombinedOutput(); err != nil {
   105  		t.Logf("build: %s\n", b)
   106  		t.Fatalf("build error: %v", err)
   107  	}
   108  
   109  	f, err := objfilepkg.Open(dst)
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  	return &builtFile{f, dst}
   114  }
   115  
   116  // Similar to gobuild() above, but uses a main package instead of a test.go file.
   117  
   118  func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile {
   119  	dst := filepath.Join(tdir, "out.exe")
   120  
   121  	// Run a build with an updated GOPATH
   122  	cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst)
   123  	cmd.Dir = pkgDir
   124  	if b, err := cmd.CombinedOutput(); err != nil {
   125  		t.Logf("build: %s\n", b)
   126  		t.Fatalf("build error: %v", err)
   127  	}
   128  
   129  	f, err := objfilepkg.Open(dst)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	return &builtFile{f, dst}
   134  }
   135  
   136  func TestEmbeddedStructMarker(t *testing.T) {
   137  	t.Parallel()
   138  	testenv.MustHaveGoBuild(t)
   139  
   140  	if runtime.GOOS == "plan9" {
   141  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   142  	}
   143  
   144  	const prog = `
   145  package main
   146  
   147  import "fmt"
   148  
   149  type Foo struct { v int }
   150  type Bar struct {
   151  	Foo
   152  	name string
   153  }
   154  type Baz struct {
   155  	*Foo
   156  	name string
   157  }
   158  
   159  func main() {
   160  	bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
   161  	baz := Baz{ Foo: &bar.Foo, name: "123" }
   162  	fmt.Println(bar, baz)
   163  }`
   164  
   165  	want := map[string]map[string]bool{
   166  		"main.Foo": {"v": false},
   167  		"main.Bar": {"Foo": true, "name": false},
   168  		"main.Baz": {"Foo": true, "name": false},
   169  	}
   170  
   171  	dir := t.TempDir()
   172  
   173  	f := gobuild(t, dir, prog, NoOpt)
   174  
   175  	defer f.Close()
   176  
   177  	d, err := f.DWARF()
   178  	if err != nil {
   179  		t.Fatalf("error reading DWARF: %v", err)
   180  	}
   181  
   182  	rdr := d.Reader()
   183  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   184  		if err != nil {
   185  			t.Fatalf("error reading DWARF: %v", err)
   186  		}
   187  		switch entry.Tag {
   188  		case dwarf.TagStructType:
   189  			name := entry.Val(dwarf.AttrName).(string)
   190  			wantMembers := want[name]
   191  			if wantMembers == nil {
   192  				continue
   193  			}
   194  			gotMembers, err := findMembers(rdr)
   195  			if err != nil {
   196  				t.Fatalf("error reading DWARF: %v", err)
   197  			}
   198  
   199  			if !reflect.DeepEqual(gotMembers, wantMembers) {
   200  				t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
   201  			}
   202  			delete(want, name)
   203  		}
   204  	}
   205  	if len(want) != 0 {
   206  		t.Errorf("failed to check all expected types: missing types = %+v", want)
   207  	}
   208  }
   209  
   210  func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
   211  	memberEmbedded := map[string]bool{}
   212  	// TODO(hyangah): define in debug/dwarf package
   213  	const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
   214  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		switch entry.Tag {
   219  		case dwarf.TagMember:
   220  			name := entry.Val(dwarf.AttrName).(string)
   221  			embedded := entry.Val(goEmbeddedStruct).(bool)
   222  			memberEmbedded[name] = embedded
   223  		case 0:
   224  			return memberEmbedded, nil
   225  		}
   226  	}
   227  	return memberEmbedded, nil
   228  }
   229  
   230  func TestSizes(t *testing.T) {
   231  	if runtime.GOOS == "plan9" {
   232  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   233  	}
   234  
   235  	// External linking may bring in C symbols with unknown size. Skip.
   236  	testenv.MustInternalLink(t)
   237  
   238  	t.Parallel()
   239  
   240  	// DWARF sizes should never be -1.
   241  	// See issue #21097
   242  	const prog = `
   243  package main
   244  var x func()
   245  var y [4]func()
   246  func main() {
   247  	x = nil
   248  	y[0] = nil
   249  }
   250  `
   251  	dir := t.TempDir()
   252  
   253  	f := gobuild(t, dir, prog, NoOpt)
   254  	defer f.Close()
   255  	d, err := f.DWARF()
   256  	if err != nil {
   257  		t.Fatalf("error reading DWARF: %v", err)
   258  	}
   259  	rdr := d.Reader()
   260  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   261  		if err != nil {
   262  			t.Fatalf("error reading DWARF: %v", err)
   263  		}
   264  		switch entry.Tag {
   265  		case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
   266  		default:
   267  			continue
   268  		}
   269  		typ, err := d.Type(entry.Offset)
   270  		if err != nil {
   271  			t.Fatalf("can't read type: %v", err)
   272  		}
   273  		if typ.Size() < 0 {
   274  			t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
   275  		}
   276  	}
   277  }
   278  
   279  func TestFieldOverlap(t *testing.T) {
   280  	if runtime.GOOS == "plan9" {
   281  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   282  	}
   283  	t.Parallel()
   284  
   285  	// This test grew out of issue 21094, where specific sudog<T> DWARF types
   286  	// had elem fields set to values instead of pointers.
   287  	const prog = `
   288  package main
   289  
   290  var c chan string
   291  
   292  func main() {
   293  	c <- "foo"
   294  }
   295  `
   296  	dir := t.TempDir()
   297  
   298  	f := gobuild(t, dir, prog, NoOpt)
   299  	defer f.Close()
   300  
   301  	d, err := f.DWARF()
   302  	if err != nil {
   303  		t.Fatalf("error reading DWARF: %v", err)
   304  	}
   305  
   306  	rdr := d.Reader()
   307  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   308  		if err != nil {
   309  			t.Fatalf("error reading DWARF: %v", err)
   310  		}
   311  		if entry.Tag != dwarf.TagStructType {
   312  			continue
   313  		}
   314  		typ, err := d.Type(entry.Offset)
   315  		if err != nil {
   316  			t.Fatalf("can't read type: %v", err)
   317  		}
   318  		s := typ.(*dwarf.StructType)
   319  		for i := 0; i < len(s.Field); i++ {
   320  			end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
   321  			var limit int64
   322  			if i == len(s.Field)-1 {
   323  				limit = s.Size()
   324  			} else {
   325  				limit = s.Field[i+1].ByteOffset
   326  			}
   327  			if end > limit {
   328  				name := entry.Val(dwarf.AttrName).(string)
   329  				t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
   330  			}
   331  		}
   332  	}
   333  }
   334  
   335  func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) {
   336  	t.Parallel()
   337  
   338  	prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
   339  
   340  	dir := t.TempDir()
   341  
   342  	f := gobuild(t, dir, prog, NoOpt)
   343  	defer f.Close()
   344  
   345  	d, err := f.DWARF()
   346  	if err != nil {
   347  		t.Fatalf("error reading DWARF: %v", err)
   348  	}
   349  
   350  	rdr := d.Reader()
   351  	ex := examiner{}
   352  	if err := ex.populate(rdr); err != nil {
   353  		t.Fatalf("error reading DWARF: %v", err)
   354  	}
   355  
   356  	// Locate the main.main DIE
   357  	mains := ex.Named("main.main")
   358  	if len(mains) == 0 {
   359  		t.Fatalf("unable to locate DIE for main.main")
   360  	}
   361  	if len(mains) != 1 {
   362  		t.Fatalf("more than one main.main DIE")
   363  	}
   364  	maindie := mains[0]
   365  
   366  	// Vet the main.main DIE
   367  	if maindie.Tag != dwarf.TagSubprogram {
   368  		t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
   369  	}
   370  
   371  	// Walk main's children and select variable "i".
   372  	mainIdx := ex.idxFromOffset(maindie.Offset)
   373  	childDies := ex.Children(mainIdx)
   374  	var iEntry *dwarf.Entry
   375  	for _, child := range childDies {
   376  		if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
   377  			iEntry = child
   378  			break
   379  		}
   380  	}
   381  	if iEntry == nil {
   382  		t.Fatalf("didn't find DW_TAG_variable for i in main.main")
   383  	}
   384  
   385  	// Verify line/file attributes.
   386  	line := iEntry.Val(dwarf.AttrDeclLine)
   387  	if line == nil || line.(int64) != int64(expectLine) {
   388  		t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine)
   389  	}
   390  
   391  	fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
   392  	if !fileIdxOK {
   393  		t.Errorf("missing or invalid DW_AT_decl_file for main")
   394  	}
   395  	file := ex.FileRef(t, d, mainIdx, fileIdx)
   396  	base := filepath.Base(file)
   397  	if base != expectFile {
   398  		t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
   399  	}
   400  }
   401  
   402  func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
   403  	testenv.MustHaveGoBuild(t)
   404  
   405  	if runtime.GOOS == "plan9" {
   406  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   407  	}
   408  
   409  	varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "")
   410  }
   411  
   412  func TestVarDeclCoordsWithLineDirective(t *testing.T) {
   413  	testenv.MustHaveGoBuild(t)
   414  
   415  	if runtime.GOOS == "plan9" {
   416  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   417  	}
   418  
   419  	varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
   420  		"foobar.go", 202, "//line /foobar.go:200")
   421  }
   422  
   423  // Helper class for supporting queries on DIEs within a DWARF .debug_info
   424  // section. Invoke the populate() method below passing in a dwarf.Reader,
   425  // which will read in all DIEs and keep track of parent/child
   426  // relationships. Queries can then be made to ask for DIEs by name or
   427  // by offset. This will hopefully reduce boilerplate for future test
   428  // writing.
   429  
   430  type examiner struct {
   431  	dies        []*dwarf.Entry
   432  	idxByOffset map[dwarf.Offset]int
   433  	kids        map[int][]int
   434  	parent      map[int]int
   435  	byname      map[string][]int
   436  }
   437  
   438  // Populate the examiner using the DIEs read from rdr.
   439  func (ex *examiner) populate(rdr *dwarf.Reader) error {
   440  	ex.idxByOffset = make(map[dwarf.Offset]int)
   441  	ex.kids = make(map[int][]int)
   442  	ex.parent = make(map[int]int)
   443  	ex.byname = make(map[string][]int)
   444  	var nesting []int
   445  	for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
   446  		if err != nil {
   447  			return err
   448  		}
   449  		if entry.Tag == 0 {
   450  			// terminator
   451  			if len(nesting) == 0 {
   452  				return errors.New("nesting stack underflow")
   453  			}
   454  			nesting = nesting[:len(nesting)-1]
   455  			continue
   456  		}
   457  		idx := len(ex.dies)
   458  		ex.dies = append(ex.dies, entry)
   459  		if _, found := ex.idxByOffset[entry.Offset]; found {
   460  			return errors.New("DIE clash on offset")
   461  		}
   462  		ex.idxByOffset[entry.Offset] = idx
   463  		if name, ok := entry.Val(dwarf.AttrName).(string); ok {
   464  			ex.byname[name] = append(ex.byname[name], idx)
   465  		}
   466  		if len(nesting) > 0 {
   467  			parent := nesting[len(nesting)-1]
   468  			ex.kids[parent] = append(ex.kids[parent], idx)
   469  			ex.parent[idx] = parent
   470  		}
   471  		if entry.Children {
   472  			nesting = append(nesting, idx)
   473  		}
   474  	}
   475  	if len(nesting) > 0 {
   476  		return errors.New("unterminated child sequence")
   477  	}
   478  	return nil
   479  }
   480  
   481  func indent(ilevel int) {
   482  	for i := 0; i < ilevel; i++ {
   483  		fmt.Printf("  ")
   484  	}
   485  }
   486  
   487  // For debugging new tests
   488  func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error {
   489  	if idx >= len(ex.dies) {
   490  		msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx)
   491  		return errors.New(msg)
   492  	}
   493  	entry := ex.dies[idx]
   494  	indent(ilevel)
   495  	fmt.Printf("0x%x: %v\n", idx, entry.Tag)
   496  	for _, f := range entry.Field {
   497  		indent(ilevel)
   498  		fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
   499  	}
   500  	if dumpKids {
   501  		ksl := ex.kids[idx]
   502  		for _, k := range ksl {
   503  			ex.dumpEntry(k, true, ilevel+2)
   504  		}
   505  	}
   506  	return nil
   507  }
   508  
   509  // Given a DIE offset, return the previously read dwarf.Entry, or nil
   510  func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry {
   511  	if idx, found := ex.idxByOffset[off]; found && idx != -1 {
   512  		return ex.entryFromIdx(idx)
   513  	}
   514  	return nil
   515  }
   516  
   517  // Return the ID that examiner uses to refer to the DIE at offset off
   518  func (ex *examiner) idxFromOffset(off dwarf.Offset) int {
   519  	if idx, found := ex.idxByOffset[off]; found {
   520  		return idx
   521  	}
   522  	return -1
   523  }
   524  
   525  // Return the dwarf.Entry pointer for the DIE with id 'idx'
   526  func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry {
   527  	if idx >= len(ex.dies) || idx < 0 {
   528  		return nil
   529  	}
   530  	return ex.dies[idx]
   531  }
   532  
   533  // Returns a list of child entries for a die with ID 'idx'
   534  func (ex *examiner) Children(idx int) []*dwarf.Entry {
   535  	sl := ex.kids[idx]
   536  	ret := make([]*dwarf.Entry, len(sl))
   537  	for i, k := range sl {
   538  		ret[i] = ex.entryFromIdx(k)
   539  	}
   540  	return ret
   541  }
   542  
   543  // Returns parent DIE for DIE 'idx', or nil if the DIE is top level
   544  func (ex *examiner) Parent(idx int) *dwarf.Entry {
   545  	p, found := ex.parent[idx]
   546  	if !found {
   547  		return nil
   548  	}
   549  	return ex.entryFromIdx(p)
   550  }
   551  
   552  // ParentCU returns the enclosing compilation unit DIE for the DIE
   553  // with a given index, or nil if for some reason we can't establish a
   554  // parent.
   555  func (ex *examiner) ParentCU(idx int) *dwarf.Entry {
   556  	for {
   557  		parentDie := ex.Parent(idx)
   558  		if parentDie == nil {
   559  			return nil
   560  		}
   561  		if parentDie.Tag == dwarf.TagCompileUnit {
   562  			return parentDie
   563  		}
   564  		idx = ex.idxFromOffset(parentDie.Offset)
   565  	}
   566  }
   567  
   568  // FileRef takes a given DIE by index and a numeric file reference
   569  // (presumably from a decl_file or call_file attribute), looks up the
   570  // reference in the .debug_line file table, and returns the proper
   571  // string for it. We need to know which DIE is making the reference
   572  // so as find the right compilation unit.
   573  func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string {
   574  
   575  	// Find the parent compilation unit DIE for the specified DIE.
   576  	cuDie := ex.ParentCU(dieIdx)
   577  	if cuDie == nil {
   578  		t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx)
   579  		return ""
   580  	}
   581  	// Construct a line reader and then use it to get the file string.
   582  	lr, lrerr := dw.LineReader(cuDie)
   583  	if lrerr != nil {
   584  		t.Fatal("d.LineReader: ", lrerr)
   585  		return ""
   586  	}
   587  	files := lr.Files()
   588  	if fileRef < 0 || int(fileRef) > len(files)-1 {
   589  		t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef)
   590  		return ""
   591  	}
   592  	return files[fileRef].Name
   593  }
   594  
   595  // Return a list of all DIEs with name 'name'. When searching for DIEs
   596  // by name, keep in mind that the returned results will include child
   597  // DIEs such as params/variables. For example, asking for all DIEs named
   598  // "p" for even a small program will give you 400-500 entries.
   599  func (ex *examiner) Named(name string) []*dwarf.Entry {
   600  	sl := ex.byname[name]
   601  	ret := make([]*dwarf.Entry, len(sl))
   602  	for i, k := range sl {
   603  		ret[i] = ex.entryFromIdx(k)
   604  	}
   605  	return ret
   606  }
   607  
   608  func TestInlinedRoutineRecords(t *testing.T) {
   609  	testenv.MustHaveGoBuild(t)
   610  
   611  	if runtime.GOOS == "plan9" {
   612  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   613  	}
   614  	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
   615  		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
   616  	}
   617  
   618  	t.Parallel()
   619  
   620  	const prog = `
   621  package main
   622  
   623  var G int
   624  
   625  func noinline(x int) int {
   626  	defer func() { G += x }()
   627  	return x
   628  }
   629  
   630  func cand(x, y int) int {
   631  	return noinline(x+y) ^ (y - x)
   632  }
   633  
   634  func main() {
   635      x := cand(G*G,G|7%G)
   636      G = x
   637  }
   638  `
   639  	dir := t.TempDir()
   640  
   641  	// Note: this is a build with "-l=4", as opposed to "-l -N". The
   642  	// test is intended to verify DWARF that is only generated when
   643  	// the inliner is active. We're only going to look at the DWARF for
   644  	// main.main, however, hence we build with "-gcflags=-l=4" as opposed
   645  	// to "-gcflags=all=-l=4".
   646  	f := gobuild(t, dir, prog, OptInl4)
   647  	defer f.Close()
   648  
   649  	d, err := f.DWARF()
   650  	if err != nil {
   651  		t.Fatalf("error reading DWARF: %v", err)
   652  	}
   653  
   654  	// The inlined subroutines we expect to visit
   655  	expectedInl := []string{"main.cand"}
   656  
   657  	rdr := d.Reader()
   658  	ex := examiner{}
   659  	if err := ex.populate(rdr); err != nil {
   660  		t.Fatalf("error reading DWARF: %v", err)
   661  	}
   662  
   663  	// Locate the main.main DIE
   664  	mains := ex.Named("main.main")
   665  	if len(mains) == 0 {
   666  		t.Fatalf("unable to locate DIE for main.main")
   667  	}
   668  	if len(mains) != 1 {
   669  		t.Fatalf("more than one main.main DIE")
   670  	}
   671  	maindie := mains[0]
   672  
   673  	// Vet the main.main DIE
   674  	if maindie.Tag != dwarf.TagSubprogram {
   675  		t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag)
   676  	}
   677  
   678  	// Walk main's children and pick out the inlined subroutines
   679  	mainIdx := ex.idxFromOffset(maindie.Offset)
   680  	childDies := ex.Children(mainIdx)
   681  	exCount := 0
   682  	for _, child := range childDies {
   683  		if child.Tag == dwarf.TagInlinedSubroutine {
   684  			// Found an inlined subroutine, locate abstract origin.
   685  			ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   686  			if !originOK {
   687  				t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
   688  			}
   689  			originDIE := ex.entryFromOffset(ooff)
   690  			if originDIE == nil {
   691  				t.Fatalf("can't locate origin DIE at off %v", ooff)
   692  			}
   693  
   694  			// Walk the children of the abstract subroutine. We expect
   695  			// to see child variables there, even if (perhaps due to
   696  			// optimization) there are no references to them from the
   697  			// inlined subroutine DIE.
   698  			absFcnIdx := ex.idxFromOffset(ooff)
   699  			absFcnChildDies := ex.Children(absFcnIdx)
   700  			if len(absFcnChildDies) != 2 {
   701  				t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
   702  			}
   703  			formalCount := 0
   704  			for _, absChild := range absFcnChildDies {
   705  				if absChild.Tag == dwarf.TagFormalParameter {
   706  					formalCount += 1
   707  					continue
   708  				}
   709  				t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
   710  			}
   711  			if formalCount != 2 {
   712  				t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
   713  			}
   714  
   715  			if exCount >= len(expectedInl) {
   716  				t.Fatalf("too many inlined subroutines found in main.main")
   717  			}
   718  
   719  			// Name should check out.
   720  			expected := expectedInl[exCount]
   721  			if name, ok := originDIE.Val(dwarf.AttrName).(string); ok {
   722  				if name != expected {
   723  					t.Fatalf("expected inlined routine %s got %s", name, expected)
   724  				}
   725  			}
   726  			exCount++
   727  
   728  			// Verify that the call_file attribute for the inlined
   729  			// instance is ok. In this case it should match the file
   730  			// for the main routine. To do this we need to locate the
   731  			// compilation unit DIE that encloses what we're looking
   732  			// at; this can be done with the examiner.
   733  			cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
   734  			if !cfOK {
   735  				t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
   736  			}
   737  			file := ex.FileRef(t, d, mainIdx, cf)
   738  			base := filepath.Base(file)
   739  			if base != "test.go" {
   740  				t.Errorf("bad call_file attribute, found '%s', want '%s'",
   741  					file, "test.go")
   742  			}
   743  
   744  			omap := make(map[dwarf.Offset]bool)
   745  
   746  			// Walk the child variables of the inlined routine. Each
   747  			// of them should have a distinct abstract origin-- if two
   748  			// vars point to the same origin things are definitely broken.
   749  			inlIdx := ex.idxFromOffset(child.Offset)
   750  			inlChildDies := ex.Children(inlIdx)
   751  			for _, k := range inlChildDies {
   752  				ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   753  				if !originOK {
   754  					t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
   755  				}
   756  				if _, found := omap[ooff]; found {
   757  					t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
   758  				}
   759  				omap[ooff] = true
   760  			}
   761  		}
   762  	}
   763  	if exCount != len(expectedInl) {
   764  		t.Fatalf("not enough inlined subroutines found in main.main")
   765  	}
   766  }
   767  
   768  func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
   769  	t.Parallel()
   770  
   771  	dir := t.TempDir()
   772  
   773  	// Build with inlining, to exercise DWARF inlining support.
   774  	f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags)
   775  	defer f.Close()
   776  
   777  	d, err := f.DWARF()
   778  	if err != nil {
   779  		t.Fatalf("error reading DWARF: %v", err)
   780  	}
   781  	rdr := d.Reader()
   782  	ex := examiner{}
   783  	if err := ex.populate(rdr); err != nil {
   784  		t.Fatalf("error reading DWARF: %v", err)
   785  	}
   786  
   787  	// Make a pass through all DIEs looking for abstract origin
   788  	// references.
   789  	abscount := 0
   790  	for i, die := range ex.dies {
   791  		// Does it have an abstract origin?
   792  		ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   793  		if !originOK {
   794  			continue
   795  		}
   796  
   797  		// All abstract origin references should be resolvable.
   798  		abscount += 1
   799  		originDIE := ex.entryFromOffset(ooff)
   800  		if originDIE == nil {
   801  			ex.dumpEntry(i, false, 0)
   802  			t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
   803  		}
   804  
   805  		// Suppose that DIE X has parameter/variable children {K1,
   806  		// K2, ... KN}. If X has an abstract origin of A, then for
   807  		// each KJ, the abstract origin of KJ should be a child of A.
   808  		// Note that this same rule doesn't hold for non-variable DIEs.
   809  		pidx := ex.idxFromOffset(die.Offset)
   810  		if pidx < 0 {
   811  			t.Fatalf("can't locate DIE id")
   812  		}
   813  		kids := ex.Children(pidx)
   814  		for _, kid := range kids {
   815  			if kid.Tag != dwarf.TagVariable &&
   816  				kid.Tag != dwarf.TagFormalParameter {
   817  				continue
   818  			}
   819  			kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
   820  			if !originOK {
   821  				continue
   822  			}
   823  			childOriginDIE := ex.entryFromOffset(kooff)
   824  			if childOriginDIE == nil {
   825  				ex.dumpEntry(i, false, 0)
   826  				t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
   827  			}
   828  			coidx := ex.idxFromOffset(childOriginDIE.Offset)
   829  			childOriginParent := ex.Parent(coidx)
   830  			if childOriginParent != originDIE {
   831  				ex.dumpEntry(i, false, 0)
   832  				t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
   833  			}
   834  		}
   835  	}
   836  	if abscount == 0 {
   837  		t.Fatalf("no abstract origin refs found, something is wrong")
   838  	}
   839  }
   840  
   841  func TestAbstractOriginSanity(t *testing.T) {
   842  	testenv.MustHaveGoBuild(t)
   843  
   844  	if testing.Short() {
   845  		t.Skip("skipping test in short mode.")
   846  	}
   847  
   848  	if runtime.GOOS == "plan9" {
   849  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   850  	}
   851  	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
   852  		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
   853  	}
   854  
   855  	if wd, err := os.Getwd(); err == nil {
   856  		gopathdir := filepath.Join(wd, "testdata", "httptest")
   857  		abstractOriginSanity(t, gopathdir, OptAllInl4)
   858  	} else {
   859  		t.Fatalf("os.Getwd() failed %v", err)
   860  	}
   861  }
   862  
   863  func TestAbstractOriginSanityIssue25459(t *testing.T) {
   864  	testenv.MustHaveGoBuild(t)
   865  
   866  	if runtime.GOOS == "plan9" {
   867  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   868  	}
   869  	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
   870  		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
   871  	}
   872  	if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
   873  		t.Skip("skipping on not-amd64 not-386; location lists not supported")
   874  	}
   875  
   876  	if wd, err := os.Getwd(); err == nil {
   877  		gopathdir := filepath.Join(wd, "testdata", "issue25459")
   878  		abstractOriginSanity(t, gopathdir, DefaultOpt)
   879  	} else {
   880  		t.Fatalf("os.Getwd() failed %v", err)
   881  	}
   882  }
   883  
   884  func TestAbstractOriginSanityIssue26237(t *testing.T) {
   885  	testenv.MustHaveGoBuild(t)
   886  
   887  	if runtime.GOOS == "plan9" {
   888  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   889  	}
   890  	if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
   891  		t.Skip("skipping on solaris, illumos, pending resolution of issue #23168")
   892  	}
   893  	if wd, err := os.Getwd(); err == nil {
   894  		gopathdir := filepath.Join(wd, "testdata", "issue26237")
   895  		abstractOriginSanity(t, gopathdir, DefaultOpt)
   896  	} else {
   897  		t.Fatalf("os.Getwd() failed %v", err)
   898  	}
   899  }
   900  
   901  func TestRuntimeTypeAttrInternal(t *testing.T) {
   902  	testenv.MustHaveGoBuild(t)
   903  	testenv.MustInternalLink(t)
   904  
   905  	if runtime.GOOS == "plan9" {
   906  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   907  	}
   908  
   909  	if runtime.GOOS == "windows" {
   910  		t.Skip("skipping on windows; test is incompatible with relocatable binaries")
   911  	}
   912  
   913  	testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
   914  }
   915  
   916  // External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732)
   917  func TestRuntimeTypeAttrExternal(t *testing.T) {
   918  	testenv.MustHaveGoBuild(t)
   919  	testenv.MustHaveCGO(t)
   920  
   921  	if runtime.GOOS == "plan9" {
   922  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
   923  	}
   924  
   925  	// Explicitly test external linking, for dsymutil compatibility on Darwin.
   926  	if runtime.GOARCH == "ppc64" {
   927  		t.Skip("-linkmode=external not supported on ppc64")
   928  	}
   929  
   930  	if runtime.GOOS == "windows" {
   931  		t.Skip("skipping on windows; test is incompatible with relocatable binaries")
   932  	}
   933  
   934  	testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
   935  }
   936  
   937  func testRuntimeTypeAttr(t *testing.T, flags string) {
   938  	t.Parallel()
   939  
   940  	const prog = `
   941  package main
   942  
   943  import "unsafe"
   944  
   945  type X struct{ _ int }
   946  
   947  func main() {
   948  	var x interface{} = &X{}
   949  	p := *(*uintptr)(unsafe.Pointer(&x))
   950  	print(p)
   951  }
   952  `
   953  	dir := t.TempDir()
   954  
   955  	f := gobuild(t, dir, prog, flags)
   956  	defer f.Close()
   957  
   958  	out, err := exec.Command(f.path).CombinedOutput()
   959  	if err != nil {
   960  		t.Fatalf("could not run test program: %v", err)
   961  	}
   962  	addr, err := strconv.ParseUint(string(out), 10, 64)
   963  	if err != nil {
   964  		t.Fatalf("could not parse type address from program output %q: %v", out, err)
   965  	}
   966  
   967  	symbols, err := f.Symbols()
   968  	if err != nil {
   969  		t.Fatalf("error reading symbols: %v", err)
   970  	}
   971  	var types *objfilepkg.Sym
   972  	for _, sym := range symbols {
   973  		if sym.Name == "runtime.types" {
   974  			types = &sym
   975  			break
   976  		}
   977  	}
   978  	if types == nil {
   979  		t.Fatal("couldn't find runtime.types in symbols")
   980  	}
   981  
   982  	d, err := f.DWARF()
   983  	if err != nil {
   984  		t.Fatalf("error reading DWARF: %v", err)
   985  	}
   986  
   987  	rdr := d.Reader()
   988  	ex := examiner{}
   989  	if err := ex.populate(rdr); err != nil {
   990  		t.Fatalf("error reading DWARF: %v", err)
   991  	}
   992  	dies := ex.Named("*main.X")
   993  	if len(dies) != 1 {
   994  		t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
   995  	}
   996  	rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
   997  	if rtAttr == nil {
   998  		t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
   999  	}
  1000  
  1001  	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
  1002  		return // everything is PIE on ARM64, addresses are relocated
  1003  	}
  1004  	if rtAttr.(uint64)+types.Addr != addr {
  1005  		t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
  1006  	}
  1007  }
  1008  
  1009  func TestIssue27614(t *testing.T) {
  1010  	// Type references in debug_info should always use the DW_TAG_typedef_type
  1011  	// for the type, when that's generated.
  1012  
  1013  	testenv.MustHaveGoBuild(t)
  1014  
  1015  	if runtime.GOOS == "plan9" {
  1016  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1017  	}
  1018  
  1019  	t.Parallel()
  1020  
  1021  	dir := t.TempDir()
  1022  
  1023  	const prog = `package main
  1024  
  1025  import "fmt"
  1026  
  1027  type astruct struct {
  1028  	X int
  1029  }
  1030  
  1031  type bstruct struct {
  1032  	X float32
  1033  }
  1034  
  1035  var globalptr *astruct
  1036  var globalvar astruct
  1037  var bvar0, bvar1, bvar2 bstruct
  1038  
  1039  func main() {
  1040  	fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
  1041  }
  1042  `
  1043  
  1044  	f := gobuild(t, dir, prog, NoOpt)
  1045  
  1046  	defer f.Close()
  1047  
  1048  	data, err := f.DWARF()
  1049  	if err != nil {
  1050  		t.Fatal(err)
  1051  	}
  1052  
  1053  	rdr := data.Reader()
  1054  
  1055  	var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
  1056  	var globalptrDIE, globalvarDIE *dwarf.Entry
  1057  	var bvarDIE [3]*dwarf.Entry
  1058  
  1059  	for {
  1060  		e, err := rdr.Next()
  1061  		if err != nil {
  1062  			t.Fatal(err)
  1063  		}
  1064  		if e == nil {
  1065  			break
  1066  		}
  1067  
  1068  		name, _ := e.Val(dwarf.AttrName).(string)
  1069  
  1070  		switch e.Tag {
  1071  		case dwarf.TagTypedef:
  1072  			switch name {
  1073  			case "main.astruct":
  1074  				astructTypeDIE = e
  1075  			case "main.bstruct":
  1076  				bstructTypeDIE = e
  1077  			}
  1078  		case dwarf.TagPointerType:
  1079  			if name == "*main.astruct" {
  1080  				ptrastructTypeDIE = e
  1081  			}
  1082  		case dwarf.TagVariable:
  1083  			switch name {
  1084  			case "main.globalptr":
  1085  				globalptrDIE = e
  1086  			case "main.globalvar":
  1087  				globalvarDIE = e
  1088  			default:
  1089  				const bvarprefix = "main.bvar"
  1090  				if strings.HasPrefix(name, bvarprefix) {
  1091  					i, _ := strconv.Atoi(name[len(bvarprefix):])
  1092  					bvarDIE[i] = e
  1093  				}
  1094  			}
  1095  		}
  1096  	}
  1097  
  1098  	typedieof := func(e *dwarf.Entry) dwarf.Offset {
  1099  		return e.Val(dwarf.AttrType).(dwarf.Offset)
  1100  	}
  1101  
  1102  	if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
  1103  		t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
  1104  	}
  1105  
  1106  	if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
  1107  		t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
  1108  	}
  1109  
  1110  	if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
  1111  		t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
  1112  	}
  1113  
  1114  	for i := range bvarDIE {
  1115  		if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
  1116  			t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
  1117  		}
  1118  	}
  1119  }
  1120  
  1121  func TestStaticTmp(t *testing.T) {
  1122  	// Checks that statictmp variables do not appear in debug_info or the
  1123  	// symbol table.
  1124  	// Also checks that statictmp variables do not collide with user defined
  1125  	// variables (issue #25113)
  1126  
  1127  	testenv.MustHaveGoBuild(t)
  1128  
  1129  	if runtime.GOOS == "plan9" {
  1130  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1131  	}
  1132  
  1133  	t.Parallel()
  1134  
  1135  	dir := t.TempDir()
  1136  
  1137  	const prog = `package main
  1138  
  1139  var stmp_0 string
  1140  var a []int
  1141  
  1142  func init() {
  1143  	a = []int{ 7 }
  1144  }
  1145  
  1146  func main() {
  1147  	println(a[0])
  1148  }
  1149  `
  1150  
  1151  	f := gobuild(t, dir, prog, NoOpt)
  1152  
  1153  	defer f.Close()
  1154  
  1155  	d, err := f.DWARF()
  1156  	if err != nil {
  1157  		t.Fatalf("error reading DWARF: %v", err)
  1158  	}
  1159  
  1160  	rdr := d.Reader()
  1161  	for {
  1162  		e, err := rdr.Next()
  1163  		if err != nil {
  1164  			t.Fatal(err)
  1165  		}
  1166  		if e == nil {
  1167  			break
  1168  		}
  1169  		if e.Tag != dwarf.TagVariable {
  1170  			continue
  1171  		}
  1172  		name, ok := e.Val(dwarf.AttrName).(string)
  1173  		if !ok {
  1174  			continue
  1175  		}
  1176  		if strings.Contains(name, "stmp") {
  1177  			t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
  1178  		}
  1179  	}
  1180  
  1181  	// When external linking, we put all symbols in the symbol table (so the
  1182  	// external linker can find them). Skip the symbol table check.
  1183  	// TODO: maybe there is some way to tell the external linker not to put
  1184  	// those symbols in the executable's symbol table? Prefix the symbol name
  1185  	// with "." or "L" to pretend it is a label?
  1186  	if !testenv.CanInternalLink() {
  1187  		return
  1188  	}
  1189  
  1190  	syms, err := f.Symbols()
  1191  	if err != nil {
  1192  		t.Fatalf("error reading symbols: %v", err)
  1193  	}
  1194  	for _, sym := range syms {
  1195  		if strings.Contains(sym.Name, "stmp") {
  1196  			t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
  1197  		}
  1198  	}
  1199  }
  1200  
  1201  func TestPackageNameAttr(t *testing.T) {
  1202  	const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
  1203  	const dwarfGoLanguage = 22
  1204  
  1205  	testenv.MustHaveGoBuild(t)
  1206  
  1207  	if runtime.GOOS == "plan9" {
  1208  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1209  	}
  1210  
  1211  	t.Parallel()
  1212  
  1213  	dir := t.TempDir()
  1214  
  1215  	const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
  1216  
  1217  	f := gobuild(t, dir, prog, NoOpt)
  1218  
  1219  	defer f.Close()
  1220  
  1221  	d, err := f.DWARF()
  1222  	if err != nil {
  1223  		t.Fatalf("error reading DWARF: %v", err)
  1224  	}
  1225  
  1226  	rdr := d.Reader()
  1227  	runtimeUnitSeen := false
  1228  	for {
  1229  		e, err := rdr.Next()
  1230  		if err != nil {
  1231  			t.Fatal(err)
  1232  		}
  1233  		if e == nil {
  1234  			break
  1235  		}
  1236  		if e.Tag != dwarf.TagCompileUnit {
  1237  			continue
  1238  		}
  1239  		if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
  1240  			continue
  1241  		}
  1242  
  1243  		pn, ok := e.Val(dwarfAttrGoPackageName).(string)
  1244  		if !ok {
  1245  			name, _ := e.Val(dwarf.AttrName).(string)
  1246  			t.Errorf("found compile unit without package name: %s", name)
  1247  
  1248  		}
  1249  		if pn == "" {
  1250  			name, _ := e.Val(dwarf.AttrName).(string)
  1251  			t.Errorf("found compile unit with empty package name: %s", name)
  1252  		} else {
  1253  			if pn == "runtime" {
  1254  				runtimeUnitSeen = true
  1255  			}
  1256  		}
  1257  	}
  1258  
  1259  	// Something is wrong if there's no runtime compilation unit.
  1260  	if !runtimeUnitSeen {
  1261  		t.Errorf("no package name for runtime unit")
  1262  	}
  1263  }
  1264  
  1265  func TestMachoIssue32233(t *testing.T) {
  1266  	testenv.MustHaveGoBuild(t)
  1267  	testenv.MustHaveCGO(t)
  1268  
  1269  	if runtime.GOOS != "darwin" {
  1270  		t.Skip("skipping; test only interesting on darwin")
  1271  	}
  1272  
  1273  	tmpdir := t.TempDir()
  1274  
  1275  	wd, err := os.Getwd()
  1276  	if err != nil {
  1277  		t.Fatalf("where am I? %v", err)
  1278  	}
  1279  	pdir := filepath.Join(wd, "testdata", "issue32233", "main")
  1280  	f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
  1281  	f.Close()
  1282  }
  1283  
  1284  func TestWindowsIssue36495(t *testing.T) {
  1285  	testenv.MustHaveGoBuild(t)
  1286  	if runtime.GOOS != "windows" {
  1287  		t.Skip("skipping: test only on windows")
  1288  	}
  1289  
  1290  	dir := t.TempDir()
  1291  
  1292  	prog := `
  1293  package main
  1294  
  1295  import "fmt"
  1296  
  1297  func main() {
  1298    fmt.Println("Hello World")
  1299  }`
  1300  	f := gobuild(t, dir, prog, NoOpt)
  1301  	defer f.Close()
  1302  	exe, err := pe.Open(f.path)
  1303  	if err != nil {
  1304  		t.Fatalf("error opening pe file: %v", err)
  1305  	}
  1306  	defer exe.Close()
  1307  	dw, err := exe.DWARF()
  1308  	if err != nil {
  1309  		t.Fatalf("error parsing DWARF: %v", err)
  1310  	}
  1311  	rdr := dw.Reader()
  1312  	for {
  1313  		e, err := rdr.Next()
  1314  		if err != nil {
  1315  			t.Fatalf("error reading DWARF: %v", err)
  1316  		}
  1317  		if e == nil {
  1318  			break
  1319  		}
  1320  		if e.Tag != dwarf.TagCompileUnit {
  1321  			continue
  1322  		}
  1323  		lnrdr, err := dw.LineReader(e)
  1324  		if err != nil {
  1325  			t.Fatalf("error creating DWARF line reader: %v", err)
  1326  		}
  1327  		if lnrdr != nil {
  1328  			var lne dwarf.LineEntry
  1329  			for {
  1330  				err := lnrdr.Next(&lne)
  1331  				if err == io.EOF {
  1332  					break
  1333  				}
  1334  				if err != nil {
  1335  					t.Fatalf("error reading next DWARF line: %v", err)
  1336  				}
  1337  				if strings.Contains(lne.File.Name, `\`) {
  1338  					t.Errorf("filename should not contain backslash: %v", lne.File.Name)
  1339  				}
  1340  			}
  1341  		}
  1342  		rdr.SkipChildren()
  1343  	}
  1344  }
  1345  
  1346  func TestIssue38192(t *testing.T) {
  1347  	testenv.MustHaveGoBuild(t)
  1348  
  1349  	if runtime.GOOS == "plan9" {
  1350  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1351  	}
  1352  
  1353  	t.Parallel()
  1354  
  1355  	// Build a test program that contains a translation unit whose
  1356  	// text (from am assembly source) contains only a single instruction.
  1357  	tmpdir := t.TempDir()
  1358  	wd, err := os.Getwd()
  1359  	if err != nil {
  1360  		t.Fatalf("where am I? %v", err)
  1361  	}
  1362  	pdir := filepath.Join(wd, "testdata", "issue38192")
  1363  	f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
  1364  	defer f.Close()
  1365  
  1366  	// Open the resulting binary and examine the DWARF it contains.
  1367  	// Look for the function of interest ("main.singleInstruction")
  1368  	// and verify that the line table has an entry not just for the
  1369  	// single instruction but also a dummy instruction following it,
  1370  	// so as to test that whoever is emitting the DWARF doesn't
  1371  	// emit an end-sequence op immediately after the last instruction
  1372  	// in the translation unit.
  1373  	//
  1374  	// NB: another way to write this test would have been to run the
  1375  	// resulting executable under GDB, set a breakpoint in
  1376  	// "main.singleInstruction", then verify that GDB displays the
  1377  	// correct line/file information.  Given the headache and flakiness
  1378  	// associated with GDB-based tests these days, a direct read of
  1379  	// the line table seems more desirable.
  1380  	rows := []dwarf.LineEntry{}
  1381  	dw, err := f.DWARF()
  1382  	if err != nil {
  1383  		t.Fatalf("error parsing DWARF: %v", err)
  1384  	}
  1385  	rdr := dw.Reader()
  1386  	for {
  1387  		e, err := rdr.Next()
  1388  		if err != nil {
  1389  			t.Fatalf("error reading DWARF: %v", err)
  1390  		}
  1391  		if e == nil {
  1392  			break
  1393  		}
  1394  		if e.Tag != dwarf.TagCompileUnit {
  1395  			continue
  1396  		}
  1397  		// NB: there can be multiple compile units named "main".
  1398  		name := e.Val(dwarf.AttrName).(string)
  1399  		if name != "main" {
  1400  			continue
  1401  		}
  1402  		lnrdr, err := dw.LineReader(e)
  1403  		if err != nil {
  1404  			t.Fatalf("error creating DWARF line reader: %v", err)
  1405  		}
  1406  		if lnrdr != nil {
  1407  			var lne dwarf.LineEntry
  1408  			for {
  1409  				err := lnrdr.Next(&lne)
  1410  				if err == io.EOF {
  1411  					break
  1412  				}
  1413  				if err != nil {
  1414  					t.Fatalf("error reading next DWARF line: %v", err)
  1415  				}
  1416  				if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
  1417  					continue
  1418  				}
  1419  				rows = append(rows, lne)
  1420  			}
  1421  		}
  1422  		rdr.SkipChildren()
  1423  	}
  1424  	f.Close()
  1425  
  1426  	// Make sure that:
  1427  	// - main.singleInstruction appears in the line table
  1428  	// - more than one PC value appears the line table for
  1429  	//   that compilation unit.
  1430  	// - at least one row has the correct line number (8)
  1431  	pcs := make(map[uint64]bool)
  1432  	line8seen := false
  1433  	for _, r := range rows {
  1434  		pcs[r.Address] = true
  1435  		if r.Line == 8 {
  1436  			line8seen = true
  1437  		}
  1438  	}
  1439  	failed := false
  1440  	if len(pcs) < 2 {
  1441  		failed = true
  1442  		t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
  1443  	}
  1444  	if !line8seen {
  1445  		failed = true
  1446  		t.Errorf("line table does not contain correct line for main.singleInstruction")
  1447  	}
  1448  	if !failed {
  1449  		return
  1450  	}
  1451  	for i, r := range rows {
  1452  		t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
  1453  	}
  1454  }
  1455  
  1456  func TestIssue39757(t *testing.T) {
  1457  	testenv.MustHaveGoBuild(t)
  1458  
  1459  	if runtime.GOOS == "plan9" {
  1460  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1461  	}
  1462  
  1463  	t.Parallel()
  1464  
  1465  	// In this bug the DWARF line table contents for the last couple of
  1466  	// instructions in a function were incorrect (bad file/line). This
  1467  	// test verifies that all of the line table rows for a function
  1468  	// of interest have the same file (no "autogenerated").
  1469  	//
  1470  	// Note: the function in this test was written with an eye towards
  1471  	// ensuring that there are no inlined routines from other packages
  1472  	// (which could introduce other source files into the DWARF); it's
  1473  	// possible that at some point things could evolve in the
  1474  	// compiler/runtime in ways that aren't happening now, so this
  1475  	// might be something to check for if it does start failing.
  1476  
  1477  	tmpdir := t.TempDir()
  1478  
  1479  	wd, err := os.Getwd()
  1480  	if err != nil {
  1481  		t.Fatalf("where am I? %v", err)
  1482  	}
  1483  	pdir := filepath.Join(wd, "testdata", "issue39757")
  1484  	f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
  1485  	defer f.Close()
  1486  
  1487  	syms, err := f.Symbols()
  1488  	if err != nil {
  1489  		t.Fatal(err)
  1490  	}
  1491  
  1492  	var addr uint64
  1493  	for _, sym := range syms {
  1494  		if sym.Name == "main.main" {
  1495  			addr = sym.Addr
  1496  			break
  1497  		}
  1498  	}
  1499  	if addr == 0 {
  1500  		t.Fatal("cannot find main.main in symbols")
  1501  	}
  1502  
  1503  	// Open the resulting binary and examine the DWARF it contains.
  1504  	// Look for the function of interest ("main.main")
  1505  	// and verify that all line table entries show the same source
  1506  	// file.
  1507  	dw, err := f.DWARF()
  1508  	if err != nil {
  1509  		t.Fatalf("error parsing DWARF: %v", err)
  1510  	}
  1511  	rdr := dw.Reader()
  1512  	ex := examiner{}
  1513  	if err := ex.populate(rdr); err != nil {
  1514  		t.Fatalf("error reading DWARF: %v", err)
  1515  	}
  1516  
  1517  	// Locate the main.main DIE
  1518  	mains := ex.Named("main.main")
  1519  	if len(mains) == 0 {
  1520  		t.Fatalf("unable to locate DIE for main.main")
  1521  	}
  1522  	if len(mains) != 1 {
  1523  		t.Fatalf("more than one main.main DIE")
  1524  	}
  1525  	maindie := mains[0]
  1526  
  1527  	// Collect the start/end PC for main.main
  1528  	lowpc := maindie.Val(dwarf.AttrLowpc).(uint64)
  1529  	highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
  1530  
  1531  	// Now read the line table for the 'main' compilation unit.
  1532  	mainIdx := ex.idxFromOffset(maindie.Offset)
  1533  	cuentry := ex.Parent(mainIdx)
  1534  	if cuentry == nil {
  1535  		t.Fatalf("main.main DIE appears orphaned")
  1536  	}
  1537  	lnrdr, lerr := dw.LineReader(cuentry)
  1538  	if lerr != nil {
  1539  		t.Fatalf("error creating DWARF line reader: %v", err)
  1540  	}
  1541  	if lnrdr == nil {
  1542  		t.Fatalf("no line table for main.main compilation unit")
  1543  	}
  1544  	rows := []dwarf.LineEntry{}
  1545  	mainrows := 0
  1546  	var lne dwarf.LineEntry
  1547  	for {
  1548  		err := lnrdr.Next(&lne)
  1549  		if err == io.EOF {
  1550  			break
  1551  		}
  1552  		rows = append(rows, lne)
  1553  		if err != nil {
  1554  			t.Fatalf("error reading next DWARF line: %v", err)
  1555  		}
  1556  		if lne.Address < lowpc || lne.Address > highpc {
  1557  			continue
  1558  		}
  1559  		if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
  1560  			t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
  1561  		}
  1562  		mainrows++
  1563  	}
  1564  	f.Close()
  1565  
  1566  	// Make sure we saw a few rows.
  1567  	if mainrows < 3 {
  1568  		t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
  1569  		for i, r := range rows {
  1570  			t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
  1571  		}
  1572  	}
  1573  }
  1574  
  1575  func TestIssue42484(t *testing.T) {
  1576  	testenv.MustHaveGoBuild(t)
  1577  
  1578  	if runtime.GOOS == "plan9" {
  1579  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1580  	}
  1581  
  1582  	t.Parallel()
  1583  
  1584  	tmpdir, err := ioutil.TempDir("", "TestIssue42484")
  1585  	if err != nil {
  1586  		t.Fatalf("could not create directory: %v", err)
  1587  	}
  1588  	defer os.RemoveAll(tmpdir)
  1589  	wd, err := os.Getwd()
  1590  	if err != nil {
  1591  		t.Fatalf("where am I? %v", err)
  1592  	}
  1593  	pdir := filepath.Join(wd, "testdata", "issue42484")
  1594  	f := gobuildTestdata(t, tmpdir, pdir, NoOpt)
  1595  
  1596  	var lastAddr uint64
  1597  	var lastFile string
  1598  	var lastLine int
  1599  
  1600  	dw, err := f.DWARF()
  1601  	if err != nil {
  1602  		t.Fatalf("error parsing DWARF: %v", err)
  1603  	}
  1604  	rdr := dw.Reader()
  1605  	for {
  1606  		e, err := rdr.Next()
  1607  		if err != nil {
  1608  			t.Fatalf("error reading DWARF: %v", err)
  1609  		}
  1610  		if e == nil {
  1611  			break
  1612  		}
  1613  		if e.Tag != dwarf.TagCompileUnit {
  1614  			continue
  1615  		}
  1616  		lnrdr, err := dw.LineReader(e)
  1617  		if err != nil {
  1618  			t.Fatalf("error creating DWARF line reader: %v", err)
  1619  		}
  1620  		if lnrdr != nil {
  1621  			var lne dwarf.LineEntry
  1622  			for {
  1623  				err := lnrdr.Next(&lne)
  1624  				if err == io.EOF {
  1625  					break
  1626  				}
  1627  				if err != nil {
  1628  					t.Fatalf("error reading next DWARF line: %v", err)
  1629  				}
  1630  				if lne.EndSequence {
  1631  					continue
  1632  				}
  1633  				if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
  1634  					t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
  1635  				}
  1636  				lastAddr = lne.Address
  1637  				lastFile = lne.File.Name
  1638  				lastLine = lne.Line
  1639  			}
  1640  		}
  1641  		rdr.SkipChildren()
  1642  	}
  1643  	f.Close()
  1644  }
  1645  
  1646  func TestOutputParamAbbrevAndAttr(t *testing.T) {
  1647  	testenv.MustHaveGoBuild(t)
  1648  
  1649  	if runtime.GOOS == "plan9" {
  1650  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
  1651  	}
  1652  	t.Parallel()
  1653  
  1654  	// This test verifies that the compiler is selecting the correct
  1655  	// DWARF abbreviation for output parameters, and that the
  1656  	// variable parameter attribute is correct for in-params and
  1657  	// out-params.
  1658  
  1659  	const prog = `
  1660  package main
  1661  
  1662  //go:noinline
  1663  func ABC(c1, c2, c3 int, d1, d2, d3, d4 string, f1, f2, f3 float32, g1 [1024]int) (r1 int, r2 int, r3 [1024]int, r4 byte, r5 string, r6 float32) {
  1664  	g1[0] = 6
  1665  	r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
  1666  	return
  1667  }
  1668  
  1669  func main() {
  1670  	a := [1024]int{}
  1671  	v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
  1672  	println(v1, v2, v3[0], v4, v5, v6)
  1673  }
  1674  `
  1675  	dir := t.TempDir()
  1676  	f := gobuild(t, dir, prog, NoOpt)
  1677  	defer f.Close()
  1678  
  1679  	d, err := f.DWARF()
  1680  	if err != nil {
  1681  		t.Fatalf("error reading DWARF: %v", err)
  1682  	}
  1683  
  1684  	rdr := d.Reader()
  1685  	ex := examiner{}
  1686  	if err := ex.populate(rdr); err != nil {
  1687  		t.Fatalf("error reading DWARF: %v", err)
  1688  	}
  1689  
  1690  	// Locate the main.ABC DIE
  1691  	abcs := ex.Named("main.ABC")
  1692  	if len(abcs) == 0 {
  1693  		t.Fatalf("unable to locate DIE for main.ABC")
  1694  	}
  1695  	if len(abcs) != 1 {
  1696  		t.Fatalf("more than one main.ABC DIE")
  1697  	}
  1698  	abcdie := abcs[0]
  1699  
  1700  	// Vet the DIE
  1701  	if abcdie.Tag != dwarf.TagSubprogram {
  1702  		t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag)
  1703  	}
  1704  
  1705  	// A setting of DW_AT_variable_parameter indicates that the
  1706  	// param in question is an output parameter; we want to see this
  1707  	// attribute set to TRUE for all Go return params. It would be
  1708  	// OK to have it missing for input parameters, but for the moment
  1709  	// we verify that the attr is present but set to false.
  1710  
  1711  	// Values in this map are of the form <order>:<varparam>
  1712  	// where order is the order within the child DIE list of the param,
  1713  	// and <varparam> is an integer:
  1714  	//
  1715  	//  -1: varparm attr not found
  1716  	//   1: varparm found with value false
  1717  	//   2: varparm found with value true
  1718  	//
  1719  	foundParams := make(map[string]string)
  1720  
  1721  	// Walk ABCs's children looking for params.
  1722  	abcIdx := ex.idxFromOffset(abcdie.Offset)
  1723  	childDies := ex.Children(abcIdx)
  1724  	idx := 0
  1725  	for _, child := range childDies {
  1726  		if child.Tag == dwarf.TagFormalParameter {
  1727  			st := -1
  1728  			if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
  1729  				if vp {
  1730  					st = 2
  1731  				} else {
  1732  					st = 1
  1733  				}
  1734  			}
  1735  			if name, ok := child.Val(dwarf.AttrName).(string); ok {
  1736  				foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
  1737  				idx++
  1738  			}
  1739  		}
  1740  	}
  1741  
  1742  	// Digest the result.
  1743  	found := make([]string, 0, len(foundParams))
  1744  	for k, v := range foundParams {
  1745  		found = append(found, fmt.Sprintf("%s:%s", k, v))
  1746  	}
  1747  	sort.Strings(found)
  1748  
  1749  	// Make sure we see all of the expected params in the proper
  1750  	// order, that they have the varparam attr, and the varparm is set
  1751  	// for the returns.
  1752  	expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]"
  1753  	if fmt.Sprintf("%+v", found) != expected {
  1754  		t.Errorf("param check failed, wanted %s got %s\n",
  1755  			expected, found)
  1756  	}
  1757  }
  1758  

View as plain text