libgo, compiler: Upgrade libgo to Go 1.4, except for runtime.
[official-gcc.git] / libgo / go / debug / pe / file.go
blob759e5674fd623de32fcf6e8d8c523179d86492f4
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.
5 // Package pe implements access to PE (Microsoft Windows Portable Executable) files.
6 package pe
8 import (
9 "debug/dwarf"
10 "encoding/binary"
11 "errors"
12 "fmt"
13 "io"
14 "os"
15 "strconv"
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
25 closer io.Closer
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
41 type Section struct {
42 SectionHeader
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
54 type Symbol struct {
55 Name string
56 Value uint32
57 SectionNumber int16
58 Type uint16
59 StorageClass uint8
62 type ImportDirectory struct {
63 OriginalFirstThunk uint32
64 TimeDateStamp uint32
65 ForwarderChain uint32
66 Name uint32
67 FirstThunk uint32
69 dll string
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
79 return dat[0:n], err
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) }
85 type FormatError struct {
86 off int64
87 msg string
88 val interface{}
91 func (e *FormatError) Error() string {
92 msg := e.msg
93 if e.val != nil {
94 msg += fmt.Sprintf(" '%v'", e.val)
96 msg += fmt.Sprintf(" in record at byte %#x", e.off)
97 return msg
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
106 ff, err := NewFile(f)
107 if err != nil {
108 f.Close()
109 return nil, err
111 ff.closer = f
112 return ff, nil
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
124 return err
127 var (
128 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
129 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
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)
137 var dosheader [96]byte
138 if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
139 return nil, err
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.")
149 base = signoff + 4
150 } else {
151 base = int64(0)
153 sr.Seek(base, os.SEEK_SET)
154 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
155 return nil, err
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.")
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
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
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
182 if aux > 0 {
183 aux--
184 continue
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[:])
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,
201 f.Symbols = append(f.Symbols, s)
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
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
217 if oh32.Magic != 0x10b { // PE32
218 return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
220 f.OptionalHeader = &oh32
221 case sizeofOptionalHeader64:
222 if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
223 return nil, err
225 if oh64.Magic != 0x20b { // PE32+
226 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
228 f.OptionalHeader = &oh64
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
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:])
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,
258 s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
259 s.ReaderAt = s.sr
260 f.Sections[i] = s
262 return f, nil
265 func cstring(b []byte) string {
266 var i int
267 for i = 0; i < len(b) && b[i] != 0; i++ {
269 return string(b[0:i])
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
278 for end := start; end < len(section); end++ {
279 if section[end] == 0 {
280 return string(section[start:end]), true
283 return "", false
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
294 return nil
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
309 b, err := s.Data()
310 if err != nil && uint32(len(b)) < s.Size {
311 return nil, err
313 dat[i] = b
316 abbrev, info, str := dat[0], dat[1], dat[2]
317 return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
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
331 d, err := ds.Data()
332 if err != nil {
333 return nil, err
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
345 ida = append(ida, dt)
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
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)
367 } else { // 32bit
368 va := binary.LittleEndian.Uint32(d[0:4])
369 d = d[4:]
370 if va == 0 {
371 break
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)
384 return all, nil
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