aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/debug
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-09-14 17:11:35 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-09-14 17:11:35 +0000
commitbc998d034f45d1828a8663b2eed928faf22a7d01 (patch)
tree8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/debug
parenta41a6142df74219f596e612d3a7775f68ca6e96f (diff)
downloadgcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip
gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz
gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753 From-SVN: r252767
Diffstat (limited to 'libgo/go/debug')
-rw-r--r--libgo/go/debug/dwarf/export_test.go7
-rw-r--r--libgo/go/debug/dwarf/line.go74
-rw-r--r--libgo/go/debug/dwarf/line_test.go85
-rw-r--r--libgo/go/debug/dwarf/testdata/line-gcc-win.binbin0 -> 133202 bytes
-rw-r--r--libgo/go/debug/dwarf/type_test.go14
-rw-r--r--libgo/go/debug/pe/file_cgo_test.go31
-rw-r--r--libgo/go/debug/pe/file_test.go141
7 files changed, 336 insertions, 16 deletions
diff --git a/libgo/go/debug/dwarf/export_test.go b/libgo/go/debug/dwarf/export_test.go
new file mode 100644
index 0000000..b8a25ff
--- /dev/null
+++ b/libgo/go/debug/dwarf/export_test.go
@@ -0,0 +1,7 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dwarf
+
+var PathJoin = pathJoin
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
index ed82fee..4e6e142 100644
--- a/libgo/go/debug/dwarf/line.go
+++ b/libgo/go/debug/dwarf/line.go
@@ -9,6 +9,7 @@ import (
"fmt"
"io"
"path"
+ "strings"
)
// A LineReader reads a sequence of LineEntry structures from a DWARF
@@ -247,10 +248,10 @@ func (r *LineReader) readHeader() error {
if len(directory) == 0 {
break
}
- if !path.IsAbs(directory) {
+ if !pathIsAbs(directory) {
// Relative paths are implicitly relative to
// the compilation directory.
- directory = path.Join(r.directories[0], directory)
+ directory = pathJoin(r.directories[0], directory)
}
r.directories = append(r.directories, directory)
}
@@ -283,11 +284,11 @@ func (r *LineReader) readFileEntry() (bool, error) {
}
off := r.buf.off
dirIndex := int(r.buf.uint())
- if !path.IsAbs(name) {
+ if !pathIsAbs(name) {
if dirIndex >= len(r.directories) {
return false, DecodeError{"line", off, "directory index too large"}
}
- name = path.Join(r.directories[dirIndex], name)
+ name = pathJoin(r.directories[dirIndex], name)
}
mtime := r.buf.uint()
length := int(r.buf.uint())
@@ -588,3 +589,68 @@ func (r *LineReader) SeekPC(pc uint64, entry *LineEntry) error {
*entry = next
}
}
+
+// pathIsAbs returns whether path is an absolute path (or "full path
+// name" in DWARF parlance). This is in "whatever form makes sense for
+// the host system", so this accepts both UNIX-style and DOS-style
+// absolute paths. We avoid the filepath package because we want this
+// to behave the same regardless of our host system and because we
+// don't know what system the paths came from.
+func pathIsAbs(path string) bool {
+ _, path = splitDrive(path)
+ return len(path) > 0 && (path[0] == '/' || path[0] == '\\')
+}
+
+// pathJoin joins dirname and filename. filename must be relative.
+// DWARF paths can be UNIX-style or DOS-style, so this handles both.
+func pathJoin(dirname, filename string) string {
+ if len(dirname) == 0 {
+ return filename
+ }
+ // dirname should be absolute, which means we can determine
+ // whether it's a DOS path reasonably reliably by looking for
+ // a drive letter or UNC path.
+ drive, dirname := splitDrive(dirname)
+ if drive == "" {
+ // UNIX-style path.
+ return path.Join(dirname, filename)
+ }
+ // DOS-style path.
+ drive2, filename := splitDrive(filename)
+ if drive2 != "" {
+ if strings.ToLower(drive) != strings.ToLower(drive2) {
+ // Different drives. There's not much we can
+ // do here, so just ignore the directory.
+ return drive2 + filename
+ }
+ // Drives are the same. Ignore drive on filename.
+ }
+ if !(strings.HasSuffix(dirname, "/") || strings.HasSuffix(dirname, `\`)) && dirname != "" {
+ dirname += `\`
+ }
+ return drive + dirname + filename
+}
+
+// splitDrive splits the DOS drive letter or UNC share point from
+// path, if any. path == drive + rest
+func splitDrive(path string) (drive, rest string) {
+ if len(path) >= 2 && path[1] == ':' {
+ if c := path[0]; 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
+ return path[:2], path[2:]
+ }
+ }
+ if len(path) > 3 && (path[0] == '\\' || path[0] == '/') && (path[1] == '\\' || path[1] == '/') {
+ // Normalize the path so we can search for just \ below.
+ npath := strings.Replace(path, "/", `\`, -1)
+ // Get the host part, which must be non-empty.
+ slash1 := strings.IndexByte(npath[2:], '\\') + 2
+ if slash1 > 2 {
+ // Get the mount-point part, which must be non-empty.
+ slash2 := strings.IndexByte(npath[slash1+1:], '\\') + slash1 + 1
+ if slash2 > slash1 {
+ return path[:slash2], path[slash2:]
+ }
+ }
+ }
+ return "", path
+}
diff --git a/libgo/go/debug/dwarf/line_test.go b/libgo/go/debug/dwarf/line_test.go
index cc363f5..11a2544 100644
--- a/libgo/go/debug/dwarf/line_test.go
+++ b/libgo/go/debug/dwarf/line_test.go
@@ -7,6 +7,7 @@ package dwarf_test
import (
. "debug/dwarf"
"io"
+ "strings"
"testing"
)
@@ -46,6 +47,46 @@ func TestLineELFGCC(t *testing.T) {
testLineTable(t, want, elfData(t, "testdata/line-gcc.elf"))
}
+func TestLineGCCWindows(t *testing.T) {
+ // Generated by:
+ // > gcc --version
+ // gcc (tdm64-1) 4.9.2
+ // > gcc -g -o line-gcc-win.bin line1.c C:\workdir\go\src\debug\dwarf\testdata\line2.c
+
+ toWindows := func(lf *LineFile) *LineFile {
+ lf2 := *lf
+ lf2.Name = strings.Replace(lf2.Name, "/home/austin/go.dev/", "C:\\workdir\\go\\", -1)
+ lf2.Name = strings.Replace(lf2.Name, "/", "\\", -1)
+ return &lf2
+ }
+ file1C := toWindows(file1C)
+ file1H := toWindows(file1H)
+ file2C := toWindows(file2C)
+
+ // Line table based on objdump --dwarf=rawline,decodedline
+ want := []LineEntry{
+ {Address: 0x401530, File: file1H, Line: 2, IsStmt: true},
+ {Address: 0x401538, File: file1H, Line: 5, IsStmt: true},
+ {Address: 0x401541, File: file1H, Line: 6, IsStmt: true, Discriminator: 3},
+ {Address: 0x40154b, File: file1H, Line: 5, IsStmt: true, Discriminator: 3},
+ {Address: 0x40154f, File: file1H, Line: 5, IsStmt: false, Discriminator: 1},
+ {Address: 0x401555, File: file1H, Line: 7, IsStmt: true},
+ {Address: 0x40155b, File: file1C, Line: 6, IsStmt: true},
+ {Address: 0x401563, File: file1C, Line: 6, IsStmt: true},
+ {Address: 0x401568, File: file1C, Line: 7, IsStmt: true},
+ {Address: 0x40156d, File: file1C, Line: 8, IsStmt: true},
+ {Address: 0x401572, File: file1C, Line: 9, IsStmt: true},
+ {Address: 0x401578, EndSequence: true},
+
+ {Address: 0x401580, File: file2C, Line: 4, IsStmt: true},
+ {Address: 0x401588, File: file2C, Line: 5, IsStmt: true},
+ {Address: 0x401595, File: file2C, Line: 6, IsStmt: true},
+ {Address: 0x40159b, EndSequence: true},
+ }
+
+ testLineTable(t, want, peData(t, "testdata/line-gcc-win.bin"))
+}
+
func TestLineELFClang(t *testing.T) {
// Generated by:
// # clang --version | head -n1
@@ -183,6 +224,11 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) {
}
t.Fatal("lr.Next:", err)
}
+ // Ignore sources from the Windows build environment.
+ if strings.HasPrefix(line.File.Name, "C:\\crossdev\\") ||
+ strings.HasPrefix(line.File.Name, "C:/crossdev/") {
+ continue
+ }
got = append(got, line)
}
}
@@ -227,3 +273,42 @@ func dumpLines(t *testing.T, lines []LineEntry) {
t.Logf(" %+v File:%+v", l, l.File)
}
}
+
+type joinTest struct {
+ dirname, filename string
+ path string
+}
+
+var joinTests = []joinTest{
+ {"a", "b", "a/b"},
+ {"a", "", "a"},
+ {"", "b", "b"},
+ {"/a", "b", "/a/b"},
+ {"/a/", "b", "/a/b"},
+
+ {`C:\Windows\`, `System32`, `C:\Windows\System32`},
+ {`C:\Windows\`, ``, `C:\Windows\`},
+ {`C:\`, `Windows`, `C:\Windows`},
+ {`C:\Windows\`, `C:System32`, `C:\Windows\System32`},
+ {`C:\Windows`, `a/b`, `C:\Windows\a/b`},
+ {`\\host\share\`, `foo`, `\\host\share\foo`},
+ {`\\host\share\`, `foo\bar`, `\\host\share\foo\bar`},
+ {`//host/share/`, `foo/bar`, `//host/share/foo/bar`},
+
+ // The following are "best effort". We shouldn't see relative
+ // base directories in DWARF, but these test that pathJoin
+ // doesn't fail miserably if it sees one.
+ {`C:`, `a`, `C:a`},
+ {`C:`, `a\b`, `C:a\b`},
+ {`C:.`, `a`, `C:.\a`},
+ {`C:a`, `b`, `C:a\b`},
+}
+
+func TestPathJoin(t *testing.T) {
+ for _, test := range joinTests {
+ got := PathJoin(test.dirname, test.filename)
+ if test.path != got {
+ t.Errorf("pathJoin(%q, %q) = %q, want %q", test.dirname, test.filename, got, test.path)
+ }
+ }
+}
diff --git a/libgo/go/debug/dwarf/testdata/line-gcc-win.bin b/libgo/go/debug/dwarf/testdata/line-gcc-win.bin
new file mode 100644
index 0000000..583ad44
--- /dev/null
+++ b/libgo/go/debug/dwarf/testdata/line-gcc-win.bin
Binary files differ
diff --git a/libgo/go/debug/dwarf/type_test.go b/libgo/go/debug/dwarf/type_test.go
index 0283466..6c06731 100644
--- a/libgo/go/debug/dwarf/type_test.go
+++ b/libgo/go/debug/dwarf/type_test.go
@@ -8,6 +8,7 @@ import (
. "debug/dwarf"
"debug/elf"
"debug/macho"
+ "debug/pe"
"testing"
)
@@ -67,6 +68,19 @@ func machoData(t *testing.T, name string) *Data {
return d
}
+func peData(t *testing.T, name string) *Data {
+ f, err := pe.Open(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return d
+}
+
func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") }
func TestTypedefsMachO(t *testing.T) {
diff --git a/libgo/go/debug/pe/file_cgo_test.go b/libgo/go/debug/pe/file_cgo_test.go
new file mode 100644
index 0000000..739671d
--- /dev/null
+++ b/libgo/go/debug/pe/file_cgo_test.go
@@ -0,0 +1,31 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package pe
+
+import (
+ "os/exec"
+ "testing"
+)
+
+func testCgoDWARF(t *testing.T, linktype int) {
+ if _, err := exec.LookPath("gcc"); err != nil {
+ t.Skip("skipping test: gcc is missing")
+ }
+ testDWARF(t, linktype)
+}
+
+func TestDefaultLinkerDWARF(t *testing.T) {
+ testCgoDWARF(t, linkCgoDefault)
+}
+
+func TestInternalLinkerDWARF(t *testing.T) {
+ testCgoDWARF(t, linkCgoInternal)
+}
+
+func TestExternalLinkerDWARF(t *testing.T) {
+ testCgoDWARF(t, linkCgoExternal)
+}
diff --git a/libgo/go/debug/pe/file_test.go b/libgo/go/debug/pe/file_test.go
index 5a740c8..8645d67 100644
--- a/libgo/go/debug/pe/file_test.go
+++ b/libgo/go/debug/pe/file_test.go
@@ -12,8 +12,11 @@ import (
"os/exec"
"path/filepath"
"reflect"
+ "regexp"
"runtime"
+ "strconv"
"testing"
+ "text/template"
)
type fileTest struct {
@@ -288,28 +291,70 @@ func TestOpenFailure(t *testing.T) {
}
}
-func TestDWARF(t *testing.T) {
+const (
+ linkNoCgo = iota
+ linkCgoDefault
+ linkCgoInternal
+ linkCgoExternal
+)
+
+func testDWARF(t *testing.T, linktype int) {
if runtime.GOOS != "windows" {
t.Skip("skipping windows only test")
}
+ testenv.MustHaveGoRun(t)
tmpdir, err := ioutil.TempDir("", "TestDWARF")
if err != nil {
- t.Fatal("TempDir failed: ", err)
+ t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
- prog := `
-package main
-func main() {
-}
-`
src := filepath.Join(tmpdir, "a.go")
+ file, err := os.Create(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = template.Must(template.New("main").Parse(testprog)).Execute(file, linktype != linkNoCgo)
+ if err != nil {
+ if err := file.Close(); err != nil {
+ t.Error(err)
+ }
+ t.Fatal(err)
+ }
+ if err := file.Close(); err != nil {
+ t.Fatal(err)
+ }
+
exe := filepath.Join(tmpdir, "a.exe")
- err = ioutil.WriteFile(src, []byte(prog), 0644)
- output, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
+ args := []string{"build", "-o", exe}
+ switch linktype {
+ case linkNoCgo:
+ case linkCgoDefault:
+ case linkCgoInternal:
+ args = append(args, "-ldflags", "-linkmode=internal")
+ case linkCgoExternal:
+ args = append(args, "-ldflags", "-linkmode=external")
+ default:
+ t.Fatalf("invalid linktype parameter of %v", linktype)
+ }
+ args = append(args, src)
+ out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("building test executable for linktype %d failed: %s %s", linktype, err, out)
+ }
+ out, err = exec.Command(exe).CombinedOutput()
if err != nil {
- t.Fatalf("building test executable failed: %s %s", err, output)
+ t.Fatalf("running test executable failed: %s %s", err, out)
+ }
+
+ matches := regexp.MustCompile("main=(.*)\n").FindStringSubmatch(string(out))
+ if len(matches) < 2 {
+ t.Fatalf("unexpected program output: %s", out)
+ }
+ wantaddr, err := strconv.ParseUint(matches[1], 0, 64)
+ if err != nil {
+ t.Fatalf("unexpected main address %q: %s", matches[1], err)
}
f, err := Open(exe)
@@ -318,6 +363,16 @@ func main() {
}
defer f.Close()
+ var foundDebugGDBScriptsSection bool
+ for _, sect := range f.Sections {
+ if sect.Name == ".debug_gdb_scripts" {
+ foundDebugGDBScriptsSection = true
+ }
+ }
+ if !foundDebugGDBScriptsSection {
+ t.Error(".debug_gdb_scripts section is not found")
+ }
+
d, err := f.DWARF()
if err != nil {
t.Fatal(err)
@@ -334,8 +389,8 @@ func main() {
break
}
if e.Tag == dwarf.TagSubprogram {
- for _, f := range e.Field {
- if f.Attr == dwarf.AttrName && e.Val(dwarf.AttrName) == "main.main" {
+ if name, ok := e.Val(dwarf.AttrName).(string); ok && name == "main.main" {
+ if addr, ok := e.Val(dwarf.AttrLowpc).(uint64); ok && addr == wantaddr {
return
}
}
@@ -415,3 +470,65 @@ main(void)
}
}
}
+
+func TestDWARF(t *testing.T) {
+ testDWARF(t, linkNoCgo)
+}
+
+const testprog = `
+package main
+
+import "fmt"
+{{if .}}import "C"
+{{end}}
+
+func main() {
+ fmt.Printf("main=%p\n", main)
+}
+`
+
+func TestBuildingWindowsGUI(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS != "windows" {
+ t.Skip("skipping windows only test")
+ }
+ tmpdir, err := ioutil.TempDir("", "TestBuildingWindowsGUI")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ src := filepath.Join(tmpdir, "a.go")
+ err = ioutil.WriteFile(src, []byte(`package main; func main() {}`), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exe := filepath.Join(tmpdir, "a.exe")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags", "-H=windowsgui", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("building test executable failed: %s %s", err, out)
+ }
+
+ f, err := Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ const _IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
+
+ switch oh := f.OptionalHeader.(type) {
+ case *OptionalHeader32:
+ if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI {
+ t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, _IMAGE_SUBSYSTEM_WINDOWS_GUI)
+ }
+ case *OptionalHeader64:
+ if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI {
+ t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, _IMAGE_SUBSYSTEM_WINDOWS_GUI)
+ }
+ default:
+ t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)
+ }
+}