Home | History | Annotate | Download | only in x86asm
      1 package x86asm
      2 
      3 import (
      4 	"bytes"
      5 	"fmt"
      6 	"io"
      7 	"log"
      8 	"os"
      9 	"strconv"
     10 	"strings"
     11 	"testing"
     12 )
     13 
     14 // xed binary from Intel sde-external-6.22.0-2014-03-06.
     15 const xedPath = "/Users/rsc/bin/xed"
     16 
     17 func testXedArch(t *testing.T, arch int, generate func(func([]byte))) {
     18 	if testing.Short() {
     19 		t.Skip("skipping xed test in short mode")
     20 	}
     21 	if _, err := os.Stat(xedPath); err != nil {
     22 		t.Skip(err)
     23 	}
     24 
     25 	testExtDis(t, "intel", arch, xed, generate, allowedMismatchXed)
     26 }
     27 
     28 func testXed32(t *testing.T, generate func(func([]byte))) {
     29 	testXedArch(t, 32, generate)
     30 }
     31 
     32 func testXed64(t *testing.T, generate func(func([]byte))) {
     33 	testXedArch(t, 64, generate)
     34 }
     35 
     36 func xed(ext *ExtDis) error {
     37 	b, err := ext.Run(xedPath, fmt.Sprintf("-%d", ext.Arch), "-n", "1G", "-ir", ext.File.Name())
     38 	if err != nil {
     39 		return err
     40 	}
     41 
     42 	nmatch := 0
     43 	next := uint32(start)
     44 	var (
     45 		addr   uint32
     46 		encbuf [32]byte
     47 		enc    []byte
     48 		text   string
     49 	)
     50 
     51 	var xedEnd = []byte("# end of text section")
     52 	var xedEnd1 = []byte("# Errors")
     53 
     54 	eof := false
     55 	for {
     56 		line, err := b.ReadSlice('\n')
     57 		if err != nil {
     58 			if err == io.EOF {
     59 				break
     60 			}
     61 			return fmt.Errorf("reading objdump output: %v", err)
     62 		}
     63 		if debug {
     64 			os.Stdout.Write(line)
     65 		}
     66 		if bytes.HasPrefix(line, xedEnd) || bytes.HasPrefix(line, xedEnd1) {
     67 			eof = true
     68 		}
     69 		if eof {
     70 			continue
     71 		}
     72 		nmatch++
     73 		addr, enc, text = parseLineXed(line, encbuf[:0])
     74 		if addr > next {
     75 			return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
     76 		}
     77 		if addr < next {
     78 			continue
     79 		}
     80 		switch text {
     81 		case "repz":
     82 			text = "rep"
     83 		case "repnz":
     84 			text = "repn"
     85 		default:
     86 			text = strings.Replace(text, "repz ", "rep ", -1)
     87 			text = strings.Replace(text, "repnz ", "repn ", -1)
     88 		}
     89 		if m := pcrelw.FindStringSubmatch(text); m != nil {
     90 			targ, _ := strconv.ParseUint(m[2], 16, 64)
     91 			text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
     92 		}
     93 		if m := pcrel.FindStringSubmatch(text); m != nil {
     94 			targ, _ := strconv.ParseUint(m[2], 16, 64)
     95 			text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
     96 		}
     97 		ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
     98 		encbuf = [32]byte{}
     99 		enc = nil
    100 		next += 32
    101 	}
    102 	if next != start+uint32(ext.Size) {
    103 		return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
    104 	}
    105 	if err := ext.Wait(); err != nil {
    106 		return fmt.Errorf("exec: %v", err)
    107 	}
    108 
    109 	return nil
    110 }
    111 
    112 var (
    113 	xedInRaw    = []byte("In raw...")
    114 	xedDots     = []byte("...")
    115 	xdis        = []byte("XDIS ")
    116 	xedError    = []byte("ERROR: ")
    117 	xedNoDecode = []byte("Could not decode at offset: 0x")
    118 )
    119 
    120 func parseLineXed(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
    121 	oline := line
    122 	if bytes.HasPrefix(line, xedInRaw) || bytes.HasPrefix(line, xedDots) {
    123 		return 0, nil, ""
    124 	}
    125 	if bytes.HasPrefix(line, xedError) {
    126 		i := bytes.IndexByte(line[len(xedError):], ' ')
    127 		if i < 0 {
    128 			log.Fatalf("cannot parse error: %q", oline)
    129 		}
    130 		errstr := string(line[len(xedError):])
    131 		i = bytes.Index(line, xedNoDecode)
    132 		if i < 0 {
    133 			log.Fatalf("cannot parse error: %q", oline)
    134 		}
    135 		i += len(xedNoDecode)
    136 		j := bytes.IndexByte(line[i:], ' ')
    137 		if j < 0 {
    138 			log.Fatalf("cannot parse error: %q", oline)
    139 		}
    140 		x, err := strconv.ParseUint(string(trimSpace(line[i:i+j])), 16, 32)
    141 		if err != nil {
    142 			log.Fatalf("cannot parse disassembly: %q", oline)
    143 		}
    144 		addr = uint32(x)
    145 		return addr, nil, errstr
    146 	}
    147 
    148 	if !bytes.HasPrefix(line, xdis) {
    149 		log.Fatalf("cannot parse disassembly: %q", oline)
    150 	}
    151 
    152 	i := bytes.IndexByte(line, ':')
    153 	if i < 0 {
    154 		log.Fatalf("cannot parse disassembly: %q", oline)
    155 	}
    156 	x, err := strconv.ParseUint(string(trimSpace(line[len(xdis):i])), 16, 32)
    157 	if err != nil {
    158 		log.Fatalf("cannot parse disassembly: %q", oline)
    159 	}
    160 	addr = uint32(x)
    161 
    162 	// spaces
    163 	i++
    164 	for i < len(line) && line[i] == ' ' {
    165 		i++
    166 	}
    167 	// instruction class, spaces
    168 	for i < len(line) && line[i] != ' ' {
    169 		i++
    170 	}
    171 	for i < len(line) && line[i] == ' ' {
    172 		i++
    173 	}
    174 	// instruction set, spaces
    175 	for i < len(line) && line[i] != ' ' {
    176 		i++
    177 	}
    178 	for i < len(line) && line[i] == ' ' {
    179 		i++
    180 	}
    181 
    182 	// hex
    183 	hexStart := i
    184 	for i < len(line) && line[i] != ' ' {
    185 		i++
    186 	}
    187 	hexEnd := i
    188 	for i < len(line) && line[i] == ' ' {
    189 		i++
    190 	}
    191 
    192 	// text
    193 	textStart := i
    194 	for i < len(line) && line[i] != '\n' {
    195 		i++
    196 	}
    197 	textEnd := i
    198 
    199 	enc, ok := parseHex(line[hexStart:hexEnd], encstart)
    200 	if !ok {
    201 		log.Fatalf("cannot parse disassembly: %q", oline)
    202 	}
    203 
    204 	return addr, enc, string(fixSpace(line[textStart:textEnd]))
    205 }
    206