github.com/bir3/gocompiler@v0.3.205/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 /* 6 Package pe implements access to PE (Microsoft Windows Portable Executable) files. 7 8 # Security 9 10 This package is not designed to be hardened against adversarial inputs, and is 11 outside the scope of https://go.dev/security/policy. In particular, only basic 12 validation is done when parsing object files. As such, care should be taken when 13 parsing untrusted inputs, as parsing malformed files may consume significant 14 resources, or cause panics. 15 */ 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 // Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap. 30 const seekStart = 0 31 32 // A File represents an open PE file. 33 type File struct { 34 FileHeader 35 OptionalHeader any // of type *OptionalHeader32 or *OptionalHeader64 36 Sections []*Section 37 Symbols []*Symbol // COFF symbols with auxiliary symbol records removed 38 COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) 39 StringTable StringTable 40 41 closer io.Closer 42 } 43 44 // Open opens the named file using os.Open and prepares it for use as a PE binary. 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 // Close closes the File. 60 // If the File was created using NewFile directly instead of Open, 61 // Close has no effect. 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 // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) 72 73 // NewFile creates a new File for accessing a PE binary in an underlying reader. 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 // ok 108 default: 109 return nil, fmt.Errorf("unrecognized PE machine: %#x", f.FileHeader.Machine) 110 } 111 112 var err error 113 114 // Read string table. 115 f.StringTable, err = readStringTable(&f.FileHeader, sr) 116 if err != nil { 117 return nil, err 118 } 119 120 // Read symbol table. 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 // Seek past file header. 131 _, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), seekStart) 132 if err != nil { 133 return nil, err 134 } 135 136 // Read optional header. 137 f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader) 138 if err != nil { 139 return nil, err 140 } 141 142 // Process sections. 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 { // .bss must have all 0s 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 // zeroReaderAt is ReaderAt that reads 0s. 186 type zeroReaderAt struct{} 187 188 // ReadAt writes len(p) 0s into p. 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 // getString extracts a string from symbol string table. 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 // Section returns the first section with the given name, or nil if no such 211 // section exists. 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 // sectionData gets the data for s and checks its size. 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 // There are many other DWARF sections, but these 264 // are the ones the debug/dwarf package uses. 265 // Don't bother loading others. 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 // Look for DWARF4 .debug_types sections and DWARF5 sections. 289 for i, s := range f.Sections { 290 suffix := dwarfSuffix(s) 291 if suffix == "" { 292 continue 293 } 294 if _, ok := dat[suffix]; ok { 295 // Already handled. 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 // TODO(brainman): document ImportDirectory once we decide what to do with it. 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 // ImportedSymbols returns the names of all symbols 330 // referred to by the binary f that are expected to be 331 // satisfied by other libraries at dynamic load time. 332 // It does not return weak symbols. 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 // grab the number of data directory entries 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 // check that the length of data directory entries is large 349 // enough to include the imports directory. 350 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 { 351 return nil, nil 352 } 353 354 // grab the import data directory entry 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 // figure out which section contains the import directory table 363 var ds *Section 364 ds = nil 365 for _, s := range f.Sections { 366 // We are using distance between s.VirtualAddress and idd.VirtualAddress 367 // to avoid potential overflow of uint32 caused by addition of s.VirtualSize 368 // to s.VirtualAddress. 369 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize { 370 ds = s 371 break 372 } 373 } 374 375 // didn't find a section, so no import libraries were found 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 // seek to the virtual address specified in the import data directory 386 d = d[idd.VirtualAddress-ds.VirtualAddress:] 387 388 // start decoding the import directory 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 // TODO(brainman): this needs to be rewritten 404 // ds.Data() returns contents of section containing import table. Why store in variable called "names"? 405 // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. 406 // getString does not extracts a string from symbol string table (as getString doco says). 407 // Why ds.Data() called again and again in the loop? 408 // Needs test before rewrite. 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 // seek to OriginalFirstThunk 415 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 416 for len(d) > 0 { 417 if pe64 { // 64bit 418 va := binary.LittleEndian.Uint64(d[0:8]) 419 d = d[8:] 420 if va == 0 { 421 break 422 } 423 if va&0x8000000000000000 > 0 { // is Ordinal 424 // TODO add dynimport ordinal support. 425 } else { 426 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 427 all = append(all, fn+":"+dt.dll) 428 } 429 } else { // 32bit 430 va := binary.LittleEndian.Uint32(d[0:4]) 431 d = d[4:] 432 if va == 0 { 433 break 434 } 435 if va&0x80000000 > 0 { // is Ordinal 436 // TODO add dynimport ordinal support. 437 //ord := va&0x0000FFFF 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 // ImportedLibraries returns the names of all libraries 450 // referred to by the binary f that are expected to be 451 // linked with the binary at dynamic link time. 452 func (f *File) ImportedLibraries() ([]string, error) { 453 // TODO 454 // cgo -dynimport don't use this for windows PE, so just return. 455 return nil, nil 456 } 457 458 // FormatError is unused. 459 // The type is retained for compatibility. 460 type FormatError struct { 461 } 462 463 func (e *FormatError) Error() string { 464 return "unknown error" 465 } 466 467 // readOptionalHeader accepts a io.ReadSeeker pointing to optional header in the PE file 468 // and its size as seen in the file header. 469 // It parses the given size of bytes and returns optional header. It infers whether the 470 // bytes being parsed refer to 32 bit or 64 bit version of optional header. 471 func readOptionalHeader(r io.ReadSeeker, sz uint16) (any, error) { 472 // If optional header size is 0, return empty optional header. 473 if sz == 0 { 474 return nil, nil 475 } 476 477 var ( 478 // First couple of bytes in option header state its type. 479 // We need to read them first to determine the type and 480 // validity of optional header. 481 ohMagic uint16 482 ohMagicSz = binary.Size(ohMagic) 483 ) 484 485 // If optional header size is greater than 0 but less than its magic size, return error. 486 if sz < uint16(ohMagicSz) { 487 return nil, fmt.Errorf("optional header size is less than optional header magic size") 488 } 489 490 // read reads from io.ReadSeeke, r, into data. 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: // PE32 504 var ( 505 oh32 OptionalHeader32 506 // There can be 0 or more data directories. So the minimum size of optional 507 // header is calculated by subtracting oh32.DataDirectory size from oh32 size. 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 // Init oh32 fields 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: // PE32+ 558 var ( 559 oh64 OptionalHeader64 560 // There can be 0 or more data directories. So the minimum size of optional 561 // header is calculated by subtracting oh64.DataDirectory size from oh64 size. 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 // Init oh64 fields 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 // readDataDirectories accepts a io.ReadSeeker pointing to data directories in the PE file, 616 // its size and number of data directories as seen in optional header. 617 // It parses the given size of bytes and returns given number of data directories. 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 }