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