diff options
author | Ryan Brown <ribrdb@google.com> | 2016-02-10 16:51:23 -0800 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2016-02-12 19:13:11 +0000 |
commit | 68aa7fb636d236c23be4fa05f87a898b5e7df362 (patch) | |
tree | 753b0abd5f78021a762b2fd39e3128658dc8951b | |
parent | cc0a04d351c69a99f65342ad5822f2bba8b247d0 (diff) | |
download | go-68aa7fb636d236c23be4fa05f87a898b5e7df362.tar.gz go-68aa7fb636d236c23be4fa05f87a898b5e7df362.zip |
cmd/link: fix padding for dwarf aranges on 32 bit platforms.
Fixes #14278
Change-Id: I6a0c1370d595f0573ff0eb933450b1eea41f4bb3
Reviewed-on: https://go-review.googlesource.com/19452
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
-rw-r--r-- | src/cmd/link/internal/ld/dwarf.go | 4 | ||||
-rw-r--r-- | src/runtime/runtime-lldb_test.go | 262 |
2 files changed, 265 insertions, 1 deletions
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 563600d9a2..a96b37a4be 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1946,7 +1946,9 @@ func writepub(ispub func(*DWDie) bool) int64 { */ func writearanges() int64 { sectionstart := Cpos() - headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize))) // don't count unit_length field itself + // The first tuple is aligned to a multiple of the size of a single tuple + // (twice the size of an address) + headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // don't count unit_length field itself for compunit := dwroot.child; compunit != nil; compunit = compunit.link { b := getattr(compunit, DW_AT_low_pc) diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go new file mode 100644 index 0000000000..2bd91c1ec0 --- /dev/null +++ b/src/runtime/runtime-lldb_test.go @@ -0,0 +1,262 @@ +// Copyright 2016 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 runtime_test + +import ( + "debug/elf" + "debug/macho" + "encoding/binary" + "internal/testenv" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var lldbPath string + +func checkLldbPython(t *testing.T) { + cmd := exec.Command("lldb", "-P") + out, err := cmd.CombinedOutput() + if err != nil { + t.Skipf("skipping due to issue running lldb: %v\n%s", err, out) + } + lldbPath = strings.TrimSpace(string(out)) + + cmd = exec.Command("/usr/bin/python2.7", "-c", "import sys;sys.path.append(sys.argv[1]);import lldb; print('go lldb python support')", lldbPath) + out, err = cmd.CombinedOutput() + + if err != nil { + t.Skipf("skipping due to issue running python: %v\n%s", err, out) + } + if string(out) != "go lldb python support\n" { + t.Skipf("skipping due to lack of python lldb support: %s", out) + } + + if runtime.GOOS == "darwin" { + // Try to see if we have debugging permissions. + cmd = exec.Command("/usr/sbin/DevToolsSecurity", "-status") + out, err = cmd.CombinedOutput() + if err != nil { + t.Skipf("DevToolsSecurity failed: %v", err) + } else if !strings.Contains(string(out), "enabled") { + t.Skip(string(out)) + } + cmd = exec.Command("/usr/bin/groups") + out, err = cmd.CombinedOutput() + if err != nil { + t.Skipf("groups failed: %v", err) + } else if !strings.Contains(string(out), "_developer") { + t.Skip("Not in _developer group") + } + } +} + +const lldbHelloSource = ` +package main +import "fmt" +func main() { + mapvar := make(map[string]string,5) + mapvar["abc"] = "def" + mapvar["ghi"] = "jkl" + intvar := 42 + ptrvar := &intvar + fmt.Println("hi") // line 10 + _ = ptrvar +} +` + +const lldbScriptSource = ` +import sys +sys.path.append(sys.argv[1]) +import lldb +import os + +TIMEOUT_SECS = 5 + +debugger = lldb.SBDebugger.Create() +debugger.SetAsync(True) +target = debugger.CreateTargetWithFileAndArch("a.exe", None) +if target: + print "Created target" + main_bp = target.BreakpointCreateByLocation("main.go", 10) + if main_bp: + print "Created breakpoint" + process = target.LaunchSimple(None, None, os.getcwd()) + if process: + print "Process launched" + listener = debugger.GetListener() + process.broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) + while True: + event = lldb.SBEvent() + if listener.WaitForEvent(TIMEOUT_SECS, event): + if lldb.SBProcess.GetRestartedFromEvent(event): + continue + state = process.GetState() + if state in [lldb.eStateUnloaded, lldb.eStateLaunching, lldb.eStateRunning]: + continue + else: + print "Timeout launching" + break + if state == lldb.eStateStopped: + for t in process.threads: + if t.GetStopReason() == lldb.eStopReasonBreakpoint: + print "Hit breakpoint" + frame = t.GetFrameAtIndex(0) + if frame: + if frame.line_entry: + print "Stopped at %s:%d" % (frame.line_entry.file.basename, frame.line_entry.line) + if frame.function: + print "Stopped in %s" % (frame.function.name,) + var = frame.FindVariable('intvar') + if var: + print "intvar = %s" % (var.GetValue(),) + else: + print "no intvar" + else: + print "Process state", state + process.Destroy() +else: + print "Failed to create target a.exe" + +lldb.SBDebugger.Destroy(debugger) +sys.exit() +` + +const expectedLldbOutput = `Created target +Created breakpoint +Process launched +Hit breakpoint +Stopped at main.go:10 +Stopped in main.main +intvar = 42 +` + +func TestLldbPython(t *testing.T) { + testenv.MustHaveGoBuild(t) + if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final { + t.Skip("gdb test can fail with GOROOT_FINAL pending") + } + + checkLldbPython(t) + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) + if err != nil { + t.Fatalf("failed to create file: %v", err) + } + + cmd := exec.Command("go", "build", "-gcflags", "-N -l", "-o", "a.exe") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("building source %v\n%s", err, out) + } + + src = filepath.Join(dir, "script.py") + err = ioutil.WriteFile(src, []byte(lldbScriptSource), 0755) + if err != nil { + t.Fatalf("failed to create script: %v", err) + } + + cmd = exec.Command("/usr/bin/python2.7", "script.py", lldbPath) + cmd.Dir = dir + got, _ := cmd.CombinedOutput() + + if string(got) != expectedLldbOutput { + if strings.Contains(string(got), "Timeout launching") { + t.Skip("Timeout launching") + } + t.Fatalf("Unexpected lldb output:\n%s", got) + } +} + +// Check that aranges are valid even when lldb isn't installed. +func TestDwarfAranges(t *testing.T) { + testenv.MustHaveGoBuild(t) + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) + if err != nil { + t.Fatalf("failed to create file: %v", err) + } + + cmd := exec.Command("go", "build", "-o", "a.exe") + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("building source %v\n%s", err, out) + } + + filename := filepath.Join(dir, "a.exe") + if f, err := elf.Open(filename); err == nil { + sect := f.Section(".debug_aranges") + if sect == nil { + t.Fatal("Missing aranges section") + } + verifyAranges(t, f.ByteOrder, sect.Open()) + } else if f, err := macho.Open(filename); err == nil { + sect := f.Section("__debug_aranges") + if sect == nil { + t.Fatal("Missing aranges section") + } + verifyAranges(t, f.ByteOrder, sect.Open()) + } else { + t.Skip("Not an elf or macho binary.") + } +} + +func verifyAranges(t *testing.T, byteorder binary.ByteOrder, data io.ReadSeeker) { + var header struct { + UnitLength uint32 // does not include the UnitLength field + Version uint16 + Offset uint32 + AddressSize uint8 + SegmentSize uint8 + } + for { + offset, err := data.Seek(0, 1) + if err != nil { + t.Fatalf("Seek error: %v", err) + } + if err = binary.Read(data, byteorder, &header); err == io.EOF { + return + } else if err != nil { + t.Fatalf("Error reading arange header: %v", err) + } + tupleSize := int64(header.SegmentSize) + 2*int64(header.AddressSize) + lastTupleOffset := offset + int64(header.UnitLength) + 4 - tupleSize + if lastTupleOffset%tupleSize != 0 { + t.Fatalf("Invalid arange length %d, (addr %d, seg %d)", header.UnitLength, header.AddressSize, header.SegmentSize) + } + if _, err = data.Seek(lastTupleOffset, 0); err != nil { + t.Fatalf("Seek error: %v", err) + } + buf := make([]byte, tupleSize) + if n, err := data.Read(buf); err != nil || int64(n) < tupleSize { + t.Fatalf("Read error: %v", err) + } + for _, val := range buf { + if val != 0 { + t.Fatalf("Invalid terminator") + } + } + } +} |