Source file
src/debug/pe/file.go
1
2
3
4
5
6 package pe
7
8 import (
9 "debug/dwarf"
10 "encoding/binary"
11 "fmt"
12 "io"
13 "os"
14 )
15
16
17 const seekStart = 0
18
19
20 type File struct {
21 FileHeader
22 OptionalHeader interface{}
23 Sections []*Section
24 Symbols []*Symbol
25 COFFSymbols []COFFSymbol
26 StringTable StringTable
27
28 closer io.Closer
29 }
30
31
32 func Open(name string) (*File, error) {
33 f, err := os.Open(name)
34 if err != nil {
35 return nil, err
36 }
37 ff, err := NewFile(f)
38 if err != nil {
39 f.Close()
40 return nil, err
41 }
42 ff.closer = f
43 return ff, nil
44 }
45
46
47
48
49 func (f *File) Close() error {
50 var err error
51 if f.closer != nil {
52 err = f.closer.Close()
53 f.closer = nil
54 }
55 return err
56 }
57
58 var (
59 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
60 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
61 )
62
63
64
65
66 func NewFile(r io.ReaderAt) (*File, error) {
67 f := new(File)
68 sr := io.NewSectionReader(r, 0, 1<<63-1)
69
70 var dosheader [96]byte
71 if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
72 return nil, err
73 }
74 var base int64
75 if dosheader[0] == 'M' && dosheader[1] == 'Z' {
76 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
77 var sign [4]byte
78 r.ReadAt(sign[:], signoff)
79 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
80 return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign)
81 }
82 base = signoff + 4
83 } else {
84 base = int64(0)
85 }
86 sr.Seek(base, seekStart)
87 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
88 return nil, err
89 }
90 switch f.FileHeader.Machine {
91 case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386:
92 default:
93 return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine)
94 }
95
96 var err error
97
98
99 f.StringTable, err = readStringTable(&f.FileHeader, sr)
100 if err != nil {
101 return nil, err
102 }
103
104
105 f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
106 if err != nil {
107 return nil, err
108 }
109 f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
110 if err != nil {
111 return nil, err
112 }
113
114
115 sr.Seek(base, seekStart)
116 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
117 return nil, err
118 }
119 var oh32 OptionalHeader32
120 var oh64 OptionalHeader64
121 switch f.FileHeader.SizeOfOptionalHeader {
122 case sizeofOptionalHeader32:
123 if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
124 return nil, err
125 }
126 if oh32.Magic != 0x10b {
127 return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
128 }
129 f.OptionalHeader = &oh32
130 case sizeofOptionalHeader64:
131 if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
132 return nil, err
133 }
134 if oh64.Magic != 0x20b {
135 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
136 }
137 f.OptionalHeader = &oh64
138 }
139
140
141 f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
142 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
143 sh := new(SectionHeader32)
144 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
145 return nil, err
146 }
147 name, err := sh.fullName(f.StringTable)
148 if err != nil {
149 return nil, err
150 }
151 s := new(Section)
152 s.SectionHeader = SectionHeader{
153 Name: name,
154 VirtualSize: sh.VirtualSize,
155 VirtualAddress: sh.VirtualAddress,
156 Size: sh.SizeOfRawData,
157 Offset: sh.PointerToRawData,
158 PointerToRelocations: sh.PointerToRelocations,
159 PointerToLineNumbers: sh.PointerToLineNumbers,
160 NumberOfRelocations: sh.NumberOfRelocations,
161 NumberOfLineNumbers: sh.NumberOfLineNumbers,
162 Characteristics: sh.Characteristics,
163 }
164 r2 := r
165 if sh.PointerToRawData == 0 {
166 r2 = zeroReaderAt{}
167 }
168 s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
169 s.ReaderAt = s.sr
170 f.Sections[i] = s
171 }
172 for i := range f.Sections {
173 var err error
174 f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
175 if err != nil {
176 return nil, err
177 }
178 }
179
180 return f, nil
181 }
182
183
184 type zeroReaderAt struct{}
185
186
187 func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
188 for i := range p {
189 p[i] = 0
190 }
191 return len(p), nil
192 }
193
194
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
209
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
221
222
223 var names = [...]string{"abbrev", "info", "line", "ranges", "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 if 0 < s.VirtualSize && s.VirtualSize < s.Size {
236 b = b[:s.VirtualSize]
237 }
238 dat[i] = b
239 }
240
241 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
242 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
243 }
244
245
246
247 type ImportDirectory struct {
248 OriginalFirstThunk uint32
249 TimeDateStamp uint32
250 ForwarderChain uint32
251 Name uint32
252 FirstThunk uint32
253
254 dll string
255 }
256
257
258
259
260
261 func (f *File) ImportedSymbols() ([]string, error) {
262 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
263 ds := f.Section(".idata")
264 if ds == nil {
265
266 return nil, nil
267 }
268 d, err := ds.Data()
269 if err != nil {
270 return nil, err
271 }
272 var ida []ImportDirectory
273 for len(d) > 0 {
274 var dt ImportDirectory
275 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
276 dt.Name = binary.LittleEndian.Uint32(d[12:16])
277 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
278 d = d[20:]
279 if dt.OriginalFirstThunk == 0 {
280 break
281 }
282 ida = append(ida, dt)
283 }
284
285
286
287
288
289
290 names, _ := ds.Data()
291 var all []string
292 for _, dt := range ida {
293 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
294 d, _ = ds.Data()
295
296 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
297 for len(d) > 0 {
298 if pe64 {
299 va := binary.LittleEndian.Uint64(d[0:8])
300 d = d[8:]
301 if va == 0 {
302 break
303 }
304 if va&0x8000000000000000 > 0 {
305
306 } else {
307 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
308 all = append(all, fn+":"+dt.dll)
309 }
310 } else {
311 va := binary.LittleEndian.Uint32(d[0:4])
312 d = d[4:]
313 if va == 0 {
314 break
315 }
316 if va&0x80000000 > 0 {
317
318
319 } else {
320 fn, _ := getString(names, int(va-ds.VirtualAddress+2))
321 all = append(all, fn+":"+dt.dll)
322 }
323 }
324 }
325 }
326
327 return all, nil
328 }
329
330
331
332
333 func (f *File) ImportedLibraries() ([]string, error) {
334
335
336 return nil, nil
337 }
338
339
340
341 type FormatError struct {
342 }
343
344 func (e *FormatError) Error() string {
345 return "unknown error"
346 }
347
View as plain text