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
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.
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
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