...
Run Format

Source file src/cmd/link/dwarf_test.go

Documentation: cmd/link

     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 main
     6  
     7  import (
     8  	"cmd/internal/objfile"
     9  	"debug/dwarf"
    10  	"internal/testenv"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path"
    16  	"path/filepath"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  )
    21  
    22  func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string) {
    23  	testenv.MustHaveCGO(t)
    24  	testenv.MustHaveGoBuild(t)
    25  
    26  	if runtime.GOOS == "plan9" {
    27  		t.Skip("skipping on plan9; no DWARF symbol table in executables")
    28  	}
    29  
    30  	out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
    31  	if err != nil {
    32  		t.Fatalf("go list: %v\n%s", err, out)
    33  	}
    34  	if string(out) != "false\n" {
    35  		if os.Getenv("GOROOT_FINAL_OLD") != "" {
    36  			t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set")
    37  		}
    38  		t.Fatalf("cmd/link is stale - run go install cmd/link")
    39  	}
    40  
    41  	tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
    42  	if err != nil {
    43  		t.Fatal("TempDir failed: ", err)
    44  	}
    45  	defer os.RemoveAll(tmpDir)
    46  
    47  	for _, prog := range []string{"testprog", "testprogcgo"} {
    48  		t.Run(prog, func(t *testing.T) {
    49  			exe := filepath.Join(tmpDir, prog+".exe")
    50  			dir := "../../runtime/testdata/" + prog
    51  			cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
    52  			if buildmode != "" {
    53  				cmd.Args = append(cmd.Args, "-buildmode", buildmode)
    54  			}
    55  			cmd.Args = append(cmd.Args, dir)
    56  			if env != nil {
    57  				cmd.Env = append(os.Environ(), env...)
    58  			}
    59  			out, err := cmd.CombinedOutput()
    60  			if err != nil {
    61  				t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
    62  			}
    63  
    64  			if buildmode == "c-archive" {
    65  				// Extract the archive and use the go.o object within.
    66  				cmd := exec.Command("ar", "-x", exe)
    67  				cmd.Dir = tmpDir
    68  				if out, err := cmd.CombinedOutput(); err != nil {
    69  					t.Fatalf("ar -x %s: %v\n%s", exe, err, out)
    70  				}
    71  				exe = filepath.Join(tmpDir, "go.o")
    72  			}
    73  			f, err := objfile.Open(exe)
    74  			if err != nil {
    75  				t.Fatal(err)
    76  			}
    77  			defer f.Close()
    78  
    79  			syms, err := f.Symbols()
    80  			if err != nil {
    81  				t.Fatal(err)
    82  			}
    83  
    84  			var addr uint64
    85  			for _, sym := range syms {
    86  				if sym.Name == "main.main" {
    87  					addr = sym.Addr
    88  					break
    89  				}
    90  			}
    91  			if addr == 0 {
    92  				t.Fatal("cannot find main.main in symbols")
    93  			}
    94  
    95  			d, err := f.DWARF()
    96  			if err != nil {
    97  				if expectDWARF {
    98  					t.Fatal(err)
    99  				}
   100  				return
   101  			} else {
   102  				if !expectDWARF {
   103  					t.Fatal("unexpected DWARF section")
   104  				}
   105  			}
   106  
   107  			// TODO: We'd like to use filepath.Join here.
   108  			// Also related: golang.org/issue/19784.
   109  			wantFile := path.Join(prog, "main.go")
   110  			wantLine := 24
   111  			r := d.Reader()
   112  			var line dwarf.LineEntry
   113  			for {
   114  				cu, err := r.Next()
   115  				if err != nil {
   116  					t.Fatal(err)
   117  				}
   118  				if cu == nil {
   119  					break
   120  				}
   121  				if cu.Tag != dwarf.TagCompileUnit {
   122  					r.SkipChildren()
   123  					continue
   124  				}
   125  				lr, err := d.LineReader(cu)
   126  				if err != nil {
   127  					t.Fatal(err)
   128  				}
   129  				for {
   130  					err := lr.Next(&line)
   131  					if err == io.EOF {
   132  						break
   133  					}
   134  					if err != nil {
   135  						t.Fatal(err)
   136  					}
   137  					if line.Address == addr {
   138  						if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
   139  							t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
   140  						}
   141  						return
   142  					}
   143  				}
   144  			}
   145  			t.Fatalf("did not find file:line for %#x (main.main)", addr)
   146  		})
   147  	}
   148  }
   149  
   150  func TestDWARF(t *testing.T) {
   151  	testDWARF(t, "", true)
   152  }
   153  
   154  func TestDWARFiOS(t *testing.T) {
   155  	// Normally we run TestDWARF on native platform. But on iOS we don't have
   156  	// go build, so we do this test with a cross build.
   157  	// Only run this on darwin/amd64, where we can cross build for iOS.
   158  	if testing.Short() {
   159  		t.Skip("skipping in short mode")
   160  	}
   161  	if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
   162  		t.Skip("skipping on non-darwin/amd64 platform")
   163  	}
   164  	if err := exec.Command("xcrun", "--help").Run(); err != nil {
   165  		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
   166  	}
   167  	cc := "CC=" + runtime.GOROOT() + "/misc/ios/clangwrap.sh"
   168  	// iOS doesn't allow unmapped segments, so iOS executables don't have DWARF.
   169  	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
   170  	testDWARF(t, "", false, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
   171  	// However, c-archive iOS objects have embedded DWARF.
   172  	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
   173  	testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
   174  }
   175  

View as plain text