| // 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 ( | 
 | 	"encoding/binary" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"internal/saferio" | 
 | 	"io" | 
 | 	"unsafe" | 
 | ) | 
 |  | 
 | const COFFSymbolSize = 18 | 
 |  | 
 | // COFFSymbol represents single COFF symbol table record. | 
 | type COFFSymbol struct { | 
 | 	Name               [8]uint8 | 
 | 	Value              uint32 | 
 | 	SectionNumber      int16 | 
 | 	Type               uint16 | 
 | 	StorageClass       uint8 | 
 | 	NumberOfAuxSymbols uint8 | 
 | } | 
 |  | 
 | // readCOFFSymbols reads in the symbol table for a PE file, returning | 
 | // a slice of COFFSymbol objects. The PE format includes both primary | 
 | // symbols (whose fields are described by COFFSymbol above) and | 
 | // auxiliary symbols; all symbols are 18 bytes in size. The auxiliary | 
 | // symbols for a given primary symbol are placed following it in the | 
 | // array, e.g. | 
 | // | 
 | //	... | 
 | //	k+0:  regular sym k | 
 | //	k+1:    1st aux symbol for k | 
 | //	k+2:    2nd aux symbol for k | 
 | //	k+3:  regular sym k+3 | 
 | //	k+4:    1st aux symbol for k+3 | 
 | //	k+5:  regular sym k+5 | 
 | //	k+6:  regular sym k+6 | 
 | // | 
 | // The PE format allows for several possible aux symbol formats. For | 
 | // more info see: | 
 | // | 
 | //	https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records | 
 | // | 
 | // At the moment this package only provides APIs for looking at | 
 | // aux symbols of format 5 (associated with section definition symbols). | 
 | func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { | 
 | 	if fh.PointerToSymbolTable == 0 { | 
 | 		return nil, nil | 
 | 	} | 
 | 	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) | 
 | 	} | 
 | 	c := saferio.SliceCap[COFFSymbol](uint64(fh.NumberOfSymbols)) | 
 | 	if c < 0 { | 
 | 		return nil, errors.New("too many symbols; file may be corrupt") | 
 | 	} | 
 | 	syms := make([]COFFSymbol, 0, c) | 
 | 	naux := 0 | 
 | 	for k := uint32(0); k < fh.NumberOfSymbols; k++ { | 
 | 		var sym COFFSymbol | 
 | 		if naux == 0 { | 
 | 			// Read a primary symbol. | 
 | 			err = binary.Read(r, binary.LittleEndian, &sym) | 
 | 			if err != nil { | 
 | 				return nil, fmt.Errorf("fail to read symbol table: %v", err) | 
 | 			} | 
 | 			// Record how many auxiliary symbols it has. | 
 | 			naux = int(sym.NumberOfAuxSymbols) | 
 | 		} else { | 
 | 			// Read an aux symbol. At the moment we assume all | 
 | 			// aux symbols are format 5 (obviously this doesn't always | 
 | 			// hold; more cases will be needed below if more aux formats | 
 | 			// are supported in the future). | 
 | 			naux-- | 
 | 			aux := (*COFFSymbolAuxFormat5)(unsafe.Pointer(&sym)) | 
 | 			err = binary.Read(r, binary.LittleEndian, aux) | 
 | 			if err != nil { | 
 | 				return nil, fmt.Errorf("fail to read symbol table: %v", err) | 
 | 			} | 
 | 		} | 
 | 		syms = append(syms, sym) | 
 | 	} | 
 | 	if naux != 0 { | 
 | 		return nil, fmt.Errorf("fail to read symbol table: %d aux symbols unread", naux) | 
 | 	} | 
 | 	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 | 
 | 	SectionNumber int16 | 
 | 	Type          uint16 | 
 | 	StorageClass  uint8 | 
 | } | 
 |  | 
 | // COFFSymbolAuxFormat5 describes the expected form of an aux symbol | 
 | // attached to a section definition symbol. The PE format defines a | 
 | // number of different aux symbol formats: format 1 for function | 
 | // definitions, format 2 for .be and .ef symbols, and so on. Format 5 | 
 | // holds extra info associated with a section definition, including | 
 | // number of relocations + line numbers, as well as COMDAT info. See | 
 | // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions | 
 | // for more on what's going on here. | 
 | type COFFSymbolAuxFormat5 struct { | 
 | 	Size           uint32 | 
 | 	NumRelocs      uint16 | 
 | 	NumLineNumbers uint16 | 
 | 	Checksum       uint32 | 
 | 	SecNum         uint16 | 
 | 	Selection      uint8 | 
 | 	_              [3]uint8 // padding | 
 | } | 
 |  | 
 | // These constants make up the possible values for the 'Selection' | 
 | // field in an AuxFormat5. | 
 | const ( | 
 | 	IMAGE_COMDAT_SELECT_NODUPLICATES = 1 | 
 | 	IMAGE_COMDAT_SELECT_ANY          = 2 | 
 | 	IMAGE_COMDAT_SELECT_SAME_SIZE    = 3 | 
 | 	IMAGE_COMDAT_SELECT_EXACT_MATCH  = 4 | 
 | 	IMAGE_COMDAT_SELECT_ASSOCIATIVE  = 5 | 
 | 	IMAGE_COMDAT_SELECT_LARGEST      = 6 | 
 | ) | 
 |  | 
 | // COFFSymbolReadSectionDefAux returns a blob of auxiliary information | 
 | // (including COMDAT info) for a section definition symbol. Here 'idx' | 
 | // is the index of a section symbol in the main [COFFSymbol] array for | 
 | // the File. Return value is a pointer to the appropriate aux symbol | 
 | // struct. For more info, see: | 
 | // | 
 | // auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records | 
 | // COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only | 
 | // auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions | 
 | func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) { | 
 | 	var rv *COFFSymbolAuxFormat5 | 
 | 	if idx < 0 || idx >= len(f.COFFSymbols) { | 
 | 		return rv, fmt.Errorf("invalid symbol index") | 
 | 	} | 
 | 	pesym := &f.COFFSymbols[idx] | 
 | 	const IMAGE_SYM_CLASS_STATIC = 3 | 
 | 	if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { | 
 | 		return rv, fmt.Errorf("incorrect symbol storage class") | 
 | 	} | 
 | 	if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) { | 
 | 		return rv, fmt.Errorf("aux symbol unavailable") | 
 | 	} | 
 | 	// Locate and return a pointer to the successor aux symbol. | 
 | 	pesymn := &f.COFFSymbols[idx+1] | 
 | 	rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn)) | 
 | 	return rv, nil | 
 | } |