github.com/tencent/goom@v1.0.1/internal/patch/jumpdata.go (about) 1 package patch 2 3 import ( 4 "errors" 5 "fmt" 6 "runtime/debug" 7 8 "github.com/tencent/goom/internal/bytecode" 9 "github.com/tencent/goom/internal/bytecode/memory" 10 "github.com/tencent/goom/internal/logger" 11 ) 12 13 const ( 14 // 默认需要修复的函数长度 15 defaultFuncSize = 1024 16 // 默认系统位数、暂时不支持32位的 17 defaultArchMod = 64 18 ) 19 20 // errAlreadyPatch 已经 patch 过了错误 21 var errAlreadyPatch = errors.New("already patched") 22 23 // genJumpData 在函数 from 里面, 织入对 to 的调用指令,同时将 from 织入前的指令恢复至 trampoline 这个地址 24 // origin 原来的函数地址 25 // replacementInAddr 要跳转到的函数调用地址 26 // replacementCode 要跳转到的函数地址, 与 replacementInAddr 的区别详细可以参考: 27 // https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub 28 func genJumpData(origin, replacementInAddr, replacementCode uintptr) (jumpData []byte, err error) { 29 defer func() { 30 if e := recover(); e != nil { 31 logger.Errorf("genJumpData origin=%d replacementInAddr=%d error:%s", origin, replacementInAddr, e) 32 logger.Error(string(debug.Stack())) 33 if e1, ok := e.(error); ok { 34 err = e1 35 } else { 36 err = fmt.Errorf("%s", e) 37 } 38 } 39 }() 40 41 logger.Infof("starting genJumpData func origin=0x%x replacementInAddr=0x%x replacementCode=0x%x ...", 42 origin, replacementInAddr, replacementCode) 43 bytecode.PrintInst("show replacementCode inst >>>>> ", replacementCode, 30, logger.DebugLevel) 44 45 // 获取原函数总长度 46 funcSize, e := bytecode.GetFuncSize(defaultArchMod, origin, false) 47 if e != nil { 48 logger.Warningf("GetFuncSize error: %v", e) 49 funcSize = defaultFuncSize 50 } 51 52 // 构造跳转到代理函数的指令 53 jumpData = jmpToFunctionValue(origin, replacementInAddr) 54 // 如果需要织入的跳转指令的长度大于原函数指令长度,则任务是无法织入指令 55 if len(jumpData) >= funcSize { 56 bytecode.PrintInst("origin inst > ", origin, bytecode.PrintShort, logger.InfoLevel) 57 return nil, fmt.Errorf( 58 "jumpInstSize[%d] is bigger than origin FuncSize[%d], cannot do pathes", len(jumpData), funcSize) 59 } 60 return jumpData, nil 61 } 62 63 // checkAndReadOriginBytes 检查原函数是否已经 patch 过, 并且发挥原函数的字节码数组 64 func checkAndReadOriginBytes(origin uintptr, jumpDataLen int) ([]byte, error) { 65 // 读取原始指令 66 result := memory.RawRead(origin, jumpDataLen) 67 // 判断是否已经被 patch 过 68 if checkAlreadyPatch(result) { 69 return nil, fmt.Errorf("origin: 0x%x is already patched, %w", origin, errAlreadyPatch) 70 } 71 bytecode.PrintInst("origin >>>>> ", origin, bytecode.PrintShort, logger.DebugLevel) 72 return result, nil 73 }