github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/gcdata.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/gcprog"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  	"unsafe"
     9  
    10  	"github.com/eh-steve/goloader/obj"
    11  	"github.com/eh-steve/goloader/objabi/symkind"
    12  )
    13  
    14  const (
    15  	KindGCProg = 1 << 6
    16  )
    17  
    18  func generategcdata(linker *Linker, codeModule *CodeModule, symbolMap map[string]uintptr, w *gcprog.Writer, sym *obj.Sym) error {
    19  	segment := &codeModule.segment
    20  	// if symbol is in loader, ignore generate gc data
    21  	firstModuleSymAddr, isInFirstModule := symbolMap[sym.Name]
    22  	if isInFirstModule && (firstModuleSymAddr < uintptr(segment.dataBase) || firstModuleSymAddr > uintptr(segment.dataBase+segment.sumDataLen)) {
    23  		return nil
    24  	}
    25  	typeName := linker.objsymbolMap[sym.Name].Type
    26  	sval := int64(symbolMap[sym.Name] - uintptr(segment.dataBase))
    27  	if int(sym.Kind) == symkind.SBSS {
    28  		sval = sval - int64(segment.dataLen+segment.noptrdataLen)
    29  	}
    30  	if ptr, ok := symbolMap[typeName]; ok {
    31  		typ := (*_type)(adduintptr(ptr, 0))
    32  		nptr := int64(typ.ptrdata) / int64(linker.Arch.PtrSize)
    33  		if typ.kind&KindGCProg == 0 {
    34  			var mask []byte
    35  			append2Slice(&mask, uintptr(unsafe.Pointer(typ.gcdata)), int(nptr+7)/8)
    36  			for i := int64(0); i < nptr; i++ {
    37  				if (mask[i/8]>>uint(i%8))&1 != 0 {
    38  					w.Ptr(sval/int64(linker.Arch.PtrSize) + i)
    39  				}
    40  			}
    41  
    42  		} else {
    43  			var prog []byte
    44  			append2Slice(&prog, uintptr(unsafe.Pointer(typ.gcdata)), Uint32Size+int((*(*uint32)(unsafe.Pointer(typ.gcdata)))))
    45  			w.ZeroUntil(sval / int64(linker.Arch.PtrSize))
    46  			w.Append(prog[4:], nptr)
    47  		}
    48  	} else {
    49  		if strings.HasPrefix(sym.Name, "_cgo_") || strings.Count(sym.Name, ".") == 0 {
    50  			// CGo symbols don't need gcdata
    51  			return nil
    52  		}
    53  		return fmt.Errorf("type: '%s' for symbol '%s' not found\n", typeName, sym.Name)
    54  	}
    55  	return nil
    56  }
    57  
    58  func sortSym(symMap map[string]*obj.Sym, kind int) []*obj.Sym {
    59  	syms := make(map[int]*obj.Sym)
    60  	keys := []int{}
    61  	for _, sym := range symMap {
    62  		if sym.Kind == kind {
    63  			syms[sym.Offset] = sym
    64  			keys = append(keys, sym.Offset)
    65  		}
    66  	}
    67  	sort.Ints(keys)
    68  	symbols := []*obj.Sym{}
    69  	for _, key := range keys {
    70  		symbols = append(symbols, syms[key])
    71  	}
    72  	return symbols
    73  }
    74  
    75  func (linker *Linker) addgcdata(codeModule *CodeModule, symbolMap map[string]uintptr) error {
    76  	module := codeModule.module
    77  	w := gcprog.Writer{}
    78  	w.Init(func(x byte) {
    79  		codeModule.gcdata = append(codeModule.gcdata, x)
    80  	})
    81  	for _, sym := range sortSym(linker.symMap, symkind.SDATA) {
    82  		err := generategcdata(linker, codeModule, symbolMap, &w, sym)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  	w.ZeroUntil(int64(module.edata-module.data) / int64(linker.Arch.PtrSize))
    88  	w.End()
    89  	module.gcdata = (*sliceHeader)(unsafe.Pointer(&codeModule.gcdata)).Data
    90  	module.gcdatamask = progToPointerMask((*byte)(adduintptr(module.gcdata, 0)), module.edata-module.data)
    91  
    92  	w = gcprog.Writer{}
    93  	w.Init(func(x byte) {
    94  		codeModule.gcbss = append(codeModule.gcbss, x)
    95  	})
    96  
    97  	for _, sym := range sortSym(linker.symMap, symkind.SBSS) {
    98  		err := generategcdata(linker, codeModule, symbolMap, &w, sym)
    99  		if err != nil {
   100  			return err
   101  		}
   102  	}
   103  	w.ZeroUntil(int64(module.ebss-module.bss) / int64(linker.Arch.PtrSize))
   104  	w.End()
   105  	module.gcbss = (*sliceHeader)(unsafe.Pointer(&codeModule.gcbss)).Data
   106  	module.gcbssmask = progToPointerMask((*byte)(adduintptr(module.gcbss, 0)), module.ebss-module.bss)
   107  	return nil
   108  }