...

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 any // 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 and DWARF5 sections.
   276  	for i, s := range f.Sections {
   277  		suffix := dwarfSuffix(s)
   278  		if suffix == "" {
   279  			continue
   280  		}
   281  		if _, ok := dat[suffix]; ok {
   282  			// Already handled.
   283  			continue
   284  		}
   285  
   286  		b, err := sectionData(s)
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  
   291  		if suffix == "types" {
   292  			err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   293  		} else {
   294  			err = d.AddSection(".debug_"+suffix, b)
   295  		}
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  	}
   300  
   301  	return d, nil
   302  }
   303  
   304  // TODO(brainman): document ImportDirectory once we decide what to do with it.
   305  
   306  type ImportDirectory struct {
   307  	OriginalFirstThunk uint32
   308  	TimeDateStamp      uint32
   309  	ForwarderChain     uint32
   310  	Name               uint32
   311  	FirstThunk         uint32
   312  
   313  	dll string
   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  	if f.OptionalHeader == nil {
   322  		return nil, nil
   323  	}
   324  
   325  	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 || f.Machine == IMAGE_FILE_MACHINE_ARM64
   326  
   327  	// grab the number of data directory entries
   328  	var dd_length uint32
   329  	if pe64 {
   330  		dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
   331  	} else {
   332  		dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
   333  	}
   334  
   335  	// check that the length of data directory entries is large
   336  	// enough to include the imports directory.
   337  	if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
   338  		return nil, nil
   339  	}
   340  
   341  	// grab the import data directory entry
   342  	var idd DataDirectory
   343  	if pe64 {
   344  		idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   345  	} else {
   346  		idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   347  	}
   348  
   349  	// figure out which section contains the import directory table
   350  	var ds *Section
   351  	ds = nil
   352  	for _, s := range f.Sections {
   353  		if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
   354  			ds = s
   355  			break
   356  		}
   357  	}
   358  
   359  	// didn't find a section, so no import libraries were found
   360  	if ds == nil {
   361  		return nil, nil
   362  	}
   363  
   364  	d, err := ds.Data()
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	// seek to the virtual address specified in the import data directory
   370  	d = d[idd.VirtualAddress-ds.VirtualAddress:]
   371  
   372  	// start decoding the import directory
   373  	var ida []ImportDirectory
   374  	for len(d) >= 20 {
   375  		var dt ImportDirectory
   376  		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   377  		dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
   378  		dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
   379  		dt.Name = binary.LittleEndian.Uint32(d[12:16])
   380  		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   381  		d = d[20:]
   382  		if dt.OriginalFirstThunk == 0 {
   383  			break
   384  		}
   385  		ida = append(ida, dt)
   386  	}
   387  	// TODO(brainman): this needs to be rewritten
   388  	//  ds.Data() returns contents of section containing import table. Why store in variable called "names"?
   389  	//  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
   390  	//  getString does not extracts a string from symbol string table (as getString doco says).
   391  	//  Why ds.Data() called again and again in the loop?
   392  	//  Needs test before rewrite.
   393  	names, _ := ds.Data()
   394  	var all []string
   395  	for _, dt := range ida {
   396  		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   397  		d, _ = ds.Data()
   398  		// seek to OriginalFirstThunk
   399  		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
   400  		for len(d) > 0 {
   401  			if pe64 { // 64bit
   402  				va := binary.LittleEndian.Uint64(d[0:8])
   403  				d = d[8:]
   404  				if va == 0 {
   405  					break
   406  				}
   407  				if va&0x8000000000000000 > 0 { // is Ordinal
   408  					// TODO add dynimport ordinal support.
   409  				} else {
   410  					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   411  					all = append(all, fn+":"+dt.dll)
   412  				}
   413  			} else { // 32bit
   414  				va := binary.LittleEndian.Uint32(d[0:4])
   415  				d = d[4:]
   416  				if va == 0 {
   417  					break
   418  				}
   419  				if va&0x80000000 > 0 { // is Ordinal
   420  					// TODO add dynimport ordinal support.
   421  					//ord := va&0x0000FFFF
   422  				} else {
   423  					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   424  					all = append(all, fn+":"+dt.dll)
   425  				}
   426  			}
   427  		}
   428  	}
   429  
   430  	return all, nil
   431  }
   432  
   433  // ImportedLibraries returns the names of all libraries
   434  // referred to by the binary f that are expected to be
   435  // linked with the binary at dynamic link time.
   436  func (f *File) ImportedLibraries() ([]string, error) {
   437  	// TODO
   438  	// cgo -dynimport don't use this for windows PE, so just return.
   439  	return nil, nil
   440  }
   441  
   442  // FormatError is unused.
   443  // The type is retained for compatibility.
   444  type FormatError struct {
   445  }
   446  
   447  func (e *FormatError) Error() string {
   448  	return "unknown error"
   449  }
   450  
   451  // readOptionalHeader accepts a io.ReadSeeker pointing to optional header in the PE file
   452  // and its size as seen in the file header.
   453  // It parses the given size of bytes and returns optional header. It infers whether the
   454  // bytes being parsed refer to 32 bit or 64 bit version of optional header.
   455  func readOptionalHeader(r io.ReadSeeker, sz uint16) (any, error) {
   456  	// If optional header size is 0, return empty optional header.
   457  	if sz == 0 {
   458  		return nil, nil
   459  	}
   460  
   461  	var (
   462  		// First couple of bytes in option header state its type.
   463  		// We need to read them first to determine the type and
   464  		// validity of optional header.
   465  		ohMagic   uint16
   466  		ohMagicSz = binary.Size(ohMagic)
   467  	)
   468  
   469  	// If optional header size is greater than 0 but less than its magic size, return error.
   470  	if sz < uint16(ohMagicSz) {
   471  		return nil, fmt.Errorf("optional header size is less than optional header magic size")
   472  	}
   473  
   474  	// read reads from io.ReadSeeke, r, into data.
   475  	var err error
   476  	read := func(data any) bool {
   477  		err = binary.Read(r, binary.LittleEndian, data)
   478  		return err == nil
   479  	}
   480  
   481  	if !read(&ohMagic) {
   482  		return nil, fmt.Errorf("failure to read optional header magic: %v", err)
   483  
   484  	}
   485  
   486  	switch ohMagic {
   487  	case 0x10b: // PE32
   488  		var (
   489  			oh32 OptionalHeader32
   490  			// There can be 0 or more data directories. So the minimum size of optional
   491  			// header is calculated by subtracting oh32.DataDirectory size from oh32 size.
   492  			oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory)
   493  		)
   494  
   495  		if sz < uint16(oh32MinSz) {
   496  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz)
   497  		}
   498  
   499  		// Init oh32 fields
   500  		oh32.Magic = ohMagic
   501  		if !read(&oh32.MajorLinkerVersion) ||
   502  			!read(&oh32.MinorLinkerVersion) ||
   503  			!read(&oh32.SizeOfCode) ||
   504  			!read(&oh32.SizeOfInitializedData) ||
   505  			!read(&oh32.SizeOfUninitializedData) ||
   506  			!read(&oh32.AddressOfEntryPoint) ||
   507  			!read(&oh32.BaseOfCode) ||
   508  			!read(&oh32.BaseOfData) ||
   509  			!read(&oh32.ImageBase) ||
   510  			!read(&oh32.SectionAlignment) ||
   511  			!read(&oh32.FileAlignment) ||
   512  			!read(&oh32.MajorOperatingSystemVersion) ||
   513  			!read(&oh32.MinorOperatingSystemVersion) ||
   514  			!read(&oh32.MajorImageVersion) ||
   515  			!read(&oh32.MinorImageVersion) ||
   516  			!read(&oh32.MajorSubsystemVersion) ||
   517  			!read(&oh32.MinorSubsystemVersion) ||
   518  			!read(&oh32.Win32VersionValue) ||
   519  			!read(&oh32.SizeOfImage) ||
   520  			!read(&oh32.SizeOfHeaders) ||
   521  			!read(&oh32.CheckSum) ||
   522  			!read(&oh32.Subsystem) ||
   523  			!read(&oh32.DllCharacteristics) ||
   524  			!read(&oh32.SizeOfStackReserve) ||
   525  			!read(&oh32.SizeOfStackCommit) ||
   526  			!read(&oh32.SizeOfHeapReserve) ||
   527  			!read(&oh32.SizeOfHeapCommit) ||
   528  			!read(&oh32.LoaderFlags) ||
   529  			!read(&oh32.NumberOfRvaAndSizes) {
   530  			return nil, fmt.Errorf("failure to read PE32 optional header: %v", err)
   531  		}
   532  
   533  		dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes)
   534  		if err != nil {
   535  			return nil, err
   536  		}
   537  
   538  		copy(oh32.DataDirectory[:], dd)
   539  
   540  		return &oh32, nil
   541  	case 0x20b: // PE32+
   542  		var (
   543  			oh64 OptionalHeader64
   544  			// There can be 0 or more data directories. So the minimum size of optional
   545  			// header is calculated by subtracting oh64.DataDirectory size from oh64 size.
   546  			oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory)
   547  		)
   548  
   549  		if sz < uint16(oh64MinSz) {
   550  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz)
   551  		}
   552  
   553  		// Init oh64 fields
   554  		oh64.Magic = ohMagic
   555  		if !read(&oh64.MajorLinkerVersion) ||
   556  			!read(&oh64.MinorLinkerVersion) ||
   557  			!read(&oh64.SizeOfCode) ||
   558  			!read(&oh64.SizeOfInitializedData) ||
   559  			!read(&oh64.SizeOfUninitializedData) ||
   560  			!read(&oh64.AddressOfEntryPoint) ||
   561  			!read(&oh64.BaseOfCode) ||
   562  			!read(&oh64.ImageBase) ||
   563  			!read(&oh64.SectionAlignment) ||
   564  			!read(&oh64.FileAlignment) ||
   565  			!read(&oh64.MajorOperatingSystemVersion) ||
   566  			!read(&oh64.MinorOperatingSystemVersion) ||
   567  			!read(&oh64.MajorImageVersion) ||
   568  			!read(&oh64.MinorImageVersion) ||
   569  			!read(&oh64.MajorSubsystemVersion) ||
   570  			!read(&oh64.MinorSubsystemVersion) ||
   571  			!read(&oh64.Win32VersionValue) ||
   572  			!read(&oh64.SizeOfImage) ||
   573  			!read(&oh64.SizeOfHeaders) ||
   574  			!read(&oh64.CheckSum) ||
   575  			!read(&oh64.Subsystem) ||
   576  			!read(&oh64.DllCharacteristics) ||
   577  			!read(&oh64.SizeOfStackReserve) ||
   578  			!read(&oh64.SizeOfStackCommit) ||
   579  			!read(&oh64.SizeOfHeapReserve) ||
   580  			!read(&oh64.SizeOfHeapCommit) ||
   581  			!read(&oh64.LoaderFlags) ||
   582  			!read(&oh64.NumberOfRvaAndSizes) {
   583  			return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err)
   584  		}
   585  
   586  		dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes)
   587  		if err != nil {
   588  			return nil, err
   589  		}
   590  
   591  		copy(oh64.DataDirectory[:], dd)
   592  
   593  		return &oh64, nil
   594  	default:
   595  		return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic)
   596  	}
   597  }
   598  
   599  // readDataDirectories accepts a io.ReadSeeker pointing to data directories in the PE file,
   600  // its size and number of data directories as seen in optional header.
   601  // It parses the given size of bytes and returns given number of data directories.
   602  func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) {
   603  	ddSz := binary.Size(DataDirectory{})
   604  	if uint32(sz) != n*uint32(ddSz) {
   605  		return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n)
   606  	}
   607  
   608  	dd := make([]DataDirectory, n)
   609  	if err := binary.Read(r, binary.LittleEndian, dd); err != nil {
   610  		return nil, fmt.Errorf("failure to read data directories: %v", err)
   611  	}
   612  
   613  	return dd, nil
   614  }
   615  

View as plain text