github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 var ( 62 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{})) 63 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) 64 ) 65 66 // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) 67 68 // NewFile creates a new File for accessing a PE binary in an underlying reader. 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 // 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 // Read optional header. 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 { // PE32 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 { // PE32+ 138 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) 139 } 140 f.OptionalHeader = &oh64 141 } 142 143 // Process sections. 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 { // .bss must have all 0s 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 // zeroReaderAt is ReaderAt that reads 0s. 187 type zeroReaderAt struct{} 188 189 // ReadAt writes len(p) 0s into p. 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 // getString extracts a string from symbol string table. 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 // Section returns the first section with the given name, or nil if no such 212 // section exists. 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 // sectionData gets the data for s and checks its size. 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 // There are many other DWARF sections, but these 265 // are the ones the debug/dwarf package uses. 266 // Don't bother loading others. 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 // Look for DWARF4 .debug_types sections. 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 // TODO(brainman): document ImportDirectory once we decide what to do with it. 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 // ImportedSymbols returns the names of all symbols 323 // referred to by the binary f that are expected to be 324 // satisfied by other libraries at dynamic load time. 325 // It does not return weak symbols. 326 func (f *File) ImportedSymbols() ([]string, error) { 327 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 328 329 // grab the number of data directory entries 330 var dd_length uint32 331 if pe64 { 332 dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes 333 } else { 334 dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes 335 } 336 337 // check that the length of data directory entries is large 338 // enough to include the imports directory. 339 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 { 340 return nil, nil 341 } 342 343 // grab the import data directory entry 344 var idd DataDirectory 345 if pe64 { 346 idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 347 } else { 348 idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 349 } 350 351 // figure out which section contains the import directory table 352 var ds *Section 353 ds = nil 354 for _, s := range f.Sections { 355 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize { 356 ds = s 357 break 358 } 359 } 360 361 // didn't find a section, so no import libraries were found 362 if ds == nil { 363 return nil, nil 364 } 365 366 d, err := ds.Data() 367 if err != nil { 368 return nil, err 369 } 370 371 // seek to the virtual address specified in the import data directory 372 d = d[idd.VirtualAddress-ds.VirtualAddress:] 373 374 // start decoding the import directory 375 var ida []ImportDirectory 376 for len(d) > 0 { 377 var dt ImportDirectory 378 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 379 dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8]) 380 dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12]) 381 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 382 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 383 d = d[20:] 384 if dt.OriginalFirstThunk == 0 { 385 break 386 } 387 ida = append(ida, dt) 388 } 389 // TODO(brainman): this needs to be rewritten 390 // ds.Data() returns contents of section containing import table. Why store in variable called "names"? 391 // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. 392 // getString does not extracts a string from symbol string table (as getString doco says). 393 // Why ds.Data() called again and again in the loop? 394 // Needs test before rewrite. 395 names, _ := ds.Data() 396 var all []string 397 for _, dt := range ida { 398 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 399 d, _ = ds.Data() 400 // seek to OriginalFirstThunk 401 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 402 for len(d) > 0 { 403 if pe64 { // 64bit 404 va := binary.LittleEndian.Uint64(d[0:8]) 405 d = d[8:] 406 if va == 0 { 407 break 408 } 409 if va&0x8000000000000000 > 0 { // is Ordinal 410 // TODO add dynimport ordinal support. 411 } else { 412 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 413 all = append(all, fn+":"+dt.dll) 414 } 415 } else { // 32bit 416 va := binary.LittleEndian.Uint32(d[0:4]) 417 d = d[4:] 418 if va == 0 { 419 break 420 } 421 if va&0x80000000 > 0 { // is Ordinal 422 // TODO add dynimport ordinal support. 423 //ord := va&0x0000FFFF 424 } else { 425 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 426 all = append(all, fn+":"+dt.dll) 427 } 428 } 429 } 430 } 431 432 return all, nil 433 } 434 435 // ImportedLibraries returns the names of all libraries 436 // referred to by the binary f that are expected to be 437 // linked with the binary at dynamic link time. 438 func (f *File) ImportedLibraries() ([]string, error) { 439 // TODO 440 // cgo -dynimport don't use this for windows PE, so just return. 441 return nil, nil 442 } 443 444 // FormatError is unused. 445 // The type is retained for compatibility. 446 type FormatError struct { 447 } 448 449 func (e *FormatError) Error() string { 450 return "unknown error" 451 }