ogle: add name->PC lookup for functions
The symbol table we can access has functions but no data, so until
that is resolved we can only evaluate function names.
LGTM=nigeltao
R=nigeltao
https://golang.org/cl/77580045
diff --git a/cmd/ogleproxy/main.go b/cmd/ogleproxy/main.go
index 3ce343a..6e1b6ea 100644
--- a/cmd/ogleproxy/main.go
+++ b/cmd/ogleproxy/main.go
@@ -29,15 +29,14 @@
flag.Usage()
os.Exit(2)
}
- fd, err := os.Open(*textFlag)
+ s, err := server.New(*textFlag)
if err != nil {
- fmt.Printf("OGLE BAD\n%s\n", err)
+ fmt.Println("OGLE BAD\n%s\n", err)
os.Exit(2)
}
- fd.Close()
- err = rpc.Register(&server.Server{})
+ err = rpc.Register(s)
if err != nil {
- fmt.Printf("OGLE BAD\n%s\n", err)
+ fmt.Println("OGLE BAD\n%s\n", err)
os.Exit(2)
}
fmt.Println("OGLE OK")
diff --git a/program/client/client.go b/program/client/client.go
index fea7c7f..3b3a3df 100644
--- a/program/client/client.go
+++ b/program/client/client.go
@@ -182,6 +182,15 @@
panic("unimplemented")
}
+func (p *Program) Eval(expr string) (string, error) {
+ req := proxyrpc.EvalRequest{
+ Expr: expr,
+ }
+ var resp proxyrpc.EvalResponse
+ err := p.client.Call("Server.Eval", &req, &resp)
+ return resp.Result, err
+}
+
// File implements the program.File interface, providing access
// to file-like resources associated with the target program.
type File struct {
diff --git a/program/program.go b/program/program.go
index 55664b4..d27da54 100644
--- a/program/program.go
+++ b/program/program.go
@@ -63,12 +63,16 @@
// "main.main" Start of function
// "main.go:23" Line number
// (more to follow; may want an expression grammar)
- // It is an OK if two breakpoints evaluate to the same PC. (TODO: verify.)
+ // It is OK if two breakpoints evaluate to the same PC. (TODO: verify.)
Breakpoint(address string) error
// DeleteBreakpoint removes the breakpoint at the specified
// address.
DeleteBreakpoint(address string) error
+
+ // Eval evaluates the expression (typically an address) and returns
+ // its string representation.
+ Eval(expr string) (string, error)
}
// The File interface provides access to file-like resources in the program.
diff --git a/program/proxyrpc/proxyrpc.go b/program/proxyrpc/proxyrpc.go
index 0017b8b..b2a2977 100644
--- a/program/proxyrpc/proxyrpc.go
+++ b/program/proxyrpc/proxyrpc.go
@@ -48,3 +48,11 @@
type OpenResponse struct {
FD int
}
+
+type EvalRequest struct {
+ Expr string
+}
+
+type EvalResponse struct {
+ Result string
+}
diff --git a/program/server/server.go b/program/server/server.go
index fcd54d7..6160966 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -10,15 +10,106 @@
"fmt"
"os"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+
+ "code.google.com/p/ogle/gosym"
"code.google.com/p/ogle/program"
"code.google.com/p/ogle/program/proxyrpc"
)
type Server struct {
+ executable string // Name of executable.
+ lines *gosym.LineTable
+ symbols *gosym.Table
// TODO: Needs mutex.
files []*file // Index == file descriptor.
}
+// New parses the executable and builds local data structures for answering requests.
+// It returns a Server ready to serve requests about the executable.
+func New(executable string) (*Server, error) {
+ fd, err := os.Open(executable)
+ if err != nil {
+ return nil, err
+ }
+ defer fd.Close()
+ textStart, symtab, pclntab, err := loadTables(fd)
+ if err != nil {
+ return nil, err
+ }
+ lines := gosym.NewLineTable(pclntab, textStart)
+ symbols, err := gosym.NewTable(symtab, lines)
+ if err != nil {
+ return nil, err
+ }
+ srv := &Server{
+ executable: executable,
+ lines: lines,
+ symbols: symbols,
+ }
+ return srv, nil
+}
+
+// This function is copied from $GOROOT/src/cmd/addr2line/main.go.
+// TODO: Make this architecture-defined? Push into gosym?
+// TODO: Why is the .gosymtab always empty?
+func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
+ if obj, err := elf.NewFile(f); err == nil {
+ if sect := obj.Section(".text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := obj.Section(".gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := obj.Section(".gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+ }
+
+ if obj, err := macho.NewFile(f); err == nil {
+ if sect := obj.Section("__text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := obj.Section("__gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := obj.Section("__gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+ }
+
+ if obj, err := pe.NewFile(f); err == nil {
+ if sect := obj.Section(".text"); sect != nil {
+ textStart = uint64(sect.VirtualAddress)
+ }
+ if sect := obj.Section(".gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := obj.Section(".gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+ }
+
+ return 0, nil, nil, fmt.Errorf("unrecognized binary format")
+}
+
type file struct {
mode string
index int
@@ -80,5 +171,16 @@
// Remove it regardless
s.files[fd] = nil
return err
+}
+func (s *Server) Eval(req *proxyrpc.EvalRequest, resp *proxyrpc.EvalResponse) error {
+ expr := req.Expr
+ // Simple version: Turn symbol into address. Must be function.
+ // TODO: Why is Table.Syms always empty?
+ sym := s.symbols.LookupFunc(expr)
+ if sym == nil {
+ return fmt.Errorf("symbol %q not found")
+ }
+ resp.Result = fmt.Sprintf("%#x", sym.Value)
+ return nil
}