diff options
Diffstat (limited to 'src/pkg/debug/elf/file.go')
| -rw-r--r-- | src/pkg/debug/elf/file.go | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go new file mode 100644 index 0000000000..b91944a855 --- /dev/null +++ b/src/pkg/debug/elf/file.go @@ -0,0 +1,324 @@ +// 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 elf + +import ( + "debug/binary"; +"fmt"; + "io"; + "os"; +) + +// TODO: error reporting detail + +/* + * Internal ELF representation + */ + +// A FileHeader represents an ELF file header. +type FileHeader struct { + Class Class; + Data Data; + Version Version; + OSABI OSABI; + ABIVersion uint8; + ByteOrder binary.ByteOrder; + Type Type; + Machine Machine; +} + +// A File represents an open ELF file. +type File struct { + FileHeader; + Sections []*Section; + Progs []*Prog; + + closer io.Closer; +} + +// A SectionHeader represents a single ELF section header. +type SectionHeader struct { + Name string; + Type SectionType; + Flags SectionFlag; + Addr uint64; + Offset uint64; + Size uint64; + Link uint32; + Info uint32; + Addralign uint64; + Entsize uint64; +} + +// A Section represents a single section in an ELF file. +type Section struct { + SectionHeader; + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt; + sr *io.SectionReader; +} + +// Open returns a new ReadSeeker reading the ELF section. +func (s *Section) Open() io.ReadSeeker { + return io.NewSectionReader(s.sr, 0, 1<<63 - 1); +} + +// A ProgHeader represents a single ELF program header. +type ProgHeader struct { + Type ProgType; + Flags ProgFlag; + Vaddr uint64; + Paddr uint64; + Filesz uint64; + Memsz uint64; + Align uint64; +} + +// A Prog represents a single ELF program header in an ELF binary. +type Prog struct { + ProgHeader; + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt; + sr *io.SectionReader; +} + +// Open returns a new ReadSeeker reading the ELF program body. +func (p *Prog) Open() io.ReadSeeker { + return io.NewSectionReader(p.sr, 0, 1<<63 - 1); +} + + +/* + * 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; +} + +// Open opens the named file using os.Open and prepares it for use as an ELF binary. +func Open(name string) (*File, os.Error) { + f, err := os.Open(name, os.O_RDONLY, 0); + if err != nil { + return nil, err; + } + ff, err := NewFile(f); + if err != nil { + f.Close(); + return nil, err; + } + ff.closer = f; + return ff, nil; +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() os.Error { + var err os.Error; + if f.closer != nil { + err = f.closer.Close(); + f.closer = nil; + } + return err; +} + +// NewFile creates a new File for acecssing an ELF binary in an underlying reader. +// The ELF binary is expected to start at position 0 in the ReaderAt. +func NewFile(r io.ReaderAt) (*File, os.Error) { + sr := io.NewSectionReader(r, 0, 1<<63 - 1); + // Read and decode ELF identifier + var ident [16]uint8; + if _, err := r.ReadAt(&ident, 0); err != nil { + return nil, err; + } + if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' { + return nil, &FormatError{0, "bad magic number", ident[0:4]}; + } + + f := new(File); + f.Class = Class(ident[EI_CLASS]); + switch f.Class { + case ELFCLASS32: + case ELFCLASS64: + // ok + default: + return nil, &FormatError{0, "unknown ELF class", f.Class}; + } + + f.Data = Data(ident[EI_DATA]); + switch f.Data { + case ELFDATA2LSB: + f.ByteOrder = binary.LittleEndian; + case ELFDATA2MSB: + f.ByteOrder = binary.BigEndian; + default: + return nil, &FormatError{0, "unknown ELF data encoding", f.Data}; + } + + f.Version = Version(ident[EI_VERSION]); + if f.Version != EV_CURRENT { + return nil, &FormatError{0, "unknown ELF version", f.Version}; + } + + f.OSABI = OSABI(ident[EI_OSABI]); + f.ABIVersion = ident[EI_ABIVERSION]; + + // Read ELF file header + var shoff int64; + var shentsize, shnum, shstrndx int; + shstrndx = -1; + switch f.Class { + case ELFCLASS32: + hdr := new(Header32); + sr.Seek(0, 0); + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err; + } + f.Type = Type(hdr.Type); + f.Machine = Machine(hdr.Machine); + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v}; + } + shoff = int64(hdr.Shoff); + shentsize = int(hdr.Shentsize); + shnum = int(hdr.Shnum); + shstrndx = int(hdr.Shstrndx); + case ELFCLASS64: + hdr := new(Header64); + sr.Seek(0, 0); + if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { + return nil, err; + } + f.Type = Type(hdr.Type); + f.Machine = Machine(hdr.Machine); + if v := Version(hdr.Version); v != f.Version { + return nil, &FormatError{0, "mismatched ELF version", v}; + } + shoff = int64(hdr.Shoff); + shentsize = int(hdr.Shentsize); + shnum = int(hdr.Shnum); + shstrndx = int(hdr.Shstrndx); + } + if shstrndx < 0 || shstrndx >= shnum { + return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}; + } + + // Read program headers + // TODO + + // Read section headers + f.Sections = make([]*Section, shnum); + names := make([]uint32, shnum); + for i := 0; i < shnum; i++ { + off := shoff + int64(i)*int64(shentsize); + sr.Seek(off, 0); + s := new(Section); + switch f.Class { + case ELFCLASS32: + sh := new(Section32); + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err; + } + names[i] = sh.Name; + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Addr: uint64(sh.Addr), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + }; + case ELFCLASS64: + sh := new(Section64); + if err := binary.Read(sr, f.ByteOrder, sh); err != nil { + return nil, err; + } + names[i] = sh.Name; + s.SectionHeader = SectionHeader{ + Type: SectionType(sh.Type), + Flags: SectionFlag(sh.Flags), + Offset: uint64(sh.Off), + Size: uint64(sh.Size), + Addr: uint64(sh.Addr), + Link: uint32(sh.Link), + Info: uint32(sh.Info), + Addralign: uint64(sh.Addralign), + Entsize: uint64(sh.Entsize), + + }; + } + s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)); + s.ReaderAt = s.sr; + f.Sections[i] = s; + } + + // Load section header string table. + s := f.Sections[shstrndx]; + shstrtab := make([]byte, s.Size); + if _, err := r.ReadAt(shstrtab, int64(s.Offset)); err != nil { + return nil, err; + } + for i, s := range f.Sections { + var ok bool; + s.Name, ok = getString(shstrtab, int(names[i])); + if !ok { + return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}; + } + } + + return f, nil; +} + +// getString extracts a string from an ELF string table. +func getString(section []byte, start int) (string, bool) { + if start < 0 || start >= len(section) { + return "", false; + } + + for end := start; end < len(section); end++ { + if section[end] == 0 { + return string(section[start:end]), true; + } + } + return "", false; +} + +// Section returns a section with the given name, or nil if no such +// section exists. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name { + return s; + } + } + return nil; +} |
