Commit 42113849 authored by Austin Clements's avatar Austin Clements

Switch ogle to in-tree gosym package. Delete my private sym

package.  If a Sym is a function symbol, include a reference
to the Func so it's easily accessible when you're traversing
the list of all symbols.  This diff is more interesting than
the proc switch because the gosym interface differs from the
old sym interface.

R=rsc
APPROVED=rsc
DELTA=1957  (34 added, 1868 deleted, 55 changed)
OCL=34969
CL=35008
parent 60098a41
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package gosym implements access to the Go symbol
// and line number tables embedded in Go binaries generated
// by the gc compilers.
......@@ -31,6 +30,8 @@ type Sym struct {
Type byte;
Name string;
GoType uint64;
// If this symbol if a function symbol, the corresponding Func
Func *Func;
}
// Static returns whether this symbol is static (not visible outside its file).
......@@ -287,6 +288,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) {
n := len(t.Funcs);
t.Funcs = t.Funcs[0:n+1];
fn := &t.Funcs[n];
sym.Func = fn;
fn.Params = make([]*Sym, 0, np);
fn.Locals = make([]*Sym, 0, na);
fn.Sym = sym;
......
......@@ -6,6 +6,7 @@ package ogle
import (
"bufio";
"debug/elf";
"debug/proc";
"eval";
"fmt";
......@@ -14,7 +15,6 @@ import (
"os";
"strconv";
"strings";
"sym";
)
var world *eval.World;
......@@ -177,7 +177,7 @@ func cmdLoad(args []byte) os.Error {
return err;
}
defer f.Close();
elf, err := sym.NewElf(f);
elf, err := elf.NewFile(f);
if err != nil {
tproc.Detach();
return err;
......@@ -366,13 +366,9 @@ func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
t.Abort(NoCurrentGoroutine{});
}
name := args[0].(eval.StringValue).Get(t);
s := curProc.syms.SymFromName(name);
if s == nil {
t.Abort(UsageError("symbol " + name + " not defined"));
fn := curProc.syms.LookupFunc(name);
if fn == nil {
t.Abort(UsageError("no such function " + name));
}
fn, ok := s.(*sym.TextSym);
if !ok {
t.Abort(UsageError("symbol " + name + " is not a function"));
}
curProc.OnBreakpoint(proc.Word(fn.Entry())).AddHandler(EventStop);
curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop);
}
......@@ -5,10 +5,10 @@
package ogle
import (
"debug/gosym";
"debug/proc";
"fmt";
"os";
"sym";
)
// A Frame represents a single frame on a remote call stack.
......@@ -20,7 +20,7 @@ type Frame struct {
// The runtime.Stktop of the active stack segment
stk remoteStruct;
// The function this stack frame is in
fn *sym.TextSym;
fn *gosym.Func;
// The path and line of the CALL or current instruction. Note
// that this differs slightly from the meaning of Frame.pc.
path string;
......@@ -100,7 +100,7 @@ func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *
// Get function
var path string;
var line int;
var fn *sym.TextSym;
var fn *gosym.Func;
for i := 0; i < 100; i++ {
// Traverse segmented stack breaks
......@@ -121,7 +121,7 @@ func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *
}
// Look up function
path, line, fn = p.syms.LineFromPC(uint64(callpc));
path, line, fn = p.syms.PCToLine(uint64(callpc));
if fn != nil {
break;
}
......@@ -154,7 +154,7 @@ func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *
//
// TODO(austin) What if we're in the call to morestack in the
// prologue? Then top == false.
if top && pc == proc.Word(fn.Entry()) {
if top && pc == proc.Word(fn.Entry) {
// We're in the function prologue, before SP
// has been adjusted for the frame.
fp -= proc.Word(fn.FrameSize - p.PtrSize());
......@@ -208,7 +208,7 @@ func (f *Frame) Inner() *Frame {
func (f *Frame) String() string {
res := f.fn.Name;
if f.pc > proc.Word(f.fn.Value) {
res += fmt.Sprintf("+%#x", f.pc - proc.Word(f.fn.Entry()));
res += fmt.Sprintf("+%#x", f.pc - proc.Word(f.fn.Entry));
}
return res + fmt.Sprintf(" %s:%d", f.path, f.line);
}
......@@ -5,13 +5,14 @@
package ogle
import (
"debug/elf";
"debug/gosym";
"debug/proc";
"eval";
"fmt";
"log";
"os";
"reflect";
"sym";
)
// A FormatError indicates a failure to process information in or
......@@ -25,10 +26,10 @@ func (e FormatError) String() string {
// An UnknownArchitecture occurs when trying to load an object file
// that indicates an architecture not supported by the debugger.
type UnknownArchitecture sym.ElfMachine
type UnknownArchitecture elf.Machine
func (e UnknownArchitecture) String() string {
return "unknown architecture: " + sym.ElfMachine(e).String();
return "unknown architecture: " + elf.Machine(e).String();
}
// A ProcessNotStopped error occurs when attempting to read or write
......@@ -65,7 +66,7 @@ type Process struct {
proc proc.Process;
// The symbol table of this process
syms *sym.GoSymTable;
syms *gosym.Table;
// A possibly-stopped OS thread, or nil
threadCache proc.Thread;
......@@ -81,7 +82,7 @@ type Process struct {
// Globals from the sys package (or from no package)
sys struct {
lessstack, goexit, newproc, deferproc, newprocreadylocked *sym.TextSym;
lessstack, goexit, newproc, deferproc, newprocreadylocked *gosym.Func;
allg remotePtr;
g0 remoteStruct;
};
......@@ -109,7 +110,7 @@ type Process struct {
// NewProcess constructs a new remote process around a traced
// process, an architecture, and a symbol table.
func NewProcess(tproc proc.Process, arch Arch, syms *sym.GoSymTable) (*Process, os.Error) {
func NewProcess(tproc proc.Process, arch Arch, syms *gosym.Table) (*Process, os.Error) {
p := &Process{
Arch: arch,
proc: tproc,
......@@ -151,8 +152,8 @@ func NewProcess(tproc proc.Process, arch Arch, syms *sym.GoSymTable) (*Process,
}
// Create internal breakpoints to catch new and exited goroutines
p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
p.OnBreakpoint(proc.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry)).(*breakpointHook).addHandler(readylockedBP, true);
p.OnBreakpoint(proc.Word(p.sys.goexit.Entry)).(*breakpointHook).addHandler(goexitBP, true);
// Select current frames
for _, g := range p.goroutines {
......@@ -164,10 +165,36 @@ func NewProcess(tproc proc.Process, arch Arch, syms *sym.GoSymTable) (*Process,
return p, nil;
}
func elfGoSyms(f *elf.File) (*gosym.Table, os.Error) {
text := f.Section(".text");
symtab := f.Section(".gosymtab");
pclntab := f.Section(".gopclntab");
if text == nil || symtab == nil || pclntab == nil {
return nil, nil;
}
symdat, err := symtab.Data();
if err != nil {
return nil, err;
}
pclndat, err := pclntab.Data();
if err != nil {
return nil, err;
}
pcln := gosym.NewLineTable(pclndat, text.Addr);
tab, err := gosym.NewTable(symdat, pcln);
if err != nil {
return nil, err;
}
return tab, nil;
}
// NewProcessElf constructs a new remote process around a traced
// process and the process' ELF object.
func NewProcessElf(tproc proc.Process, elf *sym.Elf) (*Process, os.Error) {
syms, err := sym.ElfGoSyms(elf);
func NewProcessElf(tproc proc.Process, f *elf.File) (*Process, os.Error) {
syms, err := elfGoSyms(f);
if err != nil {
return nil, err;
}
......@@ -175,11 +202,11 @@ func NewProcessElf(tproc proc.Process, elf *sym.Elf) (*Process, os.Error) {
return nil, FormatError("Failed to find symbol table");
}
var arch Arch;
switch elf.Machine {
case sym.ElfX86_64:
switch f.Machine {
case elf.EM_X86_64:
arch = Amd64;
default:
return nil, UnknownArchitecture(elf.Machine);
return nil, UnknownArchitecture(f.Machine);
}
return NewProcess(tproc, arch, syms);
}
......@@ -204,7 +231,7 @@ func (p *Process) bootstrap() {
p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch);
p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch);
// Get addresses of type·*runtime.XType for discrimination.
// Get addresses of type.*runtime.XType for discrimination.
rtv := reflect.Indirect(reflect.NewValue(&p.runtime)).(*reflect.StructValue);
rtvt := rtv.Type().(*reflect.StructType);
for i := 0; i < rtv.NumField(); i++ {
......@@ -212,11 +239,11 @@ func (p *Process) bootstrap() {
if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' {
continue;
}
sym := p.syms.SymFromName("type·*runtime." + n[1:len(n)]);
sym := p.syms.LookupSym("type.*runtime." + n[1:len(n)]);
if sym == nil {
continue;
}
rtv.Field(i).(*reflect.Uint64Value).Set(sym.Common().Value);
rtv.Field(i).(*reflect.Uint64Value).Set(sym.Value);
}
// Get runtime field indexes
......@@ -226,22 +253,16 @@ func (p *Process) bootstrap() {
p.runtime.runtimeGStatus = rt1GStatus;
// Get globals
globalFn := func(name string) *sym.TextSym {
if sym, ok := p.syms.SymFromName(name).(*sym.TextSym); ok {
return sym;
}
return nil;
};
p.sys.lessstack = globalFn("sys·lessstack");
p.sys.goexit = globalFn("goexit");
p.sys.newproc = globalFn("sys·newproc");
p.sys.deferproc = globalFn("sys·deferproc");
p.sys.newprocreadylocked = globalFn("newprocreadylocked");
if allg := p.syms.SymFromName("allg"); allg != nil {
p.sys.allg = remotePtr{remote{proc.Word(allg.Common().Value), p}, p.runtime.G};
}
if g0 := p.syms.SymFromName("g0"); g0 != nil {
p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Common().Value), p}).(remoteStruct);
p.sys.lessstack = p.syms.LookupFunc("sys.lessstack");
p.sys.goexit = p.syms.LookupFunc("goexit");
p.sys.newproc = p.syms.LookupFunc("sys.newproc");
p.sys.deferproc = p.syms.LookupFunc("sys.deferproc");
p.sys.newprocreadylocked = p.syms.LookupFunc("newprocreadylocked");
if allg := p.syms.LookupSym("allg"); allg != nil {
p.sys.allg = remotePtr{remote{proc.Word(allg.Value), p}, p.runtime.G};
}
if g0 := p.syms.LookupSym("g0"); g0 != nil {
p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct);
}
}
......
......@@ -145,10 +145,10 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType {
}
if debugParseRemoteType {
sym := p.syms.SymFromAddr(uint64(addr));
sym := p.syms.SymByAddr(uint64(addr));
name := "<unknown>";
if sym != nil {
name = sym.Common().Name;
name = sym.Name;
}
log.Stderrf("%sParsing type at %#x (%s)", prtIndent, addr, name);
prtIndent += " ";
......@@ -285,10 +285,10 @@ func parseRemoteType(a aborter, rs remoteStruct) *remoteType {
mk = mkUintptr;
default:
sym := p.syms.SymFromAddr(uint64(itype));
sym := p.syms.SymByAddr(uint64(itype));
name := "<unknown symbol>";
if sym != nil {
name = sym.Common().Name;
name = sym.Name;
}
err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name);
a.Abort(FormatError(err));
......
......@@ -5,11 +5,11 @@
package ogle
import (
"debug/gosym";
"debug/proc";
"eval";
"log";
"os";
"sym";
)
/*
......@@ -19,7 +19,7 @@ import (
// A NotOnStack error occurs when attempting to access a variable in a
// remote frame where that remote frame is not on the current stack.
type NotOnStack struct {
Fn *sym.TextSym;
Fn *gosym.Func;
Goroutine *Goroutine;
}
......@@ -34,7 +34,7 @@ func (e NotOnStack) String() string {
// that function.
type remoteFramePtr struct {
p *Process;
fn *sym.TextSym;
fn *gosym.Func;
rt *remoteType;
}
......@@ -121,14 +121,13 @@ func (p *Process) populateWorld(w *eval.World) os.Error {
packages := make(map[string] map[string] def);
for _, s := range p.syms.Syms {
sc := s.Common();
if sc.ReceiverName() != "" {
if s.ReceiverName() != "" {
// TODO(austin)
continue;
}
// Package
pkgName := sc.PackageName();
pkgName := s.PackageName();
switch pkgName {
case "", "type", "extratype", "string", "go":
// "go" is really "go.string"
......@@ -141,30 +140,30 @@ func (p *Process) populateWorld(w *eval.World) os.Error {
}
// Symbol name
name := sc.BaseName();
name := s.BaseName();
if _, ok := pkg[name]; ok {
log.Stderrf("Multiple definitions of symbol %s", sc.Name);
log.Stderrf("Multiple definitions of symbol %s", s.Name);
continue;
}
// Symbol type
rt, err := p.typeOfSym(sc);
rt, err := p.typeOfSym(&s);
if err != nil {
return err;
}
// Definition
switch sc.Type {
switch s.Type {
case 'D', 'd', 'B', 'b':
// Global variable
if rt == nil {
continue;
}
pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(sc.Value), p})};
pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})};
case 'T', 't', 'L', 'l':
// Function
s := s.(*sym.TextSym);
s := s.Func;
// TODO(austin): Ideally, this would *also* be
// callable. How does that interact with type
// conversion syntax?
......@@ -203,7 +202,7 @@ func (p *Process) populateWorld(w *eval.World) os.Error {
// typeOfSym returns the type associated with a symbol. If the symbol
// has no type, returns nil.
func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) {
func (p *Process) typeOfSym(s *gosym.Sym) (*remoteType, os.Error) {
if s.GoType == 0 {
return nil, nil;
}
......@@ -221,7 +220,7 @@ func (p *Process) typeOfSym(s *sym.CommonSym) (*remoteType, os.Error) {
// makeFrameType constructs a struct type for the frame of a function.
// The offsets in this struct type are such that the struct can be
// instantiated at this function's frame pointer.
func (p *Process) makeFrameType(s *sym.TextSym) (*remoteType, os.Error) {
func (p *Process) makeFrameType(s *gosym.Func) (*remoteType, os.Error) {
n := len(s.Params) + len(s.Locals);
fields := make([]eval.StructField, n);
layout := make([]remoteStructField, n);
......@@ -235,7 +234,7 @@ func (p *Process) makeFrameType(s *sym.TextSym) (*remoteType, os.Error) {
// things like "i", where there's an obvious right answer.
for _, param := range s.Params {
rt, err := p.typeOfSym(param.Common());
rt, err := p.typeOfSym(param);
if err != nil {
return nil, err;
}
......@@ -254,7 +253,7 @@ func (p *Process) makeFrameType(s *sym.TextSym) (*remoteType, os.Error) {
}
for _, local := range s.Locals {
rt, err := p.typeOfSym(local.Common());
rt, err := p.typeOfSym(local);
if err != nil {
return nil, err;
}
......
# Copyright 2009 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.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=sym
GOFILES=\
binary.go\
elf.go\
elffmt.go\
gosymtab.go\
include $(GOROOT)/src/Make.pkg
// Copyright 2009 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 sym
import (
"bufio";
"io";
"log";
"os";
"reflect";
)
type byteOrder interface {
Uint16(b []byte) uint16;
Uint32(b []byte) uint32;
Uint64(b []byte) uint64;
String() string;
}
type olsb struct {}
func (olsb) Uint16(b []byte) uint16 {
return uint16(b[0]) | uint16(b[1]) << 8;
}
func (olsb) Uint32(b []byte) uint32 {
return uint32(b[0]) | uint32(b[1]) << 8 | uint32(b[2]) << 16 | uint32(b[3]) << 24;
}
func (olsb) Uint64(b []byte) uint64 {
return uint64(b[0]) | uint64(b[1]) << 8 | uint64(b[2]) << 16 | uint64(b[3]) << 24 | uint64(b[4]) << 32 | uint64(b[5]) << 40 | uint64(b[6]) << 48 | uint64(b[7]) << 56;
}
func (olsb) String() string {
return "LSB";
}
type omsb struct {}
func (omsb) Uint16(b []byte) uint16 {
return uint16(b[1]) | uint16(b[0]) << 8;
}
func (omsb) Uint32(b []byte) uint32 {
return uint32(b[3]) | uint32(b[2]) << 8 | uint32(b[1]) << 16 | uint32(b[0]) << 24;
}
func (omsb) Uint64(b []byte) uint64 {
return uint64(b[7]) | uint64(b[6]) << 8 | uint64(b[5]) << 16 | uint64(b[4]) << 24 | uint64(b[3]) << 32 | uint64(b[2]) << 40 | uint64(b[1]) << 48 | uint64(b[0]) << 56;
}
func (omsb) String() string {
return "MSB";
}
var (
lsb = olsb{};
msb = omsb{};
)
// A binaryReader decodes binary data from another reader. On an
// error, the Read methods simply return 0 and record the error, to
// make it more convenient to decode long sequences of binary data.
// The caller should use the Error method when convenient to check
// for errors.
type binaryReader struct {
*bufio.Reader;
err os.Error;
order byteOrder;
}
// newBinaryReader creates a new binary data reader backed by the
// given reader and using the given byte order for decoding.
func newBinaryReader(r io.Reader, o byteOrder) *binaryReader {
return &binaryReader{bufio.NewReader(r), nil, o};
}
// Error returns the recorded error, or nil if no error has occurred.
func (r *binaryReader) Error() os.Error {
return r.err;
}
func (r *binaryReader) ReadUint8() uint8 {
var buf [1]byte;
_, err := io.ReadFull(r.Reader, &buf);
if r.err == nil && err != nil {
r.err = err;
}
return buf[0];
}
func (r *binaryReader) ReadUint16() uint16 {
var buf [2]byte;
_, err := io.ReadFull(r.Reader, &buf);
if r.err == nil && err != nil {
r.err = err;
}
return r.order.Uint16(&buf);
}
func (r *binaryReader) ReadUint32() uint32 {
var buf [4]byte;
_, err := io.ReadFull(r.Reader, &buf);
if r.err == nil && err != nil {
r.err = err;
}
return r.order.Uint32(&buf);
}
func (r *binaryReader) ReadUint64() uint64 {
var buf [8]byte;
_, err := io.ReadFull(r.Reader, &buf);
if r.err == nil && err != nil {
r.err = err;
}
return r.order.Uint64(&buf);
}
func (r *binaryReader) ReadInt8() int8 {
return int8(r.ReadUint8());
}
func (r *binaryReader) ReadInt16() int16 {
return int16(r.ReadUint16());
}
func (r *binaryReader) ReadInt32() int32 {
return int32(r.ReadUint32());
}
func (r *binaryReader) ReadInt64() int64 {
return int64(r.ReadUint64());
}
// ReadCString reads a NUL-terminated string.
func (r *binaryReader) ReadCString() string {
str, err := r.Reader.ReadString('\x00');
if r.err == nil && err != nil {
r.err = err;
}
n := len(str);
if n > 0 {
str = str[0:n-1];
}
return str;
}
// ReadValue reads a value according to its reflected type. This can
// read any of the types for which there is a regular Read method,
// plus structs and arrays. It assumes structs contain no padding.
func (r *binaryReader) ReadValue(v reflect.Value) {
switch v := v.(type) {
case *reflect.ArrayValue:
l := v.Len();
for i := 0; i < l; i++ {
r.ReadValue(v.Elem(i));
}
case *reflect.StructValue:
l := v.NumField();
for i := 0; i < l; i++ {
r.ReadValue(v.Field(i));
}
case *reflect.Uint8Value:
v.Set(r.ReadUint8());
case *reflect.Uint16Value:
v.Set(r.ReadUint16());
case *reflect.Uint32Value:
v.Set(r.ReadUint32());
case *reflect.Uint64Value:
v.Set(r.ReadUint64());
case *reflect.Int8Value:
v.Set(r.ReadInt8());
case *reflect.Int16Value:
v.Set(r.ReadInt16());
case *reflect.Int32Value:
v.Set(r.ReadInt32());
case *reflect.Int64Value:
v.Set(r.ReadInt64());
case *reflect.StringValue:
v.Set(r.ReadCString());
default:
log.Crashf("Value of unexpected type %T", v);
}
}
// ReadAny is a convenience wrapper for ReadValue. It can be passed a
// pointer any type that can be decoded by ReadValue.
func (r *binaryReader) ReadAny(out interface {}) {
r.ReadValue(reflect.Indirect(reflect.NewValue(out)));
}
// Copyright 2009 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 sym
import (
"fmt";
"io";
"os";
)
/*
* Internal ELF representation
*/
// Elf represents a decoded ELF binary.
type Elf struct {
class int;
data byteOrder;
Type ElfType;
Machine ElfMachine;
Sections []*Section;
}
// Section represents a single section in an ELF binary.
type Section struct {
r io.ReadSeeker;
Name string;
offset int64;
Size uint64;
Addr uint64;
}
/*
* ELF reader
*/
type FormatError struct {
off int64;
msg string;
val interface{};
}
func (e *FormatError) String() string {
msg := e.msg;
if e.val != nil {
msg += fmt.Sprintf(" '%v' ", e.val);
}
msg += fmt.Sprintf("in record at byte %#x", e.off);
return msg;
}
// NewElf reads and decodes an ELF binary. The ELF binary is expected
// to start where the reader is currently positioned.
func NewElf(r io.ReadSeeker) (*Elf, os.Error) {
// Read ELF identifier
var ident [eiNIdent]uint8;
off, err := r.Seek(0, 0);
if err != nil {
return nil, err;
}
start := off;
_, err = io.ReadFull(r, &ident);
if err != nil {
if err == os.EOF {
err = io.ErrUnexpectedEOF;
}
return nil, err;
}
// Decode identifier
if ident[eiMag0] != '\x7f' || ident[eiMag1] != 'E' || ident[eiMag2] != 'L' || ident[eiMag3] != 'F' {
return nil, &FormatError{off, "bad magic number", string(ident[eiMag0:eiMag3])};
}
e := &Elf{};
switch ident[eiClass] {
case elfClass32:
e.class = 32;
case elfClass64:
e.class = 64;
default:
return nil, &FormatError{off, "unknown ELF class", ident[eiClass]};
}
switch ident[eiData] {
case elfData2LSB:
e.data = lsb;
case elfData2MSB:
e.data = msb;
default:
return nil, &FormatError{off, "unknown ELF data encoding", ident[eiData]};
}
if ident[eiVersion] != evCurrent {
return nil, &FormatError{off, "unknown ELF version", ident[eiVersion]};
}
// TODO(austin) Do something with ABI?
// Read ELF file header
var shoff int64;
var shentsize, shnum, shstrndx int;
br := newBinaryReader(r, e.data);
switch e.class {
case 32:
return nil, &FormatError{off, "ELF32 not implemented", nil};
case 64:
hdr := &elf64Ehdr{};
br.ReadAny(hdr);
if err := br.Error(); err != nil {
return nil, err;
}
if hdr.Type > etCore && hdr.Type < etLoOS {
return nil, &FormatError{off, "unknown ELF file type", hdr.Type};
}
e.Type = ElfType(hdr.Type);
e.Machine = ElfMachine(hdr.Machine);
if hdr.Version != evCurrent {
return nil, &FormatError{off, "unknown second ELF version", hdr.Version};
}
shoff = int64(hdr.Shoff);
shentsize = int(hdr.Shentsize);
shnum = int(hdr.Shnum);
shstrndx = int(hdr.Shstrndx);
}
// Read section headers
e.Sections = make([]*Section, shnum);
secNames := make([]uint32, shnum);
for i := 0; i < shnum; i++ {
off, err = r.Seek(start + shoff + int64(i*shentsize), 0);
if err != nil {
return nil, err;
}
br = newBinaryReader(r, e.data);
switch e.class {
case 32:
panic("not reached");
case 64:
shdr := &elf64Shdr{};
br.ReadAny(shdr);
if err := br.Error(); err != nil {
return nil, err;
}
s := &Section{
r: r,
offset: start + int64(shdr.Off),
Size: shdr.Size,
Addr: uint64(shdr.Addr),
};
secNames[i] = shdr.Name;
e.Sections[i] = s;
}
}
// Resolve section names
off, err = r.Seek(start + e.Sections[shstrndx].offset, 0);
if err != nil {
return nil, err;
}
blob := make([]byte, e.Sections[shstrndx].Size);
_, err = io.ReadFull(r, blob);
for i, s := range e.Sections {
var ok bool;
s.Name, ok = getString(blob, int(secNames[i]));
if !ok {
return nil, &FormatError{start + shoff + int64(i*shentsize), "bad section name", secNames[i]};
}
}
return e, nil;
}
// getString extracts a string from an ELF string table.
func getString(section []byte, index int) (string, bool) {
if index < 0 || index >= len(section) {
return "", false;
}
for end := index; end < len(section); end++ {
if section[end] == 0 {
return string(section[index:end]), true;
}
}
return "", false;
}
// Section returns a section with the given name, or nil if no such
// section exists.
func (e *Elf) Section(name string) *Section {
for _, s := range e.Sections {
if s.Name == name {
return s;
}
}
return nil;
}
/*
* Sections
*/
type subReader struct {
r io.Reader;
rem uint64;
}
func (r *subReader) Read(b []byte) (ret int, err os.Error) {
if r.rem == 0 {
return 0, os.EOF;
}
if uint64(len(b)) > r.rem {
b = b[0:r.rem];
}
ret, err = r.r.Read(b);
r.rem -= uint64(ret);
if err == os.EOF {
err = io.ErrUnexpectedEOF;
}
return ret, err;
}
// Open returns a reader backed by the data in this section.
// The original ELF file must still be open for this to work.
// The returned reader assumes there will be no seeks on the
// underlying file or any other opened section between the Open call
// and the last call to Read.
func (s *Section) Open() (io.Reader, os.Error) {
_, err := s.r.Seek(s.offset, 0);
if err != nil {
return nil, err;
}
return &subReader{s.r, s.Size}, nil;
}
This diff is collapsed.
This diff is collapsed.
// Empty include file to generate z symbols
// EOF
This diff is collapsed.
// Copyright 2009 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 sym
import (
"exec";
"io";
"os";
"testing";
"syscall";
)
var goarch = os.Getenv("O")
// No ELF binaries on OS X
var darwin = syscall.OS == "darwin";
func TestLineFromAline(t *testing.T) {
if darwin {
return;
}
// Use myself for this test
f, err := os.Open(goarch + ".out", os.O_RDONLY, 0);
if err != nil {
t.Fatalf("failed to open %s.out: %s", goarch, err);
}
elf, err := NewElf(f);
if err != nil {
t.Fatalf("failed to read ELF: %s", err);
}
syms, err := ElfGoSyms(elf);
if err != nil {
t.Fatalf("failed to load syms: %s", err);
}
// Find the sym package
pkg := syms.SymFromName("sym·ElfGoSyms").(*TextSym).obj;
// Walk every absolute line and ensure that we hit every
// source line monotonically
lastline := make(map[string] int);
final := -1;
for i := 0; i < 10000; i++ {
path, line := pkg.lineFromAline(i);
// Check for end of object
if path == "" {
if final == -1 {
final = i - 1;
}
continue;
} else if final != -1 {
t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line);
}
// It's okay to see files multiple times (e.g., sys.a)
if line == 1 {
lastline[path] = 1;
continue;
}
// Check that the is the next line in path
ll, ok := lastline[path];
if !ok {
t.Errorf("file %s starts on line %d", path, line);
} else if line != ll + 1 {
t.Errorf("expected next line of file %s to be %d, got %d", path, ll + 1, line);
}
lastline[path] = line;
}
if final == -1 {
t.Errorf("never reached end of object");
}
}
func TestLineAline(t *testing.T) {
if darwin {
return;
}
// Use myself for this test
f, err := os.Open(goarch + ".out", os.O_RDONLY, 0);
if err != nil {
t.Fatalf("failed to open %s.out: %s", goarch, err);
}
elf, err := NewElf(f);
if err != nil {
t.Fatalf("failed to read ELF: %s", err);
}
syms, err := ElfGoSyms(elf);
if err != nil {
t.Fatalf("failed to load syms: %s", err);
}
for _, o := range syms.files {
// A source file can appear multiple times in a
// object. alineFromLine will always return alines in
// the first file, so track which lines we've seen.
found := make(map[string] int);
for i := 0; i < 1000; i++ {
path, line := o.lineFromAline(i);
if path == "" {
break;
}
// cgo files are full of 'Z' symbols, which we don't handle
if len(path) > 4 && path[len(path)-4:len(path)] == ".cgo" {
continue;
}
if minline, ok := found[path]; path != "" && ok {
if minline >= line {
// We've already covered this file
continue;
}
}
found[path] = line;
a, err := o.alineFromLine(path, line);
if err != nil {
t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.paths[0].Name, path, line, err);
} else if a != i {
t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.paths[0].Name, path, line, a);
}
}
}
}
// gotest: if [ "`uname`" != "Darwin" ]; then
// gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -l -o _test/pclinetest pclinetest.$O
// gotest: fi
func TestPCLine(t *testing.T) {
if darwin {
return;
}
f, err := os.Open("_test/pclinetest", os.O_RDONLY, 0);
if err != nil {
t.Fatalf("failed to open pclinetest.6: %s", err);
}
defer f.Close();
elf, err := NewElf(f);
if err != nil {
t.Fatalf("failed to read ELF: %s", err);
}
syms, err := ElfGoSyms(elf);
if err != nil {
t.Fatalf("failed to load syms: %s", err);
}
textSec := elf.Section(".text");
sf, err := textSec.Open();
if err != nil {
t.Fatalf("failed to open .text section: %s", err);
}
text, err := io.ReadAll(sf);
if err != nil {
t.Fatalf("failed to read .text section: %s", err);
}
// Test LineFromPC
sym := syms.SymFromName("linefrompc").(*TextSym);
wantLine := 0;
for pc := sym.Value; pc < sym.End; pc++ {
file, line, fn := syms.LineFromPC(pc);
wantLine += int(text[pc-textSec.Addr]);
if fn == nil {
t.Errorf("failed to get line of PC %#x", pc);
} else if len(file) < 12 || file[len(file)-12:len(file)] != "pclinetest.s" || line != wantLine || fn != sym {
t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name);
}
}
// Test PCFromLine
sym = syms.SymFromName("pcfromline").(*TextSym);
lookupline := -1;
wantLine = 0;
for pc := sym.Value; pc < sym.End; pc += 2 + uint64(text[pc+1-textSec.Addr]) {
file, line, fn := syms.LineFromPC(pc);
wantLine += int(text[pc-textSec.Addr]);
if line != wantLine {
t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line);
continue;
}
if lookupline == -1 {
lookupline = line;
}
for ; lookupline <= line; lookupline++ {
pc2, fn2, err := syms.PCFromLine(file, lookupline);
if lookupline != line {
// Should be nothing on this line
if err == nil {
t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name);
}
} else if err != nil {
t.Errorf("failed to get PC of line %d: %s", lookupline, err);
} else if pc != pc2 {
t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name);
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment