diff --git a/src/pkg/Make.deps b/src/pkg/Make.deps index 9b60510c6f0..02dfd20d378 100644 --- a/src/pkg/Make.deps +++ b/src/pkg/Make.deps @@ -17,7 +17,7 @@ crypto/md5.install: hash.install os.install crypto/sha1.install: hash.install os.install datafmt.install: bytes.install container/vector.install fmt.install go/scanner.install go/token.install io.install os.install reflect.install runtime.install strconv.install strings.install debug/binary.install: io.install math.install os.install reflect.install -debug/elf.install: fmt.install io.install os.install strconv.install +debug/elf.install: debug/binary.install fmt.install io.install os.install strconv.install ebnf.install: container/vector.install fmt.install go/scanner.install go/token.install os.install strconv.install strings.install unicode.install utf8.install exec.install: os.install strings.install exvar.install: bytes.install fmt.install http.install io.install log.install strconv.install sync.install diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go new file mode 100644 index 00000000000..b91944a855f --- /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; +} diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go new file mode 100644 index 00000000000..aceda51fee5 --- /dev/null +++ b/src/pkg/debug/elf/file_test.go @@ -0,0 +1,129 @@ +// 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"; + "reflect"; + "testing"; +) + +type fileTest struct { + file string; + hdr FileHeader; + sections []SectionHeader; +} + +var fileTests = []fileTest { + fileTest{ + "testdata/gcc-386-freebsd-exec", + FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386}, + []SectionHeader{ + SectionHeader{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + SectionHeader{".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".hash", SHT_HASH, SHF_ALLOC, 0x80480ec, 0xec, 0x90, 0x3, 0x0, 0x4, 0x4}, + SectionHeader{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x804817c, 0x17c, 0x110, 0x4, 0x1, 0x4, 0x10}, + SectionHeader{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x804828c, 0x28c, 0xbb, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".rel.plt", SHT_REL, SHF_ALLOC, 0x8048348, 0x348, 0x20, 0x3, 0x7, 0x4, 0x8}, + SectionHeader{".init", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x8048368, 0x368, 0x11, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".plt", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x804837c, 0x37c, 0x50, 0x0, 0x0, 0x4, 0x4}, + SectionHeader{".text", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x80483cc, 0x3cc, 0x180, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".fini", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x804854c, 0x54c, 0xc, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x8048558, 0x558, 0xa3, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".data", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x80495fc, 0x5fc, 0xc, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x8049608, 0x608, 0x4, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".dynamic", SHT_DYNAMIC, SHF_WRITE+SHF_ALLOC, 0x804960c, 0x60c, 0x98, 0x4, 0x0, 0x4, 0x8}, + SectionHeader{".ctors", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x80496a4, 0x6a4, 0x8, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".dtors", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x80496ac, 0x6ac, 0x8, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".jcr", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x80496b4, 0x6b4, 0x4, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".got", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x80496b8, 0x6b8, 0x1c, 0x0, 0x0, 0x4, 0x4}, + SectionHeader{".bss", SHT_NOBITS, SHF_WRITE+SHF_ALLOC, 0x80496d4, 0x6d4, 0x20, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".comment", SHT_PROGBITS, 0x0, 0x0, 0x6d4, 0x12d, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x801, 0x20, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0x821, 0x1b, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0x83c, 0x11d, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0x959, 0x41, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0x99a, 0x35, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_frame", SHT_PROGBITS, 0x0, 0x0, 0x9d0, 0x30, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".debug_str", SHT_PROGBITS, 0x0, 0x0, 0xa00, 0xd, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xa0d, 0xf8, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".symtab", SHT_SYMTAB, 0x0, 0x0, 0xfb8, 0x4b0, 0x1d, 0x38, 0x4, 0x10}, + SectionHeader{".strtab", SHT_STRTAB, 0x0, 0x0, 0x1468, 0x206, 0x0, 0x0, 0x1, 0x0}, + } + }, + fileTest{ + "testdata/gcc-amd64-linux-exec", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64}, + []SectionHeader{ + SectionHeader{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + SectionHeader{".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".note.ABI-tag", SHT_NOTE, SHF_ALLOC, 0x40021c, 0x21c, 0x20, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".hash", SHT_HASH, SHF_ALLOC, 0x400240, 0x240, 0x24, 0x5, 0x0, 0x8, 0x4}, + SectionHeader{".gnu.hash", SHT_LOOS+268435446, SHF_ALLOC, 0x400268, 0x268, 0x1c, 0x5, 0x0, 0x8, 0x0}, + SectionHeader{".dynsym", SHT_DYNSYM, SHF_ALLOC, 0x400288, 0x288, 0x60, 0x6, 0x1, 0x8, 0x18}, + SectionHeader{".dynstr", SHT_STRTAB, SHF_ALLOC, 0x4002e8, 0x2e8, 0x3d, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".gnu.version", SHT_HIOS, SHF_ALLOC, 0x400326, 0x326, 0x8, 0x5, 0x0, 0x2, 0x2}, + SectionHeader{".gnu.version_r", SHT_LOOS+268435454, SHF_ALLOC, 0x400330, 0x330, 0x20, 0x6, 0x1, 0x8, 0x0}, + SectionHeader{".rela.dyn", SHT_RELA, SHF_ALLOC, 0x400350, 0x350, 0x18, 0x5, 0x0, 0x8, 0x18}, + SectionHeader{".rela.plt", SHT_RELA, SHF_ALLOC, 0x400368, 0x368, 0x30, 0x5, 0xc, 0x8, 0x18}, + SectionHeader{".init", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x400398, 0x398, 0x18, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".plt", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x4003b0, 0x3b0, 0x30, 0x0, 0x0, 0x4, 0x10}, + SectionHeader{".text", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x4003e0, 0x3e0, 0x1b4, 0x0, 0x0, 0x10, 0x0}, + SectionHeader{".fini", SHT_PROGBITS, SHF_ALLOC+SHF_EXECINSTR, 0x400594, 0x594, 0xe, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".rodata", SHT_PROGBITS, SHF_ALLOC, 0x4005a4, 0x5a4, 0x11, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, 0x4005b8, 0x5b8, 0x24, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".eh_frame", SHT_PROGBITS, SHF_ALLOC, 0x4005e0, 0x5e0, 0xa4, 0x0, 0x0, 0x8, 0x0}, + SectionHeader{".ctors", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x600688, 0x688, 0x10, 0x0, 0x0, 0x8, 0x0}, + SectionHeader{".dtors", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x600698, 0x698, 0x10, 0x0, 0x0, 0x8, 0x0}, + SectionHeader{".jcr", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x6006a8, 0x6a8, 0x8, 0x0, 0x0, 0x8, 0x0}, + SectionHeader{".dynamic", SHT_DYNAMIC, SHF_WRITE+SHF_ALLOC, 0x6006b0, 0x6b0, 0x1a0, 0x6, 0x0, 0x8, 0x10}, + SectionHeader{".got", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x600850, 0x850, 0x8, 0x0, 0x0, 0x8, 0x8}, + SectionHeader{".got.plt", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x600858, 0x858, 0x28, 0x0, 0x0, 0x8, 0x8}, + SectionHeader{".data", SHT_PROGBITS, SHF_WRITE+SHF_ALLOC, 0x600880, 0x880, 0x18, 0x0, 0x0, 0x8, 0x0}, + SectionHeader{".bss", SHT_NOBITS, SHF_WRITE+SHF_ALLOC, 0x600898, 0x898, 0x8, 0x0, 0x0, 0x4, 0x0}, + SectionHeader{".comment", SHT_PROGBITS, 0x0, 0x0, 0x898, 0x126, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_aranges", SHT_PROGBITS, 0x0, 0x0, 0x9c0, 0x90, 0x0, 0x0, 0x10, 0x0}, + SectionHeader{".debug_pubnames", SHT_PROGBITS, 0x0, 0x0, 0xa50, 0x25, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_info", SHT_PROGBITS, 0x0, 0x0, 0xa75, 0x1a7, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_abbrev", SHT_PROGBITS, 0x0, 0x0, 0xc1c, 0x6f, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_line", SHT_PROGBITS, 0x0, 0x0, 0xc8b, 0x13f, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".debug_str", SHT_PROGBITS, SHF_MERGE+SHF_STRINGS, 0x0, 0xdca, 0xb1, 0x0, 0x0, 0x1, 0x1}, + SectionHeader{".debug_ranges", SHT_PROGBITS, 0x0, 0x0, 0xe80, 0x90, 0x0, 0x0, 0x10, 0x0}, + SectionHeader{".shstrtab", SHT_STRTAB, 0x0, 0x0, 0xf10, 0x149, 0x0, 0x0, 0x1, 0x0}, + SectionHeader{".symtab", SHT_SYMTAB, 0x0, 0x0, 0x19a0, 0x6f0, 0x24, 0x39, 0x8, 0x18}, + SectionHeader{".strtab", SHT_STRTAB, 0x0, 0x0, 0x2090, 0x1fc, 0x0, 0x0, 0x1, 0x0}, + } + } +} + +func TestOpen(t *testing.T) { + for i := range fileTests { + tt := &fileTests[i]; + + f, err := Open(tt.file); + if err != nil { + t.Error(err); + continue; + } + if !reflect.DeepEqual(f.FileHeader, tt.hdr) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr); + continue; + } + for i, s := range f.Sections { + if i >= len(tt.sections) { + break; + } + sh := &tt.sections[i]; + if !reflect.DeepEqual(&s.SectionHeader, sh) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &s.SectionHeader, sh); + } + } + tn := len(tt.sections); + fn := len(f.Sections); + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn); + } + } +} diff --git a/src/pkg/debug/elf/testdata/gcc-386-freebsd-exec b/src/pkg/debug/elf/testdata/gcc-386-freebsd-exec new file mode 100755 index 00000000000..7af9c58ca73 Binary files /dev/null and b/src/pkg/debug/elf/testdata/gcc-386-freebsd-exec differ diff --git a/src/pkg/debug/elf/testdata/gcc-amd64-linux-exec b/src/pkg/debug/elf/testdata/gcc-amd64-linux-exec new file mode 100755 index 00000000000..c6cb1de28c7 Binary files /dev/null and b/src/pkg/debug/elf/testdata/gcc-amd64-linux-exec differ