github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/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 "debug/dwarf" 10 "encoding/binary" 11 "errors" 12 "fmt" 13 "io" 14 "os" 15 "strconv" 16 ) 17 18 // A File represents an open PE file. 19 type File struct { 20 FileHeader 21 OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 22 Sections []*Section 23 Symbols []*Symbol 24 25 closer io.Closer 26 } 27 28 type SectionHeader struct { 29 Name string 30 VirtualSize uint32 31 VirtualAddress uint32 32 Size uint32 33 Offset uint32 34 PointerToRelocations uint32 35 PointerToLineNumbers uint32 36 NumberOfRelocations uint16 37 NumberOfLineNumbers uint16 38 Characteristics uint32 39 } 40 41 type Section struct { 42 SectionHeader 43 44 // Embed ReaderAt for ReadAt method. 45 // Do not embed SectionReader directly 46 // to avoid having Read and Seek. 47 // If a client wants Read and Seek it must use 48 // Open() to avoid fighting over the seek offset 49 // with other clients. 50 io.ReaderAt 51 sr *io.SectionReader 52 } 53 54 type Symbol struct { 55 Name string 56 Value uint32 57 SectionNumber int16 58 Type uint16 59 StorageClass uint8 60 } 61 62 type ImportDirectory struct { 63 OriginalFirstThunk uint32 64 TimeDateStamp uint32 65 ForwarderChain uint32 66 Name uint32 67 FirstThunk uint32 68 69 dll string 70 } 71 72 // Data reads and returns the contents of the PE section. 73 func (s *Section) Data() ([]byte, error) { 74 dat := make([]byte, s.sr.Size()) 75 n, err := s.sr.ReadAt(dat, 0) 76 if n == len(dat) { 77 err = nil 78 } 79 return dat[0:n], err 80 } 81 82 // Open returns a new ReadSeeker reading the PE section. 83 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 84 85 type FormatError struct { 86 off int64 87 msg string 88 val interface{} 89 } 90 91 func (e *FormatError) Error() string { 92 msg := e.msg 93 if e.val != nil { 94 msg += fmt.Sprintf(" '%v'", e.val) 95 } 96 msg += fmt.Sprintf(" in record at byte %#x", e.off) 97 return msg 98 } 99 100 // Open opens the named file using os.Open and prepares it for use as a PE binary. 101 func Open(name string) (*File, error) { 102 f, err := os.Open(name) 103 if err != nil { 104 return nil, err 105 } 106 ff, err := NewFile(f) 107 if err != nil { 108 f.Close() 109 return nil, err 110 } 111 ff.closer = f 112 return ff, nil 113 } 114 115 // Close closes the File. 116 // If the File was created using NewFile directly instead of Open, 117 // Close has no effect. 118 func (f *File) Close() error { 119 var err error 120 if f.closer != nil { 121 err = f.closer.Close() 122 f.closer = nil 123 } 124 return err 125 } 126 127 var ( 128 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{})) 129 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) 130 ) 131 132 // NewFile creates a new File for accessing a PE binary in an underlying reader. 133 func NewFile(r io.ReaderAt) (*File, error) { 134 f := new(File) 135 sr := io.NewSectionReader(r, 0, 1<<63-1) 136 137 var dosheader [96]byte 138 if _, err := r.ReadAt(dosheader[0:], 0); err != nil { 139 return nil, err 140 } 141 var base int64 142 if dosheader[0] == 'M' && dosheader[1] == 'Z' { 143 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) 144 var sign [4]byte 145 r.ReadAt(sign[:], signoff) 146 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { 147 return nil, errors.New("Invalid PE File Format.") 148 } 149 base = signoff + 4 150 } else { 151 base = int64(0) 152 } 153 sr.Seek(base, os.SEEK_SET) 154 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 155 return nil, err 156 } 157 if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { 158 return nil, errors.New("Invalid PE File Format.") 159 } 160 161 var ss []byte 162 if f.FileHeader.NumberOfSymbols > 0 { 163 // Get COFF string table, which is located at the end of the COFF symbol table. 164 sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) 165 var l uint32 166 if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { 167 return nil, err 168 } 169 ss = make([]byte, l) 170 if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { 171 return nil, err 172 } 173 174 // Process COFF symbol table. 175 sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) 176 aux := uint8(0) 177 for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { 178 cs := new(COFFSymbol) 179 if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { 180 return nil, err 181 } 182 if aux > 0 { 183 aux-- 184 continue 185 } 186 var name string 187 if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { 188 si := int(binary.LittleEndian.Uint32(cs.Name[4:])) 189 name, _ = getString(ss, si) 190 } else { 191 name = cstring(cs.Name[:]) 192 } 193 aux = cs.NumberOfAuxSymbols 194 s := &Symbol{ 195 Name: name, 196 Value: cs.Value, 197 SectionNumber: cs.SectionNumber, 198 Type: cs.Type, 199 StorageClass: cs.StorageClass, 200 } 201 f.Symbols = append(f.Symbols, s) 202 } 203 } 204 205 // Read optional header. 206 sr.Seek(base, os.SEEK_SET) 207 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 208 return nil, err 209 } 210 var oh32 OptionalHeader32 211 var oh64 OptionalHeader64 212 switch f.FileHeader.SizeOfOptionalHeader { 213 case sizeofOptionalHeader32: 214 if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil { 215 return nil, err 216 } 217 if oh32.Magic != 0x10b { // PE32 218 return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic) 219 } 220 f.OptionalHeader = &oh32 221 case sizeofOptionalHeader64: 222 if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil { 223 return nil, err 224 } 225 if oh64.Magic != 0x20b { // PE32+ 226 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) 227 } 228 f.OptionalHeader = &oh64 229 } 230 231 // Process sections. 232 f.Sections = make([]*Section, f.FileHeader.NumberOfSections) 233 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { 234 sh := new(SectionHeader32) 235 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { 236 return nil, err 237 } 238 var name string 239 if sh.Name[0] == '\x2F' { 240 si, _ := strconv.Atoi(cstring(sh.Name[1:])) 241 name, _ = getString(ss, si) 242 } else { 243 name = cstring(sh.Name[0:]) 244 } 245 s := new(Section) 246 s.SectionHeader = SectionHeader{ 247 Name: name, 248 VirtualSize: sh.VirtualSize, 249 VirtualAddress: sh.VirtualAddress, 250 Size: sh.SizeOfRawData, 251 Offset: sh.PointerToRawData, 252 PointerToRelocations: sh.PointerToRelocations, 253 PointerToLineNumbers: sh.PointerToLineNumbers, 254 NumberOfRelocations: sh.NumberOfRelocations, 255 NumberOfLineNumbers: sh.NumberOfLineNumbers, 256 Characteristics: sh.Characteristics, 257 } 258 s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) 259 s.ReaderAt = s.sr 260 f.Sections[i] = s 261 } 262 return f, nil 263 } 264 265 func cstring(b []byte) string { 266 var i int 267 for i = 0; i < len(b) && b[i] != 0; i++ { 268 } 269 return string(b[0:i]) 270 } 271 272 // getString extracts a string from symbol string table. 273 func getString(section []byte, start int) (string, bool) { 274 if start < 0 || start >= len(section) { 275 return "", false 276 } 277 278 for end := start; end < len(section); end++ { 279 if section[end] == 0 { 280 return string(section[start:end]), true 281 } 282 } 283 return "", false 284 } 285 286 // Section returns the first section with the given name, or nil if no such 287 // section exists. 288 func (f *File) Section(name string) *Section { 289 for _, s := range f.Sections { 290 if s.Name == name { 291 return s 292 } 293 } 294 return nil 295 } 296 297 func (f *File) DWARF() (*dwarf.Data, error) { 298 // There are many other DWARF sections, but these 299 // are the required ones, and the debug/dwarf package 300 // does not use the others, so don't bother loading them. 301 var names = [...]string{"abbrev", "info", "str"} 302 var dat [len(names)][]byte 303 for i, name := range names { 304 name = ".debug_" + name 305 s := f.Section(name) 306 if s == nil { 307 continue 308 } 309 b, err := s.Data() 310 if err != nil && uint32(len(b)) < s.Size { 311 return nil, err 312 } 313 dat[i] = b 314 } 315 316 abbrev, info, str := dat[0], dat[1], dat[2] 317 return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) 318 } 319 320 // ImportedSymbols returns the names of all symbols 321 // referred to by the binary f that are expected to be 322 // satisfied by other libraries at dynamic load time. 323 // It does not return weak symbols. 324 func (f *File) ImportedSymbols() ([]string, error) { 325 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 326 ds := f.Section(".idata") 327 if ds == nil { 328 // not dynamic, so no libraries 329 return nil, nil 330 } 331 d, err := ds.Data() 332 if err != nil { 333 return nil, err 334 } 335 var ida []ImportDirectory 336 for len(d) > 0 { 337 var dt ImportDirectory 338 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 339 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 340 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 341 d = d[20:] 342 if dt.OriginalFirstThunk == 0 { 343 break 344 } 345 ida = append(ida, dt) 346 } 347 names, _ := ds.Data() 348 var all []string 349 for _, dt := range ida { 350 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 351 d, _ = ds.Data() 352 // seek to OriginalFirstThunk 353 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 354 for len(d) > 0 { 355 if pe64 { // 64bit 356 va := binary.LittleEndian.Uint64(d[0:8]) 357 d = d[8:] 358 if va == 0 { 359 break 360 } 361 if va&0x8000000000000000 > 0 { // is Ordinal 362 // TODO add dynimport ordinal support. 363 } else { 364 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 365 all = append(all, fn+":"+dt.dll) 366 } 367 } else { // 32bit 368 va := binary.LittleEndian.Uint32(d[0:4]) 369 d = d[4:] 370 if va == 0 { 371 break 372 } 373 if va&0x80000000 > 0 { // is Ordinal 374 // TODO add dynimport ordinal support. 375 //ord := va&0x0000FFFF 376 } else { 377 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 378 all = append(all, fn+":"+dt.dll) 379 } 380 } 381 } 382 } 383 384 return all, nil 385 } 386 387 // ImportedLibraries returns the names of all libraries 388 // referred to by the binary f that are expected to be 389 // linked with the binary at dynamic link time. 390 func (f *File) ImportedLibraries() ([]string, error) { 391 // TODO 392 // cgo -dynimport don't use this for windows PE, so just return. 393 return nil, nil 394 }