...
Run Format

Source file src/debug/pe/file.go

Documentation: debug/pe

     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  	"bytes"
    10  	"compress/zlib"
    11  	"debug/dwarf"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"strings"
    17  )
    18  
    19  // Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap.
    20  const seekStart = 0
    21  
    22  // A File represents an open PE file.
    23  type File struct {
    24  	FileHeader
    25  	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
    26  	Sections       []*Section
    27  	Symbols        []*Symbol    // COFF symbols with auxiliary symbol records removed
    28  	COFFSymbols    []COFFSymbol // all COFF symbols (including auxiliary symbol records)
    29  	StringTable    StringTable
    30  
    31  	closer io.Closer
    32  }
    33  
    34  // Open opens the named file using os.Open and prepares it for use as a PE binary.
    35  func Open(name string) (*File, error) {
    36  	f, err := os.Open(name)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	ff, err := NewFile(f)
    41  	if err != nil {
    42  		f.Close()
    43  		return nil, err
    44  	}
    45  	ff.closer = f
    46  	return ff, nil
    47  }
    48  
    49  // Close closes the File.
    50  // If the File was created using NewFile directly instead of Open,
    51  // Close has no effect.
    52  func (f *File) Close() error {
    53  	var err error
    54  	if f.closer != nil {
    55  		err = f.closer.Close()
    56  		f.closer = nil
    57  	}
    58  	return err
    59  }
    60  
    61  var (
    62  	sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
    63  	sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
    64  )
    65  
    66  // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
    67  
    68  // NewFile creates a new File for accessing a PE binary in an underlying reader.
    69  func NewFile(r io.ReaderAt) (*File, error) {
    70  	f := new(File)
    71  	sr := io.NewSectionReader(r, 0, 1<<63-1)
    72  
    73  	var dosheader [96]byte
    74  	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
    75  		return nil, err
    76  	}
    77  	var base int64
    78  	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
    79  		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
    80  		var sign [4]byte
    81  		r.ReadAt(sign[:], signoff)
    82  		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
    83  			return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign)
    84  		}
    85  		base = signoff + 4
    86  	} else {
    87  		base = int64(0)
    88  	}
    89  	sr.Seek(base, seekStart)
    90  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
    91  		return nil, err
    92  	}
    93  	switch f.FileHeader.Machine {
    94  	case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386:
    95  	default:
    96  		return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine)
    97  	}
    98  
    99  	var err error
   100  
   101  	// Read string table.
   102  	f.StringTable, err = readStringTable(&f.FileHeader, sr)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	// Read symbol table.
   108  	f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	// Read optional header.
   118  	sr.Seek(base, seekStart)
   119  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
   120  		return nil, err
   121  	}
   122  	var oh32 OptionalHeader32
   123  	var oh64 OptionalHeader64
   124  	switch f.FileHeader.SizeOfOptionalHeader {
   125  	case sizeofOptionalHeader32:
   126  		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
   127  			return nil, err
   128  		}
   129  		if oh32.Magic != 0x10b { // PE32
   130  			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
   131  		}
   132  		f.OptionalHeader = &oh32
   133  	case sizeofOptionalHeader64:
   134  		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
   135  			return nil, err
   136  		}
   137  		if oh64.Magic != 0x20b { // PE32+
   138  			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
   139  		}
   140  		f.OptionalHeader = &oh64
   141  	}
   142  
   143  	// Process sections.
   144  	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
   145  	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
   146  		sh := new(SectionHeader32)
   147  		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
   148  			return nil, err
   149  		}
   150  		name, err := sh.fullName(f.StringTable)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		s := new(Section)
   155  		s.SectionHeader = SectionHeader{
   156  			Name:                 name,
   157  			VirtualSize:          sh.VirtualSize,
   158  			VirtualAddress:       sh.VirtualAddress,
   159  			Size:                 sh.SizeOfRawData,
   160  			Offset:               sh.PointerToRawData,
   161  			PointerToRelocations: sh.PointerToRelocations,
   162  			PointerToLineNumbers: sh.PointerToLineNumbers,
   163  			NumberOfRelocations:  sh.NumberOfRelocations,
   164  			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
   165  			Characteristics:      sh.Characteristics,
   166  		}
   167  		r2 := r
   168  		if sh.PointerToRawData == 0 { // .bss must have all 0s
   169  			r2 = zeroReaderAt{}
   170  		}
   171  		s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
   172  		s.ReaderAt = s.sr
   173  		f.Sections[i] = s
   174  	}
   175  	for i := range f.Sections {
   176  		var err error
   177  		f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  
   183  	return f, nil
   184  }
   185  
   186  // zeroReaderAt is ReaderAt that reads 0s.
   187  type zeroReaderAt struct{}
   188  
   189  // ReadAt writes len(p) 0s into p.
   190  func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
   191  	for i := range p {
   192  		p[i] = 0
   193  	}
   194  	return len(p), nil
   195  }
   196  
   197  // getString extracts a string from symbol string table.
   198  func getString(section []byte, start int) (string, bool) {
   199  	if start < 0 || start >= len(section) {
   200  		return "", false
   201  	}
   202  
   203  	for end := start; end < len(section); end++ {
   204  		if section[end] == 0 {
   205  			return string(section[start:end]), true
   206  		}
   207  	}
   208  	return "", false
   209  }
   210  
   211  // Section returns the first section with the given name, or nil if no such
   212  // section exists.
   213  func (f *File) Section(name string) *Section {
   214  	for _, s := range f.Sections {
   215  		if s.Name == name {
   216  			return s
   217  		}
   218  	}
   219  	return nil
   220  }
   221  
   222  func (f *File) DWARF() (*dwarf.Data, error) {
   223  	dwarfSuffix := func(s *Section) string {
   224  		switch {
   225  		case strings.HasPrefix(s.Name, ".debug_"):
   226  			return s.Name[7:]
   227  		case strings.HasPrefix(s.Name, ".zdebug_"):
   228  			return s.Name[8:]
   229  		default:
   230  			return ""
   231  		}
   232  
   233  	}
   234  
   235  	// sectionData gets the data for s and checks its size.
   236  	sectionData := func(s *Section) ([]byte, error) {
   237  		b, err := s.Data()
   238  		if err != nil && uint32(len(b)) < s.Size {
   239  			return nil, err
   240  		}
   241  
   242  		if 0 < s.VirtualSize && s.VirtualSize < s.Size {
   243  			b = b[:s.VirtualSize]
   244  		}
   245  
   246  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
   247  			dlen := binary.BigEndian.Uint64(b[4:12])
   248  			dbuf := make([]byte, dlen)
   249  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
   250  			if err != nil {
   251  				return nil, err
   252  			}
   253  			if _, err := io.ReadFull(r, dbuf); err != nil {
   254  				return nil, err
   255  			}
   256  			if err := r.Close(); err != nil {
   257  				return nil, err
   258  			}
   259  			b = dbuf
   260  		}
   261  		return b, nil
   262  	}
   263  
   264  	// There are many other DWARF sections, but these
   265  	// are the ones the debug/dwarf package uses.
   266  	// Don't bother loading others.
   267  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
   268  	for _, s := range f.Sections {
   269  		suffix := dwarfSuffix(s)
   270  		if suffix == "" {
   271  			continue
   272  		}
   273  		if _, ok := dat[suffix]; !ok {
   274  			continue
   275  		}
   276  
   277  		b, err := sectionData(s)
   278  		if err != nil {
   279  			return nil, err
   280  		}
   281  		dat[suffix] = b
   282  	}
   283  
   284  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	// Look for DWARF4 .debug_types sections.
   290  	for i, s := range f.Sections {
   291  		suffix := dwarfSuffix(s)
   292  		if suffix != "types" {
   293  			continue
   294  		}
   295  
   296  		b, err := sectionData(s)
   297  		if err != nil {
   298  			return nil, err
   299  		}
   300  
   301  		err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  	}
   306  
   307  	return d, nil
   308  }
   309  
   310  // TODO(brainman): document ImportDirectory once we decide what to do with it.
   311  
   312  type ImportDirectory struct {
   313  	OriginalFirstThunk uint32
   314  	TimeDateStamp      uint32
   315  	ForwarderChain     uint32
   316  	Name               uint32
   317  	FirstThunk         uint32
   318  
   319  	dll string
   320  }
   321  
   322  // ImportedSymbols returns the names of all symbols
   323  // referred to by the binary f that are expected to be
   324  // satisfied by other libraries at dynamic load time.
   325  // It does not return weak symbols.
   326  func (f *File) ImportedSymbols() ([]string, error) {
   327  	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
   328  
   329  	// grab the number of data directory entries
   330  	var dd_length uint32
   331  	if pe64 {
   332  		dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
   333  	} else {
   334  		dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
   335  	}
   336  
   337  	// check that the length of data directory entries is large
   338  	// enough to include the imports directory.
   339  	if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
   340  		return nil, nil
   341  	}
   342  
   343  	// grab the import data directory entry
   344  	var idd DataDirectory
   345  	if pe64 {
   346  		idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   347  	} else {
   348  		idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   349  	}
   350  
   351  	// figure out which section contains the import directory table
   352  	var ds *Section
   353  	ds = nil
   354  	for _, s := range f.Sections {
   355  		if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
   356  			ds = s
   357  			break
   358  		}
   359  	}
   360  
   361  	// didn't find a section, so no import libraries were found
   362  	if ds == nil {
   363  		return nil, nil
   364  	}
   365  
   366  	d, err := ds.Data()
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  
   371  	// seek to the virtual address specified in the import data directory
   372  	d = d[idd.VirtualAddress-ds.VirtualAddress:]
   373  
   374  	// start decoding the import directory
   375  	var ida []ImportDirectory
   376  	for len(d) > 0 {
   377  		var dt ImportDirectory
   378  		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   379  		dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
   380  		dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
   381  		dt.Name = binary.LittleEndian.Uint32(d[12:16])
   382  		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   383  		d = d[20:]
   384  		if dt.OriginalFirstThunk == 0 {
   385  			break
   386  		}
   387  		ida = append(ida, dt)
   388  	}
   389  	// TODO(brainman): this needs to be rewritten
   390  	//  ds.Data() returns contents of section containing import table. Why store in variable called "names"?
   391  	//  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
   392  	//  getString does not extracts a string from symbol string table (as getString doco says).
   393  	//  Why ds.Data() called again and again in the loop?
   394  	//  Needs test before rewrite.
   395  	names, _ := ds.Data()
   396  	var all []string
   397  	for _, dt := range ida {
   398  		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   399  		d, _ = ds.Data()
   400  		// seek to OriginalFirstThunk
   401  		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
   402  		for len(d) > 0 {
   403  			if pe64 { // 64bit
   404  				va := binary.LittleEndian.Uint64(d[0:8])
   405  				d = d[8:]
   406  				if va == 0 {
   407  					break
   408  				}
   409  				if va&0x8000000000000000 > 0 { // is Ordinal
   410  					// TODO add dynimport ordinal support.
   411  				} else {
   412  					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   413  					all = append(all, fn+":"+dt.dll)
   414  				}
   415  			} else { // 32bit
   416  				va := binary.LittleEndian.Uint32(d[0:4])
   417  				d = d[4:]
   418  				if va == 0 {
   419  					break
   420  				}
   421  				if va&0x80000000 > 0 { // is Ordinal
   422  					// TODO add dynimport ordinal support.
   423  					//ord := va&0x0000FFFF
   424  				} else {
   425  					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   426  					all = append(all, fn+":"+dt.dll)
   427  				}
   428  			}
   429  		}
   430  	}
   431  
   432  	return all, nil
   433  }
   434  
   435  // ImportedLibraries returns the names of all libraries
   436  // referred to by the binary f that are expected to be
   437  // linked with the binary at dynamic link time.
   438  func (f *File) ImportedLibraries() ([]string, error) {
   439  	// TODO
   440  	// cgo -dynimport don't use this for windows PE, so just return.
   441  	return nil, nil
   442  }
   443  
   444  // FormatError is unused.
   445  // The type is retained for compatibility.
   446  type FormatError struct {
   447  }
   448  
   449  func (e *FormatError) Error() string {
   450  	return "unknown error"
   451  }
   452  

View as plain text