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.
18 // A File represents an open PE file.
21 OptionalHeader
interface{} // of type *OptionalHeader32 or *OptionalHeader64
28 type SectionHeader
struct {
34 PointerToRelocations
uint32
35 PointerToLineNumbers
uint32
36 NumberOfRelocations
uint16
37 NumberOfLineNumbers
uint16
38 Characteristics
uint32
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.
62 type ImportDirectory
struct {
63 OriginalFirstThunk
uint32
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)
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 {
91 func (e
*FormatError
) Error() string {
94 msg
+= fmt
.Sprintf(" '%v'", e
.val
)
96 msg
+= fmt
.Sprintf(" in record at byte %#x", e
.off
)
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
)
106 ff
, err
:= NewFile(f
)
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
{
121 err
= f
.closer
.Close()
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
) {
135 sr
:= io
.NewSectionReader(r
, 0, 1<<63-1)
137 var dosheader
[96]byte
138 if _
, err
:= r
.ReadAt(dosheader
[0:], 0); err
!= nil {
142 if dosheader
[0] == 'M' && dosheader
[1] == 'Z' {
143 signoff
:= int64(binary
.LittleEndian
.Uint32(dosheader
[0x3c:]))
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.")
153 sr
.Seek(base
, os
.SEEK_SET
)
154 if err
:= binary
.Read(sr
, binary
.LittleEndian
, &f
.FileHeader
); err
!= nil {
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.")
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
)
166 if err
:= binary
.Read(sr
, binary
.LittleEndian
, &l
); err
!= nil {
170 if _
, err
:= r
.ReadAt(ss
, int64(f
.FileHeader
.PointerToSymbolTable
+COFFSymbolSize
*f
.FileHeader
.NumberOfSymbols
)); err
!= nil {
174 // Process COFF symbol table.
175 sr
.Seek(int64(f
.FileHeader
.PointerToSymbolTable
), os
.SEEK_SET
)
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 {
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
)
191 name
= cstring(cs
.Name
[:])
193 aux
= cs
.NumberOfAuxSymbols
197 SectionNumber
: cs
.SectionNumber
,
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 {
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 {
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 {
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
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 {
239 if sh
.Name
[0] == '\x2F' {
240 si
, _
:= strconv
.Atoi(cstring(sh
.Name
[1:]))
241 name
, _
= getString(ss
, si
)
243 name
= cstring(sh
.Name
[0:])
246 s
.SectionHeader
= SectionHeader
{
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
))
265 func cstring(b
[]byte) string {
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
) {
278 for end
:= start
; end
< len(section
); end
++ {
279 if section
[end
] == 0 {
280 return string(section
[start
:end
]), true
286 // Section returns the first section with the given name, or nil if no such
288 func (f
*File
) Section(name
string) *Section
{
289 for _
, s
:= range f
.Sections
{
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
310 if err
!= nil && uint32(len(b
)) < s
.Size
{
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")
328 // not dynamic, so no libraries
335 var ida
[]ImportDirectory
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])
342 if dt
.OriginalFirstThunk
== 0 {
345 ida
= append(ida
, dt
)
347 names
, _
:= ds
.Data()
349 for _
, dt
:= range ida
{
350 dt
.dll
, _
= getString(names
, int(dt
.Name
-ds
.VirtualAddress
))
352 // seek to OriginalFirstThunk
353 d
= d
[dt
.OriginalFirstThunk
-ds
.VirtualAddress
:]
356 va
:= binary
.LittleEndian
.Uint64(d
[0:8])
361 if va
&0x8000000000000000 > 0 { // is Ordinal
362 // TODO add dynimport ordinal support.
364 fn
, _
:= getString(names
, int(uint32(va
)-ds
.VirtualAddress
+2))
365 all
= append(all
, fn
+":"+dt
.dll
)
368 va
:= binary
.LittleEndian
.Uint32(d
[0:4])
373 if va
&0x80000000 > 0 { // is Ordinal
374 // TODO add dynimport ordinal support.
375 //ord := va&0x0000FFFF
377 fn
, _
:= getString(names
, int(va
-ds
.VirtualAddress
+2))
378 all
= append(all
, fn
+":"+dt
.dll
)
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
) {
392 // cgo -dynimport don't use this for windows PE, so just return.