github.com/pkujhd/goloader@v0.0.0-20240411034752-1a28096bd7bd/pcvalue.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/sys"
     5  	"encoding/binary"
     6  	"fmt"
     7  
     8  	"github.com/pkujhd/goloader/obj"
     9  )
    10  
    11  func dumpPCValue(b []byte, prefix string) {
    12  	fmt.Println(prefix, b)
    13  	var pc uintptr
    14  	val := int32(-1)
    15  	var ok bool
    16  	b, ok = step(b, &pc, &val, true)
    17  	for {
    18  		if !ok || len(b) <= 0 {
    19  			fmt.Println(prefix, "step end")
    20  			break
    21  		}
    22  		fmt.Println(prefix, "pc:", pc, "val:", val)
    23  		b, ok = step(b, &pc, &val, false)
    24  	}
    25  }
    26  
    27  func writePCValue(p []byte, val int64, pc uint64) []byte {
    28  	buf := make([]byte, 32)
    29  	n := binary.PutVarint(buf, val)
    30  	p = append(p, buf[:n]...)
    31  	n = binary.PutUvarint(buf, pc)
    32  	p = append(p, buf[:n]...)
    33  	return p
    34  }
    35  
    36  func rewritePCFile(symbol *obj.ObjSymbol, linker *Linker) {
    37  	// golang version <= 1.15 PCFile need rewrite, PCFile (pc, val), val only adapte symbol.Func.File
    38  	p := symbol.Func.PCFile
    39  	pcfile := make([]byte, 0)
    40  	pc := uintptr(0)
    41  	lastpc := uintptr(0)
    42  	val := int32(-1)
    43  	lastval := int32(-1)
    44  	var ok bool
    45  	p, ok = step(p, &pc, &val, true)
    46  	for {
    47  		if !ok || len(p) <= 0 {
    48  			break
    49  		}
    50  		nval := obj.FindFileTab(symbol.Func.File[val], linker.NameMap, linker.Filetab)
    51  		pcfile = writePCValue(pcfile, int64(nval-lastval), uint64(pc-lastpc))
    52  		lastpc = pc
    53  		lastval = nval
    54  		p, ok = step(p, &pc, &val, false)
    55  	}
    56  	pcfile = append(pcfile, 0)
    57  	symbol.Func.PCFile = pcfile
    58  }
    59  
    60  func pcValue(p []byte, targetPC uintptr) (int32, uintptr) {
    61  	startPC := uintptr(0)
    62  	pc := uintptr(0)
    63  	val := int32(-1)
    64  	prevpc := pc
    65  	for {
    66  		var ok bool
    67  		p, ok = step(p, &pc, &val, pc == startPC)
    68  		if !ok || len(p) == 0 {
    69  			break
    70  		}
    71  		if targetPC < pc {
    72  			return val, prevpc
    73  		}
    74  		prevpc = pc
    75  	}
    76  	return -1, InvalidHandleValue
    77  }
    78  
    79  func updateLastPCValue(pcVals *[]byte, nval int32, npc, pcQuantum uintptr) {
    80  	p := *pcVals
    81  	pc := uintptr(0)
    82  	lastpc := uintptr(0)
    83  	val := int32(-1)
    84  	lastval := int32(-1)
    85  	npcVals := make([]byte, 0)
    86  	for {
    87  		var ok bool
    88  		p, ok = step(p, &pc, &val, pc == 0)
    89  		if len(p) == 1 && p[0] == 0 && val == nval {
    90  			npcVals = writePCValue(npcVals, int64(nval-lastval), uint64((npc-lastpc)/pcQuantum))
    91  			break
    92  		}
    93  		if !ok || len(p) == 0 {
    94  			npcVals = writePCValue(npcVals, int64(nval-lastval), uint64((npc-lastpc)/pcQuantum))
    95  			break
    96  		}
    97  		npcVals = writePCValue(npcVals, int64(val-lastval), uint64((pc-lastpc)/pcQuantum))
    98  		lastpc = pc
    99  		lastval = val
   100  	}
   101  	npcVals = append(npcVals, 0)
   102  	*pcVals = npcVals
   103  }
   104  
   105  func patchPCValues(linker *Linker, pcVals *[]byte, reloc obj.Reloc) {
   106  	// Use the pcvalue at the offset of the reloc for the entire of that reloc's epilogue.
   107  	// This ensures that if the code is pre-empted or the stack unwound while we're inside the epilogue, the runtime behaves correctly
   108  	if len(*pcVals) == 0 {
   109  		return
   110  	}
   111  	var pcQuantum uintptr = 1
   112  	if linker.Arch.Family == sys.ARM64 {
   113  		pcQuantum = 4
   114  	}
   115  	val, startPC := pcValue(*pcVals, uintptr(reloc.Offset))
   116  	if startPC == InvalidHandleValue && val == -1 {
   117  		panic(fmt.Sprintf("couldn't interpret pcvalue data with pc offset: %d", reloc.Offset))
   118  	}
   119  	updateLastPCValue(pcVals, val, uintptr(reloc.Epilogue.Offset+reloc.Epilogue.Size), pcQuantum)
   120  }