github.com/euank/go@v0.0.0-20160829210321-495514729181/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 "fmt" 12 "io" 13 "os" 14 ) 15 16 // A File represents an open PE file. 17 type File struct { 18 FileHeader 19 OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 20 Sections []*Section 21 Symbols []*Symbol // COFF symbols with auxiliary symbol records removed 22 COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) 23 StringTable StringTable 24 25 closer io.Closer 26 } 27 28 // Open opens the named file using os.Open and prepares it for use as a PE binary. 29 func Open(name string) (*File, error) { 30 f, err := os.Open(name) 31 if err != nil { 32 return nil, err 33 } 34 ff, err := NewFile(f) 35 if err != nil { 36 f.Close() 37 return nil, err 38 } 39 ff.closer = f 40 return ff, nil 41 } 42 43 // Close closes the File. 44 // If the File was created using NewFile directly instead of Open, 45 // Close has no effect. 46 func (f *File) Close() error { 47 var err error 48 if f.closer != nil { 49 err = f.closer.Close() 50 f.closer = nil 51 } 52 return err 53 } 54 55 var ( 56 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{})) 57 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) 58 ) 59 60 // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) 61 62 // NewFile creates a new File for accessing a PE binary in an underlying reader. 63 func NewFile(r io.ReaderAt) (*File, error) { 64 f := new(File) 65 sr := io.NewSectionReader(r, 0, 1<<63-1) 66 67 var dosheader [96]byte 68 if _, err := r.ReadAt(dosheader[0:], 0); err != nil { 69 return nil, err 70 } 71 var base int64 72 if dosheader[0] == 'M' && dosheader[1] == 'Z' { 73 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) 74 var sign [4]byte 75 r.ReadAt(sign[:], signoff) 76 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { 77 return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign) 78 } 79 base = signoff + 4 80 } else { 81 base = int64(0) 82 } 83 sr.Seek(base, io.SeekStart) 84 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 85 return nil, err 86 } 87 switch f.FileHeader.Machine { 88 case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386: 89 default: 90 return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine) 91 } 92 93 var err error 94 95 // Read string table. 96 f.StringTable, err = readStringTable(&f.FileHeader, sr) 97 if err != nil { 98 return nil, err 99 } 100 101 // Read symbol table. 102 f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) 103 if err != nil { 104 return nil, err 105 } 106 f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable) 107 if err != nil { 108 return nil, err 109 } 110 111 // Read optional header. 112 sr.Seek(base, io.SeekStart) 113 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 114 return nil, err 115 } 116 var oh32 OptionalHeader32 117 var oh64 OptionalHeader64 118 switch f.FileHeader.SizeOfOptionalHeader { 119 case sizeofOptionalHeader32: 120 if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil { 121 return nil, err 122 } 123 if oh32.Magic != 0x10b { // PE32 124 return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic) 125 } 126 f.OptionalHeader = &oh32 127 case sizeofOptionalHeader64: 128 if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil { 129 return nil, err 130 } 131 if oh64.Magic != 0x20b { // PE32+ 132 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) 133 } 134 f.OptionalHeader = &oh64 135 } 136 137 // Process sections. 138 f.Sections = make([]*Section, f.FileHeader.NumberOfSections) 139 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { 140 sh := new(SectionHeader32) 141 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { 142 return nil, err 143 } 144 name, err := sh.fullName(f.StringTable) 145 if err != nil { 146 return nil, err 147 } 148 s := new(Section) 149 s.SectionHeader = SectionHeader{ 150 Name: name, 151 VirtualSize: sh.VirtualSize, 152 VirtualAddress: sh.VirtualAddress, 153 Size: sh.SizeOfRawData, 154 Offset: sh.PointerToRawData, 155 PointerToRelocations: sh.PointerToRelocations, 156 PointerToLineNumbers: sh.PointerToLineNumbers, 157 NumberOfRelocations: sh.NumberOfRelocations, 158 NumberOfLineNumbers: sh.NumberOfLineNumbers, 159 Characteristics: sh.Characteristics, 160 } 161 r2 := r 162 if sh.PointerToRawData == 0 { // .bss must have all 0s 163 r2 = zeroReaderAt{} 164 } 165 s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) 166 s.ReaderAt = s.sr 167 f.Sections[i] = s 168 } 169 for i := range f.Sections { 170 var err error 171 f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr) 172 if err != nil { 173 return nil, err 174 } 175 } 176 177 return f, nil 178 } 179 180 // zeroReaderAt is ReaderAt that reads 0s. 181 type zeroReaderAt struct{} 182 183 // ReadAt writes len(p) 0s into p. 184 func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) { 185 for i := range p { 186 p[i] = 0 187 } 188 return len(p), nil 189 } 190 191 // getString extracts a string from symbol string table. 192 func getString(section []byte, start int) (string, bool) { 193 if start < 0 || start >= len(section) { 194 return "", false 195 } 196 197 for end := start; end < len(section); end++ { 198 if section[end] == 0 { 199 return string(section[start:end]), true 200 } 201 } 202 return "", false 203 } 204 205 // Section returns the first section with the given name, or nil if no such 206 // section exists. 207 func (f *File) Section(name string) *Section { 208 for _, s := range f.Sections { 209 if s.Name == name { 210 return s 211 } 212 } 213 return nil 214 } 215 216 func (f *File) DWARF() (*dwarf.Data, error) { 217 // There are many other DWARF sections, but these 218 // are the ones the debug/dwarf package uses. 219 // Don't bother loading others. 220 var names = [...]string{"abbrev", "info", "line", "ranges", "str"} 221 var dat [len(names)][]byte 222 for i, name := range names { 223 name = ".debug_" + name 224 s := f.Section(name) 225 if s == nil { 226 continue 227 } 228 b, err := s.Data() 229 if err != nil && uint32(len(b)) < s.Size { 230 return nil, err 231 } 232 if 0 < s.VirtualSize && s.VirtualSize < s.Size { 233 b = b[:s.VirtualSize] 234 } 235 dat[i] = b 236 } 237 238 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] 239 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) 240 } 241 242 // TODO(brainman): document ImportDirectory once we decide what to do with it. 243 244 type ImportDirectory struct { 245 OriginalFirstThunk uint32 246 TimeDateStamp uint32 247 ForwarderChain uint32 248 Name uint32 249 FirstThunk uint32 250 251 dll string 252 } 253 254 // ImportedSymbols returns the names of all symbols 255 // referred to by the binary f that are expected to be 256 // satisfied by other libraries at dynamic load time. 257 // It does not return weak symbols. 258 func (f *File) ImportedSymbols() ([]string, error) { 259 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 260 ds := f.Section(".idata") 261 if ds == nil { 262 // not dynamic, so no libraries 263 return nil, nil 264 } 265 d, err := ds.Data() 266 if err != nil { 267 return nil, err 268 } 269 var ida []ImportDirectory 270 for len(d) > 0 { 271 var dt ImportDirectory 272 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 273 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 274 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 275 d = d[20:] 276 if dt.OriginalFirstThunk == 0 { 277 break 278 } 279 ida = append(ida, dt) 280 } 281 // TODO(brainman): this needs to be rewritten 282 // ds.Data() return contets of .idata section. Why store in variable called "names"? 283 // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. 284 // getString does not extracts a string from symbol string table (as getString doco says). 285 // Why ds.Data() called again and again in the loop? 286 // Needs test before rewrite. 287 names, _ := ds.Data() 288 var all []string 289 for _, dt := range ida { 290 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 291 d, _ = ds.Data() 292 // seek to OriginalFirstThunk 293 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 294 for len(d) > 0 { 295 if pe64 { // 64bit 296 va := binary.LittleEndian.Uint64(d[0:8]) 297 d = d[8:] 298 if va == 0 { 299 break 300 } 301 if va&0x8000000000000000 > 0 { // is Ordinal 302 // TODO add dynimport ordinal support. 303 } else { 304 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 305 all = append(all, fn+":"+dt.dll) 306 } 307 } else { // 32bit 308 va := binary.LittleEndian.Uint32(d[0:4]) 309 d = d[4:] 310 if va == 0 { 311 break 312 } 313 if va&0x80000000 > 0 { // is Ordinal 314 // TODO add dynimport ordinal support. 315 //ord := va&0x0000FFFF 316 } else { 317 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 318 all = append(all, fn+":"+dt.dll) 319 } 320 } 321 } 322 } 323 324 return all, nil 325 } 326 327 // ImportedLibraries returns the names of all libraries 328 // referred to by the binary f that are expected to be 329 // linked with the binary at dynamic link time. 330 func (f *File) ImportedLibraries() ([]string, error) { 331 // TODO 332 // cgo -dynimport don't use this for windows PE, so just return. 333 return nil, nil 334 } 335 336 // FormatError is unused. 337 // The type is retained for compatibility. 338 type FormatError struct { 339 } 340 341 func (e *FormatError) Error() string { 342 return "unknown error" 343 }