Source file src/pkg/debug/pe/file.go
1
2
3
4
5
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
20 const seekStart = 0
21
22
23 type File struct {
24 FileHeader
25 OptionalHeader interface{}
26 Sections []*Section
27 Symbols []*Symbol
28 COFFSymbols []COFFSymbol
29 StringTable StringTable
30
31 closer io.Closer
32 }
33
34
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
50
51
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
67
68
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
102 f.StringTable, err = readStringTable(&f.FileHeader, sr)
103 if err != nil {
104 return nil, err
105 }
106
107
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
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 {
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 {
138 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
139 }
140 f.OptionalHeader = &oh64
141 }
142
143
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 {
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
187 type zeroReaderAt struct{}
188
189
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
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
212
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
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
265
266
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
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
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
323
324
325
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
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
342
343 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
344 return nil, nil
345 }
346
347
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
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
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
376 d = d[idd.VirtualAddress-ds.VirtualAddress:]
377
378
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
394
395
396
397
398
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
405 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
406 for len(d) > 0 {
407 if pe64 {
408 va := binary.LittleEndian.Uint64(d[0:8])
409 d = d[8:]
410 if va == 0 {
411 break
412 }
413 if va&0x8000000000000000 > 0 {
414
415 } else {
416 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
417 all = append(all, fn+":"+dt.dll)
418 }
419 } else {
420 va := binary.LittleEndian.Uint32(d[0:4])
421 d = d[4:]
422 if va == 0 {
423 break
424 }
425 if va&0x80000000 > 0 {
426
427
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
440
441
442 func (f *File) ImportedLibraries() ([]string, error) {
443
444
445 return nil, nil
446 }
447
448
449
450 type FormatError struct {
451 }
452
453 func (e *FormatError) Error() string {
454 return "unknown error"
455 }
456
View as plain text