github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/hooker/prologue/prologue.go (about) 1 package prologue 2 3 import ( 4 "github.com/Rookout/GoSDK/pkg/logger" 5 "github.com/Rookout/GoSDK/pkg/rookoutErrors" 6 "github.com/Rookout/GoSDK/pkg/services/assembler" 7 "github.com/Rookout/GoSDK/pkg/services/disassembler" 8 "github.com/Rookout/GoSDK/pkg/services/instrumentation/hooker/regbackup" 9 ) 10 11 12 var ForceLongPrologue = false 13 14 const startLabel = "prologueStart" 15 const endLabel = "prologueEnd" 16 const fallbackLabel = "fallback" 17 18 type Generator struct { 19 stackUsage int 20 fallbackAddr uintptr 21 goPrologueExists bool 22 epilogueInstructions []*disassembler.Instruction 23 getRegsUsed func() ([]assembler.Reg, rookoutErrors.RookoutError) 24 morestackAddr uintptr 25 } 26 27 28 func NewGenerator(funcEntry uintptr, funcEnd uintptr, stackUsage int, fallbackAddr uintptr, getRegsUsed func() ([]assembler.Reg, rookoutErrors.RookoutError)) (*Generator, rookoutErrors.RookoutError) { 29 instructions, err := disassembler.Decode(funcEntry, funcEnd, true) 30 if err != nil { 31 return nil, err 32 } 33 34 g := &Generator{stackUsage: stackUsage, fallbackAddr: fallbackAddr, getRegsUsed: getRegsUsed} 35 g.epilogueInstructions, g.morestackAddr, g.goPrologueExists = getOriginalEpilogue(instructions) 36 if g.morestackAddr == 0 { 37 g.morestackAddr = morestackAddr 38 } 39 return g, nil 40 } 41 42 func (g *Generator) Generate() ([]byte, rookoutErrors.RookoutError) { 43 if !g.goPrologueExists || ForceLongPrologue { 44 logger.Logger().Debug("Generating long prologue") 45 return g.generateLongPrologue() 46 } 47 logger.Logger().Debug("Generating short prologue") 48 return g.generateShortPrologue() 49 } 50 51 func (g *Generator) generateShortPrologue() ([]byte, rookoutErrors.RookoutError) { 52 regBackup, regRestore := g.getOriginalRegBackup() 53 b := assembler.NewBuilder() 54 55 err := g.generateCheckStackUsage(b) 56 if err != nil { 57 return nil, err 58 } 59 err = b.AddInstructions(b.Bytes(regBackup)) 60 if err != nil { 61 return nil, err 62 } 63 err = g.generateCallMorestack(b) 64 if err != nil { 65 return nil, err 66 } 67 err = b.AddInstructions(b.Bytes(regRestore)) 68 if err != nil { 69 return nil, err 70 } 71 err = g.generateJumpToStart(b) 72 if err != nil { 73 return nil, err 74 } 75 76 return b.Assemble() 77 } 78 79 func (g *Generator) generateLongPrologue() ([]byte, rookoutErrors.RookoutError) { 80 regsToUpdate, err := g.getRegsUsed() 81 if err != nil { 82 return nil, err 83 } 84 logger.Logger().Debugf("Updating regs: %v\n", regsToUpdate) 85 86 regBackupGenerator := regbackup.NewGenerator(regsBackupBuffer, fallbackLabel, regsToUpdate) 87 b := assembler.NewBuilder() 88 89 err = g.generateCheckStackUsage(b) 90 if err != nil { 91 return nil, err 92 } 93 94 err = regBackupGenerator.GenerateRegBackup(b) 95 if err != nil { 96 return nil, err 97 } 98 err = g.generateCallMorestack(b) 99 if err != nil { 100 return nil, err 101 } 102 err = regBackupGenerator.GenerateRegRestore(b) 103 if err != nil { 104 return nil, err 105 } 106 107 err = g.generateCallFallback(b) 108 if err != nil { 109 return nil, err 110 } 111 err = g.generateJumpToStart(b) 112 if err != nil { 113 return nil, err 114 } 115 116 return b.Assemble() 117 } 118 119 120 func findFirstJmp(instructions []*disassembler.Instruction, startIndex int) (jmpIndex int, jmpDestIndex int, ok bool) { 121 for i := startIndex; i < len(instructions); i++ { 122 jmp, jmpIndex, ok := disassembler.GetFirstInstruction(instructions[i:], disassembler.IsDirectJump) 123 if !ok { 124 return 0, 0, false 125 } 126 jmpIndex += i 127 128 jmpDest, err := jmp.GetDestPC() 129 if err != nil { 130 logger.Logger().WithError(err).Warningf("Failed to get first jmp dest PC") 131 return 0, 0, false 132 } 133 134 destFound := false 135 for j, inst := range instructions { 136 if inst.PC == jmpDest { 137 destFound = true 138 jmpDestIndex = j 139 break 140 } 141 } 142 if !destFound { 143 logger.Logger().Warningf("Unable to find destination instruction of jump. Jump PC: 0x%x, dest PC: 0x%x", jmp.PC, jmpDest) 144 return 0, 0, false 145 } 146 147 148 if jmpDestIndex == jmpIndex+1 { 149 continue 150 } 151 152 return jmpIndex, jmpDestIndex, true 153 } 154 155 return 0, 0, false 156 } 157 158 func findMorestackCall(instructions []*disassembler.Instruction, startIndex int) (callIndex int, callDest uintptr, ok bool) { 159 call, callIndex, ok := disassembler.GetFirstInstruction(instructions[startIndex:], disassembler.IsDirectCall) 160 if !ok { 161 logger.Logger().Debug("Epilogue not found: no call in epilogue") 162 return 0, 0, false 163 } 164 callIndex += startIndex 165 166 callDest, err := call.GetDestPC() 167 if err != nil { 168 logger.Logger().WithError(err).Warningf("Failed to get call dest PC") 169 return 0, 0, false 170 } 171 if _, ok := morestackAddrs[callDest]; !ok { 172 logger.Logger().Debugf("Epilogue not found: call is not to morestack. callDest: %x, morestackAddrs: %x", callDest, morestackAddrs) 173 return 0, 0, false 174 } 175 176 return callIndex, callDest, true 177 } 178 179 func findJmpToStart(instructions []*disassembler.Instruction, epilogueStart int) (jmpIndex int, ok bool) { 180 jmpIndex, jmpDestIndex, ok := findFirstJmp(instructions, epilogueStart) 181 if !ok || jmpDestIndex != 0 { 182 return 0, false 183 } 184 return jmpIndex, true 185 } 186 187 func getOriginalEpilogue(instructions []*disassembler.Instruction) ([]*disassembler.Instruction, uintptr, bool) { 188 _, epilogueStart, ok := findFirstJmp(instructions, 0) 189 if !ok { 190 return nil, 0, false 191 } 192 193 morestackCallIndex, morestackAddr, ok := findMorestackCall(instructions, epilogueStart) 194 if !ok { 195 return nil, 0, false 196 } 197 198 epilogueEnd, ok := findJmpToStart(instructions, morestackCallIndex) 199 if !ok { 200 return nil, 0, false 201 } 202 203 204 return instructions[epilogueStart:epilogueEnd], morestackAddr, true 205 }