diff options
Diffstat (limited to 'src/debug/pe')
-rw-r--r-- | src/debug/pe/file.go | 394 | ||||
-rw-r--r-- | src/debug/pe/file_test.go | 243 | ||||
-rw-r--r-- | src/debug/pe/pe.go | 134 | ||||
-rw-r--r-- | src/debug/pe/testdata/gcc-386-mingw-exec | bin | 0 -> 29941 bytes | |||
-rw-r--r-- | src/debug/pe/testdata/gcc-386-mingw-obj | bin | 0 -> 2372 bytes | |||
-rw-r--r-- | src/debug/pe/testdata/gcc-amd64-mingw-exec | bin | 0 -> 273083 bytes | |||
-rw-r--r-- | src/debug/pe/testdata/gcc-amd64-mingw-obj | bin | 0 -> 736 bytes | |||
-rw-r--r-- | src/debug/pe/testdata/hello.c | 8 |
8 files changed, 779 insertions, 0 deletions
diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go new file mode 100644 index 000000000..759e5674f --- /dev/null +++ b/src/debug/pe/file.go @@ -0,0 +1,394 @@ +// 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 pe implements access to PE (Microsoft Windows Portable Executable) files. +package pe + +import ( + "debug/dwarf" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "strconv" +) + +// A File represents an open PE file. +type File struct { + FileHeader + OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 + Sections []*Section + Symbols []*Symbol + + closer io.Closer +} + +type SectionHeader struct { + Name string + VirtualSize uint32 + VirtualAddress uint32 + Size uint32 + Offset uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +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 +} + +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} + +type ImportDirectory struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 + + dll string +} + +// Data reads and returns the contents of the PE section. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the PE section. +func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +type FormatError struct { + off int64 + msg string + val interface{} +} + +func (e *FormatError) Error() 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 a PE binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + 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() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +var ( + sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{})) + sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) +) + +// NewFile creates a new File for accessing a PE binary in an underlying reader. +func NewFile(r io.ReaderAt) (*File, error) { + f := new(File) + sr := io.NewSectionReader(r, 0, 1<<63-1) + + var dosheader [96]byte + if _, err := r.ReadAt(dosheader[0:], 0); err != nil { + return nil, err + } + var base int64 + if dosheader[0] == 'M' && dosheader[1] == 'Z' { + signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) + var sign [4]byte + r.ReadAt(sign[:], signoff) + if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { + return nil, errors.New("Invalid PE File Format.") + } + base = signoff + 4 + } else { + base = int64(0) + } + sr.Seek(base, os.SEEK_SET) + if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { + return nil, err + } + if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { + return nil, errors.New("Invalid PE File Format.") + } + + var ss []byte + if f.FileHeader.NumberOfSymbols > 0 { + // Get COFF string table, which is located at the end of the COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) + var l uint32 + if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { + return nil, err + } + ss = make([]byte, l) + if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { + return nil, err + } + + // Process COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) + aux := uint8(0) + for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { + cs := new(COFFSymbol) + if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { + return nil, err + } + if aux > 0 { + aux-- + continue + } + var name string + if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { + si := int(binary.LittleEndian.Uint32(cs.Name[4:])) + name, _ = getString(ss, si) + } else { + name = cstring(cs.Name[:]) + } + aux = cs.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: cs.Value, + SectionNumber: cs.SectionNumber, + Type: cs.Type, + StorageClass: cs.StorageClass, + } + f.Symbols = append(f.Symbols, s) + } + } + + // Read optional header. + sr.Seek(base, os.SEEK_SET) + if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { + return nil, err + } + var oh32 OptionalHeader32 + var oh64 OptionalHeader64 + switch f.FileHeader.SizeOfOptionalHeader { + case sizeofOptionalHeader32: + if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil { + return nil, err + } + if oh32.Magic != 0x10b { // PE32 + return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic) + } + f.OptionalHeader = &oh32 + case sizeofOptionalHeader64: + if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil { + return nil, err + } + if oh64.Magic != 0x20b { // PE32+ + return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) + } + f.OptionalHeader = &oh64 + } + + // Process sections. + f.Sections = make([]*Section, f.FileHeader.NumberOfSections) + for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { + sh := new(SectionHeader32) + if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { + return nil, err + } + var name string + if sh.Name[0] == '\x2F' { + si, _ := strconv.Atoi(cstring(sh.Name[1:])) + name, _ = getString(ss, si) + } else { + name = cstring(sh.Name[0:]) + } + s := new(Section) + s.SectionHeader = SectionHeader{ + Name: name, + VirtualSize: sh.VirtualSize, + VirtualAddress: sh.VirtualAddress, + Size: sh.SizeOfRawData, + Offset: sh.PointerToRawData, + PointerToRelocations: sh.PointerToRelocations, + PointerToLineNumbers: sh.PointerToLineNumbers, + NumberOfRelocations: sh.NumberOfRelocations, + NumberOfLineNumbers: sh.NumberOfLineNumbers, + Characteristics: sh.Characteristics, + } + s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) + s.ReaderAt = s.sr + f.Sections[i] = s + } + return f, nil +} + +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[0:i]) +} + +// getString extracts a string from symbol 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 the first 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 +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the required ones, and the debug/dwarf package + // does not use the others, so don't bother loading them. + var names = [...]string{"abbrev", "info", "str"} + var dat [len(names)][]byte + for i, name := range names { + name = ".debug_" + name + s := f.Section(name) + if s == nil { + continue + } + b, err := s.Data() + if err != nil && uint32(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + + abbrev, info, str := dat[0], dat[1], dat[2] + return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]string, error) { + pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 + ds := f.Section(".idata") + if ds == nil { + // not dynamic, so no libraries + return nil, nil + } + d, err := ds.Data() + if err != nil { + return nil, err + } + var ida []ImportDirectory + for len(d) > 0 { + var dt ImportDirectory + dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) + dt.Name = binary.LittleEndian.Uint32(d[12:16]) + dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) + d = d[20:] + if dt.OriginalFirstThunk == 0 { + break + } + ida = append(ida, dt) + } + names, _ := ds.Data() + var all []string + for _, dt := range ida { + dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) + d, _ = ds.Data() + // seek to OriginalFirstThunk + d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] + for len(d) > 0 { + if pe64 { // 64bit + va := binary.LittleEndian.Uint64(d[0:8]) + d = d[8:] + if va == 0 { + break + } + if va&0x8000000000000000 > 0 { // is Ordinal + // TODO add dynimport ordinal support. + } else { + fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) + all = append(all, fn+":"+dt.dll) + } + } else { // 32bit + va := binary.LittleEndian.Uint32(d[0:4]) + d = d[4:] + if va == 0 { + break + } + if va&0x80000000 > 0 { // is Ordinal + // TODO add dynimport ordinal support. + //ord := va&0x0000FFFF + } else { + fn, _ := getString(names, int(va-ds.VirtualAddress+2)) + all = append(all, fn+":"+dt.dll) + } + } + } + } + + return all, nil +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + // TODO + // cgo -dynimport don't use this for windows PE, so just return. + return nil, nil +} diff --git a/src/debug/pe/file_test.go b/src/debug/pe/file_test.go new file mode 100644 index 000000000..0d73969bc --- /dev/null +++ b/src/debug/pe/file_test.go @@ -0,0 +1,243 @@ +// 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 pe + +import ( + "reflect" + "testing" +) + +type fileTest struct { + file string + hdr FileHeader + opthdr interface{} + sections []*SectionHeader + symbols []*Symbol +} + +var fileTests = []fileTest{ + { + "testdata/gcc-386-mingw-obj", + FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, + nil, + []*SectionHeader{ + {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, + {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, + {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, + {".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, + {".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, + {".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, + {".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, + {".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, + {".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, + {".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, + {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, + {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, + }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"_main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".debug_abbrev", 0x0, 4, 0x0, 0x3}, + {".debug_info", 0x0, 5, 0x0, 0x3}, + {".debug_line", 0x0, 6, 0x0, 0x3}, + {".rdata", 0x0, 7, 0x0, 0x3}, + {".debug_frame", 0x0, 8, 0x0, 0x3}, + {".debug_loc", 0x0, 9, 0x0, 0x3}, + {".debug_pubnames", 0x0, 10, 0x0, 0x3}, + {".debug_pubtypes", 0x0, 11, 0x0, 0x3}, + {".debug_aranges", 0x0, 12, 0x0, 0x3}, + {"___main", 0x0, 0, 0x20, 0x2}, + {"_puts", 0x0, 0, 0x20, 0x2}, + }, + }, + { + "testdata/gcc-386-mingw-exec", + FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, + &OptionalHeader32{ + 0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, + [16]DataDirectory{ + {0x0, 0x0}, + {0x5000, 0x3c8}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x7000, 0x18}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + }, + }, + []*SectionHeader{ + {".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060}, + {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080}, + {".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + {".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000}, + {".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000}, + }, + []*Symbol{}, + }, + { + "testdata/gcc-amd64-mingw-obj", + FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4}, + nil, + []*SectionHeader{ + {".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020}, + {".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, + {".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080}, + {".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040}, + {".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040}, + }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".rdata", 0x0, 4, 0x0, 0x3}, + {".xdata", 0x0, 5, 0x0, 0x3}, + {".pdata", 0x0, 6, 0x0, 0x3}, + {"__main", 0x0, 0, 0x20, 0x2}, + {"puts", 0x0, 0, 0x20, 0x2}, + }, + }, + { + "testdata/gcc-amd64-mingw-exec", + FileHeader{0x8664, 0x11, 0x53e4364f, 0x39600, 0x6fc, 0xf0, 0x27}, + &OptionalHeader64{ + 0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x45000, 0x600, 0x46f19, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10, + [16]DataDirectory{ + {0x0, 0x0}, + {0xe000, 0x990}, + {0x0, 0x0}, + {0xa000, 0x498}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x10000, 0x28}, + {0x0, 0x0}, + {0x0, 0x0}, + {0xe254, 0x218}, + {0x0, 0x0}, + {0x0, 0x0}, + {0x0, 0x0}, + }}, + []*SectionHeader{ + {".text", 0x6860, 0x1000, 0x6a00, 0x600, 0x0, 0x0, 0x0, 0x0, 0x60500020}, + {".data", 0xe0, 0x8000, 0x200, 0x7000, 0x0, 0x0, 0x0, 0x0, 0xc0500040}, + {".rdata", 0x6b0, 0x9000, 0x800, 0x7200, 0x0, 0x0, 0x0, 0x0, 0x40600040}, + {".pdata", 0x498, 0xa000, 0x600, 0x7a00, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".xdata", 0x488, 0xb000, 0x600, 0x8000, 0x0, 0x0, 0x0, 0x0, 0x40300040}, + {".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080}, + {".idata", 0x990, 0xe000, 0xa00, 0x8600, 0x0, 0x0, 0x0, 0x0, 0xc0300040}, + {".CRT", 0x68, 0xf000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0400040}, + {".tls", 0x48, 0x10000, 0x200, 0x9200, 0x0, 0x0, 0x0, 0x0, 0xc0600040}, + {".debug_aranges", 0x600, 0x11000, 0x600, 0x9400, 0x0, 0x0, 0x0, 0x0, 0x42500040}, + {".debug_info", 0x1316e, 0x12000, 0x13200, 0x9a00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_abbrev", 0x2ccb, 0x26000, 0x2e00, 0x1cc00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_line", 0x3c4d, 0x29000, 0x3e00, 0x1fa00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_frame", 0x18b8, 0x2d000, 0x1a00, 0x23800, 0x0, 0x0, 0x0, 0x0, 0x42400040}, + {".debug_str", 0x396, 0x2f000, 0x400, 0x25200, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_loc", 0x13240, 0x30000, 0x13400, 0x25600, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + {".debug_ranges", 0xa70, 0x44000, 0xc00, 0x38a00, 0x0, 0x0, 0x0, 0x0, 0x42100040}, + }, + []*Symbol{}, + }, +} + +func isOptHdrEq(a, b interface{}) bool { + switch va := a.(type) { + case *OptionalHeader32: + vb, ok := b.(*OptionalHeader32) + if !ok { + return false + } + return *vb == *va + case *OptionalHeader64: + vb, ok := b.(*OptionalHeader64) + if !ok { + return false + } + return *vb == *va + case nil: + return b == nil + } + return false +} + +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 + } + if !isOptHdrEq(tt.opthdr, f.OptionalHeader) { + t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr) + continue + } + + for i, sh := range f.Sections { + if i >= len(tt.sections) { + break + } + have := &sh.SectionHeader + want := tt.sections[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + tn := len(tt.sections) + fn := len(f.Sections) + if tn != fn { + t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) + } + for i, have := range f.Symbols { + if i >= len(tt.symbols) { + break + } + want := tt.symbols[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } + } +} + +func TestOpenFailure(t *testing.T) { + filename := "file.go" // not a PE file + _, err := Open(filename) // don't crash + if err == nil { + t.Errorf("open %s: succeeded unexpectedly", filename) + } +} diff --git a/src/debug/pe/pe.go b/src/debug/pe/pe.go new file mode 100644 index 000000000..8e90b1b51 --- /dev/null +++ b/src/debug/pe/pe.go @@ -0,0 +1,134 @@ +// 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 pe + +type FileHeader struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +type DataDirectory struct { + VirtualAddress uint32 + Size uint32 +} + +type OptionalHeader32 struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + BaseOfData uint32 + ImageBase uint32 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint32 + SizeOfStackCommit uint32 + SizeOfHeapReserve uint32 + SizeOfHeapCommit uint32 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]DataDirectory +} + +type OptionalHeader64 struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uint64 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint64 + SizeOfStackCommit uint64 + SizeOfHeapReserve uint64 + SizeOfHeapCommit uint64 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]DataDirectory +} + +type SectionHeader32 struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +const COFFSymbolSize = 18 + +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + +const ( + IMAGE_FILE_MACHINE_UNKNOWN = 0x0 + IMAGE_FILE_MACHINE_AM33 = 0x1d3 + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_MACHINE_ARM = 0x1c0 + IMAGE_FILE_MACHINE_EBC = 0xebc + IMAGE_FILE_MACHINE_I386 = 0x14c + IMAGE_FILE_MACHINE_IA64 = 0x200 + IMAGE_FILE_MACHINE_M32R = 0x9041 + IMAGE_FILE_MACHINE_MIPS16 = 0x266 + IMAGE_FILE_MACHINE_MIPSFPU = 0x366 + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466 + IMAGE_FILE_MACHINE_POWERPC = 0x1f0 + IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1 + IMAGE_FILE_MACHINE_R4000 = 0x166 + IMAGE_FILE_MACHINE_SH3 = 0x1a2 + IMAGE_FILE_MACHINE_SH3DSP = 0x1a3 + IMAGE_FILE_MACHINE_SH4 = 0x1a6 + IMAGE_FILE_MACHINE_SH5 = 0x1a8 + IMAGE_FILE_MACHINE_THUMB = 0x1c2 + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 +) diff --git a/src/debug/pe/testdata/gcc-386-mingw-exec b/src/debug/pe/testdata/gcc-386-mingw-exec Binary files differnew file mode 100644 index 000000000..4b808d043 --- /dev/null +++ b/src/debug/pe/testdata/gcc-386-mingw-exec diff --git a/src/debug/pe/testdata/gcc-386-mingw-obj b/src/debug/pe/testdata/gcc-386-mingw-obj Binary files differnew file mode 100644 index 000000000..0c84d898d --- /dev/null +++ b/src/debug/pe/testdata/gcc-386-mingw-obj diff --git a/src/debug/pe/testdata/gcc-amd64-mingw-exec b/src/debug/pe/testdata/gcc-amd64-mingw-exec Binary files differnew file mode 100644 index 000000000..ce6feb6b7 --- /dev/null +++ b/src/debug/pe/testdata/gcc-amd64-mingw-exec diff --git a/src/debug/pe/testdata/gcc-amd64-mingw-obj b/src/debug/pe/testdata/gcc-amd64-mingw-obj Binary files differnew file mode 100644 index 000000000..48ae7921f --- /dev/null +++ b/src/debug/pe/testdata/gcc-amd64-mingw-obj diff --git a/src/debug/pe/testdata/hello.c b/src/debug/pe/testdata/hello.c new file mode 100644 index 000000000..a689d3644 --- /dev/null +++ b/src/debug/pe/testdata/hello.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int +main(void) +{ + printf("hello, world\n"); + return 0; +} |