From 381e5eee39edfb3a43e294023957aff05e9ff349 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 13 Apr 2016 04:35:37 +0000 Subject: all: use new io.SeekFoo constants instead of os.SEEK_FOO Automated change. Fixes #15269 Change-Id: I8deb2ac0101d3f7c390467ceb0a1561b72edbb2f Reviewed-on: https://go-review.googlesource.com/21962 Run-TryBot: Brad Fitzpatrick Reviewed-by: Andrew Gerrand TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 1acc368e1b..c68ff1bdce 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -150,7 +150,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } 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 } @@ -161,7 +161,7 @@ func NewFile(r io.ReaderAt) (*File, error) { 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) + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), io.SeekStart) var l uint32 if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { return nil, err @@ -172,7 +172,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } // Process COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) + sr.Seek(int64(f.FileHeader.PointerToSymbolTable), io.SeekStart) aux := uint8(0) for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { cs := new(COFFSymbol) @@ -203,7 +203,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } // 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 } -- cgit v1.3 From 5183ad696c708ab5fc65006413019b1ef96aa91b Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Mon, 18 Apr 2016 16:42:17 +1000 Subject: debug/pe: add some documentation and TODO No code changes. Just moved ImportDirectory next to ImportedSymbols. And moved useless FormatError to the bottom of file.go. Updates #15345 Change-Id: I91ff243cefd18008b1c5ee9ec4326583deee431b Reviewed-on: https://go-review.googlesource.com/22182 Reviewed-by: David Crawshaw Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index c68ff1bdce..bfc4cf8a18 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -59,16 +59,6 @@ type Symbol struct { 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()) @@ -82,21 +72,6 @@ func (s *Section) Data() ([]byte, error) { // 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) @@ -320,6 +295,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 +334,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 +388,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" +} -- cgit v1.3 From d697a9d5d7d75cecd8d49b95ed9a0d1f2f3e8ed4 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Mon, 18 Apr 2016 16:12:48 +1000 Subject: debug/pe: introduce StringTable type PE specification requires that long section and symbol names are stored in PE string table. Introduce StringTable that implements this functionality. Only string table reading is implemented. Updates #15345 Change-Id: Ib9638617f2ab1881ad707111d96fc68b0e47340e Reviewed-on: https://go-review.googlesource.com/22181 TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- src/debug/pe/file.go | 16 +++++++------ src/debug/pe/string.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 src/debug/pe/string.go (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index bfc4cf8a18..f7b74e92a4 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -21,6 +21,7 @@ type File struct { OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 Sections []*Section Symbols []*Symbol + StringTable StringTable closer io.Closer } @@ -133,6 +134,14 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, errors.New("Invalid PE File Format.") } + var err error + + // Read string table. + f.StringTable, err = readStringTable(&f.FileHeader, sr) + if err != nil { + return nil, err + } + var ss []byte if f.FileHeader.NumberOfSymbols > 0 { // Get COFF string table, which is located at the end of the COFF symbol table. @@ -237,13 +246,6 @@ func NewFile(r io.ReaderAt) (*File, error) { return f, nil } -func cstring(b []byte) string { - var i int - for i = 0; i < len(b) && b[i] != 0; i++ { - } - return string(b[0:i]) -} - // getString extracts a string from symbol string table. func getString(section []byte, start int) (string, bool) { if start < 0 || start >= len(section) { diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go new file mode 100644 index 0000000000..f0928d09c5 --- /dev/null +++ b/src/debug/pe/string.go @@ -0,0 +1,64 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "encoding/binary" + "fmt" + "io" +) + +// TODO(brainman): return error from cstring and see what errors we get and what to do about it + +// cstring converts ASCII byte sequence b to string. It stops once it finds 0. +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[:i]) +} + +// StringTable is a COFF string table. +type StringTable []byte + +func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) { + // COFF string table is located right after COFF symbol table. + offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols + _, err := r.Seek(int64(offset), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to string table: %v", err) + } + var l uint32 + err = binary.Read(r, binary.LittleEndian, &l) + if err != nil { + return nil, fmt.Errorf("fail to read string table length: %v", err) + } + // string table length includes itself + if l <= 4 { + return nil, nil + } + l -= 4 + buf := make([]byte, l) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, fmt.Errorf("fail to read string table: %v", err) + } + return StringTable(buf), nil +} + +// TODO(brainman): decide if start parameter should be int instead of uint32 + +// String extracts string from COFF string table st at offset start. +func (st StringTable) String(start uint32) (string, error) { + // start includes 4 bytes of string table length + if start < 4 { + return "", fmt.Errorf("offset %d is before the start of string table", start) + } + start -= 4 + if int(start) > len(st) { + return "", fmt.Errorf("offset %d is beyond the end of string table", start) + } + return cstring(st[start:]), nil +} -- cgit v1.3 From 731531980a36f1fa6434c947c54daf8ba530a65f Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Wed, 20 Apr 2016 13:02:41 +1000 Subject: debug/pe: move some code into section.go and symbol.go Just moving code. No code changes. Updates #15345 Change-Id: I89c257b7aae4fbd78ce59a42909ecb3ff493659d Reviewed-on: https://go-review.googlesource.com/22300 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 47 ------------------------------------- src/debug/pe/pe.go | 24 ------------------- src/debug/pe/section.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ src/debug/pe/symbol.go | 24 +++++++++++++++++++ 4 files changed, 85 insertions(+), 71 deletions(-) create mode 100644 src/debug/pe/section.go create mode 100644 src/debug/pe/symbol.go (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index f7b74e92a4..3affd25185 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -26,53 +26,6 @@ type File struct { 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 -} - -// 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) } - // 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) diff --git a/src/debug/pe/pe.go b/src/debug/pe/pe.go index 6af40e2c78..8050d59c70 100644 --- a/src/debug/pe/pe.go +++ b/src/debug/pe/pe.go @@ -86,30 +86,6 @@ type OptionalHeader64 struct { DataDirectory [16]DataDirectory } -type SectionHeader32 struct { - Name [8]uint8 - VirtualSize uint32 - VirtualAddress uint32 - SizeOfRawData uint32 - PointerToRawData uint32 - PointerToRelocations uint32 - PointerToLineNumbers uint32 - NumberOfRelocations uint16 - NumberOfLineNumbers uint16 - Characteristics uint32 -} - -const COFFSymbolSize = 18 - -type COFFSymbol struct { - Name [8]uint8 - Value uint32 - SectionNumber int16 - Type uint16 - StorageClass uint8 - NumberOfAuxSymbols uint8 -} - const ( IMAGE_FILE_MACHINE_UNKNOWN = 0x0 IMAGE_FILE_MACHINE_AM33 = 0x1d3 diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go new file mode 100644 index 0000000000..31cff272f3 --- /dev/null +++ b/src/debug/pe/section.go @@ -0,0 +1,61 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "io" +) + +type SectionHeader32 struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +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 +} + +// 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) } diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go new file mode 100644 index 0000000000..559174838c --- /dev/null +++ b/src/debug/pe/symbol.go @@ -0,0 +1,24 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +const COFFSymbolSize = 18 + +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} -- cgit v1.3 From 285a18436d480ef91b2af236d5ddd2fa7fa49de8 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Wed, 20 Apr 2016 15:58:55 +1000 Subject: debug/pe: pretty section.go code Introduce (*SectionHeader32).fullName and add documentation comments. Updates #15345 Change-Id: I8f3b8ab9492642d62e7aad010c91c68daea3f14b Reviewed-on: https://go-review.googlesource.com/22301 Reviewed-by: Ian Lance Taylor Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 10 +++------- src/debug/pe/section.go | 27 ++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 10 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 3affd25185..73b7c1cba2 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -12,7 +12,6 @@ import ( "fmt" "io" "os" - "strconv" ) // A File represents an open PE file. @@ -172,12 +171,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{ diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go index 31cff272f3..ded3ec4393 100644 --- a/src/debug/pe/section.go +++ b/src/debug/pe/section.go @@ -6,8 +6,10 @@ package pe import ( "io" + "strconv" ) +// SectionHeader32 represents real PE COFF section header. type SectionHeader32 struct { Name [8]uint8 VirtualSize uint32 @@ -21,6 +23,22 @@ type SectionHeader32 struct { Characteristics uint32 } +// fullName finds real name of section sh. Normally name is stored +// in sh.Name, but if it is longer then 8 characters, it is stored +// in COFF string table st instead. +func (sh *SectionHeader32) fullName(st StringTable) (string, error) { + if sh.Name[0] != '/' { + return cstring(sh.Name[:]), nil + } + i, err := strconv.Atoi(cstring(sh.Name[1:])) + if err != nil { + return "", err + } + return st.String(uint32(i)) +} + +// SectionHeader is similar to SectionHeader32 with Name +// field replaced by Go string. type SectionHeader struct { Name string VirtualSize uint32 @@ -34,6 +52,7 @@ type SectionHeader struct { Characteristics uint32 } +// Section provides access to PE COFF section. type Section struct { SectionHeader @@ -47,7 +66,7 @@ type Section struct { sr *io.SectionReader } -// Data reads and returns the contents of the PE section. +// Data reads and returns the contents of the PE section s. func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) @@ -57,5 +76,7 @@ func (s *Section) Data() ([]byte, error) { 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) } +// Open returns a new ReadSeeker reading the PE section s. +func (s *Section) Open() io.ReadSeeker { + return io.NewSectionReader(s.sr, 0, 1<<63-1) +} -- cgit v1.3 From 11f1041022e001869de076699f297b28d25fc558 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Thu, 21 Apr 2016 10:42:25 +1000 Subject: debug/pe: update cstring documentation Updates #15345 Change-Id: If1fca1f6042571cb0ac689bbb3c294309dd6e7b4 Reviewed-on: https://go-review.googlesource.com/22331 Reviewed-by: Ian Lance Taylor Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/string.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go index f0928d09c5..e00bd97dd4 100644 --- a/src/debug/pe/string.go +++ b/src/debug/pe/string.go @@ -10,9 +10,8 @@ import ( "io" ) -// TODO(brainman): return error from cstring and see what errors we get and what to do about it - -// cstring converts ASCII byte sequence b to string. It stops once it finds 0. +// cstring converts ASCII byte sequence b to string. +// It stops once it finds 0 or reaches end of b. func cstring(b []byte) string { var i int for i = 0; i < len(b) && b[i] != 0; i++ { -- cgit v1.3 From 45522a6a93efe0fd487f6875f2b104d772a26469 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Thu, 21 Apr 2016 11:44:05 +1000 Subject: debug/pe: introduce Section.Relocs cmd/link reads PE object files when building programs with cgo. cmd/link accesses object relocations. Add new Section.Relocs that provides similar functionality in debug/pe. Updates #15345 Change-Id: I34de91b7f18cf1c9e4cdb3aedd685486a625ac92 Reviewed-on: https://go-review.googlesource.com/22332 TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- src/debug/pe/file.go | 8 ++++++++ src/debug/pe/section.go | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 73b7c1cba2..cfd8e08a63 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -192,6 +192,14 @@ func NewFile(r io.ReaderAt) (*File, error) { s.ReaderAt = s.sr f.Sections[i] = s } + 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 f, nil } diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go index ded3ec4393..69fe41fd7a 100644 --- a/src/debug/pe/section.go +++ b/src/debug/pe/section.go @@ -5,6 +5,8 @@ package pe import ( + "encoding/binary" + "fmt" "io" "strconv" ) @@ -37,6 +39,30 @@ func (sh *SectionHeader32) fullName(st StringTable) (string, error) { return st.String(uint32(i)) } +// Reloc represents a PE COFF relocation. +// Each section contains its own relocation list. +type Reloc struct { + VirtualAddress uint32 + SymbolTableIndex uint32 + Type uint16 +} + +func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) { + if sh.NumberOfRelocations <= 0 { + return nil, nil + } + _, err := r.Seek(int64(sh.PointerToRelocations), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err) + } + relocs := make([]Reloc, sh.NumberOfRelocations) + err = binary.Read(r, binary.LittleEndian, relocs) + if err != nil { + return nil, fmt.Errorf("fail to read section relocations: %v", err) + } + return relocs, nil +} + // SectionHeader is similar to SectionHeader32 with Name // field replaced by Go string. type SectionHeader struct { @@ -55,6 +81,7 @@ type SectionHeader struct { // Section provides access to PE COFF section. type Section struct { SectionHeader + Relocs []Reloc // Embed ReaderAt for ReadAt method. // Do not embed SectionReader directly -- cgit v1.3 From 687fe991e42f15fe1f491680c615ef96568f780a Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Thu, 21 Apr 2016 16:51:36 +1000 Subject: debug/pe: introduce File.COFFSymbols and (*COFFSymbol).FullName Reloc.SymbolTableIndex is an index into symbol table. But Reloc.SymbolTableIndex cannot be used as index into File.Symbols, because File.Symbols slice has Aux lines removed as it is built. We cannot change the way File.Symbols works, so I propose we introduce new File.COFFSymbols that does not have that limitation. Also unlike File.Symbols, File.COFFSymbols will consist of COFFSymbol. COFFSymbol matches PE COFF specification exactly, and it is simpler to use. Updates #15345 Change-Id: Icbc265853a472529cd6d64a76427b27e5459e373 Reviewed-on: https://go-review.googlesource.com/22336 Reviewed-by: David Crawshaw Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 53 +++++++----------------------------- src/debug/pe/section.go | 2 ++ src/debug/pe/symbol.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 43 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index cfd8e08a63..abc33dfea8 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -19,7 +19,8 @@ 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 @@ -94,48 +95,14 @@ func NewFile(r io.ReaderAt) (*File, error) { return nil, err } - 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), io.SeekStart) - 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 - } - - // Process COFF symbol table. - sr.Seek(int64(f.FileHeader.PointerToSymbolTable), io.SeekStart) - 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 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. diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go index 69fe41fd7a..5d881577d3 100644 --- a/src/debug/pe/section.go +++ b/src/debug/pe/section.go @@ -39,6 +39,8 @@ func (sh *SectionHeader32) fullName(st StringTable) (string, error) { return st.String(uint32(i)) } +// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here + // Reloc represents a PE COFF relocation. // Each section contains its own relocation list. type Reloc struct { diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go index 559174838c..3cf5a101e7 100644 --- a/src/debug/pe/symbol.go +++ b/src/debug/pe/symbol.go @@ -4,8 +4,15 @@ package pe +import ( + "encoding/binary" + "fmt" + "io" +) + const COFFSymbolSize = 18 +// COFFSymbol represents single COFF symbol table record. type COFFSymbol struct { Name [8]uint8 Value uint32 @@ -15,6 +22,70 @@ type COFFSymbol struct { NumberOfAuxSymbols uint8 } +func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { + if fh.NumberOfSymbols <= 0 { + return nil, nil + } + _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to symbol table: %v", err) + } + syms := make([]COFFSymbol, fh.NumberOfSymbols) + err = binary.Read(r, binary.LittleEndian, syms) + if err != nil { + return nil, fmt.Errorf("fail to read symbol table: %v", err) + } + return syms, nil +} + +// isSymNameOffset checks symbol name if it is encoded as offset into string table. +func isSymNameOffset(name [8]byte) (bool, uint32) { + if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { + return true, binary.LittleEndian.Uint32(name[4:]) + } + return false, 0 +} + +// FullName finds real name of symbol sym. Normally name is stored +// in sym.Name, but if it is longer then 8 characters, it is stored +// in COFF string table st instead. +func (sym *COFFSymbol) FullName(st StringTable) (string, error) { + if ok, offset := isSymNameOffset(sym.Name); ok { + return st.String(offset) + } + return cstring(sym.Name[:]), nil +} + +func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { + if len(allsyms) == 0 { + return nil, nil + } + syms := make([]*Symbol, 0) + aux := uint8(0) + for _, sym := range allsyms { + if aux > 0 { + aux-- + continue + } + name, err := sym.FullName(st) + if err != nil { + return nil, err + } + aux = sym.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: sym.Value, + SectionNumber: sym.SectionNumber, + Type: sym.Type, + StorageClass: sym.StorageClass, + } + syms = append(syms, s) + } + return syms, nil +} + +// Symbol is similar to COFFSymbol with Name field replaced +// by Go string. Symbol also does not have NumberOfAuxSymbols. type Symbol struct { Name string Value uint32 -- cgit v1.3 From 093ac15a14137b4a9454442ae9fea282e5c09180 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Sun, 24 Apr 2016 15:09:00 +1000 Subject: debug/pe: better error messages Updates #15345 Change-Id: Iae35d3e378cbc8157ba1ff91e4971ed4515a5e5c Reviewed-on: https://go-review.googlesource.com/22394 Reviewed-by: David Crawshaw Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/debug/pe/file.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/debug/pe') diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index abc33dfea8..1cd84d5727 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -8,7 +8,6 @@ package pe import ( "debug/dwarf" "encoding/binary" - "errors" "fmt" "io" "os" @@ -58,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) @@ -73,7 +74,7 @@ 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 { @@ -83,8 +84,10 @@ func NewFile(r io.ReaderAt) (*File, error) { 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 err error -- cgit v1.3