...

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 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) (interface{}, 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 interface{}) 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