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