src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }