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/section.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/debug/pe/section.go (limited to 'src/debug/pe/section.go') 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) } -- 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/section.go') 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 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/section.go') 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/section.go') 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