...

Source file src/pkg/debug/pe/file.go

     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_ARMNT, 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		if f.OptionalHeader == nil {
   328			return nil, nil
   329		}
   330	
   331		pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
   332	
   333		// grab the number of data directory entries
   334		var dd_length uint32
   335		if pe64 {
   336			dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
   337		} else {
   338			dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
   339		}
   340	
   341		// check that the length of data directory entries is large
   342		// enough to include the imports directory.
   343		if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
   344			return nil, nil
   345		}
   346	
   347		// grab the import data directory entry
   348		var idd DataDirectory
   349		if pe64 {
   350			idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   351		} else {
   352			idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   353		}
   354	
   355		// figure out which section contains the import directory table
   356		var ds *Section
   357		ds = nil
   358		for _, s := range f.Sections {
   359			if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
   360				ds = s
   361				break
   362			}
   363		}
   364	
   365		// didn't find a section, so no import libraries were found
   366		if ds == nil {
   367			return nil, nil
   368		}
   369	
   370		d, err := ds.Data()
   371		if err != nil {
   372			return nil, err
   373		}
   374	
   375		// seek to the virtual address specified in the import data directory
   376		d = d[idd.VirtualAddress-ds.VirtualAddress:]
   377	
   378		// start decoding the import directory
   379		var ida []ImportDirectory
   380		for len(d) >= 20 {
   381			var dt ImportDirectory
   382			dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   383			dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
   384			dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
   385			dt.Name = binary.LittleEndian.Uint32(d[12:16])
   386			dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   387			d = d[20:]
   388			if dt.OriginalFirstThunk == 0 {
   389				break
   390			}
   391			ida = append(ida, dt)
   392		}
   393		// TODO(brainman): this needs to be rewritten
   394		//  ds.Data() returns contents of section containing import table. Why store in variable called "names"?
   395		//  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
   396		//  getString does not extracts a string from symbol string table (as getString doco says).
   397		//  Why ds.Data() called again and again in the loop?
   398		//  Needs test before rewrite.
   399		names, _ := ds.Data()
   400		var all []string
   401		for _, dt := range ida {
   402			dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   403			d, _ = ds.Data()
   404			// seek to OriginalFirstThunk
   405			d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
   406			for len(d) > 0 {
   407				if pe64 { // 64bit
   408					va := binary.LittleEndian.Uint64(d[0:8])
   409					d = d[8:]
   410					if va == 0 {
   411						break
   412					}
   413					if va&0x8000000000000000 > 0 { // is Ordinal
   414						// TODO add dynimport ordinal support.
   415					} else {
   416						fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   417						all = append(all, fn+":"+dt.dll)
   418					}
   419				} else { // 32bit
   420					va := binary.LittleEndian.Uint32(d[0:4])
   421					d = d[4:]
   422					if va == 0 {
   423						break
   424					}
   425					if va&0x80000000 > 0 { // is Ordinal
   426						// TODO add dynimport ordinal support.
   427						//ord := va&0x0000FFFF
   428					} else {
   429						fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   430						all = append(all, fn+":"+dt.dll)
   431					}
   432				}
   433			}
   434		}
   435	
   436		return all, nil
   437	}
   438	
   439	// ImportedLibraries returns the names of all libraries
   440	// referred to by the binary f that are expected to be
   441	// linked with the binary at dynamic link time.
   442	func (f *File) ImportedLibraries() ([]string, error) {
   443		// TODO
   444		// cgo -dynimport don't use this for windows PE, so just return.
   445		return nil, nil
   446	}
   447	
   448	// FormatError is unused.
   449	// The type is retained for compatibility.
   450	type FormatError struct {
   451	}
   452	
   453	func (e *FormatError) Error() string {
   454		return "unknown error"
   455	}
   456	

View as plain text