diff options
Diffstat (limited to 'src/debug/pe/file.go')
| -rw-r--r-- | src/debug/pe/file.go | 199 |
1 files changed, 65 insertions, 134 deletions
diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 1acc368e1b..1cd84d5727 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -8,11 +8,9 @@ package pe import ( "debug/dwarf" "encoding/binary" - "errors" "fmt" "io" "os" - "strconv" ) // A File represents an open PE file. @@ -20,83 +18,13 @@ type File struct { FileHeader OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 Sections []*Section - Symbols []*Symbol + Symbols []*Symbol // COFF symbols with auxiliary symbol records removed + COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) + StringTable StringTable closer io.Closer } -type SectionHeader struct { - Name string - VirtualSize uint32 - VirtualAddress uint32 - Size uint32 - Offset uint32 - PointerToRelocations uint32 - PointerToLineNumbers uint32 - NumberOfRelocations uint16 - NumberOfLineNumbers uint16 - Characteristics uint32 -} - -type Section struct { - SectionHeader - - // Embed ReaderAt for ReadAt method. - // Do not embed SectionReader directly - // to avoid having Read and Seek. - // If a client wants Read and Seek it must use - // Open() to avoid fighting over the seek offset - // with other clients. - io.ReaderAt - sr *io.SectionReader -} - -type Symbol struct { - Name string - Value uint32 - SectionNumber int16 - Type uint16 - StorageClass uint8 -} - -type ImportDirectory struct { - OriginalFirstThunk uint32 - TimeDateStamp uint32 - ForwarderChain uint32 - Name uint32 - FirstThunk uint32 - - dll string -} - -// Data reads and returns the contents of the PE section. -func (s *Section) Data() ([]byte, error) { - dat := make([]byte, s.sr.Size()) - n, err := s.sr.ReadAt(dat, 0) - if n == len(dat) { - err = nil - } - return dat[0:n], err -} - -// Open returns a new ReadSeeker reading the PE section. -func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } - -type FormatError struct { - off int64 - msg string - val interface{} -} - -func (e *FormatError) Error() string { - msg := e.msg - if e.val != nil { - msg += fmt.Sprintf(" '%v'", e.val) - } - msg += fmt.Sprintf(" in record at byte %#x", e.off) - return msg -} - // Open opens the named file using os.Open and prepares it for use as a PE binary. func Open(name string) (*File, error) { f, err := os.Open(name) @@ -129,6 +57,8 @@ var ( sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) ) +// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) + // NewFile creates a new File for accessing a PE binary in an underlying reader. func NewFile(r io.ReaderAt) (*File, error) { f := new(File) @@ -144,66 +74,42 @@ func NewFile(r io.ReaderAt) (*File, error) { var sign [4]byte r.ReadAt(sign[:], signoff) if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { - return nil, errors.New("Invalid PE File Format.") + return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign) } base = signoff + 4 } else { base = int64(0) } - sr.Seek(base, os.SEEK_SET) + sr.Seek(base, io.SeekStart) if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { return nil, err } - if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { - return nil, errors.New("Invalid PE File Format.") + switch f.FileHeader.Machine { + case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386: + default: + return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine) } - var ss []byte - if f.FileHeader.NumberOfSymbols > 0 { - // Get COFF string table, which is located at the end of the COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) - var l uint32 - if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { - return nil, err - } - ss = make([]byte, l) - if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { - return nil, err - } + var err error - // Process COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) - aux := uint8(0) - for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { - cs := new(COFFSymbol) - if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { - return nil, err - } - if aux > 0 { - aux-- - continue - } - var name string - if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { - si := int(binary.LittleEndian.Uint32(cs.Name[4:])) - name, _ = getString(ss, si) - } else { - name = cstring(cs.Name[:]) - } - aux = cs.NumberOfAuxSymbols - s := &Symbol{ - Name: name, - Value: cs.Value, - SectionNumber: cs.SectionNumber, - Type: cs.Type, - StorageClass: cs.StorageClass, - } - f.Symbols = append(f.Symbols, s) - } + // Read string table. + f.StringTable, err = readStringTable(&f.FileHeader, sr) + if err != nil { + return nil, err + } + + // Read symbol table. + f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) + if err != nil { + return nil, err + } + f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable) + if err != nil { + return nil, err } // Read optional header. - sr.Seek(base, os.SEEK_SET) + sr.Seek(base, io.SeekStart) if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { return nil, err } @@ -235,12 +141,9 @@ func NewFile(r io.ReaderAt) (*File, error) { if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { return nil, err } - var name string - if sh.Name[0] == '\x2F' { - si, _ := strconv.Atoi(cstring(sh.Name[1:])) - name, _ = getString(ss, si) - } else { - name = cstring(sh.Name[0:]) + name, err := sh.fullName(f.StringTable) + if err != nil { + return nil, err } s := new(Section) s.SectionHeader = SectionHeader{ @@ -259,14 +162,15 @@ func NewFile(r io.ReaderAt) (*File, error) { s.ReaderAt = s.sr f.Sections[i] = s } - return f, nil -} - -func cstring(b []byte) string { - var i int - for i = 0; i < len(b) && b[i] != 0; i++ { + for i := range f.Sections { + var err error + f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr) + if err != nil { + return nil, err + } } - return string(b[0:i]) + + return f, nil } // getString extracts a string from symbol string table. @@ -320,6 +224,18 @@ func (f *File) DWARF() (*dwarf.Data, error) { return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) } +// TODO(brainman): document ImportDirectory once we decide what to do with it. + +type ImportDirectory struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 + + dll string +} + // ImportedSymbols returns the names of all symbols // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. @@ -347,6 +263,12 @@ func (f *File) ImportedSymbols() ([]string, error) { } ida = append(ida, dt) } + // TODO(brainman): this needs to be rewritten + // ds.Data() return contets of .idata section. Why store in variable called "names"? + // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. + // getString does not extracts a string from symbol string table (as getString doco says). + // Why ds.Data() called again and again in the loop? + // Needs test before rewrite. names, _ := ds.Data() var all []string for _, dt := range ida { @@ -395,3 +317,12 @@ func (f *File) ImportedLibraries() ([]string, error) { // cgo -dynimport don't use this for windows PE, so just return. return nil, nil } + +// FormatError is unused. +// The type is retained for compatibility. +type FormatError struct { +} + +func (e *FormatError) Error() string { + return "unknown error" +} |
