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  }