proc,proc/native,proc/gdbserial: initial plugin support (#1413)
Adds initial support for plugins, this is only the code needed to keep track of loaded plugins on linux (both native and gdbserial backend). It does not actually implement support for debugging plugins on linux. Updates #865
This commit is contained in:
parent
09c92c75b9
commit
af1ffc8504
@ -24,6 +24,7 @@ Command | Description
|
||||
[goroutine](#goroutine) | Shows or changes current goroutine
|
||||
[goroutines](#goroutines) | List program goroutines.
|
||||
[help](#help) | Prints the help message.
|
||||
[libraries](#libraries) | List loaded dynamic libraries
|
||||
[list](#list) | Show source code.
|
||||
[locals](#locals) | Print local variables.
|
||||
[next](#next) | Step over to next source line.
|
||||
@ -266,6 +267,10 @@ Type "help" followed by the name of a command for more information about it.
|
||||
|
||||
Aliases: h
|
||||
|
||||
## libraries
|
||||
List loaded dynamic libraries
|
||||
|
||||
|
||||
## list
|
||||
Show source code.
|
||||
|
||||
|
9
_fixtures/internal/pluginsupport/pluginsupport.go
Normal file
9
_fixtures/internal/pluginsupport/pluginsupport.go
Normal file
@ -0,0 +1,9 @@
|
||||
package pluginsupport
|
||||
|
||||
type Something interface {
|
||||
Callback(int) int
|
||||
}
|
||||
|
||||
type SomethingElse interface {
|
||||
Callback2(int, int) float64
|
||||
}
|
13
_fixtures/plugin1/plugin1.go
Normal file
13
_fixtures/plugin1/plugin1.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Fn1() string {
|
||||
return "hello"
|
||||
}
|
||||
|
||||
func HelloFn(n int) string {
|
||||
n++
|
||||
s := fmt.Sprintf("hello%d", n)
|
||||
return s
|
||||
}
|
33
_fixtures/plugin2/plugin2.go
Normal file
33
_fixtures/plugin2/plugin2.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-delve/delve/_fixtures/internal/pluginsupport"
|
||||
)
|
||||
|
||||
func Fn2() string {
|
||||
return "world"
|
||||
}
|
||||
|
||||
type asomethingelse struct {
|
||||
x, y float64
|
||||
}
|
||||
|
||||
func (a *asomethingelse) Callback2(n, m int) float64 {
|
||||
r := a.x + 2*a.y
|
||||
r += float64(n) / float64(m)
|
||||
return r
|
||||
}
|
||||
|
||||
func TypesTest(s pluginsupport.Something) pluginsupport.SomethingElse {
|
||||
if A != nil {
|
||||
aIsNotNil(fmt.Sprintf("%s", A))
|
||||
}
|
||||
return &asomethingelse{1.0, float64(s.Callback(2))}
|
||||
}
|
||||
|
||||
var A interface{}
|
||||
|
||||
func aIsNotNil(str string) {
|
||||
// nothing here
|
||||
}
|
36
_fixtures/plugintest.go
Normal file
36
_fixtures/plugintest.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"plugin"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
plug1, err := plugin.Open(os.Args[1])
|
||||
must(err)
|
||||
|
||||
runtime.Breakpoint()
|
||||
|
||||
plug2, err := plugin.Open(os.Args[2])
|
||||
must(err)
|
||||
|
||||
runtime.Breakpoint()
|
||||
|
||||
fn1, err := plug1.Lookup("Fn1")
|
||||
must(err)
|
||||
fn2, err := plug2.Lookup("Fn2")
|
||||
must(err)
|
||||
|
||||
a := fn1.(func() string)()
|
||||
b := fn2.(func() string)()
|
||||
|
||||
fmt.Println(plug1, plug2, fn1, fn2, a, b)
|
||||
}
|
44
_fixtures/plugintest2.go
Normal file
44
_fixtures/plugintest2.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-delve/delve/_fixtures/internal/pluginsupport"
|
||||
"os"
|
||||
"plugin"
|
||||
)
|
||||
|
||||
type asomething struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func (a *asomething) Callback(n int) int {
|
||||
return a.n + n
|
||||
}
|
||||
|
||||
func (a *asomething) String() string {
|
||||
return "success"
|
||||
}
|
||||
|
||||
var ExeGlobal = &asomething{2}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
plug1, err := plugin.Open(os.Args[1])
|
||||
must(err)
|
||||
plug2, err := plugin.Open(os.Args[2])
|
||||
must(err)
|
||||
fn1iface, err := plug1.Lookup("HelloFn")
|
||||
must(err)
|
||||
fn2iface, err := plug2.Lookup("TypesTest")
|
||||
must(err)
|
||||
fn1 := fn1iface.(func(int) string)
|
||||
fn2 := fn2iface.(func(pluginsupport.Something) pluginsupport.SomethingElse)
|
||||
a := fn1(3)
|
||||
b := fn2(&asomething{2})
|
||||
fmt.Println(a, b, ExeGlobal)
|
||||
}
|
@ -26,7 +26,8 @@ import (
|
||||
"github.com/go-delve/delve/pkg/goversion"
|
||||
)
|
||||
|
||||
// BinaryInfo holds information on the binary being executed.
|
||||
// BinaryInfo holds information on the binaries being executed (this
|
||||
// includes both the executable and also any loaded libraries).
|
||||
type BinaryInfo struct {
|
||||
// Path on disk of the binary being executed.
|
||||
Path string
|
||||
@ -43,6 +44,12 @@ type BinaryInfo struct {
|
||||
// LookupFunc maps function names to a description of the function.
|
||||
LookupFunc map[string]*Function
|
||||
|
||||
// Images is a list of loaded shared libraries (also known as
|
||||
// shared objects on linux or DLLs on windws).
|
||||
Images []*Image
|
||||
|
||||
ElfDynamicSection ElfDynamicSection
|
||||
|
||||
lastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
closer io.Closer
|
||||
@ -289,6 +296,12 @@ type buildIDHeader struct {
|
||||
Type uint32
|
||||
}
|
||||
|
||||
// ElfDynamicSection describes the .dynamic section of an ELF executable.
|
||||
type ElfDynamicSection struct {
|
||||
Addr uint64 // relocated address of where the .dynamic section is mapped in memory
|
||||
Size uint64 // size of the .dynamic section of the executable
|
||||
}
|
||||
|
||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
||||
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||
@ -412,6 +425,26 @@ func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Image represents a loaded library file (shared object on linux, DLL on windows).
|
||||
type Image struct {
|
||||
Path string
|
||||
addr uint64
|
||||
}
|
||||
|
||||
// AddImage adds the specified image to bi.
|
||||
func (bi *BinaryInfo) AddImage(path string, addr uint64) {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return
|
||||
}
|
||||
for _, image := range bi.Images {
|
||||
if image.Path == path && image.addr == addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
//TODO(aarzilli): actually load informations about the image here
|
||||
bi.Images = append(bi.Images, &Image{Path: path, addr: addr})
|
||||
}
|
||||
|
||||
// Close closes all internal readers.
|
||||
func (bi *BinaryInfo) Close() error {
|
||||
if bi.sepDebugCloser != nil {
|
||||
@ -671,6 +704,11 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInf
|
||||
}
|
||||
}
|
||||
|
||||
if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
|
||||
bi.ElfDynamicSection.Addr = dynsec.Addr + bi.staticBase
|
||||
bi.ElfDynamicSection.Size = dynsec.Size
|
||||
}
|
||||
|
||||
dwarfFile := elfFile
|
||||
|
||||
bi.dwarf, err = elfFile.DWARF()
|
||||
|
@ -702,6 +702,12 @@ continueLoop:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.BinInfo().GOOS == "linux" {
|
||||
if err := linutil.ElfUpdateSharedObjects(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.setCurrentBreakpoints(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
172
pkg/proc/linutil/dynamic.go
Normal file
172
pkg/proc/linutil/dynamic.go
Normal file
@ -0,0 +1,172 @@
|
||||
package linutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
const (
|
||||
maxNumLibraries = 1000000 // maximum number of loaded libraries, to avoid loading forever on corrupted memory
|
||||
maxLibraryPathLength = 1000000 // maximum length for the path of a library, to avoid loading forever on corrupted memory
|
||||
)
|
||||
|
||||
var ErrTooManyLibraries = errors.New("number of loaded libraries exceeds maximum")
|
||||
|
||||
const (
|
||||
_DT_NULL = 0 // DT_NULL as defined by SysV ABI specification
|
||||
_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
|
||||
)
|
||||
|
||||
// dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
|
||||
func dynamicSearchDebug(p proc.Process) (uint64, error) {
|
||||
bi := p.BinInfo()
|
||||
mem := p.CurrentThread()
|
||||
|
||||
dynbuf := make([]byte, bi.ElfDynamicSection.Size)
|
||||
_, err := mem.ReadMemory(dynbuf, uintptr(bi.ElfDynamicSection.Addr))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rd := bytes.NewReader(dynbuf)
|
||||
|
||||
for {
|
||||
var tag, val uint64
|
||||
if err := binary.Read(rd, binary.LittleEndian, &tag); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := binary.Read(rd, binary.LittleEndian, &val); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch tag {
|
||||
case _DT_NULL:
|
||||
return 0, nil
|
||||
case _DT_DEBUG:
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hard-coded offsets of the fields of the r_debug and link_map structs, see
|
||||
// /usr/include/elf/link.h for a full description of those structs.
|
||||
const (
|
||||
_R_DEBUG_MAP_OFFSET = 8
|
||||
_LINK_MAP_ADDR_OFFSET = 0 // offset of link_map.l_addr field (base address shared object is loaded at)
|
||||
_LINK_MAP_NAME_OFFSET = 8 // offset of link_map.l_name field (absolute file name object was found in)
|
||||
_LINK_MAP_LD = 16 // offset of link_map.l_ld field (dynamic section of the shared object)
|
||||
_LINK_MAP_NEXT = 24 // offset of link_map.l_next field
|
||||
_LINK_MAP_PREV = 32 // offset of link_map.l_prev field
|
||||
)
|
||||
|
||||
func readPtr(p proc.Process, addr uint64) (uint64, error) {
|
||||
ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
|
||||
_, err := p.CurrentThread().ReadMemory(ptrbuf, uintptr(addr))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.LittleEndian.Uint64(ptrbuf), nil
|
||||
}
|
||||
|
||||
type linkMap struct {
|
||||
addr uint64
|
||||
name string
|
||||
ld uint64
|
||||
next, prev uint64
|
||||
}
|
||||
|
||||
func readLinkMapNode(p proc.Process, r_map uint64) (*linkMap, error) {
|
||||
bi := p.BinInfo()
|
||||
|
||||
var lm linkMap
|
||||
var ptrs [5]uint64
|
||||
for i := range ptrs {
|
||||
var err error
|
||||
ptrs[i], err = readPtr(p, r_map+uint64(bi.Arch.PtrSize()*i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
lm.addr = ptrs[0]
|
||||
var err error
|
||||
lm.name, err = readCString(p, ptrs[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lm.ld = ptrs[2]
|
||||
lm.next = ptrs[3]
|
||||
lm.prev = ptrs[4]
|
||||
return &lm, nil
|
||||
}
|
||||
|
||||
func readCString(p proc.Process, addr uint64) (string, error) {
|
||||
if addr == 0 {
|
||||
return "", nil
|
||||
}
|
||||
mem := p.CurrentThread()
|
||||
buf := make([]byte, 1)
|
||||
r := []byte{}
|
||||
for {
|
||||
if len(r) > maxLibraryPathLength {
|
||||
return "", fmt.Errorf("error reading libraries: string too long (%d)", len(r))
|
||||
}
|
||||
_, err := mem.ReadMemory(buf, uintptr(addr))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if buf[0] == 0 {
|
||||
break
|
||||
}
|
||||
r = append(r, buf[0])
|
||||
addr++
|
||||
}
|
||||
return string(r), nil
|
||||
}
|
||||
|
||||
// ElfUpdateSharedObjects reads the list of dynamic libraries loaded by the
|
||||
// dynamic linker from the .dynamic section and uses it to update p.BinInfo().
|
||||
// See the SysV ABI for a description of how the .dynamic section works:
|
||||
// http://www.sco.com/developers/gabi/latest/contents.html
|
||||
func ElfUpdateSharedObjects(p proc.Process) error {
|
||||
bi := p.BinInfo()
|
||||
if bi.ElfDynamicSection.Addr == 0 {
|
||||
// no dynamic section, therefore nothing to do here
|
||||
return nil
|
||||
}
|
||||
debugAddr, err := dynamicSearchDebug(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if debugAddr == 0 {
|
||||
// no DT_DEBUG entry
|
||||
return nil
|
||||
}
|
||||
|
||||
r_map, err := readPtr(p, debugAddr+_R_DEBUG_MAP_OFFSET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
libs := []string{}
|
||||
|
||||
for {
|
||||
if r_map == 0 {
|
||||
break
|
||||
}
|
||||
if len(libs) > maxNumLibraries {
|
||||
return ErrTooManyLibraries
|
||||
}
|
||||
lm, err := readLinkMapNode(p, r_map)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.AddImage(lm.name, lm.addr)
|
||||
libs = append(libs, lm.name)
|
||||
r_map = lm.next
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -233,7 +233,7 @@ func (dbp *Process) updateThreadList() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return linutil.ElfUpdateSharedObjects(dbp)
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
@ -453,6 +453,10 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := linutil.ElfUpdateSharedObjects(dbp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set breakpoints on all threads
|
||||
for _, th := range dbp.threads {
|
||||
if th.CurrentBreakpoint.Breakpoint == nil {
|
||||
|
@ -4214,3 +4214,40 @@ func TestDeadlockBreakpoint(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestListImages(t *testing.T) {
|
||||
pluginFixtures := protest.WithPlugins(t, "plugin1/", "plugin2/")
|
||||
|
||||
withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, 0, func(p proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(proc.Continue(p), t, "first continue")
|
||||
plugin1Found := false
|
||||
t.Logf("Libraries before:")
|
||||
for _, image := range p.BinInfo().Images {
|
||||
t.Logf("\t%#v", image)
|
||||
if image.Path == pluginFixtures[0].Path {
|
||||
plugin1Found = true
|
||||
}
|
||||
}
|
||||
if !plugin1Found {
|
||||
t.Fatalf("Could not find plugin1")
|
||||
}
|
||||
assertNoError(proc.Continue(p), t, "second continue")
|
||||
plugin1Found, plugin2Found := false, false
|
||||
t.Logf("Libraries after:")
|
||||
for _, image := range p.BinInfo().Images {
|
||||
t.Logf("\t%#v", image)
|
||||
switch image.Path {
|
||||
case pluginFixtures[0].Path:
|
||||
plugin1Found = true
|
||||
case pluginFixtures[1].Path:
|
||||
plugin2Found = true
|
||||
}
|
||||
}
|
||||
if !plugin1Found {
|
||||
t.Fatalf("Could not find plugin1")
|
||||
}
|
||||
if !plugin2Found {
|
||||
t.Fatalf("Could not find plugin2")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ type Fixture struct {
|
||||
Path string
|
||||
// Source is the absolute path of the test binary source.
|
||||
Source string
|
||||
// BuildDir is the directory where the build command was run.
|
||||
BuildDir string
|
||||
}
|
||||
|
||||
// FixtureKey holds the name and builds flags used for a test fixture.
|
||||
@ -72,6 +74,7 @@ const (
|
||||
// EnableDWZCompression will enable DWZ compression of DWARF sections.
|
||||
EnableDWZCompression
|
||||
BuildModePIE
|
||||
BuildModePlugin
|
||||
)
|
||||
|
||||
// BuildFixture will compile the fixture 'name' using the provided build flags.
|
||||
@ -126,6 +129,9 @@ func BuildFixture(name string, flags BuildFlags) Fixture {
|
||||
if flags&BuildModePIE != 0 {
|
||||
buildFlags = append(buildFlags, "-buildmode=pie")
|
||||
}
|
||||
if flags&BuildModePlugin != 0 {
|
||||
buildFlags = append(buildFlags, "-buildmode=plugin")
|
||||
}
|
||||
if ver.AfterOrEqual(goversion.GoVersion{1, 11, -1, 0, 0, ""}) {
|
||||
if flags&EnableDWZCompression != 0 {
|
||||
buildFlags = append(buildFlags, "-ldflags=-compressdwarf=false")
|
||||
@ -161,7 +167,9 @@ func BuildFixture(name string, flags BuildFlags) Fixture {
|
||||
source = strings.Replace(sympath, "\\", "/", -1)
|
||||
}
|
||||
|
||||
fixture := Fixture{Name: name, Path: tmpfile, Source: source}
|
||||
absdir, _ := filepath.Abs(dir)
|
||||
|
||||
fixture := Fixture{Name: name, Path: tmpfile, Source: source, BuildDir: absdir}
|
||||
|
||||
Fixtures[fk] = fixture
|
||||
return Fixtures[fk]
|
||||
@ -312,3 +320,22 @@ func DefaultTestBackend(testBackend *string) {
|
||||
*testBackend = "native"
|
||||
}
|
||||
}
|
||||
|
||||
// WithPlugins builds the fixtures in plugins as plugins and returns them.
|
||||
// The test calling WithPlugins will be skipped if the current combination
|
||||
// of OS, architecture and version of GO doesn't support plugins or
|
||||
// debugging plugins.
|
||||
func WithPlugins(t *testing.T, plugins ...string) []Fixture {
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 12) {
|
||||
t.Skip("versions of Go before 1.12 do not include debug information in packages that import plugin (or they do but it's wrong)")
|
||||
}
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("only supported on linux")
|
||||
}
|
||||
|
||||
r := make([]Fixture, len(plugins))
|
||||
for i := range plugins {
|
||||
r[i] = BuildFixture(plugins[i], BuildModePlugin)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
@ -343,6 +343,7 @@ Defines <alias> as an alias to <command> or removes an alias.`},
|
||||
edit [locspec]
|
||||
|
||||
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
|
||||
{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
|
||||
}
|
||||
|
||||
if client == nil || client.Recorded() {
|
||||
@ -1581,6 +1582,18 @@ func disassCommand(t *Term, ctx callContext, args string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func libraries(t *Term, ctx callContext, args string) error {
|
||||
libs, err := t.client.ListDynamicLibraries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d := digits(len(libs))
|
||||
for i := range libs {
|
||||
fmt.Printf("%"+strconv.Itoa(d)+"d. %s\n", i, libs[i].Path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func digits(n int) int {
|
||||
if n <= 0 {
|
||||
return 1
|
||||
|
@ -314,3 +314,7 @@ func ConvertRegisters(in []proc.Register) (out []Register) {
|
||||
func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
|
||||
return Checkpoint(in)
|
||||
}
|
||||
|
||||
func ConvertImage(image *proc.Image) Image {
|
||||
return Image{Path: image.Path}
|
||||
}
|
||||
|
@ -442,3 +442,8 @@ type Checkpoint struct {
|
||||
When string
|
||||
Where string
|
||||
}
|
||||
|
||||
// Image represents a loaded shared object (go plugin or shared library)
|
||||
type Image struct {
|
||||
Path string
|
||||
}
|
||||
|
@ -138,6 +138,9 @@ type Client interface {
|
||||
// IsMulticlien returns true if the headless instance is multiclient.
|
||||
IsMulticlient() bool
|
||||
|
||||
// ListDynamicLibraries returns a list of loaded dynamic libraries.
|
||||
ListDynamicLibraries() ([]api.Image, error)
|
||||
|
||||
// Disconnect closes the connection to the server without sending a Detach request first.
|
||||
// If cont is true a continue command will be sent instead.
|
||||
Disconnect(cont bool) error
|
||||
|
@ -1118,6 +1118,18 @@ func (d *Debugger) ClearCheckpoint(id int) error {
|
||||
return d.target.ClearCheckpoint(id)
|
||||
}
|
||||
|
||||
// ListDynamicLibraries returns a list of loaded dynamic libraries.
|
||||
func (d *Debugger) ListDynamicLibraries() []api.Image {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
bi := d.target.BinInfo()
|
||||
r := make([]api.Image, len(bi.Images))
|
||||
for i := range bi.Images {
|
||||
r[i] = api.ConvertImage(bi.Images[i])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func go11DecodeErrorCheck(err error) error {
|
||||
if _, isdecodeerr := err.(dwarf.DecodeError); !isdecodeerr {
|
||||
return err
|
||||
|
@ -395,6 +395,12 @@ func (c *RPCClient) Disconnect(cont bool) error {
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
|
||||
var out ListDynamicLibrariesOut
|
||||
c.call("ListDynamicLibraries", ListDynamicLibrariesIn{}, &out)
|
||||
return out.List, nil
|
||||
}
|
||||
|
||||
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
||||
return c.client.Call("RPCServer."+method, args, reply)
|
||||
}
|
||||
|
@ -694,3 +694,17 @@ func (s *RPCServer) FunctionReturnLocations(in FunctionReturnLocationsIn, out *F
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListDynamicLibrariesIn holds the arguments of ListDynamicLibraries
|
||||
type ListDynamicLibrariesIn struct {
|
||||
}
|
||||
|
||||
// ListDynamicLibrariesOut holds the return values of ListDynamicLibraries
|
||||
type ListDynamicLibrariesOut struct {
|
||||
List []api.Image
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDynamicLibrariesOut) error {
|
||||
out.List = s.debugger.ListDynamicLibraries()
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user