blob: 95b7cc41c4a87bcad4febc96b0642e71cc04f1c2 [file] [log] [blame]
// 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);
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;
}