Source file src/debug/pe/symbol.go

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

View as plain text