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 "debug/dwarf"
10 "encoding/binary"
11 "errors"
12 "fmt"
13 "io"
14 "os"
15 "strconv"
16 )
17
18 // A File represents an open PE file.
19 type File struct {
20 FileHeader
21 Sections []*Section
22
23 closer io.Closer
24 }
25
26 type SectionHeader struct {
27 Name string
28 VirtualSize uint32
29 VirtualAddress uint32
30 Size uint32
31 Offset uint32
32 PointerToRelocations uint32
33 PointerToLineNumbers uint32
34 NumberOfRelocations uint16
35 NumberOfLineNumbers uint16
36 Characteristics uint32
37 }
38
39 type Section struct {
40 SectionHeader
41
42 // Embed ReaderAt for ReadAt method.
43 // Do not embed SectionReader directly
44 // to avoid having Read and Seek.
45 // If a client wants Read and Seek it must use
46 // Open() to avoid fighting over the seek offset
47 // with other clients.
48 io.ReaderAt
49 sr *io.SectionReader
50 }
51
52 type ImportDirectory struct {
53 OriginalFirstThunk uint32
54 TimeDateStamp uint32
55 ForwarderChain uint32
56 Name uint32
57 FirstThunk uint32
58
59 dll string
60 }
61
62 // Data reads and returns the contents of the PE section.
63 func (s *Section) Data() ([]byte, error) {
64 dat := make([]byte, s.sr.Size())
65 n, err := s.sr.ReadAt(dat, 0)
66 return dat[0:n], err
67 }
68
69 // Open returns a new ReadSeeker reading the PE section.
70 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
71
72 type FormatError struct {
73 off int64
74 msg string
75 val interface{}
76 }
77
78 func (e *FormatError) Error() string {
79 msg := e.msg
80 if e.val != nil {
81 msg += fmt.Sprintf(" '%v'", e.val)
82 }
83 msg += fmt.Sprintf(" in record at byte %#x", e.off)
84 return msg
85 }
86
87 // Open opens the named file using os.Open and prepares it for use as a PE binary.
88 func Open(name string) (*File, error) {
89 f, err := os.Open(name)
90 if err != nil {
91 return nil, err
92 }
93 ff, err := NewFile(f)
94 if err != nil {
95 f.Close()
96 return nil, err
97 }
98 ff.closer = f
99 return ff, nil
100 }
101
102 // Close closes the File.
103 // If the File was created using NewFile directly instead of Open,
104 // Close has no effect.
105 func (f *File) Close() error {
106 var err error
107 if f.closer != nil {
108 err = f.closer.Close()
109 f.closer = nil
110 }
111 return err
112 }
113
114 // NewFile creates a new File for accessing a PE binary in an underlying reader.
115 func NewFile(r io.ReaderAt) (*File, error) {
116 f := new(File)
117 sr := io.NewSectionReader(r, 0, 1<<63-1)
118
119 var dosheader [96]byte
120 if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
121 return nil, err
122 }
123 var base int64
124 if dosheader[0] == 'M' && dosheader[1] == 'Z' {
125 var sign [4]byte
126 r.ReadAt(sign[0:], int64(dosheader[0x3c]))
127 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
128 return nil, errors.New("Invalid PE File Format.")
129 }
130 base = int64(dosheader[0x3c]) + 4
131 } else {
132 base = int64(0)
133 }
134 sr.Seek(base, os.SEEK_SET)
135 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
136 return nil, err
137 }
138 if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
139 return nil, errors.New("Invalid PE File Format.")
140 }
141 // get symbol string table
142 sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
143 var l uint32
144 if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
145 return nil, err
146 }
147 ss := make([]byte, l)
148 if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil {
149 return nil, err
150 }
151 sr.Seek(base, os.SEEK_SET)
152 binary.Read(sr, binary.LittleEndian, &f.FileHeader)
153 sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader
154 f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
155 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
156 sh := new(SectionHeader32)
157 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
158 return nil, err
159 }
160 var name string
161 if sh.Name[0] == '\x2F' {
162 si, _ := strconv.Atoi(cstring(sh.Name[1:]))
163 name, _ = getString(ss, si)
164 } else {
165 name = cstring(sh.Name[0:])
166 }
167 s := new(Section)
168 s.SectionHeader = SectionHeader{
169 Name: name,
170 VirtualSize: uint32(sh.VirtualSize),
171 VirtualAddress: uint32(sh.VirtualAddress),
172 Size: uint32(sh.SizeOfRawData),
173 Offset: uint32(sh.PointerToRawData),
174 PointerToRelocations: uint32(sh.PointerToRelocations),
175 PointerToLineNumbers: uint32(sh.PointerToLineNumbers),
176 NumberOfRelocations: uint16(sh.NumberOfRelocations),
177 NumberOfLineNumbers: uint16(sh.NumberOfLineNumbers),
178 Characteristics: uint32(sh.Characteristics),
179 }
180 s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
181 s.ReaderAt = s.sr
182 f.Sections[i] = s
183 }
184 return f, nil
185 }
186
187 func cstring(b []byte) string {
188 var i int
189 for i = 0; i < len(b) && b[i] != 0; i++ {
190 }
191 return string(b[0:i])
192 }
193
194 // getString extracts a string from symbol string table.
195 func getString(section []byte, start int) (string, bool) {
196 if start < 0 || start >= len(section) {
197 return "", false
198 }
199
200 for end := start; end < len(section); end++ {
201 if section[end] == 0 {
202 return string(section[start:end]), true
203 }
204 }
205 return "", false
206 }
207
208 // Section returns the first section with the given name, or nil if no such
209 // section exists.
210 func (f *File) Section(name string) *Section {
211 for _, s := range f.Sections {
212 if s.Name == name {
213 return s
214 }
215 }
216 return nil
217 }
218
219 func (f *File) DWARF() (*dwarf.Data, error) {
220 // There are many other DWARF sections, but these
221 // are the required ones, and the debug/dwarf package
222 // does not use the others, so don't bother loading them.
223 var names = [...]string{"abbrev", "info", "str"}
224 var dat [len(names)][]byte
225 for i, name := range names {
226 name = ".debug_" + name
227 s := f.Section(name)
228 if s == nil {
229 continue
230 }
231 b, err := s.Data()
232 if err != nil && uint32(len(b)) < s.Size {
233 return nil, err
234 }
235 dat[i] = b
236 }
237
238 abbrev, info, str := dat[0], dat[1], dat[2]
239 return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
240 }
241
242 // ImportedSymbols returns the names of all symbols
243 // referred to by the binary f that are expected to be
244 // satisfied by other libraries at dynamic load time.
245 // It does not return weak symbols.
246 func (f *File) ImportedSymbols() ([]string, error) {
247 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
248 ds := f.Section(".idata")
249 if ds == nil {
250 // not dynamic, so no libraries
251 return nil, nil
252 }
253 d, err := ds.Data()
254 if err != nil {
255 return nil, err
256 }
257 var ida []ImportDirectory
258 for len(d) > 0 {
259 var dt ImportDirectory
260 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
261 dt.Name = binary.LittleEndian.Uint32(d[12:16])
262 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
263 d = d[20:]
264 if dt.OriginalFirstThunk == 0 {
265 break
266 }
267 ida = append(ida, dt)
268 }
269 names, _ := ds.Data()
270 var all []string
271 for _, dt := range ida {
272 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
273 d, _ = ds.Data()
274 // seek to OriginalFirstThunk
275 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
276 for len(d) > 0 {
277 if pe64 { // 64bit
278 va := binary.LittleEndian.Uint64(d[0:8])
279 d = d[8:]
280 if va == 0 {
281 break
282 }
283 if va&0x8000000000000000 > 0 { // is Ordinal
284 // TODO add dynimport ordinal support.
285 } else {
286 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
287 all = append(all, fn+":"+dt.dll)
288 }
289 } else { // 32bit
290 va := binary.LittleEndian.Uint32(d[0:4])
291 d = d[4:]
292 if va == 0 {
293 break
294 }
295 if va&0x80000000 > 0 { // is Ordinal
296 // TODO add dynimport ordinal support.
297 //ord := va&0x0000FFFF
298 } else {
299 fn, _ := getString(names, int(va-ds.VirtualAddress+2))
300 all = append(all, fn+":"+dt.dll)
301 }
302 }
303 }
304 }
305
306 return all, nil
307 }
308
309 // ImportedLibraries returns the names of all libraries
310 // referred to by the binary f that are expected to be
311 // linked with the binary at dynamic link time.
312 func (f *File) ImportedLibraries() ([]string, error) {
313 // TODO
314 // cgo -dynimport don't use this for windows PE, so just return.
315 return nil, nil
316 }