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. 6package pe 7 8import ( 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. 20const seekStart = 0 21 22// A File represents an open PE file. 23type 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. 35func 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. 52func (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 61var ( 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. 69func 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. 187type zeroReaderAt struct{} 188 189// ReadAt writes len(p) 0s into p. 190func (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. 198func 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. 213func (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 222func (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 312type 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. 326func (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. 438func (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. 446type FormatError struct { 447} 448 449func (e *FormatError) Error() string { 450 return "unknown error" 451} 452