提交 9ee21686 编写于 作者: A aarzilli 提交者: Derek Parker

proc: report errors when loading executable on attach

Fixes #940
上级 f553c95e
......@@ -43,6 +43,9 @@ type BinaryInfo struct {
loadModuleDataOnce sync.Once
moduleData []moduleData
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
loadErrMu sync.Mutex
loadErr error
}
var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
......@@ -131,6 +134,16 @@ func (bi *BinaryInfo) Close() error {
return bi.closer.Close()
}
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
bi.loadErrMu.Lock()
bi.loadErr = fmt.Errorf(fmtstr, args...)
bi.loadErrMu.Unlock()
}
func (bi *BinaryInfo) LoadError() error {
return bi.loadErr
}
// ELF ///////////////////////////////////////////////////////////////
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
......@@ -169,18 +182,18 @@ func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section(".debug_frame").Data()
if err != nil {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_frame section: %v", err)
return
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_frame section: %v", err)
return
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
bi.setLoadError("could not find .debug_frame section in binary")
return
}
}
......@@ -196,24 +209,24 @@ func (bi *BinaryInfo) obtainGoSymbolsElf(exe *elf.File, wg *sync.WaitGroup) {
if sec := exe.Section(".gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
bi.setLoadError("could not get .gosymtab section: %v", err)
return
}
}
if sec := exe.Section(".gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
bi.setLoadError("could not get .gopclntab section: %v", err)
return
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
bi.setLoadError("could not get initialize line table: %v", err)
return
}
bi.goSymTable = tab
......@@ -225,13 +238,13 @@ func (bi *BinaryInfo) parseDebugLineInfoElf(exe *elf.File, wg *sync.WaitGroup) {
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := exe.Section(".debug_line").Data()
if err != nil {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_line section: %v", err)
return
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
bi.setLoadError("could not find .debug_line section in binary")
return
}
}
......@@ -246,8 +259,8 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
// offset in libc's TLS block.
symbols, err := exe.Symbols()
if err != nil {
fmt.Println("could not parse ELF symbols", err)
os.Exit(1)
bi.setLoadError("could not parse ELF symbols: %v", err)
return
}
var tlsg *elf.Symbol
for _, symbol := range symbols {
......@@ -325,41 +338,41 @@ func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := debugFrameSec.Data()
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
fmt.Println("could not get .debug_frame section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_frame section: %v", err)
return
}
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_info section: %v", err)
return
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find .debug_frame section in binary")
os.Exit(1)
bi.setLoadError("could not find .debug_frame section in binary")
return
}
}
func (dbp *BinaryInfo) obtainGoSymbolsPE(exe *pe.File, wg *sync.WaitGroup) {
func (bi *BinaryInfo) obtainGoSymbolsPE(exe *pe.File, wg *sync.WaitGroup) {
defer wg.Done()
_, symdat, pclndat, err := pclnPE(exe)
if err != nil {
fmt.Println("could not get Go symbols", err)
os.Exit(1)
bi.setLoadError("could not get Go symbols: %v", err)
return
}
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
bi.setLoadError("could not get initialize line table: %v", err)
return
}
dbp.goSymTable = tab
bi.goSymTable = tab
}
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
......@@ -438,16 +451,16 @@ func (bi *BinaryInfo) parseDebugLineInfoPE(exe *pe.File, wg *sync.WaitGroup) {
if sec := exe.Section(".debug_line"); sec != nil {
debugLine, err := sec.Data()
if err != nil && uint32(len(debugLine)) < sec.Size {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_line section: %v", err)
return
}
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
debugLine = debugLine[:sec.VirtualSize]
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find .debug_line section in binary")
os.Exit(1)
bi.setLoadError("could not find .debug_line section in binary")
return
}
}
......@@ -485,18 +498,18 @@ func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup)
if debugFrameSec != nil && debugInfoSec != nil {
debugFrame, err := exe.Section("__debug_frame").Data()
if err != nil {
fmt.Println("could not get __debug_frame section", err)
os.Exit(1)
bi.setLoadError("could not get __debug_frame section: %v", err)
return
}
dat, err := debugInfoSec.Data()
if err != nil {
fmt.Println("could not get .debug_info section", err)
os.Exit(1)
bi.setLoadError("could not get .debug_info section: %v", err)
return
}
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
} else {
fmt.Println("could not find __debug_frame section in binary")
os.Exit(1)
bi.setLoadError("could not find __debug_frame section in binary")
return
}
}
......@@ -512,24 +525,24 @@ func (bi *BinaryInfo) obtainGoSymbolsMacho(exe *macho.File, wg *sync.WaitGroup)
if sec := exe.Section("__gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
bi.setLoadError("could not get .gosymtab section: %v", err)
return
}
}
if sec := exe.Section("__gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
bi.setLoadError("could not get .gopclntab section: %v", err)
return
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
bi.setLoadError("could not get initialize line table: %v", err)
return
}
bi.goSymTable = tab
......@@ -541,12 +554,12 @@ func (bi *BinaryInfo) parseDebugLineInfoMacho(exe *macho.File, wg *sync.WaitGrou
if sec := exe.Section("__debug_line"); sec != nil {
debugLine, err := exe.Section("__debug_line").Data()
if err != nil {
fmt.Println("could not get __debug_line section", err)
os.Exit(1)
bi.setLoadError("could not get __debug_line section: %v", err)
return
}
bi.lineInfo = line.Parse(debugLine)
} else {
fmt.Println("could not find __debug_line section in binary")
os.Exit(1)
bi.setLoadError("could not find __debug_line section in binary")
return
}
}
......@@ -175,8 +175,14 @@ func OpenCore(corePath, exePath string) (*Process, error) {
}
var wg sync.WaitGroup
p.bi.LoadBinaryInfo(exePath, &wg)
err = p.bi.LoadBinaryInfo(exePath, &wg)
wg.Wait()
if err == nil {
err = p.bi.LoadError()
}
if err != nil {
return nil, err
}
for _, th := range p.core.Threads {
p.currentThread = th
......
......@@ -132,7 +132,7 @@ func withCoreFile(t *testing.T, name, args string) *Process {
if err != nil {
t.Fatal(err)
}
fix := test.BuildFixture(name)
fix := test.BuildFixture(name, 0)
bashCmd := fmt.Sprintf("cd %v && ulimit -c unlimited && GOTRACEBACK=crash %v %s", tempDir, fix.Path, args)
exec.Command("bash", "-c", bashCmd).Run()
cores, err := filepath.Glob(path.Join(tempDir, "core*"))
......
......@@ -263,11 +263,14 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
var wg sync.WaitGroup
err = p.bi.LoadBinaryInfo(path, &wg)
wg.Wait()
if err == nil {
err = p.bi.LoadError()
}
if err != nil {
conn.Close()
return err
}
wg.Wait()
// None of the stubs we support returns the value of fs_base or gs_base
// along with the registers, therefore we have to resort to executing a MOV
......
......@@ -13,7 +13,7 @@ import (
)
func withTestRecording(name string, t testing.TB, fn func(p *gdbserial.Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
fixture := protest.BuildFixture(name, 0)
protest.MustHaveRecordingAllowed(t)
if path, _ := exec.LookPath("rr"); path == "" {
t.Skip("test skipped, rr not found")
......
......@@ -163,10 +163,13 @@ func (dbp *Process) LoadInformation(path string) error {
wg.Add(1)
go dbp.loadProcessInformation(&wg)
dbp.bi.LoadBinaryInfo(path, &wg)
err := dbp.bi.LoadBinaryInfo(path, &wg)
wg.Wait()
if err == nil {
err = dbp.bi.LoadError()
}
return nil
return err
}
// RequestManualStop sets the `halt` flag and
......@@ -388,11 +391,11 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
err := dbp.LoadInformation(path)
if err != nil {
return nil, err
return dbp, err
}
if err := dbp.updateThreadList(); err != nil {
return nil, err
return dbp, err
}
// selectedGoroutine can not be set correctly by the call to updateThreadList
......
......@@ -152,7 +152,12 @@ func Attach(pid int) (*Process, error) {
return nil, err
}
return initializeDebugProcess(dbp, "")
dbp, err = initializeDebugProcess(dbp, "")
if err != nil {
dbp.Detach(false)
return nil, err
}
return dbp, nil
}
// Kill kills the process.
......
......@@ -90,7 +90,12 @@ func Attach(pid int) (*Process, error) {
return nil, err
}
return initializeDebugProcess(dbp, "")
dbp, err = initializeDebugProcess(dbp, "")
if err != nil {
dbp.Detach(false)
return nil, err
}
return dbp, nil
}
// Kill kills the target process.
......
......@@ -162,7 +162,12 @@ func Attach(pid int) (*Process, error) {
if err != nil {
return nil, err
}
return newDebugProcess(New(pid), exepath)
dbp, err := newDebugProcess(New(pid), exepath)
if err != nil {
dbp.Detach(false)
return nil, err
}
return dbp, nil
}
// Kill kills the process.
......
......@@ -48,7 +48,7 @@ func TestMain(m *testing.M) {
}
func withTestProcess(name string, t testing.TB, fn func(p proc.Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
fixture := protest.BuildFixture(name, 0)
var p proc.Process
var err error
var tracedir string
......@@ -81,7 +81,7 @@ func withTestProcess(name string, t testing.TB, fn func(p proc.Process, fixture
}
func withTestProcessArgs(name string, t testing.TB, wd string, fn func(p proc.Process, fixture protest.Fixture), args []string) {
fixture := protest.BuildFixture(name)
fixture := protest.BuildFixture(name, 0)
var p proc.Process
var err error
var tracedir string
......@@ -2794,7 +2794,7 @@ func TestAttachDetach(t *testing.T) {
if testBackend == "rr" {
return
}
fixture := protest.BuildFixture("testnextnethttp")
fixture := protest.BuildFixture("testnextnethttp", 0)
cmd := exec.Command(fixture.Path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
......@@ -3085,3 +3085,65 @@ func TestShadowedFlag(t *testing.T) {
}
})
}
func TestAttachStripped(t *testing.T) {
if testBackend == "lldb" && runtime.GOOS == "linux" {
bs, _ := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
if bs == nil || strings.TrimSpace(string(bs)) != "0" {
t.Logf("can not run TestAttachStripped: %v\n", bs)
return
}
}
if testBackend == "rr" {
return
}
if runtime.GOOS == "darwin" {
t.Log("-s does not produce stripped executables on macOS")
return
}
fixture := protest.BuildFixture("testnextnethttp", protest.LinkStrip)
cmd := exec.Command(fixture.Path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
assertNoError(cmd.Start(), t, "starting fixture")
// wait for testnextnethttp to start listening
t0 := time.Now()
for {
conn, err := net.Dial("tcp", "localhost:9191")
if err == nil {
conn.Close()
break
}
time.Sleep(50 * time.Millisecond)
if time.Since(t0) > 10*time.Second {
t.Fatal("fixture did not start")
}
}
var p proc.Process
var err error
switch testBackend {
case "native":
p, err = native.Attach(cmd.Process.Pid)
case "lldb":
path := ""
if runtime.GOOS == "darwin" {
path = fixture.Path
}
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path)
default:
t.Fatalf("unknown backend %q", testBackend)
}
t.Logf("error is %v", err)
if err == nil {
p.Detach(true)
t.Fatalf("expected error after attach, got nothing")
} else {
cmd.Process.Kill()
}
os.Remove(fixture.Path)
}
......@@ -38,8 +38,14 @@ func FindFixturesDir() string {
return fixturesDir
}
func BuildFixture(name string) Fixture {
if f, ok := Fixtures[name]; ok {
type BuildFlags uint32
const (
LinkStrip = 1 << iota
)
func BuildFixture(name string, flags BuildFlags) Fixture {
if f, ok := Fixtures[name]; ok && flags == 0 {
return f
}
......@@ -62,6 +68,9 @@ func BuildFixture(name string) Fixture {
// Work-around for https://github.com/golang/go/issues/13154
buildFlags = append(buildFlags, "-ldflags=-linkmode internal")
}
if flags&LinkStrip != 0 {
buildFlags = append(buildFlags, "-ldflags=-s")
}
buildFlags = append(buildFlags, "-gcflags=-N -l", "-o", tmpfile)
if path != "" {
buildFlags = append(buildFlags, name+".go")
......@@ -80,7 +89,13 @@ func BuildFixture(name string) Fixture {
source, _ := filepath.Abs(path)
source = filepath.ToSlash(source)
Fixtures[name] = Fixture{Name: name, Path: tmpfile, Source: source}
fixture := Fixture{Name: name, Path: tmpfile, Source: source}
if flags != 0 {
return fixture
}
Fixtures[name] = fixture
return Fixtures[name]
}
......
......@@ -104,7 +104,7 @@ func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
defer listener.Close()
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{test.BuildFixture(name).Path},
ProcessArgs: []string{test.BuildFixture(name, 0).Path},
Backend: testBackend,
}, false)
if err := server.Run(); err != nil {
......
......@@ -31,7 +31,7 @@ func withTestClient1(name string, t *testing.T, fn func(c *rpc1.RPCClient)) {
defer listener.Close()
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{protest.BuildFixture(name).Path},
ProcessArgs: []string{protest.BuildFixture(name, 0).Path},
Backend: testBackend,
}, false)
if err := server.Run(); err != nil {
......@@ -740,7 +740,7 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
if arg.Name != "i" {
continue
}
t.Logf("frame %d, variable i is %v\n", i, arg)
t.Logf("frame %d, variable i is %v\n", i, arg)
argn, err := strconv.Atoi(arg.Value)
if err == nil {
found[argn] = true
......
......@@ -48,7 +48,7 @@ func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
defer listener.Close()
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{protest.BuildFixture(name).Path},
ProcessArgs: []string{protest.BuildFixture(name, 0).Path},
Backend: testBackend,
}, false)
if err := server.Run(); err != nil {
......
......@@ -106,7 +106,7 @@ func setVariable(p proc.Process, symbol, value string) error {
}
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
fixture := protest.BuildFixture(name)
fixture := protest.BuildFixture(name, 0)
var p proc.Process
var err error
var tracedir string
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册