Skip to content

Commit

Permalink
debug/pe: introduce File.COFFSymbols and (*COFFSymbol).FullName
Browse files Browse the repository at this point in the history
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 <crawshaw@golang.org>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
  • Loading branch information
alexbrainman committed Apr 24, 2016
1 parent d224e98 commit 687fe99
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 43 deletions.
53 changes: 10 additions & 43 deletions src/debug/pe/file.go
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/debug/pe/section.go
Expand Up @@ -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 {
Expand Down
71 changes: 71 additions & 0 deletions src/debug/pe/symbol.go
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 687fe99

Please sign in to comment.