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 }