github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/debug/pe/file.go (about) 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 }