github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/debug/pe/file.go (about)

     1  // Copyright 2009 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package pe implements access to PE (Microsoft Windows Portable Executable) files.
     6  package pe
     7  
     8  import (
     9  	"debug/dwarf"
    10  	"encoding/binary"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"os"
    15  	"strconv"
    16  	"unsafe"
    17  )
    18  
    19  // A File represents an open PE file.
    20  type File struct {
    21  	FileHeader
    22  	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
    23  	Sections       []*Section
    24  	Symbols        []*Symbol
    25  
    26  	closer io.Closer
    27  }
    28  
    29  type SectionHeader struct {
    30  	Name                 string
    31  	VirtualSize          uint32
    32  	VirtualAddress       uint32
    33  	Size                 uint32
    34  	Offset               uint32
    35  	PointerToRelocations uint32
    36  	PointerToLineNumbers uint32
    37  	NumberOfRelocations  uint16
    38  	NumberOfLineNumbers  uint16
    39  	Characteristics      uint32
    40  }
    41  
    42  type Section struct {
    43  	SectionHeader
    44  
    45  	// Embed ReaderAt for ReadAt method.
    46  	// Do not embed SectionReader directly
    47  	// to avoid having Read and Seek.
    48  	// If a client wants Read and Seek it must use
    49  	// Open() to avoid fighting over the seek offset
    50  	// with other clients.
    51  	io.ReaderAt
    52  	sr *io.SectionReader
    53  }
    54  
    55  type Symbol struct {
    56  	Name          string
    57  	Value         uint32
    58  	SectionNumber int16
    59  	Type          uint16
    60  	StorageClass  uint8
    61  }
    62  
    63  type ImportDirectory struct {
    64  	OriginalFirstThunk uint32
    65  	TimeDateStamp      uint32
    66  	ForwarderChain     uint32
    67  	Name               uint32
    68  	FirstThunk         uint32
    69  
    70  	dll string
    71  }
    72  
    73  // Data reads and returns the contents of the PE section.
    74  func (s *Section) Data() ([]byte, error) {
    75  	dat := make([]byte, s.sr.Size())
    76  	n, err := s.sr.ReadAt(dat, 0)
    77  	if n == len(dat) {
    78  		err = nil
    79  	}
    80  	return dat[0:n], err
    81  }
    82  
    83  // Open returns a new ReadSeeker reading the PE section.
    84  func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
    85  
    86  type FormatError struct {
    87  	off int64
    88  	msg string
    89  	val interface{}
    90  }
    91  
    92  func (e *FormatError) Error() string {
    93  	msg := e.msg
    94  	if e.val != nil {
    95  		msg += fmt.Sprintf(" '%v'", e.val)
    96  	}
    97  	msg += fmt.Sprintf(" in record at byte %#x", e.off)
    98  	return msg
    99  }
   100  
   101  // Open opens the named file using os.Open and prepares it for use as a PE binary.
   102  func Open(name string) (*File, error) {
   103  	f, err := os.Open(name)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	ff, err := NewFile(f)
   108  	if err != nil {
   109  		f.Close()
   110  		return nil, err
   111  	}
   112  	ff.closer = f
   113  	return ff, nil
   114  }
   115  
   116  // Close closes the File.
   117  // If the File was created using NewFile directly instead of Open,
   118  // Close has no effect.
   119  func (f *File) Close() error {
   120  	var err error
   121  	if f.closer != nil {
   122  		err = f.closer.Close()
   123  		f.closer = nil
   124  	}
   125  	return err
   126  }
   127  
   128  // NewFile creates a new File for accessing a PE binary in an underlying reader.
   129  func NewFile(r io.ReaderAt) (*File, error) {
   130  	f := new(File)
   131  	sr := io.NewSectionReader(r, 0, 1<<63-1)
   132  
   133  	var dosheader [96]byte
   134  	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
   135  		return nil, err
   136  	}
   137  	var base int64
   138  	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
   139  		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
   140  		var sign [4]byte
   141  		r.ReadAt(sign[:], signoff)
   142  		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
   143  			return nil, errors.New("Invalid PE File Format.")
   144  		}
   145  		base = signoff + 4
   146  	} else {
   147  		base = int64(0)
   148  	}
   149  	sr.Seek(base, os.SEEK_SET)
   150  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
   151  		return nil, err
   152  	}
   153  	if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
   154  		return nil, errors.New("Invalid PE File Format.")
   155  	}
   156  
   157  	var ss []byte
   158  	if f.FileHeader.NumberOfSymbols > 0 {
   159  		// Get COFF string table, which is located at the end of the COFF symbol table.
   160  		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
   161  		var l uint32
   162  		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
   163  			return nil, err
   164  		}
   165  		ss = make([]byte, l)
   166  		if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
   167  			return nil, err
   168  		}
   169  
   170  		// Process COFF symbol table.
   171  		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
   172  		aux := uint8(0)
   173  		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
   174  			cs := new(COFFSymbol)
   175  			if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
   176  				return nil, err
   177  			}
   178  			if aux > 0 {
   179  				aux--
   180  				continue
   181  			}
   182  			var name string
   183  			if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
   184  				si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
   185  				name, _ = getString(ss, si)
   186  			} else {
   187  				name = cstring(cs.Name[:])
   188  			}
   189  			aux = cs.NumberOfAuxSymbols
   190  			s := &Symbol{
   191  				Name:          name,
   192  				Value:         cs.Value,
   193  				SectionNumber: cs.SectionNumber,
   194  				Type:          cs.Type,
   195  				StorageClass:  cs.StorageClass,
   196  			}
   197  			f.Symbols = append(f.Symbols, s)
   198  		}
   199  	}
   200  
   201  	// Read optional header.
   202  	sr.Seek(base, os.SEEK_SET)
   203  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
   204  		return nil, err
   205  	}
   206  	var oh32 OptionalHeader32
   207  	var oh64 OptionalHeader64
   208  	switch uintptr(f.FileHeader.SizeOfOptionalHeader) {
   209  	case unsafe.Sizeof(oh32):
   210  		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
   211  			return nil, err
   212  		}
   213  		if oh32.Magic != 0x10b { // PE32
   214  			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
   215  		}
   216  		f.OptionalHeader = &oh32
   217  	case unsafe.Sizeof(oh64):
   218  		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
   219  			return nil, err
   220  		}
   221  		if oh64.Magic != 0x20b { // PE32+
   222  			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
   223  		}
   224  		f.OptionalHeader = &oh64
   225  	}
   226  
   227  	// Process sections.
   228  	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
   229  	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
   230  		sh := new(SectionHeader32)
   231  		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
   232  			return nil, err
   233  		}
   234  		var name string
   235  		if sh.Name[0] == '\x2F' {
   236  			si, _ := strconv.Atoi(cstring(sh.Name[1:]))
   237  			name, _ = getString(ss, si)
   238  		} else {
   239  			name = cstring(sh.Name[0:])
   240  		}
   241  		s := new(Section)
   242  		s.SectionHeader = SectionHeader{
   243  			Name:                 name,
   244  			VirtualSize:          sh.VirtualSize,
   245  			VirtualAddress:       sh.VirtualAddress,
   246  			Size:                 sh.SizeOfRawData,
   247  			Offset:               sh.PointerToRawData,
   248  			PointerToRelocations: sh.PointerToRelocations,
   249  			PointerToLineNumbers: sh.PointerToLineNumbers,
   250  			NumberOfRelocations:  sh.NumberOfRelocations,
   251  			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
   252  			Characteristics:      sh.Characteristics,
   253  		}
   254  		s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
   255  		s.ReaderAt = s.sr
   256  		f.Sections[i] = s
   257  	}
   258  	return f, nil
   259  }
   260  
   261  func cstring(b []byte) string {
   262  	var i int
   263  	for i = 0; i < len(b) && b[i] != 0; i++ {
   264  	}
   265  	return string(b[0:i])
   266  }
   267  
   268  // getString extracts a string from symbol string table.
   269  func getString(section []byte, start int) (string, bool) {
   270  	if start < 0 || start >= len(section) {
   271  		return "", false
   272  	}
   273  
   274  	for end := start; end < len(section); end++ {
   275  		if section[end] == 0 {
   276  			return string(section[start:end]), true
   277  		}
   278  	}
   279  	return "", false
   280  }
   281  
   282  // Section returns the first section with the given name, or nil if no such
   283  // section exists.
   284  func (f *File) Section(name string) *Section {
   285  	for _, s := range f.Sections {
   286  		if s.Name == name {
   287  			return s
   288  		}
   289  	}
   290  	return nil
   291  }
   292  
   293  func (f *File) DWARF() (*dwarf.Data, error) {
   294  	// There are many other DWARF sections, but these
   295  	// are the required ones, and the debug/dwarf package
   296  	// does not use the others, so don't bother loading them.
   297  	var names = [...]string{"abbrev", "info", "str"}
   298  	var dat [len(names)][]byte
   299  	for i, name := range names {
   300  		name = ".debug_" + name
   301  		s := f.Section(name)
   302  		if s == nil {
   303  			continue
   304  		}
   305  		b, err := s.Data()
   306  		if err != nil && uint32(len(b)) < s.Size {
   307  			return nil, err
   308  		}
   309  		dat[i] = b
   310  	}
   311  
   312  	abbrev, info, str := dat[0], dat[1], dat[2]
   313  	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
   314  }
   315  
   316  // ImportedSymbols returns the names of all symbols
   317  // referred to by the binary f that are expected to be
   318  // satisfied by other libraries at dynamic load time.
   319  // It does not return weak symbols.
   320  func (f *File) ImportedSymbols() ([]string, error) {
   321  	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
   322  	ds := f.Section(".idata")
   323  	if ds == nil {
   324  		// not dynamic, so no libraries
   325  		return nil, nil
   326  	}
   327  	d, err := ds.Data()
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  	var ida []ImportDirectory
   332  	for len(d) > 0 {
   333  		var dt ImportDirectory
   334  		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   335  		dt.Name = binary.LittleEndian.Uint32(d[12:16])
   336  		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   337  		d = d[20:]
   338  		if dt.OriginalFirstThunk == 0 {
   339  			break
   340  		}
   341  		ida = append(ida, dt)
   342  	}
   343  	names, _ := ds.Data()
   344  	var all []string
   345  	for _, dt := range ida {
   346  		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   347  		d, _ = ds.Data()
   348  		// seek to OriginalFirstThunk
   349  		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
   350  		for len(d) > 0 {
   351  			if pe64 { // 64bit
   352  				va := binary.LittleEndian.Uint64(d[0:8])
   353  				d = d[8:]
   354  				if va == 0 {
   355  					break
   356  				}
   357  				if va&0x8000000000000000 > 0 { // is Ordinal
   358  					// TODO add dynimport ordinal support.
   359  				} else {
   360  					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   361  					all = append(all, fn+":"+dt.dll)
   362  				}
   363  			} else { // 32bit
   364  				va := binary.LittleEndian.Uint32(d[0:4])
   365  				d = d[4:]
   366  				if va == 0 {
   367  					break
   368  				}
   369  				if va&0x80000000 > 0 { // is Ordinal
   370  					// TODO add dynimport ordinal support.
   371  					//ord := va&0x0000FFFF
   372  				} else {
   373  					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   374  					all = append(all, fn+":"+dt.dll)
   375  				}
   376  			}
   377  		}
   378  	}
   379  
   380  	return all, nil
   381  }
   382  
   383  // ImportedLibraries returns the names of all libraries
   384  // referred to by the binary f that are expected to be
   385  // linked with the binary at dynamic link time.
   386  func (f *File) ImportedLibraries() ([]string, error) {
   387  	// TODO
   388  	// cgo -dynimport don't use this for windows PE, so just return.
   389  	return nil, nil
   390  }