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 }