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