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