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.
6package pe
7
8import (
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.
20const seekStart = 0
21
22// A File represents an open PE file.
23type 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.
35func 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.
52func (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
61var (
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.
69func 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.
187type zeroReaderAt struct{}
188
189// ReadAt writes len(p) 0s into p.
190func (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.
198func 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.
213func (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
222func (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
312type 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.
326func (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.
438func (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.
446type FormatError struct {
447}
448
449func (e *FormatError) Error() string {
450	return "unknown error"
451}
452