1
0
mirror of https://github.com/golang/go synced 2024-11-25 06:47:56 -07:00

ELF reader and Go symbol table and PC/line table decoder.

R=rsc
APPROVED=rsc
DELTA=1425  (1425 added, 0 deleted, 0 changed)
OCL=33432
CL=33517
This commit is contained in:
Austin Clements 2009-08-19 10:05:11 -07:00
parent 382a19c3de
commit 6ece1b561c
5 changed files with 1445 additions and 0 deletions

14
usr/austin/sym/Makefile Normal file
View File

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

190
usr/austin/sym/binary.go Normal file
View File

@ -0,0 +1,190 @@
// 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;
n, 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;
n, 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;
n, 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;
n, 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 NULL-terminated string.
func (r *binaryReader) ReadCString() string {
str, err := r.Reader.ReadLineString('\x00', false);
if r.err == nil && err != nil {
r.err = err;
}
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)));
}

237
usr/austin/sym/elf.go Normal file
View File

@ -0,0 +1,237 @@
// 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";
"log";
"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;
n, 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);
n, err = io.ReadFull(r, blob);
strings := make(map[uint32] string);
strStart := uint32(0);
for i, c := range blob {
if c == 0 {
strings[strStart] = string(blob[strStart:i]);
strStart = uint32(i+1);
}
}
for i, s := range e.Sections {
var ok bool;
s.Name, ok = strings[secNames[i]];
if !ok {
return nil, &FormatError{start + shoff + int64(i*shentsize), "bad section name", secNames[i]};
}
}
return e, nil;
}
// 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;
}

389
usr/austin/sym/elffmt.go Normal file
View File

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

615
usr/austin/sym/gosymtab.go Normal file
View File

@ -0,0 +1,615 @@
// 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";
"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 {
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;
// 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;
}
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;
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);
}
objCount = 0;
i = end-1;
case *TextSym:
if sym.Name == "etext" {
continue;
}
// 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);
}
// 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;
}
// 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;
}
// TODO(austin) Implement PCFromLine. This is more difficult because
// we first have to figure out which object file PC is in, and which
// segment of the line table that corresponds to.
//
// For each place path appears (either from push or pop),
// 1. Turn line into an absolute line number using the history stack
// 2. minpc = Entry of the first text sym in the object
// 3. maxpc = Entry of the first text sym in the next object
// 4. lt = lt.slice(minpc);
// 5. Find PC of first occurrence of absolute line number between minpc and maxpc
//
// I'm not sure if this guarantees a PC at the begining of an
// instruction.
/*
* Object files
*/
func (o *object) lineFromAline(aline int) (string, int) {
type stackEnt struct {
path string;
start int;
offset int;
prev *stackEnt;
};
noPath := &stackEnt{"<malformed absolute line>", 0, 0, nil};
tos := noPath;
// TODO(austin) I have no idea how 'Z' symbols work, except
// that they pop the stack.
for _, s := range o.paths {
val := int(s.Value);
switch {
case val > aline:
break;
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};
}
}
return tos.path, aline - tos.start - tos.offset + 1;
}
/*
* Line tables
*/
func (lt *lineTable) parse(targetPC uint64) ([]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.
const quantum = 1;
b, pc, line := lt.blob, lt.pc, lt.line;
for pc <= targetPC && len(b) != 0 {
code := b[0];
b = b[1:len(b)];
switch {
case code == 0:
if len(b) < 4 {
b = b[0:1];
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);
return &lineTable{blob, pc, line};
}
func (lt *lineTable) alineFromPC(targetPC uint64) int {
_1, _2, aline := lt.parse(targetPC);
return aline;
}
/*
* 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};
// 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;
}