Source file
src/debug/pe/file.go
1
2
3
4
5
16 package pe
17
18 import (
19 "bytes"
20 "compress/zlib"
21 "debug/dwarf"
22 "encoding/binary"
23 "fmt"
24 "io"
25 "os"
26 "strings"
27 )
28
29
30 const seekStart = 0
31
32
33 type File struct {
34 FileHeader
35 OptionalHeader any
36 Sections []*Section
37 Symbols []*Symbol
38 COFFSymbols []COFFSymbol
39 StringTable StringTable
40
41 closer io.Closer
42 }
43
44
45 func Open(name string) (*File, error) {
46 f, err := os.Open(name)
47 if err != nil {
48 return nil, err
49 }
50 ff, err := NewFile(f)
51 if err != nil {
52 f.Close()
53 return nil, err
54 }
55 ff.closer = f
56 return ff, nil
57 }
58
59
60
61
62 func (f *File) Close() error {
63 var err error
64 if f.closer != nil {
65 err = f.closer.Close()
66 f.closer = nil
67 }
68 return err
69 }
70
71
72
73
74 func NewFile(r io.ReaderAt) (*File, error) {
75 f := new(File)
76 sr := io.NewSectionReader(r, 0, 1<<63-1)
77
78 var dosheader [96]byte
79 if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
80 return nil, err
81 }
82 var base int64
83 if dosheader[0] == 'M' && dosheader[1] == 'Z' {
84 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
85 var sign [4]byte
86 r.ReadAt(sign[:], signoff)
87 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
88 return nil, fmt.Errorf("invalid PE file signature: % x", sign)
89 }
90 base = signoff + 4
91 } else {
92 base = int64(0)
93 }
94 sr.Seek(base, seekStart)
95 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
96 return nil, err
97 }
98 switch f.FileHeader.Machine {
99 case IMAGE_FILE_MACHINE_AMD64,
100 IMAGE_FILE_MACHINE_ARM64,
101 IMAGE_FILE_MACHINE_ARMNT,
102 IMAGE_FILE_MACHINE_I386,
103 IMAGE_FILE_MACHINE_RISCV32,
104 IMAGE_FILE_MACHINE_RISCV64,
105 IMAGE_FILE_MACHINE_RISCV128,
106 IMAGE_FILE_MACHINE_UNKNOWN:
107
108 default:
109 return nil, fmt.Errorf("unrecognized PE machine: %#x", f.FileHeader.Machine)
110 }
111
112 var err error
113
114
115 f.StringTable, err = readStringTable(&f.FileHeader, sr)
116 if err != nil {
117 return nil, err
118 }
119
120
121 f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
122 if err != nil {
123 return nil, err
124 }
125 f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
126 if err != nil {
127 return nil, err
128 }
129
130
131 _, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), seekStart)
132 if err != nil {
133 return nil, err
134 }
135
136
137 f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader)
138 if err != nil {
139 return nil, err
140 }
141
142
143 f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
144 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
145 sh := new(SectionHeader32)
146 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
147 return nil, err
148 }
149 name, err := sh.fullName(f.StringTable)
150 if err != nil {
151 return nil, err
152 }
153 s := new(Section)
154 s.SectionHeader = SectionHeader{
155 Name: name,
156 VirtualSize: sh.VirtualSize,
157 VirtualAddress: sh.VirtualAddress,
158 Size: sh.SizeOfRawData,
159 Offset: sh.PointerToRawData,
160 PointerToRelocations: sh.PointerToRelocations,
161 PointerToLineNumbers: sh.PointerToLineNumbers,
162 NumberOfRelocations: sh.NumberOfRelocations,
163 NumberOfLineNumbers: sh.NumberOfLineNumbers,
164 Characteristics: sh.Characteristics,
165 }
166 r2 := r
167 if sh.PointerToRawData == 0 {
168 r2 = zeroReaderAt{}
169 }
170 s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
171 s.ReaderAt = s.sr
172 f.Sections[i] = s
173 }
174 for i := range f.Sections {
175 var err error
176 f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
177 if err != nil {
178 return nil, err
179 }
180 }
181
182 return f, nil
183 }
184
185
186 type zeroReaderAt struct{}
187
188
189 func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
190 for i := range p {
191 p[i] = 0
192 }
193 return len(p), nil
194 }
195
196
197 func getString(section []byte, start int) (string, bool) {
198 if start < 0 || start >= len(section) {
199 return "", false
200 }
201
202 for end := start; end < len(section); end++ {
203 if section[end] == 0 {
204 return string(section[start:end]), true
205 }
206 }
207 return "", false
208 }
209
210
211
212 func (f *File) Section(name string) *Section {
213 for _, s := range f.Sections {
214 if s.Name == name {
215 return s
216 }
217 }
218 return nil
219 }
220
221 func (f *File) DWARF() (*dwarf.Data, error) {
222 dwarfSuffix := func(s *Section) string {
223 switch {
224 case strings.HasPrefix(s.Name, ".debug_"):
225 return s.Name[7:]
226 case strings.HasPrefix(s.Name, ".zdebug_"):
227 return s.Name[8:]
228 default:
229 return ""
230 }
231
232 }
233
234
235 sectionData := func(s *Section) ([]byte, error) {
236 b, err := s.Data()
237 if err != nil && uint32(len(b)) < s.Size {
238 return nil, err
239 }
240
241 if 0 < s.VirtualSize && s.VirtualSize < s.Size {
242 b = b[:s.VirtualSize]
243 }
244
245 if len(b) >= 12 && string(b[:4]) == "ZLIB" {
246 dlen := binary.BigEndian.Uint64(b[4:12])
247 dbuf := make([]byte, dlen)
248 r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
249 if err != nil {
250 return nil, err
251 }
252 if _, err := io.ReadFull(r, dbuf); err != nil {
253 return nil, err
254 }
255 if err := r.Close(); err != nil {
256 return nil, err
257 }
258 b = dbuf
259 }
260 return b, nil
261 }
262
263
264
265
266 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
267 for _, s := range f.Sections {
268 suffix := dwarfSuffix(s)
269 if suffix == "" {
270 continue
271 }
272 if _, ok := dat[suffix]; !ok {
273 continue
274 }
275
276 b, err := sectionData(s)
277 if err != nil {
278 return nil, err
279 }
280 dat[suffix] = b
281 }
282
283 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
284 if err != nil {
285 return nil, err
286 }
287
288
289 for i, s := range f.Sections {
290 suffix := dwarfSuffix(s)
291 if suffix == "" {
292 continue
293 }
294 if _, ok := dat[suffix]; ok {
295
296 continue
297 }
298
299 b, err := sectionData(s)
300 if err != nil {
301 return nil, err
302 }
303
304 if suffix == "types" {
305 err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
306 } else {
307 err = d.AddSection(".debug_"+suffix, b)
308 }
309 if err != nil {
310 return nil, err
311 }
312 }
313
314 return d, nil
315 }
316
317
318
319 type ImportDirectory struct {
320 OriginalFirstThunk uint32
321 TimeDateStamp uint32
322 ForwarderChain uint32
323 Name uint32
324 FirstThunk uint32
325
326 dll string
327 }
328
329
330
331
332
333 func (f *File) ImportedSymbols() ([]string, error) {
334 if f.OptionalHeader == nil {
335 return nil, nil
336 }
337
338 _, pe64 := f.OptionalHeader.(*OptionalHeader64)
339
340
341 var dd_length uint32
342 if pe64 {
343 dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
344 } else {
345 dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
346 }
347
348
349
350 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
351 return nil, nil
352 }
353
354
355 var idd DataDirectory
356 if pe64 {
357 idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
358 } else {
359 idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
360 }
361
362
363 var ds *Section
364 ds = nil
365 for _, s := range f.Sections {
366
367
368
369 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
370 ds = s
371 break
372 }
373 }
374
375
376 if ds == nil {
377 return nil, nil
378 }
379
380 d, err := ds.Data()
381 if err != nil {
382 return nil, err
383 }
384
385
386 d = d[idd.VirtualAddress-ds.VirtualAddress:]
387
388
389 var ida []ImportDirectory
390 for len(d) >= 20 {
391 var dt ImportDirectory
392 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
393 dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
394 dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
395 dt.Name = binary.LittleEndian.Uint32(d[12:16])
396 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
397 d = d[20:]
398 if dt.OriginalFirstThunk == 0 {
399 break
400 }
401 ida = append(ida, dt)
402 }
403
404
405
406
407
408
409 names, _ := ds.Data()
410 var all []string
411 for _, dt := range ida {
412 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
413 d, _ = ds.Data()
414
415 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
416 for len(d) > 0 {
417 if pe64 {
418 va := binary.LittleEndian.Uint64(d[0:8])
419 d = d[8:]
420 if va == 0 {
421 break
422 }
423 if va&0x8000000000000000 > 0 {
424
425 } else {
426 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
427 all = append(all, fn+":"+dt.dll)
428 }
429 } else {
430 va := binary.LittleEndian.Uint32(d[0:4])
431 d = d[4:]
432 if va == 0 {
433 break
434 }
435 if va&0x80000000 > 0 {
436
437
438 } else {
439 fn, _ := getString(names, int(va-ds.VirtualAddress+2))
440 all = append(all, fn+":"+dt.dll)
441 }
442 }
443 }
444 }
445
446 return all, nil
447 }
448
449
450
451
452 func (f *File) ImportedLibraries() ([]string, error) {
453
454
455 return nil, nil
456 }
457
458
459
460 type FormatError struct {
461 }
462
463 func (e *FormatError) Error() string {
464 return "unknown error"
465 }
466
467
468
469
470
471 func readOptionalHeader(r io.ReadSeeker, sz uint16) (any, error) {
472
473 if sz == 0 {
474 return nil, nil
475 }
476
477 var (
478
479
480
481 ohMagic uint16
482 ohMagicSz = binary.Size(ohMagic)
483 )
484
485
486 if sz < uint16(ohMagicSz) {
487 return nil, fmt.Errorf("optional header size is less than optional header magic size")
488 }
489
490
491 var err error
492 read := func(data any) bool {
493 err = binary.Read(r, binary.LittleEndian, data)
494 return err == nil
495 }
496
497 if !read(&ohMagic) {
498 return nil, fmt.Errorf("failure to read optional header magic: %v", err)
499
500 }
501
502 switch ohMagic {
503 case 0x10b:
504 var (
505 oh32 OptionalHeader32
506
507
508 oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory)
509 )
510
511 if sz < uint16(oh32MinSz) {
512 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz)
513 }
514
515
516 oh32.Magic = ohMagic
517 if !read(&oh32.MajorLinkerVersion) ||
518 !read(&oh32.MinorLinkerVersion) ||
519 !read(&oh32.SizeOfCode) ||
520 !read(&oh32.SizeOfInitializedData) ||
521 !read(&oh32.SizeOfUninitializedData) ||
522 !read(&oh32.AddressOfEntryPoint) ||
523 !read(&oh32.BaseOfCode) ||
524 !read(&oh32.BaseOfData) ||
525 !read(&oh32.ImageBase) ||
526 !read(&oh32.SectionAlignment) ||
527 !read(&oh32.FileAlignment) ||
528 !read(&oh32.MajorOperatingSystemVersion) ||
529 !read(&oh32.MinorOperatingSystemVersion) ||
530 !read(&oh32.MajorImageVersion) ||
531 !read(&oh32.MinorImageVersion) ||
532 !read(&oh32.MajorSubsystemVersion) ||
533 !read(&oh32.MinorSubsystemVersion) ||
534 !read(&oh32.Win32VersionValue) ||
535 !read(&oh32.SizeOfImage) ||
536 !read(&oh32.SizeOfHeaders) ||
537 !read(&oh32.CheckSum) ||
538 !read(&oh32.Subsystem) ||
539 !read(&oh32.DllCharacteristics) ||
540 !read(&oh32.SizeOfStackReserve) ||
541 !read(&oh32.SizeOfStackCommit) ||
542 !read(&oh32.SizeOfHeapReserve) ||
543 !read(&oh32.SizeOfHeapCommit) ||
544 !read(&oh32.LoaderFlags) ||
545 !read(&oh32.NumberOfRvaAndSizes) {
546 return nil, fmt.Errorf("failure to read PE32 optional header: %v", err)
547 }
548
549 dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes)
550 if err != nil {
551 return nil, err
552 }
553
554 copy(oh32.DataDirectory[:], dd)
555
556 return &oh32, nil
557 case 0x20b:
558 var (
559 oh64 OptionalHeader64
560
561
562 oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory)
563 )
564
565 if sz < uint16(oh64MinSz) {
566 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz)
567 }
568
569
570 oh64.Magic = ohMagic
571 if !read(&oh64.MajorLinkerVersion) ||
572 !read(&oh64.MinorLinkerVersion) ||
573 !read(&oh64.SizeOfCode) ||
574 !read(&oh64.SizeOfInitializedData) ||
575 !read(&oh64.SizeOfUninitializedData) ||
576 !read(&oh64.AddressOfEntryPoint) ||
577 !read(&oh64.BaseOfCode) ||
578 !read(&oh64.ImageBase) ||
579 !read(&oh64.SectionAlignment) ||
580 !read(&oh64.FileAlignment) ||
581 !read(&oh64.MajorOperatingSystemVersion) ||
582 !read(&oh64.MinorOperatingSystemVersion) ||
583 !read(&oh64.MajorImageVersion) ||
584 !read(&oh64.MinorImageVersion) ||
585 !read(&oh64.MajorSubsystemVersion) ||
586 !read(&oh64.MinorSubsystemVersion) ||
587 !read(&oh64.Win32VersionValue) ||
588 !read(&oh64.SizeOfImage) ||
589 !read(&oh64.SizeOfHeaders) ||
590 !read(&oh64.CheckSum) ||
591 !read(&oh64.Subsystem) ||
592 !read(&oh64.DllCharacteristics) ||
593 !read(&oh64.SizeOfStackReserve) ||
594 !read(&oh64.SizeOfStackCommit) ||
595 !read(&oh64.SizeOfHeapReserve) ||
596 !read(&oh64.SizeOfHeapCommit) ||
597 !read(&oh64.LoaderFlags) ||
598 !read(&oh64.NumberOfRvaAndSizes) {
599 return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err)
600 }
601
602 dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes)
603 if err != nil {
604 return nil, err
605 }
606
607 copy(oh64.DataDirectory[:], dd)
608
609 return &oh64, nil
610 default:
611 return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic)
612 }
613 }
614
615
616
617
618 func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) {
619 ddSz := uint64(binary.Size(DataDirectory{}))
620 if uint64(sz) != uint64(n)*ddSz {
621 return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n)
622 }
623
624 dd := make([]DataDirectory, n)
625 if err := binary.Read(r, binary.LittleEndian, dd); err != nil {
626 return nil, fmt.Errorf("failure to read data directories: %v", err)
627 }
628
629 return dd, nil
630 }
631
View as plain text