asm.go (8333B)
1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package goversion 6 7 import ( 8 "encoding/binary" 9 "fmt" 10 "os" 11 ) 12 13 type matcher [][]uint32 14 15 const ( 16 pWild uint32 = 0xff00 17 pAddr uint32 = 0x10000 18 pEnd uint32 = 0x20000 19 pRelAddr uint32 = 0x30000 20 21 opMaybe = 1 + iota 22 opMust 23 opDone 24 opAnchor = 0x100 25 opSub8 = 0x200 26 opFlags = opAnchor | opSub8 27 ) 28 29 var amd64Matcher = matcher{ 30 {opMaybe | opAnchor, 31 // __rt0_amd64_darwin: 32 // JMP __rt0_amd64 33 0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd, 0xcc, 0xcc, 0xcc, 34 }, 35 {opMaybe, 36 // _rt0_amd64_linux: 37 // lea 0x8(%rsp), %rsi 38 // mov (%rsp), %rdi 39 // lea ADDR(%rip), %rax # main 40 // jmpq *%rax 41 0x48, 0x8d, 0x74, 0x24, 0x08, 42 0x48, 0x8b, 0x3c, 0x24, 0x48, 43 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, 44 0xff, 0xe0, 45 }, 46 {opMaybe, 47 // _rt0_amd64_linux: 48 // lea 0x8(%rsp), %rsi 49 // mov (%rsp), %rdi 50 // mov $ADDR, %eax # main 51 // jmpq *%rax 52 0x48, 0x8d, 0x74, 0x24, 0x08, 53 0x48, 0x8b, 0x3c, 0x24, 54 0xb8, pWild | pAddr, pWild, pWild, pWild, 55 0xff, 0xe0, 56 }, 57 {opMaybe, 58 // __rt0_amd64: 59 // mov (%rsp), %rdi 60 // lea 8(%rsp), %rsi 61 // jmp runtime.rt0_g0 62 0x48, 0x8b, 0x3c, 0x24, 63 0x48, 0x8d, 0x74, 0x24, 0x08, 64 0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd, 65 0xcc, 0xcc, 66 }, 67 {opMaybe, 68 // _start (toward end) 69 // lea __libc_csu_fini(%rip), %r8 70 // lea __libc_csu_init(%rip), %rcx 71 // lea ADDR(%rip), %rdi # main 72 // callq *xxx(%rip) 73 0x4c, 0x8d, 0x05, pWild, pWild, pWild, pWild, 74 0x48, 0x8d, 0x0d, pWild, pWild, pWild, pWild, 75 0x48, 0x8d, 0x3d, pWild | pAddr, pWild, pWild, pWild | pEnd, 76 0xff, 0x15, 77 }, 78 {opMaybe, 79 // _start (toward end) 80 // push %rsp (1) 81 // mov $__libc_csu_fini, %r8 (7) 82 // mov $__libc_csu_init, %rcx (7) 83 // mov $ADDR, %rdi # main (7) 84 // callq *xxx(%rip) 85 0x54, 86 0x49, 0xc7, 0xc0, pWild, pWild, pWild, pWild, 87 0x48, 0xc7, 0xc1, pWild, pWild, pWild, pWild, 88 0x48, 0xc7, 0xc7, pAddr | pWild, pWild, pWild, pWild, 89 }, 90 {opMaybe | opAnchor, 91 // main: 92 // lea ADDR(%rip), %rax # rt0_go 93 // jmpq *%rax 94 0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, 95 0xff, 0xe0, 96 }, 97 {opMaybe | opAnchor, 98 // main: 99 // mov $ADDR, %eax 100 // jmpq *%rax 101 0xb8, pWild | pAddr, pWild, pWild, pWild, 102 0xff, 0xe0, 103 }, 104 {opMaybe | opAnchor, 105 // main: 106 // JMP runtime.rt0_go(SB) 107 0xe9, pWild | pAddr, pWild, pWild, pWild | pEnd, 0xcc, 0xcc, 0xcc, 108 }, 109 {opMust | opAnchor, 110 // rt0_go: 111 // mov %rdi, %rax 112 // mov %rsi, %rbx 113 // sub %0x27, %rsp 114 // and $0xfffffffffffffff0,%rsp 115 // mov %rax,0x10(%rsp) 116 // mov %rbx,0x18(%rsp) 117 0x48, 0x89, 0xf8, 118 0x48, 0x89, 0xf3, 119 0x48, 0x83, 0xec, 0x27, 120 0x48, 0x83, 0xe4, 0xf0, 121 0x48, 0x89, 0x44, 0x24, 0x10, 122 0x48, 0x89, 0x5c, 0x24, 0x18, 123 }, 124 {opMust, 125 // later in rt0_go: 126 // mov %eax, (%rsp) 127 // mov 0x18(%rsp), %rax 128 // mov %rax, 0x8(%rsp) 129 // callq runtime.args 130 // callq runtime.osinit 131 // callq runtime.schedinit (ADDR) 132 0x89, 0x04, 0x24, 133 0x48, 0x8b, 0x44, 0x24, 0x18, 134 0x48, 0x89, 0x44, 0x24, 0x08, 135 0xe8, pWild, pWild, pWild, pWild, 136 0xe8, pWild, pWild, pWild, pWild, 137 0xe8, pWild, pWild, pWild, pWild, 138 }, 139 {opMaybe, 140 // later in rt0_go: 141 // mov %eax, (%rsp) 142 // mov 0x18(%rsp), %rax 143 // mov %rax, 0x8(%rsp) 144 // callq runtime.args 145 // callq runtime.osinit 146 // callq runtime.schedinit (ADDR) 147 // lea other(%rip), %rdi 148 0x89, 0x04, 0x24, 149 0x48, 0x8b, 0x44, 0x24, 0x18, 150 0x48, 0x89, 0x44, 0x24, 0x08, 151 0xe8, pWild, pWild, pWild, pWild, 152 0xe8, pWild, pWild, pWild, pWild, 153 0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd, 154 0x48, 0x8d, 0x05, 155 }, 156 {opMaybe, 157 // later in rt0_go: 158 // mov %eax, (%rsp) 159 // mov 0x18(%rsp), %rax 160 // mov %rax, 0x8(%rsp) 161 // callq runtime.args 162 // callq runtime.osinit 163 // callq runtime.hashinit 164 // callq runtime.schedinit (ADDR) 165 // pushq $main.main 166 0x89, 0x04, 0x24, 167 0x48, 0x8b, 0x44, 0x24, 0x18, 168 0x48, 0x89, 0x44, 0x24, 0x08, 169 0xe8, pWild, pWild, pWild, pWild, 170 0xe8, pWild, pWild, pWild, pWild, 171 0xe8, pWild, pWild, pWild, pWild, 172 0xe8, pWild | pAddr, pWild, pWild, pWild | pEnd, 173 0x68, 174 }, 175 {opDone | opSub8, 176 // schedinit (toward end) 177 // mov ADDR(%rip), %rax 178 // test %rax, %rax 179 // jne <short> 180 // movq $0x7, ADDR(%rip) 181 // 182 0x48, 0x8b, 0x05, pWild, pWild, pWild, pWild, 183 0x48, 0x85, 0xc0, 184 0x75, pWild, 185 0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd, 186 }, 187 {opDone | opSub8, 188 // schedinit (toward end) 189 // mov ADDR(%rip), %rbx 190 // cmp $0x0, %rbx 191 // jne <short> 192 // lea "unknown"(%rip), %rbx 193 // mov %rbx, ADDR(%rip) 194 // movq $7, (ADDR+8)(%rip) 195 0x48, 0x8b, 0x1d, pWild, pWild, pWild, pWild, 196 0x48, 0x83, 0xfb, 0x00, 197 0x75, pWild, 198 0x48, 0x8d, 0x1d, pWild, pWild, pWild, pWild, 199 0x48, 0x89, 0x1d, pWild, pWild, pWild, pWild, 200 0x48, 0xc7, 0x05, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00 | pEnd, 201 }, 202 {opDone, 203 // schedinit (toward end) 204 // cmpq $0x0, ADDR(%rip) 205 // jne <short> 206 // lea "unknown"(%rip), %rax 207 // mov %rax, ADDR(%rip) 208 // lea ADDR(%rip), %rax 209 // movq $7, 8(%rax) 210 0x48, 0x83, 0x3d, pWild | pAddr, pWild, pWild, pWild, 0x00, 211 0x75, pWild, 212 0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild, 213 0x48, 0x89, 0x05, pWild, pWild, pWild, pWild, 214 0x48, 0x8d, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, 215 0x48, 0xc7, 0x40, 0x08, 0x07, 0x00, 0x00, 0x00, 216 }, 217 {opDone, 218 // schedinit (toward end) 219 // cmpq $0x0, ADDR(%rip) 220 // jne <short> 221 // movq $0x7, ADDR(%rip) 222 0x48, 0x83, 0x3d, pWild | pAddr, pWild, pWild, pWild, 0x00, 223 0x75, pWild, 224 0x48, 0xc7, 0x05 | pEnd, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00, 225 }, 226 {opDone, 227 // test %eax, %eax 228 // jne <later> 229 // lea "unknown"(RIP), %rax 230 // mov %rax, ADDR(%rip) 231 0x48, 0x85, 0xc0, 0x75, pWild, 0x48, 0x8d, 0x05, pWild, pWild, pWild, pWild, 0x48, 0x89, 0x05, pWild | pAddr, pWild, pWild, pWild | pEnd, 232 }, 233 {opDone, 234 // schedinit (toward end) 235 // mov ADDR(%rip), %rcx 236 // test %rcx, %rcx 237 // jne <short> 238 // movq $0x7, ADDR(%rip) 239 // 240 0x48, 0x8b, 0x0d, pWild, pWild, pWild, pWild, 241 0x48, 0x85, 0xc9, 242 0x75, pWild, 243 0x48, 0xc7, 0x05 | pEnd, pWild | pAddr, pWild, pWild, pWild, 0x07, 0x00, 0x00, 0x00, 244 }, 245 } 246 247 var DebugMatch bool 248 249 func (m matcher) match(f exe, addr uint64) (uint64, bool) { 250 data, err := f.ReadData(addr, 512) 251 if DebugMatch { 252 fmt.Fprintf(os.Stderr, "data @%#x: %x\n", addr, data[:16]) 253 } 254 if err != nil { 255 if DebugMatch { 256 fmt.Fprintf(os.Stderr, "match: %v\n", err) 257 } 258 return 0, false 259 } 260 if DebugMatch { 261 fmt.Fprintf(os.Stderr, "data: %x\n", data[:32]) 262 } 263 Matchers: 264 for pc, p := range m { 265 op := p[0] 266 p = p[1:] 267 Search: 268 for i := 0; i <= len(data)-len(p); i++ { 269 a := -1 270 e := -1 271 if i > 0 && op&opAnchor != 0 { 272 break 273 } 274 for j := 0; j < len(p); j++ { 275 b := byte(p[j]) 276 m := byte(p[j] >> 8) 277 if data[i+j]&^m != b { 278 continue Search 279 } 280 if p[j]&pAddr != 0 { 281 a = j 282 } 283 if p[j]&pEnd != 0 { 284 e = j + 1 285 } 286 } 287 // matched 288 if DebugMatch { 289 fmt.Fprintf(os.Stderr, "match (%d) %#x+%d %x %x\n", pc, addr, i, p, data[i:i+len(p)]) 290 } 291 if a != -1 { 292 val := uint64(int32(binary.LittleEndian.Uint32(data[i+a:]))) 293 if e == -1 { 294 addr = val 295 } else { 296 addr += uint64(i+e) + val 297 } 298 if op&opSub8 != 0 { 299 addr -= 8 300 } 301 } 302 if op&^opFlags == opDone { 303 if DebugMatch { 304 fmt.Fprintf(os.Stderr, "done %x\n", addr) 305 } 306 return addr, true 307 } 308 if a != -1 { 309 // changed addr, so reload 310 data, err = f.ReadData(addr, 512) 311 if err != nil { 312 return 0, false 313 } 314 if DebugMatch { 315 fmt.Fprintf(os.Stderr, "reload @%#x: %x\n", addr, data[:32]) 316 } 317 } 318 continue Matchers 319 } 320 // not matched 321 if DebugMatch { 322 fmt.Fprintf(os.Stderr, "no match (%d) %#x %x %x\n", pc, addr, p, data[:32]) 323 } 324 if op&^opFlags == opMust { 325 return 0, false 326 } 327 } 328 // ran off end of matcher 329 return 0, false 330 } 331 332 func readBuildVersionX86Asm(f exe) (isGo bool, buildVersion string) { 333 entry := f.Entry() 334 if entry == 0 { 335 if DebugMatch { 336 fmt.Fprintf(os.Stderr, "missing entry!\n") 337 } 338 return 339 } 340 addr, ok := amd64Matcher.match(f, entry) 341 if !ok { 342 return 343 } 344 v, err := readBuildVersion(f, addr, 16) 345 if err != nil { 346 return 347 } 348 return true, v 349 }