Black Lives Matter. Support the Equal Justice Initiative.

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  // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
    62  
    63  // NewFile creates a new File for accessing a PE binary in an underlying reader.
    64  func NewFile(r io.ReaderAt) (*File, error) {
    65  	f := new(File)
    66  	sr := io.NewSectionReader(r, 0, 1<<63-1)
    67  
    68  	var dosheader [96]byte
    69  	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
    70  		return nil, err
    71  	}
    72  	var base int64
    73  	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
    74  		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
    75  		var sign [4]byte
    76  		r.ReadAt(sign[:], signoff)
    77  		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
    78  			return nil, fmt.Errorf("invalid PE file signature: % x", sign)
    79  		}
    80  		base = signoff + 4
    81  	} else {
    82  		base = int64(0)
    83  	}
    84  	sr.Seek(base, seekStart)
    85  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
    86  		return nil, err
    87  	}
    88  	switch f.FileHeader.Machine {
    89  	case IMAGE_FILE_MACHINE_AMD64,
    90  		IMAGE_FILE_MACHINE_ARM64,
    91  		IMAGE_FILE_MACHINE_ARMNT,
    92  		IMAGE_FILE_MACHINE_I386,
    93  		IMAGE_FILE_MACHINE_UNKNOWN:
    94  		// ok
    95  	default:
    96  		return nil, fmt.Errorf("unrecognized PE machine: %#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  	// Seek past file header.
   118  	_, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), seekStart)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	// Read optional header.
   124  	f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	// Process sections.
   130  	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
   131  	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
   132  		sh := new(SectionHeader32)
   133  		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
   134  			return nil, err
   135  		}
   136  		name, err := sh.fullName(f.StringTable)
   137  		if err != nil {
   138  			return nil, err
   139  		}
   140  		s := new(Section)
   141  		s.SectionHeader = SectionHeader{
   142  			Name:                 name,
   143  			VirtualSize:          sh.VirtualSize,
   144  			VirtualAddress:       sh.VirtualAddress,
   145  			Size:                 sh.SizeOfRawData,
   146  			Offset:               sh.PointerToRawData,
   147  			PointerToRelocations: sh.PointerToRelocations,
   148  			PointerToLineNumbers: sh.PointerToLineNumbers,
   149  			NumberOfRelocations:  sh.NumberOfRelocations,
   150  			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
   151  			Characteristics:      sh.Characteristics,
   152  		}
   153  		r2 := r
   154  		if sh.PointerToRawData == 0 { // .bss must have all 0s
   155  			r2 = zeroReaderAt{}
   156  		}
   157  		s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
   158  		s.ReaderAt = s.sr
   159  		f.Sections[i] = s
   160  	}
   161  	for i := range f.Sections {
   162  		var err error
   163  		f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  	}
   168  
   169  	return f, nil
   170  }
   171  
   172  // zeroReaderAt is ReaderAt that reads 0s.
   173  type zeroReaderAt struct{}
   174  
   175  // ReadAt writes len(p) 0s into p.
   176  func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
   177  	for i := range p {
   178  		p[i] = 0
   179  	}
   180  	return len(p), nil
   181  }
   182  
   183  // getString extracts a string from symbol string table.
   184  func getString(section []byte, start int) (string, bool) {
   185  	if start < 0 || start >= len(section) {
   186  		return "", false
   187  	}
   188  
   189  	for end := start; end < len(section); end++ {
   190  		if section[end] == 0 {
   191  			return string(section[start:end]), true
   192  		}
   193  	}
   194  	return "", false
   195  }
   196  
   197  // Section returns the first section with the given name, or nil if no such
   198  // section exists.
   199  func (f *File) Section(name string) *Section {
   200  	for _, s := range f.Sections {
   201  		if s.Name == name {
   202  			return s
   203  		}
   204  	}
   205  	return nil
   206  }
   207  
   208  func (f *File) DWARF() (*dwarf.Data, error) {
   209  	dwarfSuffix := func(s *Section) string {
   210  		switch {
   211  		case strings.HasPrefix(s.Name, ".debug_"):
   212  			return s.Name[7:]
   213  		case strings.HasPrefix(s.Name, ".zdebug_"):
   214  			return s.Name[8:]
   215  		default:
   216  			return ""
   217  		}
   218  
   219  	}
   220  
   221  	// sectionData gets the data for s and checks its size.
   222  	sectionData := func(s *Section) ([]byte, error) {
   223  		b, err := s.Data()
   224  		if err != nil && uint32(len(b)) < s.Size {
   225  			return nil, err
   226  		}
   227  
   228  		if 0 < s.VirtualSize && s.VirtualSize < s.Size {
   229  			b = b[:s.VirtualSize]
   230  		}
   231  
   232  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
   233  			dlen := binary.BigEndian.Uint64(b[4:12])
   234  			dbuf := make([]byte, dlen)
   235  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
   236  			if err != nil {
   237  				return nil, err
   238  			}
   239  			if _, err := io.ReadFull(r, dbuf); err != nil {
   240  				return nil, err
   241  			}
   242  			if err := r.Close(); err != nil {
   243  				return nil, err
   244  			}
   245  			b = dbuf
   246  		}
   247  		return b, nil
   248  	}
   249  
   250  	// There are many other DWARF sections, but these
   251  	// are the ones the debug/dwarf package uses.
   252  	// Don't bother loading others.
   253  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
   254  	for _, s := range f.Sections {
   255  		suffix := dwarfSuffix(s)
   256  		if suffix == "" {
   257  			continue
   258  		}
   259  		if _, ok := dat[suffix]; !ok {
   260  			continue
   261  		}
   262  
   263  		b, err := sectionData(s)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  		dat[suffix] = b
   268  	}
   269  
   270  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	// Look for DWARF4 .debug_types sections.
   276  	for i, s := range f.Sections {
   277  		suffix := dwarfSuffix(s)
   278  		if suffix != "types" {
   279  			continue
   280  		}
   281  
   282  		b, err := sectionData(s)
   283  		if err != nil {
   284  			return nil, err
   285  		}
   286  
   287  		err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  	}
   292  
   293  	return d, nil
   294  }
   295  
   296  // TODO(brainman): document ImportDirectory once we decide what to do with it.
   297  
   298  type ImportDirectory struct {
   299  	OriginalFirstThunk uint32
   300  	TimeDateStamp      uint32
   301  	ForwarderChain     uint32
   302  	Name               uint32
   303  	FirstThunk         uint32
   304  
   305  	dll string
   306  }
   307  
   308  // ImportedSymbols returns the names of all symbols
   309  // referred to by the binary f that are expected to be
   310  // satisfied by other libraries at dynamic load time.
   311  // It does not return weak symbols.
   312  func (f *File) ImportedSymbols() ([]string, error) {
   313  	if f.OptionalHeader == nil {
   314  		return nil, nil
   315  	}
   316  
   317  	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 || f.Machine == IMAGE_FILE_MACHINE_ARM64
   318  
   319  	// grab the number of data directory entries
   320  	var dd_length uint32
   321  	if pe64 {
   322  		dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
   323  	} else {
   324  		dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
   325  	}
   326  
   327  	// check that the length of data directory entries is large
   328  	// enough to include the imports directory.
   329  	if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
   330  		return nil, nil
   331  	}
   332  
   333  	// grab the import data directory entry
   334  	var idd DataDirectory
   335  	if pe64 {
   336  		idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   337  	} else {
   338  		idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   339  	}
   340  
   341  	// figure out which section contains the import directory table
   342  	var ds *Section
   343  	ds = nil
   344  	for _, s := range f.Sections {
   345  		if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
   346  			ds = s
   347  			break
   348  		}
   349  	}
   350  
   351  	// didn't find a section, so no import libraries were found
   352  	if ds == nil {
   353  		return nil, nil
   354  	}
   355  
   356  	d, err := ds.Data()
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	// seek to the virtual address specified in the import data directory
   362  	d = d[idd.VirtualAddress-ds.VirtualAddress:]
   363  
   364  	// start decoding the import directory
   365  	var ida []ImportDirectory
   366  	for len(d) >= 20 {
   367  		var dt ImportDirectory
   368  		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   369  		dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
   370  		dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
   371  		dt.Name = binary.LittleEndian.Uint32(d[12:16])
   372  		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   373  		d = d[20:]
   374  		if dt.OriginalFirstThunk == 0 {
   375  			break
   376  		}
   377  		ida = append(ida, dt)
   378  	}
   379  	// TODO(brainman): this needs to be rewritten
   380  	//  ds.Data() returns contents of section containing import table. Why store in variable called "names"?
   381  	//  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
   382  	//  getString does not extracts a string from symbol string table (as getString doco says).
   383  	//  Why ds.Data() called again and again in the loop?
   384  	//  Needs test before rewrite.
   385  	names, _ := ds.Data()
   386  	var all []string
   387  	for _, dt := range ida {
   388  		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   389  		d, _ = ds.Data()
   390  		// seek to OriginalFirstThunk
   391  		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
   392  		for len(d) > 0 {
   393  			if pe64 { // 64bit
   394  				va := binary.LittleEndian.Uint64(d[0:8])
   395  				d = d[8:]
   396  				if va == 0 {
   397  					break
   398  				}
   399  				if va&0x8000000000000000 > 0 { // is Ordinal
   400  					// TODO add dynimport ordinal support.
   401  				} else {
   402  					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   403  					all = append(all, fn+":"+dt.dll)
   404  				}
   405  			} else { // 32bit
   406  				va := binary.LittleEndian.Uint32(d[0:4])
   407  				d = d[4:]
   408  				if va == 0 {
   409  					break
   410  				}
   411  				if va&0x80000000 > 0 { // is Ordinal
   412  					// TODO add dynimport ordinal support.
   413  					//ord := va&0x0000FFFF
   414  				} else {
   415  					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   416  					all = append(all, fn+":"+dt.dll)
   417  				}
   418  			}
   419  		}
   420  	}
   421  
   422  	return all, nil
   423  }
   424  
   425  // ImportedLibraries returns the names of all libraries
   426  // referred to by the binary f that are expected to be
   427  // linked with the binary at dynamic link time.
   428  func (f *File) ImportedLibraries() ([]string, error) {
   429  	// TODO
   430  	// cgo -dynimport don't use this for windows PE, so just return.
   431  	return nil, nil
   432  }
   433  
   434  // FormatError is unused.
   435  // The type is retained for compatibility.
   436  type FormatError struct {
   437  }
   438  
   439  func (e *FormatError) Error() string {
   440  	return "unknown error"
   441  }
   442  
   443  // readOptionalHeader accepts a io.ReadSeeker pointing to optional header in the PE file
   444  // and its size as seen in the file header.
   445  // It parses the given size of bytes and returns optional header. It infers whether the
   446  // bytes being parsed refer to 32 bit or 64 bit version of optional header.
   447  func readOptionalHeader(r io.ReadSeeker, sz uint16) (interface{}, error) {
   448  	// If optional header size is 0, return empty optional header.
   449  	if sz == 0 {
   450  		return nil, nil
   451  	}
   452  
   453  	var (
   454  		// First couple of bytes in option header state its type.
   455  		// We need to read them first to determine the type and
   456  		// validity of optional header.
   457  		ohMagic   uint16
   458  		ohMagicSz = binary.Size(ohMagic)
   459  	)
   460  
   461  	// If optional header size is greater than 0 but less than its magic size, return error.
   462  	if sz < uint16(ohMagicSz) {
   463  		return nil, fmt.Errorf("optional header size is less than optional header magic size")
   464  	}
   465  
   466  	// read reads from io.ReadSeeke, r, into data.
   467  	var err error
   468  	read := func(data interface{}) bool {
   469  		err = binary.Read(r, binary.LittleEndian, data)
   470  		return err == nil
   471  	}
   472  
   473  	if !read(&ohMagic) {
   474  		return nil, fmt.Errorf("failure to read optional header magic: %v", err)
   475  
   476  	}
   477  
   478  	switch ohMagic {
   479  	case 0x10b: // PE32
   480  		var (
   481  			oh32 OptionalHeader32
   482  			// There can be 0 or more data directories. So the minimum size of optional
   483  			// header is calculated by subtracting oh32.DataDirectory size from oh32 size.
   484  			oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory)
   485  		)
   486  
   487  		if sz < uint16(oh32MinSz) {
   488  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz)
   489  		}
   490  
   491  		// Init oh32 fields
   492  		oh32.Magic = ohMagic
   493  		if !read(&oh32.MajorLinkerVersion) ||
   494  			!read(&oh32.MinorLinkerVersion) ||
   495  			!read(&oh32.SizeOfCode) ||
   496  			!read(&oh32.SizeOfInitializedData) ||
   497  			!read(&oh32.SizeOfUninitializedData) ||
   498  			!read(&oh32.AddressOfEntryPoint) ||
   499  			!read(&oh32.BaseOfCode) ||
   500  			!read(&oh32.BaseOfData) ||
   501  			!read(&oh32.ImageBase) ||
   502  			!read(&oh32.SectionAlignment) ||
   503  			!read(&oh32.FileAlignment) ||
   504  			!read(&oh32.MajorOperatingSystemVersion) ||
   505  			!read(&oh32.MinorOperatingSystemVersion) ||
   506  			!read(&oh32.MajorImageVersion) ||
   507  			!read(&oh32.MinorImageVersion) ||
   508  			!read(&oh32.MajorSubsystemVersion) ||
   509  			!read(&oh32.MinorSubsystemVersion) ||
   510  			!read(&oh32.Win32VersionValue) ||
   511  			!read(&oh32.SizeOfImage) ||
   512  			!read(&oh32.SizeOfHeaders) ||
   513  			!read(&oh32.CheckSum) ||
   514  			!read(&oh32.Subsystem) ||
   515  			!read(&oh32.DllCharacteristics) ||
   516  			!read(&oh32.SizeOfStackReserve) ||
   517  			!read(&oh32.SizeOfStackCommit) ||
   518  			!read(&oh32.SizeOfHeapReserve) ||
   519  			!read(&oh32.SizeOfHeapCommit) ||
   520  			!read(&oh32.LoaderFlags) ||
   521  			!read(&oh32.NumberOfRvaAndSizes) {
   522  			return nil, fmt.Errorf("failure to read PE32 optional header: %v", err)
   523  		}
   524  
   525  		dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes)
   526  		if err != nil {
   527  			return nil, err
   528  		}
   529  
   530  		copy(oh32.DataDirectory[:], dd)
   531  
   532  		return &oh32, nil
   533  	case 0x20b: // PE32+
   534  		var (
   535  			oh64 OptionalHeader64
   536  			// There can be 0 or more data directories. So the minimum size of optional
   537  			// header is calculated by subtracting oh64.DataDirectory size from oh64 size.
   538  			oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory)
   539  		)
   540  
   541  		if sz < uint16(oh64MinSz) {
   542  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz)
   543  		}
   544  
   545  		// Init oh64 fields
   546  		oh64.Magic = ohMagic
   547  		if !read(&oh64.MajorLinkerVersion) ||
   548  			!read(&oh64.MinorLinkerVersion) ||
   549  			!read(&oh64.SizeOfCode) ||
   550  			!read(&oh64.SizeOfInitializedData) ||
   551  			!read(&oh64.SizeOfUninitializedData) ||
   552  			!read(&oh64.AddressOfEntryPoint) ||
   553  			!read(&oh64.BaseOfCode) ||
   554  			!read(&oh64.ImageBase) ||
   555  			!read(&oh64.SectionAlignment) ||
   556  			!read(&oh64.FileAlignment) ||
   557  			!read(&oh64.MajorOperatingSystemVersion) ||
   558  			!read(&oh64.MinorOperatingSystemVersion) ||
   559  			!read(&oh64.MajorImageVersion) ||
   560  			!read(&oh64.MinorImageVersion) ||
   561  			!read(&oh64.MajorSubsystemVersion) ||
   562  			!read(&oh64.MinorSubsystemVersion) ||
   563  			!read(&oh64.Win32VersionValue) ||
   564  			!read(&oh64.SizeOfImage) ||
   565  			!read(&oh64.SizeOfHeaders) ||
   566  			!read(&oh64.CheckSum) ||
   567  			!read(&oh64.Subsystem) ||
   568  			!read(&oh64.DllCharacteristics) ||
   569  			!read(&oh64.SizeOfStackReserve) ||
   570  			!read(&oh64.SizeOfStackCommit) ||
   571  			!read(&oh64.SizeOfHeapReserve) ||
   572  			!read(&oh64.SizeOfHeapCommit) ||
   573  			!read(&oh64.LoaderFlags) ||
   574  			!read(&oh64.NumberOfRvaAndSizes) {
   575  			return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err)
   576  		}
   577  
   578  		dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes)
   579  		if err != nil {
   580  			return nil, err
   581  		}
   582  
   583  		copy(oh64.DataDirectory[:], dd)
   584  
   585  		return &oh64, nil
   586  	default:
   587  		return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic)
   588  	}
   589  }
   590  
   591  // readDataDirectories accepts a io.ReadSeeker pointing to data directories in the PE file,
   592  // its size and number of data directories as seen in optional header.
   593  // It parses the given size of bytes and returns given number of data directories.
   594  func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) {
   595  	ddSz := binary.Size(DataDirectory{})
   596  	if uint32(sz) != n*uint32(ddSz) {
   597  		return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n)
   598  	}
   599  
   600  	dd := make([]DataDirectory, n)
   601  	if err := binary.Read(r, binary.LittleEndian, dd); err != nil {
   602  		return nil, fmt.Errorf("failure to read data directories: %v", err)
   603  	}
   604  
   605  	return dd, nil
   606  }
   607  

View as plain text