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