1
0
mirror of https://github.com/golang/go synced 2024-11-12 08:50:22 -07:00

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
This commit is contained in:
Austin Clements 2009-09-25 09:39:08 -07:00
parent 60098a41fb
commit 4211384976
14 changed files with 86 additions and 1945 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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};
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.SymFromName("g0"); g0 != nil {
p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Common().Value), p}).(remoteStruct);
if g0 := p.syms.LookupSym("g0"); g0 != nil {
p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct);
}
}

View File

@ -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));

View File

@ -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;
}

View File

@ -1,14 +0,0 @@
# 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

View File

@ -1,194 +0,0 @@
// 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)));
}

View File

@ -1,242 +0,0 @@
// 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;
}

View File

@ -1,389 +0,0 @@
// 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";
/*
* ELF64 file format
*/
type elf64Addr uint64
type elf64Off uint64
type elf64Ehdr struct {
// Ident [elfIdentLen]uint8; // ELF identification
Type uint16; // Object file type
Machine uint16; // Machine type
Version uint32; // Object file version
Entry elf64Addr; // Entry point address
Phoff elf64Off; // Program header offset
Shoff elf64Off; // Section header offset
Flags uint32; // Processor-specific flags
Ehsize uint16; // ELF header size
Phentsize uint16; // Size of program header entry
Phnum uint16; // Number of program header entries
Shentsize uint16; // Size of section header entry
Shnum uint16; // Number of section header entries
Shstrndx uint16; // Section name string table indexes
}
const (
// Ident indexes
eiMag0 = 0; // File identification
eiMag1 = 1;
eiMag2 = 2;
eiMag3 = 3;
eiClass = 4; // File class
eiData = 5; // Data encoding
eiVersion = 6; // File version
eiOsABI = 7; // OS/ABI identification
eiABIVersion = 8; // ABI version
eiPad = 9; // Start of padding bytes
eiNIdent = 16; // Size of ident
// Classes
elfClass32 = 1; // 32-bit objects
elfClass64 = 2; // 64-bit objects
// Endians
elfData2LSB = 1; // Little-endian
elfData2MSB = 2; // Big-endian
// Types
etNone = 0; // No file type
etRel = 1; // Relocatable object file
etExec = 2; // Executable file
etDyn = 3; // Shared object file
etCore = 4; // Core file
etLoOS = 0xFE00; // Environment-specific use
etHiOS = 0xFEFF;
etLoProc = 0xFF00; // Processor-specific use
etHiProc = 0xFFFF;
evCurrent = 1; // Current version of format
)
type elf64Shdr struct {
Name uint32; // Section name
Type uint32; // Section type
Flags uint64; // Section attributes
Addr elf64Addr; // Virtual address in memory
Off elf64Off; // Offset in file
Size uint64; // Size of section
Link uint32; // Link to other section
Info uint32; // Miscellaneous information
Addralign uint64; // Address alignment boundary
Entsize uint64; // Size of entries, if section has table
}
const (
// Section indices
shnUndef = 0; // Used to mark an undefined or meaningless section reference
shnLoProc = 0xFF00; // Processor-specific use
shnHiProc = 0xFF1F;
shnLoOS = 0xFF20; // Environment-specific use
shnHiOS = 0xFF3F;
shnAbs = 0xFFF1; // Indicates that the coresponding reference is an absolute value
shnCommon = 0xFFF2; // Indicates a symbol that has been declared as a common block
// Section header types
shtNull = 0; // Unused section header
shtProgBits = 1; // Information defined by the program
shtSymTab = 2; // Linker symbol table
shtStrTab = 3; // String table
shtRela = 4; // "Rela" type relocation entries
shtHash = 5; // Symbol hash table
shtDynamic = 6; // Dynamic linking tables
shtNote = 7; // Note information
shtNoBits = 8; // Uninitialized space; does not occupy any space in the file
shtRel = 9; // "Rel" type relocation entries
shtShlib = 10; // Reserved
shtDynSym = 11; // A dynamic loader symbol table
shtLoOS = 0x60000000; // Environment-specific use
shtHiOS = 0x6FFFFFFF;
shtLoProc = 0x70000000; // Processor-specific use
shtHiProc = 0x7FFFFFFF;
// Section header flags
shfWrite = 0x1; // Writable data
shfAlloc = 0x2; // Allocated in memory image of program
shfExecInstr = 0x4; // Executable instructions
shfMaskOS = 0x0F000000; // Environment-specific use
shfMaskProc = 0xF0000000; // Processor-specific use
)
type elf64Phdr struct {
Type uint32; // Type of segment
Flags uint32; // Segment attributes
Off elf64Off; // Offset in file
Vaddr elf64Addr; // Virtual address in memory
Paddr elf64Addr; // Reserved
Filesz uint64; // Size of segment in file
Memsz uint64; // Size of segment in memory
Align uint64; // Alignment of segment
}
const (
ptNull = 0; // Unused entry
ptLoad = 1; // Loadable segment
ptDynamic = 2; // Dynamic linking tables
ptInterp = 3; // Program interpreter path name
ptNote = 4; // Note sections
ptPhdr = 6; // Program header table
// Program header flags
pfX = 0x1; // Execute permission
pfW = 0x2; // Write permission
pfR = 0x4; // Read permission
pfMaskOS = 0x00FF0000; // Reserved for environment-specific use
pfMaskProc = 0xFF000000; // Reserved for processor-specific use
)
/*
* Exported constants
*/
type ElfType int
const (
ElfNone ElfType = etNone;
ElfRel = etRel;
ElfExec = etExec;
ElfDyn = etDyn;
ElfCore = etCore;
)
type ElfMachine int
const (
ElfM32 ElfMachine = 1;
ElfSPARC ElfMachine = 2;
Elf386 ElfMachine = 3;
Elf68K ElfMachine = 4;
Elf88K ElfMachine = 5;
Elf860 ElfMachine = 7;
ElfMIPS ElfMachine = 8;
ElfS370 ElfMachine = 9;
ElfMIPS_RS3_LE ElfMachine = 10;
ElfPARISC ElfMachine = 15;
ElfVPP500 ElfMachine = 17;
ElfSPARC32PLUS ElfMachine = 18;
Elf960 ElfMachine = 19;
ElfPPC ElfMachine = 20;
ElfPPC64 ElfMachine = 21;
ElfS390 ElfMachine = 22;
ElfV800 ElfMachine = 36;
ElfFR20 ElfMachine = 37;
ElfRH32 ElfMachine = 38;
ElfRCE ElfMachine = 39;
ElfARM ElfMachine = 40;
ElfFAKE_ALPHA ElfMachine = 41;
ElfSH ElfMachine = 42;
ElfSPARCV9 ElfMachine = 43;
ElfTRICORE ElfMachine = 44;
ElfARC ElfMachine = 45;
ElfH8_300 ElfMachine = 46;
ElfH8_300H ElfMachine = 47;
ElfH8S ElfMachine = 48;
ElfH8_500 ElfMachine = 49;
ElfIA_64 ElfMachine = 50;
ElfMIPS_X ElfMachine = 51;
ElfCOLDFIRE ElfMachine = 52;
Elf68HC12 ElfMachine = 53;
ElfMMA ElfMachine = 54;
ElfPCP ElfMachine = 55;
ElfNCPU ElfMachine = 56;
ElfNDR1 ElfMachine = 57;
ElfSTARCORE ElfMachine = 58;
ElfME16 ElfMachine = 59;
ElfST100 ElfMachine = 60;
ElfTINYJ ElfMachine = 61;
ElfX86_64 ElfMachine = 62;
ElfPDSP ElfMachine = 63;
ElfFX66 ElfMachine = 66;
ElfST9PLUS ElfMachine = 67;
ElfST7 ElfMachine = 68;
Elf68HC16 ElfMachine = 69;
Elf68HC11 ElfMachine = 70;
Elf68HC08 ElfMachine = 71;
Elf68HC05 ElfMachine = 72;
ElfSVX ElfMachine = 73;
ElfST19 ElfMachine = 74;
ElfVAX ElfMachine = 75;
ElfCRIS ElfMachine = 76;
ElfJAVELIN ElfMachine = 77;
ElfFIREPATH ElfMachine = 78;
ElfZSP ElfMachine = 79;
ElfMMIX ElfMachine = 80;
ElfHUANY ElfMachine = 81;
ElfPRISM ElfMachine = 82;
ElfAVR ElfMachine = 83;
ElfFR30 ElfMachine = 84;
ElfD10V ElfMachine = 85;
ElfD30V ElfMachine = 86;
ElfV850 ElfMachine = 87;
ElfM32R ElfMachine = 88;
ElfMN10300 ElfMachine = 89;
ElfMN10200 ElfMachine = 90;
ElfPJ ElfMachine = 91;
ElfOPENRISC ElfMachine = 92;
ElfARC_A5 ElfMachine = 93;
ElfXTENSA ElfMachine = 94;
)
func (m ElfMachine) String() string {
switch m {
case ElfMachine(0):
return "No machine";
case ElfM32:
return "AT&T WE 32100";
case ElfSPARC:
return "SUN SPARC";
case Elf386:
return "Intel 80386";
case Elf68K:
return "Motorola m68k family";
case Elf88K:
return "Motorola m88k family";
case Elf860:
return "Intel 80860";
case ElfMIPS:
return "MIPS R3000 big-endian";
case ElfS370:
return "IBM System/370";
case ElfMIPS_RS3_LE:
return "MIPS R3000 little-endian";
case ElfPARISC:
return "HPPA";
case ElfVPP500:
return "Fujitsu VPP500";
case ElfSPARC32PLUS:
return "Sun's \"v8plus\"";
case Elf960:
return "Intel 80960";
case ElfPPC:
return "PowerPC";
case ElfPPC64:
return "PowerPC 64-bit";
case ElfS390:
return "IBM S390";
case ElfV800:
return "NEC V800 series";
case ElfFR20:
return "Fujitsu FR20";
case ElfRH32:
return "TRW RH-32";
case ElfRCE:
return "Motorola RCE";
case ElfARM:
return "ARM";
case ElfFAKE_ALPHA:
return "Digital Alpha";
case ElfSH:
return "Hitachi SH";
case ElfSPARCV9:
return "SPARC v9 64-bit";
case ElfTRICORE:
return "Siemens Tricore";
case ElfARC:
return "Argonaut RISC Core";
case ElfH8_300:
return "Hitachi H8/300";
case ElfH8_300H:
return "Hitachi H8/300H";
case ElfH8S:
return "Hitachi H8S";
case ElfH8_500:
return "Hitachi H8/500";
case ElfIA_64:
return "Intel Merced";
case ElfMIPS_X:
return "Stanford MIPS-X";
case ElfCOLDFIRE:
return "Motorola Coldfire";
case Elf68HC12:
return "Motorola M68HC12";
case ElfMMA:
return "Fujitsu MMA Multimedia Accelerato";
case ElfPCP:
return "Siemens PCP";
case ElfNCPU:
return "Sony nCPU embeeded RISC";
case ElfNDR1:
return "Denso NDR1 microprocessor";
case ElfSTARCORE:
return "Motorola Start*Core processor";
case ElfME16:
return "Toyota ME16 processor";
case ElfST100:
return "STMicroelectronic ST100 processor";
case ElfTINYJ:
return "Advanced Logic Corp. Tinyj emb.fa";
case ElfX86_64:
return "AMD x86-64 architecture";
case ElfPDSP:
return "Sony DSP Processor";
case ElfFX66:
return "Siemens FX66 microcontroller";
case ElfST9PLUS:
return "STMicroelectronics ST9+ 8/16 mc";
case ElfST7:
return "STmicroelectronics ST7 8 bit mc";
case Elf68HC16:
return "Motorola MC68HC16 microcontroller";
case Elf68HC11:
return "Motorola MC68HC11 microcontroller";
case Elf68HC08:
return "Motorola MC68HC08 microcontroller";
case Elf68HC05:
return "Motorola MC68HC05 microcontroller";
case ElfSVX:
return "Silicon Graphics SVx";
case ElfST19:
return "STMicroelectronics ST19 8 bit mc";
case ElfVAX:
return "Digital VAX";
case ElfCRIS:
return "Axis Communications 32-bit embedded processor";
case ElfJAVELIN:
return "Infineon Technologies 32-bit embedded processor";
case ElfFIREPATH:
return "Element 14 64-bit DSP Processor";
case ElfZSP:
return "LSI Logic 16-bit DSP Processor";
case ElfMMIX:
return "Donald Knuth's educational 64-bit processor";
case ElfHUANY:
return "Harvard University machine-independent object files";
case ElfPRISM:
return "SiTera Prism";
case ElfAVR:
return "Atmel AVR 8-bit microcontroller";
case ElfFR30:
return "Fujitsu FR30";
case ElfD10V:
return "Mitsubishi D10V";
case ElfD30V:
return "Mitsubishi D30V";
case ElfV850:
return "NEC v850";
case ElfM32R:
return "Mitsubishi M32R";
case ElfMN10300:
return "Matsushita MN10300";
case ElfMN10200:
return "Matsushita MN10200";
case ElfPJ:
return "picoJava";
case ElfOPENRISC:
return "OpenRISC 32-bit embedded processor";
case ElfARC_A5:
return "ARC Cores Tangent-A5";
case ElfXTENSA:
return "Tensilica Xtensa Architecture";
}
return fmt.Sprintf("<unknown %#x>", m);
}

View File

@ -1,735 +0,0 @@
// 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
// The Go symbol table and line number table formats are based on
// the Plan9 a.out format, which is documented here:
// http://plan9.bell-labs.com/magic/man2html/6/a.out
// The best reference for the differences between the Plan9 format and
// the Go format is the runtime source, particularly:
// src/pkg/runtime/symtab.c
import (
"io";
"os";
"sort";
"strconv";
"strings";
)
/*
* Symbols
*/
type GoSym interface {
Common() *CommonSym;
}
// CommonSym represents information that all symbols have in common.
// The meaning of the symbol value differs between symbol types.
type CommonSym struct {
Value uint64;
Type byte;
Name string;
GoType uint64;
}
func (c *CommonSym) Common() *CommonSym {
return c;
}
// Static returns whether this symbol is static (not visible outside its file).
func (c *CommonSym) Static() bool {
switch c.Type {
case 't', 'l', 'd', 'b':
return true;
}
return false;
}
// PackageName returns the package part of the symbol name, or empty
// string if there is none.
func (c *CommonSym) PackageName() string {
if i := strings.Index(c.Name, "·"); i != -1 {
return c.Name[0:i];
}
return "";
}
// ReceiverName returns the receiver type name of this symbol, or
// empty string if there is none.
func (c *CommonSym) ReceiverName() string {
l := strings.Index(c.Name, "·");
r := strings.LastIndex(c.Name, "·");
if l == -1 || r == -1 || l == r {
return "";
}
return c.Name[l+len("·"):r];
}
// BaseName returns the symbol name without the package or receiver name.
func (c *CommonSym) BaseName() string {
if i := strings.LastIndex(c.Name, "·"); i != -1 {
return c.Name[i+len("·"):len(c.Name)];
}
return c.Name;
}
// TextSym represents a function symbol. In addition to the common
// symbol fields, it has a frame size, parameters, and local variables.
type TextSym struct {
CommonSym;
obj *object;
lt *lineTable;
// The value of the next text sym, or the end of the text segment.
End uint64;
// Ths size of this function's frame.
FrameSize int;
// The value of each parameter symbol is its positive offset
// from the stack base pointer. This includes out parameters,
// even if they are unnamed.
Params []*ParamSym;
// The value of each local symbol is its negative offset from
// the stack base pointer.
Locals []*LocalSym;
}
func (s *TextSym) Entry() uint64 {
return s.Value;
}
type LeafSym struct {
CommonSym;
}
type DataSym struct {
CommonSym;
}
type BSSSym struct {
CommonSym;
}
type FrameSym struct {
CommonSym;
}
type LocalSym struct {
CommonSym;
}
type ParamSym struct {
CommonSym;
}
type PathSym struct {
CommonSym;
}
/*
* Symbol tables
*/
type object struct {
paths []*PathSym;
funcs []*TextSym;
}
type lineTable struct {
blob []byte;
pc uint64;
line int;
}
// GoSymTable represents a Go symbol table. It stores all of the
// symbols decoded from the program and provides methods to translate
// between symbols, names, and addresses.
type GoSymTable struct {
textEnd uint64;
Syms []GoSym;
funcs []*TextSym;
files map[string] *object;
}
func growGoSyms(s *[]GoSym) (*GoSym) {
n := len(*s);
if n == cap(*s) {
n := make([]GoSym, n, n * 2);
for i := range *s {
n[i] = (*s)[i];
}
*s = n;
}
*s = (*s)[0:n+1];
return &(*s)[n];
}
func (t *GoSymTable) readGoSymTab(r io.Reader) os.Error {
t.Syms = make([]GoSym, 0, 16);
filenames := make(map[uint32] string);
br := newBinaryReader(r, msb);
off := int64(0);
for {
// Read symbol
value := br.ReadUint32();
if br.Error() == os.EOF {
break;
}
typ := br.ReadUint8();
if br.Error() == nil && typ & 0x80 == 0 {
return &FormatError{off, "bad symbol type code", typ};
}
typ &^= 0x80;
name := br.ReadCString();
extraOff := int64(0);
if typ == 'z' || typ == 'Z' {
if name != "" {
return &FormatError{off, "path symbol has non-empty name", name};
}
// Decode path entry
for i := 0; ; i++ {
eltIdx := uint32(br.ReadUint16());
extraOff += 2;
if eltIdx == 0 {
break;
}
elt, ok := filenames[eltIdx];
if !ok {
return &FormatError{off, "bad filename code", eltIdx};
}
if name != "" && name[len(name)-1] != '/' {
name += "/";
}
name += elt;
}
}
gotype := br.ReadUint32();
if err := br.Error(); err != nil {
if err == os.EOF {
err = io.ErrUnexpectedEOF;
}
return err;
}
off += 4 + 1 + int64(len(name)) + 1 + extraOff + 4;
// Handle file name components
if typ == 'f' {
filenames[value] = name;
}
// Create the GoSym
sym := growGoSyms(&t.Syms);
switch typ {
case 'T', 't':
*sym = &TextSym{};
case 'L', 'l':
*sym = &LeafSym{};
case 'D', 'd':
*sym = &DataSym{};
case 'B', 'b':
*sym = &BSSSym{};
case 'm':
*sym = &FrameSym{};
case 'a':
*sym = &LocalSym{};
case 'p':
*sym = &ParamSym{};
case 'z', 'Z':
*sym = &PathSym{};
default:
*sym = &CommonSym{};
}
common := sym.Common();
common.Value = uint64(value);
common.Type = typ;
common.Name = name;
common.GoType = uint64(gotype);
}
return nil;
}
// byValue is a []*TextSym sorter.
type byValue []*TextSym
func (s byValue) Len() int {
return len(s);
}
func (s byValue) Less(i, j int) bool {
return s[i].Value < s[j].Value;
}
func (s byValue) Swap(i, j int) {
t := s[i];
s[i] = s[j];
s[j] = t;
}
func (t *GoSymTable) processTextSyms() {
// Count text symbols and attach frame sizes, parameters, and
// locals to them. Also, find object file boundaries.
count := 0;
var obj *object;
var objCount int;
var prevTextSym *TextSym;
for i := 0; i < len(t.Syms); i++ {
switch sym := t.Syms[i].(type) {
case *PathSym:
// Finish the current object
if obj != nil {
obj.funcs = make([]*TextSym, 0, objCount);
}
// Count path symbols
end := i+1;
for ; end < len(t.Syms); end++ {
_, ok := t.Syms[end].(*PathSym);
if !ok {
break;
}
}
// Copy path symbols
obj = &object{make([]*PathSym, end - i), nil};
for j, s := range t.Syms[i:end] {
obj.paths[j] = s.(*PathSym);
}
// Record file names
depth := 0;
for _, s := range obj.paths {
if s.Name == "" {
depth--;
} else {
if depth == 0 {
t.files[s.Name] = obj;
}
depth++;
}
}
objCount = 0;
i = end-1;
case *TextSym:
if sym.Name == "etext" {
continue;
}
if prevTextSym != nil {
prevTextSym.End = sym.Value;
}
prevTextSym = sym;
// Count parameter and local syms
var np, nl int;
end := i+1;
countloop:
for ; end < len(t.Syms); end++ {
switch _ := t.Syms[end].(type) {
// TODO(austin) Use type switch list
case *TextSym:
break countloop;
case *PathSym:
break countloop;
case *ParamSym:
np++;
case *LocalSym:
nl++;
}
}
// Fill in the function symbol
var ip, ia int;
sym.obj = obj;
sym.Params = make([]*ParamSym, np);
sym.Locals = make([]*LocalSym, nl);
for _, s := range t.Syms[i:end] {
switch s := s.(type) {
case *FrameSym:
sym.FrameSize = int(s.Value);
case *ParamSym:
sym.Params[ip] = s;
ip++;
case *LocalSym:
sym.Locals[ia] = s;
ia++;
}
}
count++;
objCount++;
i = end-1;
}
}
if obj != nil {
obj.funcs = make([]*TextSym, 0, objCount);
}
if prevTextSym != nil {
prevTextSym.End = t.textEnd;
}
// Extract text symbols into function array and individual
// object function arrys.
t.funcs = make([]*TextSym, 0, count);
for _, sym := range t.Syms {
sym, ok := sym.(*TextSym);
if !ok || sym.Name == "etext" {
continue;
}
t.funcs = t.funcs[0:len(t.funcs)+1];
t.funcs[len(t.funcs)-1] = sym;
sym.obj.funcs = sym.obj.funcs[0:len(sym.obj.funcs)+1];
sym.obj.funcs[len(sym.obj.funcs)-1] = sym;
}
// Sort text symbols
sort.Sort(byValue(t.funcs));
}
func (t *GoSymTable) sliceLineTable(lt *lineTable) {
for _, fn := range t.funcs {
fn.lt = lt.slice(fn.Entry());
lt = fn.lt;;
}
}
// SymFromPC looks up a text symbol given a program counter within
// some function. Returns nil if no function contains this PC.
func (t *GoSymTable) SymFromPC(pc uint64) *TextSym {
syms := t.funcs;
if pc > t.textEnd {
return nil;
}
if len(syms) == 0 || pc < syms[0].Value {
return nil;
}
if pc >= syms[len(syms)-1].Value {
return syms[len(syms)-1];
}
l := 0;
n := len(syms);
for n > 0 {
m := n/2;
s := syms[l+m];
switch {
case s.Value <= pc && pc < syms[l+m+1].Value:
return s;
case pc < s.Value:
n = m;
default:
l += m+1;
n -= m+1;
}
}
panic("not reached, pc=", pc);
}
// LineFromPC looks up line number information for a program counter.
// Returns a file path, a line number within that file, and the
// TextSym at pc.
func (t *GoSymTable) LineFromPC(pc uint64) (string, int, *TextSym) {
sym := t.SymFromPC(pc);
if sym == nil {
return "", 0, nil;
}
aline := sym.lt.alineFromPC(pc);
path, line := sym.obj.lineFromAline(aline);
return path, line, sym;
}
// PCFromLine looks up the first program counter on the given line in
// the named file. Returns UnknownPathError or UnknownLineError if
// there is an error looking up this line.
func (t *GoSymTable) PCFromLine(file string, line int) (uint64, *TextSym, os.Error) {
obj, ok := t.files[file];
if !ok {
return 0, nil, UnknownFileError(file);
}
aline, err := obj.alineFromLine(file, line);
if err != nil {
return 0, nil, err;
}
for _, f := range obj.funcs {
pc := f.lt.pcFromAline(aline, f.End);
if pc != 0 {
return pc, f, nil;
}
}
return 0, nil, &UnknownLineError{file, line};
}
// SymFromName looks up a symbol by name. The name must refer to a
// global text, data, or BSS symbol.
func (t *GoSymTable) SymFromName(name string) GoSym {
// TODO(austin) Maybe make a map
for _, v := range t.Syms {
c := v.Common();
switch c.Type {
case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
if c.Name == name {
return v;
}
}
}
return nil;
}
// SymFromAddr looks up a symbol by address. The symbol will be a
// text, data, or BSS symbol. addr must be the exact address of the
// symbol, unlike for SymFromPC.
func (t *GoSymTable) SymFromAddr(addr uint64) GoSym {
// TODO(austin) Maybe make a map
for _, v := range t.Syms {
c := v.Common();
switch c.Type {
case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b':
if c.Value == addr {
return v;
}
}
}
return nil;
}
/*
* Object files
*/
func (o *object) lineFromAline(aline int) (string, int) {
type stackEnt struct {
path string;
start int;
offset int;
prev *stackEnt;
};
noPath := &stackEnt{"", 0, 0, nil};
tos := noPath;
// TODO(austin) I have no idea how 'Z' symbols work, except
// that they pop the stack.
pathloop:
for _, s := range o.paths {
val := int(s.Value);
switch {
case val > aline:
break pathloop;
case val == 1:
// Start a new stack
tos = &stackEnt{s.Name, val, 0, noPath};
case s.Name == "":
// Pop
if tos == noPath {
return "<malformed symbol table>", 0;
}
tos.prev.offset += val - tos.start;
tos = tos.prev;
default:
// Push
tos = &stackEnt{s.Name, val, 0, tos};
}
}
if tos == noPath {
return "", 0;
}
return tos.path, aline - tos.start - tos.offset + 1;
}
func (o *object) alineFromLine(path string, line int) (int, os.Error) {
if line < 1 {
return 0, &UnknownLineError{path, line};
}
for i, s := range o.paths {
// Find this path
if s.Name != path {
continue;
}
// Find this line at this stack level
depth := 0;
var incstart int;
line += int(s.Value);
pathloop:
for _, s := range o.paths[i:len(o.paths)] {
val := int(s.Value);
switch {
case depth == 1 && val >= line:
return line - 1, nil;
case s.Name == "":
depth--;
if depth == 0 {
break pathloop;
} else if depth == 1 {
line += val - incstart;
}
default:
if depth == 1 {
incstart = val;
}
depth++;
}
}
return 0, &UnknownLineError{path, line};
}
return 0, UnknownFileError(path);
}
/*
* Line tables
*/
const quantum = 1;
func (lt *lineTable) parse(targetPC uint64, targetLine int) ([]byte, uint64, int) {
// The PC/line table can be thought of as a sequence of
// <pc update>* <line update>
// batches. Each update batch results in a (pc, line) pair,
// where line applies to every PC from pc up to but not
// including the pc of the next pair.
//
// Here we process each update individually, which simplifies
// the code, but makes the corner cases more confusing.
b, pc, line := lt.blob, lt.pc, lt.line;
for pc <= targetPC && line != targetLine && len(b) != 0 {
code := b[0];
b = b[1:len(b)];
switch {
case code == 0:
if len(b) < 4 {
b = b[0:0];
break;
}
val := msb.Uint32(b);
b = b[4:len(b)];
line += int(val);
case code <= 64:
line += int(code);
case code <= 128:
line -= int(code - 64);
default:
pc += quantum*uint64(code - 128);
continue;
}
pc += quantum;
}
return b, pc, line;
}
func (lt *lineTable) slice(pc uint64) *lineTable {
blob, pc, line := lt.parse(pc, -1);
return &lineTable{blob, pc, line};
}
func (lt *lineTable) alineFromPC(targetPC uint64) int {
_, _, aline := lt.parse(targetPC, -1);
return aline;
}
func (lt *lineTable) pcFromAline(aline int, maxPC uint64) uint64 {
_, pc, line := lt.parse(maxPC, aline);
if line != aline {
// Never found aline
return 0;
}
// Subtract quantum from PC to account for post-line increment
return pc - quantum;
}
/*
* Errors
*/
// UnknownFileError represents a failure to find the specific file in
// the symbol table.
type UnknownFileError string
func (e UnknownFileError) String() string {
// TODO(austin) string conversion required because of 6g bug
return "unknown file " + string(e);
}
// UnknownLineError represents a failure to map a line to a program
// counter, either because the line is beyond the bounds of the file
// or because there is no code on the given line.
type UnknownLineError struct {
File string;
Line int;
}
func (e *UnknownLineError) String() string {
return "no code on line " + e.File + ":" + strconv.Itoa(e.Line);
}
/*
* ELF
*/
func ElfGoSyms(elf *Elf) (*GoSymTable, os.Error) {
text := elf.Section(".text");
if text == nil {
return nil, nil;
}
tab := &GoSymTable{
textEnd: text.Addr + text.Size,
files: make(map[string] *object),
};
// Symbol table
sec := elf.Section(".gosymtab");
if sec == nil {
return nil, nil;
}
sr, err := sec.Open();
if err != nil {
return nil, err;
}
err = tab.readGoSymTab(sr);
if err != nil {
return nil, err;
}
// Line table
sec = elf.Section(".gopclntab");
if sec == nil {
return nil, nil;
}
sr, err = sec.Open();
if err != nil {
return nil, err;
}
blob, err := io.ReadAll(sr);
if err != nil {
return nil, err;
}
lt := &lineTable{blob, text.Addr, 0};
tab.processTextSyms();
tab.sliceLineTable(lt);
return tab, nil;
}

View File

@ -1,7 +0,0 @@
// Empty include file to generate z symbols
// EOF

View File

@ -1,89 +0,0 @@
TEXT linefrompc(SB),7,$0 // Each byte stores its line delta
BYTE $2;
BYTE $1;
BYTE $1; BYTE $0;
BYTE $1; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1;
BYTE $1;
BYTE $1; BYTE $0;
BYTE $1; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
#include "pclinetest.h"
BYTE $2;
#include "pclinetest.h"
BYTE $2;
TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes
BYTE $31; BYTE $0;
BYTE $1; BYTE $1; BYTE $0;
BYTE $1; BYTE $0;
BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0;
#include "pclinetest.h"
BYTE $4; BYTE $0;
BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
#include "pclinetest.h"
BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0;
TEXT main(SB),7,$0
// Prevent GC of our test symbols
CALL linefrompc(SB)
CALL pcfromline(SB)
// Keep the linker happy
TEXT sys·morestack(SB),7,$0
RET
TEXT sys·morestack00(SB),7,$0
RET
TEXT sys·morestack10(SB),7,$0
RET
TEXT sys·morestack01(SB),7,$0
RET
TEXT sys·morestack11(SB),7,$0
RET
TEXT sys·morestack8(SB),7,$0
RET
TEXT sys·morestack16(SB),7,$0
RET
TEXT sys·morestack24(SB),7,$0
RET
TEXT sys·morestack32(SB),7,$0
RET
TEXT sys·morestack40(SB),7,$0
RET
TEXT sys·morestack48(SB),7,$0
RET
TEXT sys·morestack8(SB),7,$0
RET

View File

@ -1,207 +0,0 @@
// 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);
}
}
}
}