github.com/tencent/goom@v1.0.1/internal/bytecode/func_amd64.go (about)

     1  package bytecode
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"strings"
     7  
     8  	"github.com/tencent/goom/internal/arch/x86asm"
     9  	"github.com/tencent/goom/internal/bytecode/memory"
    10  	"github.com/tencent/goom/internal/logger"
    11  )
    12  
    13  // defaultInsLen 默认一次解析指令的长度
    14  const defaultInsLen = 16
    15  
    16  // funcPrologue 函数的开头指纹,用于不同 OS 获取不同的默认值
    17  var funcPrologue = defaultFuncPrologue64
    18  
    19  // CallInsName call 指令名称
    20  const CallInsName = "CALL"
    21  
    22  // GetFuncSize get func binary size
    23  // not absolutely safe
    24  func GetFuncSize(mode int, start uintptr, minimal bool) (length int, err error) {
    25  	funcSizeReadLock.Lock()
    26  	defer func() {
    27  		funcSizeCache[start] = length
    28  		funcSizeReadLock.Unlock()
    29  	}()
    30  
    31  	if l, ok := funcSizeCache[start]; ok {
    32  		return l, nil
    33  	}
    34  
    35  	prologueLen := len(funcPrologue)
    36  	code := memory.RawRead(start, defaultInsLen)
    37  
    38  	var (
    39  		int3Found = false
    40  		curLen    = 0
    41  	)
    42  	for {
    43  		inst, err := x86asm.Decode(code, mode)
    44  		if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(code[0])) {
    45  			return curLen, nil
    46  		}
    47  
    48  		if inst.Len == 1 && code[0] == 0xcc {
    49  			// 0xcc -> int3, trap to debugger, padding to function end
    50  			if minimal {
    51  				return curLen, nil
    52  			}
    53  			int3Found = true
    54  		} else if int3Found {
    55  			return curLen, nil
    56  		}
    57  
    58  		curLen = curLen + inst.Len
    59  		code = memory.RawRead(start+uintptr(curLen), defaultInsLen)
    60  		if bytes.Equal(funcPrologue, code[:prologueLen]) {
    61  			return curLen, nil
    62  		}
    63  	}
    64  }
    65  
    66  // PrintInstf 调试内存指令替换,对原指令、替换之后的指令进行输出对比
    67  func PrintInstf(title string, from uintptr, copyOrigin []byte, level int) {
    68  	if logger.LogLevel < level {
    69  		return
    70  	}
    71  	logger.Important(title)
    72  
    73  	startAddr := (uint64)(from)
    74  	for pos := 0; pos < len(copyOrigin); {
    75  		to := pos + defaultInsLen
    76  		if to > len(copyOrigin) {
    77  			to = len(copyOrigin)
    78  		}
    79  
    80  		code := copyOrigin[pos:to]
    81  		ins, err := x86asm.Decode(code, 64)
    82  
    83  		if err != nil {
    84  			logger.Importantf("[0] 0x%x: inst decode error:%s", startAddr+(uint64)(pos), err)
    85  			if ins.Len == 0 {
    86  				pos = pos + 1
    87  			} else {
    88  				pos = pos + ins.Len
    89  			}
    90  			continue
    91  		}
    92  
    93  		if ins.Opcode == 0 {
    94  			if ins.Len == 0 {
    95  				pos = pos + 1
    96  			} else {
    97  				pos = pos + ins.Len
    98  			}
    99  			continue
   100  		}
   101  
   102  		if ins.PCRelOff <= 0 {
   103  			logger.Importantf("[%d] 0x%x:\t%s\t\t%-30s\t\t%s", ins.Len,
   104  				startAddr+(uint64)(pos), ins.Op, ins.String(), hex.EncodeToString(code[:ins.Len]))
   105  			pos = pos + ins.Len
   106  			continue
   107  		}
   108  
   109  		offset := pos + ins.PCRelOff
   110  		relativeAddr := DecodeAddress(copyOrigin[offset:offset+ins.PCRel], ins.PCRel)
   111  		if !isRelativeAdd(ins) && relativeAddr > 0 {
   112  			relativeAddr = -relativeAddr
   113  		}
   114  
   115  		logger.Importantf("[%d] 0x%x:\t%s\t\t%-30s\t\t%s\t\tabs:0x%x", ins.Len,
   116  			startAddr+(uint64)(pos), ins.Op, ins.String(), hex.EncodeToString(code[:ins.Len]),
   117  			from+uintptr(pos)+uintptr(relativeAddr)+uintptr(ins.Len))
   118  
   119  		pos = pos + ins.Len
   120  	}
   121  }
   122  
   123  func isRelativeAdd(ins x86asm.Inst) bool {
   124  	isAdd := true
   125  	for i := 0; i < len(ins.Args); i++ {
   126  		arg := ins.Args[i]
   127  		if arg == nil {
   128  			break
   129  		}
   130  		addrArgs := arg.String()
   131  		if strings.HasPrefix(addrArgs, ".-") || strings.Contains(addrArgs, "RIP-") {
   132  			isAdd = false
   133  		}
   134  	}
   135  	return isAdd
   136  }
   137  
   138  // GetInnerFunc Get the first real func location from wrapper
   139  // not absolutely safe
   140  func GetInnerFunc(mode int, start uintptr) (uintptr, error) {
   141  	prologueLen := len(funcPrologue)
   142  	code := memory.RawRead(start, defaultInsLen)
   143  
   144  	var (
   145  		int3Found = false
   146  		curLen    = 0
   147  	)
   148  	for {
   149  		inst, err := x86asm.Decode(code, mode)
   150  		if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(code[0])) {
   151  			return 0, nil
   152  		}
   153  
   154  		if inst.Len == 1 && code[0] == 0xcc {
   155  			int3Found = true
   156  		} else if int3Found {
   157  			return 0, nil
   158  		}
   159  
   160  		if inst.Op.String() == CallInsName {
   161  			relativeAddr := DecodeRelativeAddr(&inst, code, inst.PCRelOff)
   162  			if relativeAddr >= 0 {
   163  				return start + uintptr(curLen) + uintptr(relativeAddr) + uintptr(inst.Len), nil
   164  			}
   165  			if curLen+int(relativeAddr) < 0 {
   166  				return start + uintptr(curLen) - uintptr(-relativeAddr) + uintptr(inst.Len), nil
   167  			}
   168  		}
   169  
   170  		curLen = curLen + inst.Len
   171  		code = memory.RawRead(start+uintptr(curLen), defaultInsLen)
   172  		if bytes.Equal(funcPrologue, code[:prologueLen]) {
   173  			return 0, nil
   174  		}
   175  	}
   176  }