github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/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. 276 for i, s := range f.Sections { 277 suffix := dwarfSuffix(s) 278 if suffix != "types" { 279 continue 280 } 281 282 b, err := sectionData(s) 283 if err != nil { 284 return nil, err 285 } 286 287 err = d.AddTypes(fmt.Sprintf("types-%d", i), b) 288 if err != nil { 289 return nil, err 290 } 291 } 292 293 return d, nil 294 } 295 296 // TODO(brainman): document ImportDirectory once we decide what to do with it. 297 298 type ImportDirectory struct { 299 OriginalFirstThunk uint32 300 TimeDateStamp uint32 301 ForwarderChain uint32 302 Name uint32 303 FirstThunk uint32 304 305 dll string 306 } 307 308 // ImportedSymbols returns the names of all symbols 309 // referred to by the binary f that are expected to be 310 // satisfied by other libraries at dynamic load time. 311 // It does not return weak symbols. 312 func (f *File) ImportedSymbols() ([]string, error) { 313 if f.OptionalHeader == nil { 314 return nil, nil 315 } 316 317 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 || f.Machine == IMAGE_FILE_MACHINE_ARM64 318 319 // grab the number of data directory entries 320 var dd_length uint32 321 if pe64 { 322 dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes 323 } else { 324 dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes 325 } 326 327 // check that the length of data directory entries is large 328 // enough to include the imports directory. 329 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 { 330 return nil, nil 331 } 332 333 // grab the import data directory entry 334 var idd DataDirectory 335 if pe64 { 336 idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 337 } else { 338 idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 339 } 340 341 // figure out which section contains the import directory table 342 var ds *Section 343 ds = nil 344 for _, s := range f.Sections { 345 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize { 346 ds = s 347 break 348 } 349 } 350 351 // didn't find a section, so no import libraries were found 352 if ds == nil { 353 return nil, nil 354 } 355 356 d, err := ds.Data() 357 if err != nil { 358 return nil, err 359 } 360 361 // seek to the virtual address specified in the import data directory 362 d = d[idd.VirtualAddress-ds.VirtualAddress:] 363 364 // start decoding the import directory 365 var ida []ImportDirectory 366 for len(d) >= 20 { 367 var dt ImportDirectory 368 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 369 dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8]) 370 dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12]) 371 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 372 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 373 d = d[20:] 374 if dt.OriginalFirstThunk == 0 { 375 break 376 } 377 ida = append(ida, dt) 378 } 379 // TODO(brainman): this needs to be rewritten 380 // ds.Data() returns contents of section containing import table. Why store in variable called "names"? 381 // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. 382 // getString does not extracts a string from symbol string table (as getString doco says). 383 // Why ds.Data() called again and again in the loop? 384 // Needs test before rewrite. 385 names, _ := ds.Data() 386 var all []string 387 for _, dt := range ida { 388 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 389 d, _ = ds.Data() 390 // seek to OriginalFirstThunk 391 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 392 for len(d) > 0 { 393 if pe64 { // 64bit 394 va := binary.LittleEndian.Uint64(d[0:8]) 395 d = d[8:] 396 if va == 0 { 397 break 398 } 399 if va&0x8000000000000000 > 0 { // is Ordinal 400 // TODO add dynimport ordinal support. 401 } else { 402 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 403 all = append(all, fn+":"+dt.dll) 404 } 405 } else { // 32bit 406 va := binary.LittleEndian.Uint32(d[0:4]) 407 d = d[4:] 408 if va == 0 { 409 break 410 } 411 if va&0x80000000 > 0 { // is Ordinal 412 // TODO add dynimport ordinal support. 413 //ord := va&0x0000FFFF 414 } else { 415 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 416 all = append(all, fn+":"+dt.dll) 417 } 418 } 419 } 420 } 421 422 return all, nil 423 } 424 425 // ImportedLibraries returns the names of all libraries 426 // referred to by the binary f that are expected to be 427 // linked with the binary at dynamic link time. 428 func (f *File) ImportedLibraries() ([]string, error) { 429 // TODO 430 // cgo -dynimport don't use this for windows PE, so just return. 431 return nil, nil 432 } 433 434 // FormatError is unused. 435 // The type is retained for compatibility. 436 type FormatError struct { 437 } 438 439 func (e *FormatError) Error() string { 440 return "unknown error" 441 } 442 443 // readOptionalHeader accepts a io.ReadSeeker pointing to optional header in the PE file 444 // and its size as seen in the file header. 445 // It parses the given size of bytes and returns optional header. It infers whether the 446 // bytes being parsed refer to 32 bit or 64 bit version of optional header. 447 func readOptionalHeader(r io.ReadSeeker, sz uint16) (interface{}, error) { 448 // If optional header size is 0, return empty optional header. 449 if sz == 0 { 450 return nil, nil 451 } 452 453 var ( 454 // First couple of bytes in option header state its type. 455 // We need to read them first to determine the type and 456 // validity of optional header. 457 ohMagic uint16 458 ohMagicSz = binary.Size(ohMagic) 459 ) 460 461 // If optional header size is greater than 0 but less than its magic size, return error. 462 if sz < uint16(ohMagicSz) { 463 return nil, fmt.Errorf("optional header size is less than optional header magic size") 464 } 465 466 // read reads from io.ReadSeeke, r, into data. 467 var err error 468 read := func(data interface{}) bool { 469 err = binary.Read(r, binary.LittleEndian, data) 470 return err == nil 471 } 472 473 if !read(&ohMagic) { 474 return nil, fmt.Errorf("failure to read optional header magic: %v", err) 475 476 } 477 478 switch ohMagic { 479 case 0x10b: // PE32 480 var ( 481 oh32 OptionalHeader32 482 // There can be 0 or more data directories. So the minimum size of optional 483 // header is calculated by subtracting oh32.DataDirectory size from oh32 size. 484 oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory) 485 ) 486 487 if sz < uint16(oh32MinSz) { 488 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz) 489 } 490 491 // Init oh32 fields 492 oh32.Magic = ohMagic 493 if !read(&oh32.MajorLinkerVersion) || 494 !read(&oh32.MinorLinkerVersion) || 495 !read(&oh32.SizeOfCode) || 496 !read(&oh32.SizeOfInitializedData) || 497 !read(&oh32.SizeOfUninitializedData) || 498 !read(&oh32.AddressOfEntryPoint) || 499 !read(&oh32.BaseOfCode) || 500 !read(&oh32.BaseOfData) || 501 !read(&oh32.ImageBase) || 502 !read(&oh32.SectionAlignment) || 503 !read(&oh32.FileAlignment) || 504 !read(&oh32.MajorOperatingSystemVersion) || 505 !read(&oh32.MinorOperatingSystemVersion) || 506 !read(&oh32.MajorImageVersion) || 507 !read(&oh32.MinorImageVersion) || 508 !read(&oh32.MajorSubsystemVersion) || 509 !read(&oh32.MinorSubsystemVersion) || 510 !read(&oh32.Win32VersionValue) || 511 !read(&oh32.SizeOfImage) || 512 !read(&oh32.SizeOfHeaders) || 513 !read(&oh32.CheckSum) || 514 !read(&oh32.Subsystem) || 515 !read(&oh32.DllCharacteristics) || 516 !read(&oh32.SizeOfStackReserve) || 517 !read(&oh32.SizeOfStackCommit) || 518 !read(&oh32.SizeOfHeapReserve) || 519 !read(&oh32.SizeOfHeapCommit) || 520 !read(&oh32.LoaderFlags) || 521 !read(&oh32.NumberOfRvaAndSizes) { 522 return nil, fmt.Errorf("failure to read PE32 optional header: %v", err) 523 } 524 525 dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes) 526 if err != nil { 527 return nil, err 528 } 529 530 copy(oh32.DataDirectory[:], dd) 531 532 return &oh32, nil 533 case 0x20b: // PE32+ 534 var ( 535 oh64 OptionalHeader64 536 // There can be 0 or more data directories. So the minimum size of optional 537 // header is calculated by subtracting oh64.DataDirectory size from oh64 size. 538 oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory) 539 ) 540 541 if sz < uint16(oh64MinSz) { 542 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz) 543 } 544 545 // Init oh64 fields 546 oh64.Magic = ohMagic 547 if !read(&oh64.MajorLinkerVersion) || 548 !read(&oh64.MinorLinkerVersion) || 549 !read(&oh64.SizeOfCode) || 550 !read(&oh64.SizeOfInitializedData) || 551 !read(&oh64.SizeOfUninitializedData) || 552 !read(&oh64.AddressOfEntryPoint) || 553 !read(&oh64.BaseOfCode) || 554 !read(&oh64.ImageBase) || 555 !read(&oh64.SectionAlignment) || 556 !read(&oh64.FileAlignment) || 557 !read(&oh64.MajorOperatingSystemVersion) || 558 !read(&oh64.MinorOperatingSystemVersion) || 559 !read(&oh64.MajorImageVersion) || 560 !read(&oh64.MinorImageVersion) || 561 !read(&oh64.MajorSubsystemVersion) || 562 !read(&oh64.MinorSubsystemVersion) || 563 !read(&oh64.Win32VersionValue) || 564 !read(&oh64.SizeOfImage) || 565 !read(&oh64.SizeOfHeaders) || 566 !read(&oh64.CheckSum) || 567 !read(&oh64.Subsystem) || 568 !read(&oh64.DllCharacteristics) || 569 !read(&oh64.SizeOfStackReserve) || 570 !read(&oh64.SizeOfStackCommit) || 571 !read(&oh64.SizeOfHeapReserve) || 572 !read(&oh64.SizeOfHeapCommit) || 573 !read(&oh64.LoaderFlags) || 574 !read(&oh64.NumberOfRvaAndSizes) { 575 return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err) 576 } 577 578 dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes) 579 if err != nil { 580 return nil, err 581 } 582 583 copy(oh64.DataDirectory[:], dd) 584 585 return &oh64, nil 586 default: 587 return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic) 588 } 589 } 590 591 // readDataDirectories accepts a io.ReadSeeker pointing to data directories in the PE file, 592 // its size and number of data directories as seen in optional header. 593 // It parses the given size of bytes and returns given number of data directories. 594 func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) { 595 ddSz := binary.Size(DataDirectory{}) 596 if uint32(sz) != n*uint32(ddSz) { 597 return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n) 598 } 599 600 dd := make([]DataDirectory, n) 601 if err := binary.Read(r, binary.LittleEndian, dd); err != nil { 602 return nil, fmt.Errorf("failure to read data directories: %v", err) 603 } 604 605 return dd, nil 606 }