github.com/tencent/goom@v1.0.1/internal/patch/fix_addr_amd64.go (about)

     1  package patch
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/tencent/goom/internal/arch/x86asm"
     9  	"github.com/tencent/goom/internal/bytecode"
    10  	"github.com/tencent/goom/internal/logger"
    11  )
    12  
    13  // allowCopyCall 是否允许拷贝 Call 指令
    14  const allowCopyCall = true
    15  
    16  // fixRelativeAddr 修复函数字节码中的相对地址(如果有的话)
    17  // from 函数起始地址
    18  // copyOrigin 函数字节码
    19  // trampoline 需要移动到的目标地址
    20  // funcSize 函数字节码整体长度
    21  // leastSize 要替换的字节长度的最小限制
    22  func fixRelativeAddr(from uintptr, copyOrigin []byte, trampoline uintptr, funcSize int, leastSize int) (
    23  	fixedData []byte, fixedDataSize int, err error) {
    24  
    25  	// try to replace and get the len(endPos) to replace
    26  	_, fixedDataSize, err = fixBlock(from, copyOrigin, trampoline, leastSize, funcSize)
    27  	if err != nil {
    28  		return
    29  	}
    30  
    31  	// check if exists jump back to [0:endPos], return not support
    32  	if err = checkJumpBetween(from, fixedDataSize, copyOrigin, funcSize); err != nil {
    33  		return
    34  	}
    35  
    36  	logger.Debugf("fix size: %d", fixedDataSize)
    37  
    38  	// real replace
    39  	fixedData, _, err = fixBlock(from, copyOrigin, trampoline, fixedDataSize, fixedDataSize)
    40  	return
    41  }
    42  
    43  // fixBlock 替换函数字节码中的相对地址(如果有的话)
    44  // from 起始位置(实际地址)
    45  // block 被替换的目标字节区块
    46  // trampoline 跳板函数起始地址
    47  // leastSize 最少替换范围
    48  // blockSize 目标区块范围, 用于判断地址是否超出 block 范围, 超出才需要替换
    49  // return []byte 修复后的指令
    50  func fixBlock(from uintptr, block []byte, trampoline uintptr,
    51  	leastSize int, blockSize int) (fixedData []byte, fixedDataSize int, err error) {
    52  	var (
    53  		fixedBlock = make([]byte, 0)
    54  	)
    55  	logger.Debug("target fix ins >>>>>")
    56  
    57  	for pos := 0; pos < len(block); {
    58  		ins, _, err := bytecode.ParseIns(pos, block)
    59  		if err != nil {
    60  			panic("fixRelativeAddr err:" + err.Error())
    61  		}
    62  
    63  		if ins != nil && ins.Opcode != 0 {
    64  			if !allowCopyCall && ins.Op.String() == bytecode.CallInsName {
    65  				return nil, 0,
    66  					fmt.Errorf("copy call instruction is not allowed in auto trampoline model. size: %d", leastSize)
    67  			}
    68  			// 拷贝一份 block,防止被修改; replace 之后的 block 是回参 fixedBlock
    69  			copyBlock := make([]byte, len(block))
    70  			if l := copy(copyBlock, block); l != len(block) {
    71  				return nil, 0, errors.New("copy block array error")
    72  			}
    73  			fixedInsData := fixIns(ins, pos, copyBlock, blockSize, (uint64)(from), trampoline)
    74  			fixedBlock = append(fixedBlock, fixedInsData...)
    75  
    76  			logger.Debugf("[%d]>[%d] 0x%x:\t%s\t\t%s\t\t%s", ins.Len, len(fixedInsData),
    77  				(uint64)(from)+(uint64)(pos), ins.Op, ins.String(), hex.EncodeToString(fixedInsData))
    78  		}
    79  
    80  		pos = pos + ins.Len
    81  
    82  		// for fix only first few inst, not copy all func inst
    83  		if leastSize > 0 && pos >= leastSize {
    84  			ins, _, err := bytecode.ParseIns(pos, block)
    85  			if err != nil {
    86  				panic("fixRelativeAddr err:" + err.Error())
    87  			}
    88  			// fix jump to RET err: signal SIGSEGV: segmentation violation
    89  			if ins != nil && ins.String() != "RET" {
    90  				return fixedBlock, pos, nil
    91  			}
    92  		}
    93  	}
    94  
    95  	return fixedBlock, len(fixedBlock), nil
    96  }
    97  
    98  // fixIns 替换单条指令的偏移地址
    99  func fixIns(ins *x86asm.Inst, pos int, block []byte, blockSize int,
   100  	from uint64, trampoline uintptr) []byte {
   101  	if ins.PCRelOff <= 0 {
   102  		// 不需要替换偏移地址
   103  		return block[pos : pos+ins.Len]
   104  	}
   105  	offset := pos + ins.PCRelOff
   106  	addr := bytecode.DecodeRelativeAddr(ins, block, offset)
   107  
   108  	// TODO 待实现
   109  	//if ins.PCRel <= 1 {
   110  	//	// 1字节相对地址暂时忽略
   111  	//	return
   112  	//}
   113  
   114  	logger.Debugf("ins relative [%d] need fix : ", (addr)+pos+ins.Len)
   115  
   116  	if (addr > 0 && (addr)+pos+ins.Len >= blockSize) ||
   117  		(addr < 0 && (addr)+pos+ins.Len < 0) {
   118  		if ins.Op.String() == bytecode.CallInsName {
   119  			logger.Debug((int64)(from)-(int64)(trampoline), from, trampoline, int32(addr))
   120  		}
   121  		if logger.LogLevel <= logger.DebugLevel {
   122  			ins, err := x86asm.Decode(block[pos:pos+ins.Len], 64)
   123  			if err == nil {
   124  				logger.Infof("replaced: \t%s\t\t%s", ins.Op, ins.String())
   125  			}
   126  		}
   127  
   128  		result := bytecode.EncodeAddress(block[pos:offset],
   129  			block[offset:offset+ins.PCRel], ins.PCRel, addr, (int)(from)-(int)(trampoline))
   130  		if len(result) > ins.PCRel {
   131  			return result
   132  		}
   133  	} else {
   134  		if ins.Op.String() == bytecode.CallInsName {
   135  			logger.Debug((addr)+pos+ins.Len, blockSize, (addr)+pos+ins.Len)
   136  		}
   137  	}
   138  
   139  	// nothing to fix
   140  	return block[pos : pos+ins.Len]
   141  }
   142  
   143  // checkJumpBetween check if exists the instruction in originData of function
   144  // that jump into address between :from and :to
   145  // if exists, return error and not support to fix.
   146  func checkJumpBetween(from uintptr, to int, originData []byte, funcSize int) error {
   147  	for pos := 0; pos <= funcSize; {
   148  		ins, code, err := bytecode.ParseIns(pos, originData)
   149  		if err != nil {
   150  			panic("checkJumpBetween err:" + err.Error())
   151  		}
   152  		if ins == nil {
   153  			break
   154  		}
   155  		if ins.PCRelOff <= 0 {
   156  			pos = pos + ins.Len
   157  			continue
   158  		}
   159  		offset := pos + ins.PCRelOff
   160  		relativeAddr := bytecode.DecodeRelativeAddr(ins, originData, offset)
   161  		if ((relativeAddr)+pos+ins.Len < to) &&
   162  			((relativeAddr)+pos+ins.Len > 0) {
   163  			logger.Errorf("[%d] 0x%x:\t%s\t\t%-30s\t\t%s\t\tabs:0x%x", ins.Len,
   164  				from+uintptr(pos), ins.Op, ins.String(), hex.EncodeToString(code),
   165  				from+uintptr(pos)+uintptr(relativeAddr)+uintptr(ins.Len))
   166  			return fmt.Errorf("not support of jump to inside of the first 13 bytes\n"+
   167  				"jump address is: 0x%x", (uintptr)((relativeAddr)+pos+ins.Len)+from)
   168  		}
   169  		pos = pos + ins.Len
   170  	}
   171  	return nil
   172  }