]> Cypherpunks repositories - gostls13.git/commitdiff
elf file parser
authorRuss Cox <rsc@golang.org>
Mon, 31 Aug 2009 23:48:44 +0000 (16:48 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 31 Aug 2009 23:48:44 +0000 (16:48 -0700)
R=austin
DELTA=448  (447 added, 0 deleted, 1 changed)
OCL=34139
CL=34150

src/pkg/Make.deps
src/pkg/debug/elf/file.go [new file with mode: 0644]
src/pkg/debug/elf/file_test.go [new file with mode: 0644]
src/pkg/debug/elf/testdata/gcc-386-freebsd-exec [new file with mode: 0755]
src/pkg/debug/elf/testdata/gcc-amd64-linux-exec [new file with mode: 0755]

index 9b60510c6f079a456b333105a7f2749d32256516..02dfd20d37858df70fec9bb73569bc974b7e3e2c 100644 (file)
@@ -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 (file)
index 0000000..b91944a
--- /dev/null
@@ -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 (file)
index 0000000..aceda51
--- /dev/null
@@ -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 (executable)
index 0000000..7af9c58
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 (executable)
index 0000000..c6cb1de
Binary files /dev/null and b/src/pkg/debug/elf/testdata/gcc-amd64-linux-exec differ