...

Source file src/debug/pe/symbol.go

Documentation: debug/pe

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pe
     6  
     7  import (
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  	"unsafe"
    12  )
    13  
    14  const COFFSymbolSize = 18
    15  
    16  // COFFSymbol represents single COFF symbol table record.
    17  type COFFSymbol struct {
    18  	Name               [8]uint8
    19  	Value              uint32
    20  	SectionNumber      int16
    21  	Type               uint16
    22  	StorageClass       uint8
    23  	NumberOfAuxSymbols uint8
    24  }
    25  
    26  // readCOFFSymbols reads in the symbol table for a PE file, returning
    27  // a slice of COFFSymbol objects. The PE format includes both primary
    28  // symbols (whose fields are described by COFFSymbol above) and
    29  // auxiliary symbols; all symbols are 18 bytes in size. The auxiliary
    30  // symbols for a given primary symbol are placed following it in the
    31  // array, e.g.
    32  //
    33  //	...
    34  //	k+0:  regular sym k
    35  //	k+1:    1st aux symbol for k
    36  //	k+2:    2nd aux symbol for k
    37  //	k+3:  regular sym k+3
    38  //	k+4:    1st aux symbol for k+3
    39  //	k+5:  regular sym k+5
    40  //	k+6:  regular sym k+6
    41  //
    42  // The PE format allows for several possible aux symbol formats. For
    43  // more info see:
    44  //
    45  //	https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records
    46  //
    47  // At the moment this package only provides APIs for looking at
    48  // aux symbols of format 5 (associated with section definition symbols).
    49  func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
    50  	if fh.PointerToSymbolTable == 0 {
    51  		return nil, nil
    52  	}
    53  	if fh.NumberOfSymbols <= 0 {
    54  		return nil, nil
    55  	}
    56  	_, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
    59  	}
    60  	syms := make([]COFFSymbol, fh.NumberOfSymbols)
    61  	naux := 0
    62  	for k := range syms {
    63  		if naux == 0 {
    64  			// Read a primary symbol.
    65  			err = binary.Read(r, binary.LittleEndian, &syms[k])
    66  			if err != nil {
    67  				return nil, fmt.Errorf("fail to read symbol table: %v", err)
    68  			}
    69  			// Record how many auxiliary symbols it has.
    70  			naux = int(syms[k].NumberOfAuxSymbols)
    71  		} else {
    72  			// Read an aux symbol. At the moment we assume all
    73  			// aux symbols are format 5 (obviously this doesn't always
    74  			// hold; more cases will be needed below if more aux formats
    75  			// are supported in the future).
    76  			naux--
    77  			aux := (*COFFSymbolAuxFormat5)(unsafe.Pointer(&syms[k]))
    78  			err = binary.Read(r, binary.LittleEndian, aux)
    79  			if err != nil {
    80  				return nil, fmt.Errorf("fail to read symbol table: %v", err)
    81  			}
    82  		}
    83  	}
    84  	if naux != 0 {
    85  		return nil, fmt.Errorf("fail to read symbol table: %d aux symbols unread", naux)
    86  	}
    87  	return syms, nil
    88  }
    89  
    90  // isSymNameOffset checks symbol name if it is encoded as offset into string table.
    91  func isSymNameOffset(name [8]byte) (bool, uint32) {
    92  	if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {
    93  		return true, binary.LittleEndian.Uint32(name[4:])
    94  	}
    95  	return false, 0
    96  }
    97  
    98  // FullName finds real name of symbol sym. Normally name is stored
    99  // in sym.Name, but if it is longer then 8 characters, it is stored
   100  // in COFF string table st instead.
   101  func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
   102  	if ok, offset := isSymNameOffset(sym.Name); ok {
   103  		return st.String(offset)
   104  	}
   105  	return cstring(sym.Name[:]), nil
   106  }
   107  
   108  func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
   109  	if len(allsyms) == 0 {
   110  		return nil, nil
   111  	}
   112  	syms := make([]*Symbol, 0)
   113  	aux := uint8(0)
   114  	for _, sym := range allsyms {
   115  		if aux > 0 {
   116  			aux--
   117  			continue
   118  		}
   119  		name, err := sym.FullName(st)
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		aux = sym.NumberOfAuxSymbols
   124  		s := &Symbol{
   125  			Name:          name,
   126  			Value:         sym.Value,
   127  			SectionNumber: sym.SectionNumber,
   128  			Type:          sym.Type,
   129  			StorageClass:  sym.StorageClass,
   130  		}
   131  		syms = append(syms, s)
   132  	}
   133  	return syms, nil
   134  }
   135  
   136  // Symbol is similar to COFFSymbol with Name field replaced
   137  // by Go string. Symbol also does not have NumberOfAuxSymbols.
   138  type Symbol struct {
   139  	Name          string
   140  	Value         uint32
   141  	SectionNumber int16
   142  	Type          uint16
   143  	StorageClass  uint8
   144  }
   145  
   146  // COFFSymbolAuxFormat5 describes the expected form of an aux symbol
   147  // attached to a section definition symbol. The PE format defines a
   148  // number of different aux symbol formats: format 1 for function
   149  // definitions, format 2 for .be and .ef symbols, and so on. Format 5
   150  // holds extra info associated with a section definition, including
   151  // number of relocations + line numbers, as well as COMDAT info. See
   152  // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
   153  // for more on what's going on here.
   154  type COFFSymbolAuxFormat5 struct {
   155  	Size           uint32
   156  	NumRelocs      uint16
   157  	NumLineNumbers uint16
   158  	Checksum       uint32
   159  	SecNum         uint16
   160  	Selection      uint8
   161  	_              [3]uint8 // padding
   162  }
   163  
   164  // These constants make up the possible values for the 'Selection'
   165  // field in an AuxFormat5.
   166  const (
   167  	IMAGE_COMDAT_SELECT_NODUPLICATES = 1
   168  	IMAGE_COMDAT_SELECT_ANY          = 2
   169  	IMAGE_COMDAT_SELECT_SAME_SIZE    = 3
   170  	IMAGE_COMDAT_SELECT_EXACT_MATCH  = 4
   171  	IMAGE_COMDAT_SELECT_ASSOCIATIVE  = 5
   172  	IMAGE_COMDAT_SELECT_LARGEST      = 6
   173  )
   174  
   175  // COFFSymbolReadSectionDefAux returns a blob of axiliary information
   176  // (including COMDAT info) for a section definition symbol. Here 'idx'
   177  // is the index of a section symbol in the main COFFSymbol array for
   178  // the File. Return value is a pointer to the appropriate aux symbol
   179  // struct. For more info, see:
   180  //
   181  // auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records
   182  // COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only
   183  // auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
   184  func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) {
   185  	var rv *COFFSymbolAuxFormat5
   186  	if idx < 0 || idx >= len(f.COFFSymbols) {
   187  		return rv, fmt.Errorf("invalid symbol index")
   188  	}
   189  	pesym := &f.COFFSymbols[idx]
   190  	const IMAGE_SYM_CLASS_STATIC = 3
   191  	if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) {
   192  		return rv, fmt.Errorf("incorrect symbol storage class")
   193  	}
   194  	if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) {
   195  		return rv, fmt.Errorf("aux symbol unavailable")
   196  	}
   197  	// Locate and return a pointer to the successor aux symbol.
   198  	pesymn := &f.COFFSymbols[idx+1]
   199  	rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn))
   200  	return rv, nil
   201  }
   202  

View as plain text