proc,vendor: show global variables in disassembly

updates vendored version of x86asm, adds a symbol lookup function to
pass to the disassembler.

This will show global symbol names in the disassembly like go tool
objdump does.
This commit is contained in:
aarzilli 2017-10-26 13:37:19 +02:00 committed by Derek Parker
parent 84ce278352
commit ec8dc3a10d
30 changed files with 4571 additions and 35 deletions

2
glide.lock generated

@ -24,7 +24,7 @@ imports:
- name: github.com/spf13/pflag
version: 9e4c21054fa135711121b557932b1887f2405d92
- name: golang.org/x/arch
version: 58ea1a195b1a354bcd572b7ef6bbbd264dc63732
version: 077ac972c2e48fdb75168a5ba36a387f0c72d8aa
subpackages:
- x86/x86asm
- name: golang.org/x/sys

@ -17,7 +17,7 @@ import:
- package: github.com/spf13/pflag
version: 9e4c21054fa135711121b557932b1887f2405d92
- package: golang.org/x/arch
version: 58ea1a195b1a354bcd572b7ef6bbbd264dc63732
version: 077ac972c2e48fdb75168a5ba36a387f0c72d8aa
subpackages:
- x86/x86asm
- package: golang.org/x/sys

@ -38,10 +38,10 @@ type BinaryInfo struct {
loclist loclistReader
compileUnits []*compileUnit
types map[string]dwarf.Offset
packageVars map[string]dwarf.Offset
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
gStructOffset uint64
// Functions is a list of all DW_TAG_subprogram entries in debug_info.
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
Functions []Function
// Sources is a list of all source files found in debug_line.
Sources []string
@ -149,6 +149,15 @@ type constantValue struct {
singleBit bool
}
// packageVar represents a package-level variable (or a C global variable).
// If a global variable does not have an address (for example it's stored in
// a register, or non-contiguously) addr will be 0.
type packageVar struct {
name string
offset dwarf.Offset
addr uint64
}
type loclistReader struct {
data []byte
cur int

@ -1,5 +1,7 @@
package proc
import "sort"
type AsmInstruction struct {
Loc Location
DestLoc *Location
@ -14,6 +16,7 @@ type AssemblyFlavour int
const (
GNUFlavour = AssemblyFlavour(iota)
IntelFlavour
GoFlavour
)
// Disassemble disassembles target memory between startPC and endPC, marking
@ -83,3 +86,31 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
}
return r, nil
}
// Looks up symbol (either functions or global variables) at address addr.
// Used by disassembly formatter.
func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
fn := bi.PCToFunc(addr)
if fn != nil {
if fn.Entry == addr {
// only report the function name if it's the exact address because it's
// easier to read the absolute address than function_name+offset.
return fn.Name, fn.Entry
}
return "", 0
}
i := sort.Search(len(bi.packageVars), func(i int) bool {
return bi.packageVars[i].addr >= addr
})
if i >= len(bi.packageVars) {
return "", 0
}
if bi.packageVars[i].addr > addr {
// report previous variable + offset if i-th variable starts after addr
i--
}
if i > 0 {
return bi.packageVars[i].name, bi.packageVars[i].addr
}
return "", 0
}

@ -34,7 +34,7 @@ func patchPCRel(pc uint64, inst *x86asm.Inst) {
}
}
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
if inst.Inst == nil {
return "?"
}
@ -43,15 +43,13 @@ func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
switch flavour {
case GNUFlavour:
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
case GoFlavour:
text = x86asm.GoSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
case IntelFlavour:
fallthrough
default:
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
}
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
text += " " + inst.DestLoc.Fn.Name
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
}
return text

@ -3455,3 +3455,21 @@ func TestIssue1145(t *testing.T) {
}
})
}
func TestDisassembleGlobalVars(t *testing.T) {
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
mainfn := p.BinInfo().LookupFunc["main.main"]
text, err := proc.Disassemble(p, nil, mainfn.Entry, mainfn.End)
assertNoError(err, t, "Disassemble")
found := false
for i := range text {
if strings.Index(text[i].Text(proc.IntelFlavour, p.BinInfo()), "main.v") > 0 {
found = true
break
}
}
if !found {
t.Fatalf("could not find main.v reference in disassembly")
}
})
}

@ -3,6 +3,7 @@ package proc
import (
"bytes"
"debug/dwarf"
"encoding/binary"
"errors"
"fmt"
"go/ast"
@ -18,6 +19,7 @@ import (
"github.com/derekparker/delve/pkg/dwarf/godwarf"
"github.com/derekparker/delve/pkg/dwarf/line"
"github.com/derekparker/delve/pkg/dwarf/op"
"github.com/derekparker/delve/pkg/dwarf/reader"
)
@ -169,12 +171,18 @@ func (v compileUnitsByLowpc) Len() int { return len(v) }
func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].LowPC < v[j].LowPC }
func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
type packageVarsByAddr []packageVar
func (v packageVarsByAddr) Len() int { return len(v) }
func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr }
func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
bi.types = make(map[string]dwarf.Offset)
bi.packageVars = make(map[string]dwarf.Offset)
bi.packageVars = []packageVar{}
bi.Functions = []Function{}
bi.compileUnits = []*compileUnit{}
bi.consts = make(map[dwarf.Offset]*constantType)
@ -226,7 +234,13 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
if !cu.isgo {
n = "C." + n
}
bi.packageVars[n] = entry.Offset
var addr uint64
if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok {
if len(loc) == bi.Arch.PtrSize()+1 && op.Opcode(loc[0]) == op.DW_OP_addr {
addr = binary.LittleEndian.Uint64(loc[1:])
}
}
bi.packageVars = append(bi.packageVars, packageVar{n, entry.Offset, addr})
}
case dwarf.TagConstant:
@ -271,6 +285,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
}
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
sort.Sort(packageVarsByAddr(bi.packageVars))
bi.LookupFunc = make(map[string]*Function)
for i := range bi.Functions {

@ -653,10 +653,10 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
}
func (scope *EvalScope) findGlobal(name string) (*Variable, error) {
for n, off := range scope.BinInfo.packageVars {
if n == name || strings.HasSuffix(n, "/"+name) {
for _, pkgvar := range scope.BinInfo.packageVars {
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
reader := scope.DwarfReader()
reader.Seek(off)
reader.Seek(pkgvar.offset)
entry, err := reader.Next()
if err != nil {
return nil, err

@ -944,7 +944,7 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
disass := make(api.AsmInstructions, len(insts))
for i := range insts {
disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour)))
disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour), d.target.BinInfo()))
}
return disass, nil

3
vendor/golang.org/x/arch/AUTHORS generated vendored Normal file

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at https://tip.golang.org/AUTHORS.

31
vendor/golang.org/x/arch/CONTRIBUTING.md generated vendored Normal file

@ -0,0 +1,31 @@
# Contributing to Go
Go is an open source project.
It is the work of hundreds of contributors. We appreciate your help!
## Filing issues
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
## Contributing code
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
before sending patches.
**We do not accept GitHub pull requests**
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
Unless otherwise noted, the Go source files are distributed under
the BSD-style license found in the LICENSE file.

3
vendor/golang.org/x/arch/CONTRIBUTORS generated vendored Normal file

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at https://tip.golang.org/CONTRIBUTORS.

6
vendor/golang.org/x/arch/README.md generated vendored Normal file

@ -0,0 +1,6 @@
# arch
This repository holds machine architecture information used by the Go toolchain.
The parts needed in the main Go repository are copied in.
This repository requires Go 1.5+ with the vendor experiment enabled.

1
vendor/golang.org/x/arch/codereview.cfg generated vendored Normal file

@ -0,0 +1 @@
issuerepo: golang/go

3
vendor/golang.org/x/arch/x86/x86asm/Makefile generated vendored Normal file

@ -0,0 +1,3 @@
tables.go: ../x86map/map.go ../x86.csv
go run ../x86map/map.go -fmt=decoder ../x86.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go

71
vendor/golang.org/x/arch/x86/x86asm/decode_test.go generated vendored Normal file

@ -0,0 +1,71 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"encoding/hex"
"io/ioutil"
"strconv"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 4)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
mode, err := strconv.Atoi(f[1])
if err != nil {
t.Errorf("invalid mode %q in: %s", f[1], line)
continue
}
syntax, asm := f[2], f[3]
inst, err := Decode(code, mode)
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst, 0, nil)
case "intel":
out = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
out = GoSyntax(inst, 0, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
if out != asm || inst.Len != size {
t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size)
}
}
}

810
vendor/golang.org/x/arch/x86/x86asm/ext_test.go generated vendored Normal file

@ -0,0 +1,810 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
package x86asm
import (
"bufio"
"bytes"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var (
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint32
enc [32]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch int
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and be run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch int,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
) {
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, arch, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text || inst.Len != dec.nenc {
suffix := ""
if allowedMismatch(text, size, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
const start = 0x8000 // start address of text
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "x86map")
if err != nil {
return
}
file = f.Name()
f.Seek(start, 0)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if len(x) > 16 {
x = x[:16]
}
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
}
w.Write(x)
w.Write(pops[len(x):])
size += len(pops)
})
return file, f, size, nil
}
// 0x5F is a single-byte pop instruction.
// We pad the bytes we want decoded with enough 0x5Fs
// that no matter what state the instruction stream is in
// after reading our bytes, the pops will get us back to
// a forced instruction boundary.
var pops = []byte{
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
return append(enc[:len(enc):len(enc)], pops...)
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
// If printTests is set, we record the coverage value
// before and after, and we write out the inputs for which
// coverage went up, in the format expected in testdata/decode.text.
// This produces a fairly small set of test cases that exercise nearly
// all the code.
var cover float64
if *printTests {
cover -= coverage()
}
inst, err := decode1(src, mode, syntax == "gnu")
if err != nil {
text = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
text = GNUSyntax(inst, 0, nil)
case "intel":
text = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
text = GoSyntax(inst, 0, nil)
default:
text = "error: unknown syntax " + syntax
}
}
if *printTests {
cover += coverage()
if cover > 0 {
max := len(src)
if max > 16 && inst.Len <= 16 {
max = 16
}
fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
}
}
return
}
// coverage returns a floating point number denoting the
// test coverage until now. The number increases when new code paths are exercised,
// both in the Go program and in the decoder byte code.
func coverage() float64 {
/*
testing.Coverage is not in the main distribution.
The implementation, which must go in package testing, is:
// Coverage reports the current code coverage as a fraction in the range [0, 1].
func Coverage() float64 {
var n, d int64
for _, counters := range cover.Counters {
for _, c := range counters {
if c > 0 {
n++
}
d++
}
}
if d == 0 {
return 0
}
return float64(n) / float64(d)
}
*/
var f float64
// f += testing.Coverage()
f += decodeCoverage()
return f
}
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// isPrefix reports whether text is the name of an instruction prefix.
func isPrefix(text string) bool {
return prefixByte[text] > 0
}
// prefixByte maps instruction prefix text to actual prefix byte values.
var prefixByte = map[string]byte{
"es": 0x26,
"cs": 0x2e,
"ss": 0x36,
"ds": 0x3e,
"fs": 0x64,
"gs": 0x65,
"data16": 0x66,
"addr16": 0x67,
"lock": 0xf0,
"repn": 0xf2,
"repne": 0xf2,
"rep": 0xf3,
"repe": 0xf3,
"xacquire": 0xf2,
"xrelease": 0xf3,
"bnd": 0xf2,
"addr32": 0x66,
"data32": 0x67,
}
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for s := s; s != ""; {
if strings.HasPrefix(s, prefix) {
return true
}
i := strings.Index(s, " ")
if i < 0 {
break
}
s = s[i+1:]
}
}
return false
}
// contains reports whether the text s contains any of the given substrings.
func contains(s string, substrings ...string) bool {
for _, sub := range substrings {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool signals whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = trimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
j += 2
}
return raw, true
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'A': 10,
'B': 11,
'C': 12,
'D': 13,
'E': 14,
'F': 15,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = trimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// trimSpace trims leading and trailing space from s, returning a subslice of s.
func trimSpace(s []byte) []byte {
j := len(s)
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
j--
}
i := 0
for i < j && (s[i] == ' ' || s[i] == '\t') {
i++
}
return s[i:j]
}
// pcrel and pcrelw match instructions using relative addressing mode.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`)
)
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/decode.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
// manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes.
// The relative ordering of the prefixes within the combinations varies deterministically.
func manyPrefixes(try func([]byte)) {
var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
var enc []byte
for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
enc = enc[:0]
for j, p := range prefixBytes {
if i&(1<<uint(j)) != 0 {
enc = append(enc, p)
}
}
if len(enc) > 0 {
k := i % len(enc)
enc[0], enc[k] = enc[k], enc[0]
}
try(enc)
}
}
// basicPrefixes geneartes 8 different possible prefix cases: no prefix
// and then one each of seven different prefix bytes.
func basicPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
try([]byte{b})
}
}
func rexPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
try([]byte{b})
}
}
// concat takes two generators and returns a generator for the
// cross product of the two, concatenating the results from each.
func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
try(append(enc1[:len(enc1):len(enc1)], enc2...))
})
})
}
}
// concat3 takes three generators and returns a generator for the
// cross product of the three, concatenating the results from each.
func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...))
})
})
})
}
}
// concat4 takes four generators and returns a generator for the
// cross product of the four, concatenating the results from each.
func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
gen4(func(enc4 []byte) {
try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...))
})
})
})
})
}
}
// filter generates the sequences from gen that satisfy ok.
func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
return func(try func([]byte)) {
gen(func(enc []byte) {
if ok(enc) {
try(enc)
}
})
}
}
// enum8bit generates all possible 1-byte sequences, followed by distinctive padding.
func enum8bit(try func([]byte)) {
for i := 0; i < 1<<8; i++ {
try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum8bit generates all possible 2-byte sequences, followed by distinctive padding.
func enum16bit(try func([]byte)) {
for i := 0; i < 1<<16; i++ {
try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum24bit generates all possible 3-byte sequences, followed by distinctive padding.
func enum24bit(try func([]byte)) {
for i := 0; i < 1<<24; i++ {
try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enumModRM generates all possible modrm bytes and, for modrm values that indicate
// a following sib byte, all possible modrm, sib combinations.
func enumModRM(try func([]byte)) {
for i := 0; i < 256; i++ {
if (i>>3)&07 == 04 && i>>6 != 3 { // has sib
for j := 0; j < 256; j++ {
try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
} else {
try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
}
}
// fixed generates the single case b.
// It's mainly useful to prepare an argument for concat or concat3.
func fixed(b ...byte) func(func([]byte)) {
return func(try func([]byte)) {
try(b)
}
}
// testBasic runs the given test function with cases all using opcode as the initial opcode bytes.
// It runs three phases:
//
// First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values.
// If in -short mode, that's all.
//
// Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values.
// If not in -long mode, that's all. This phase and the next run in parallel with other tests
// (using t.Parallel).
//
// Finally, opcode followed by all possible 3-byte values. The test can take a very long time
// and prints progress messages to package log.
func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
if testing.Short() {
return
}
t.Parallel()
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, concat(fixed(op1...), enum16bit))
}
}
func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX))
if testing.Short() {
return
}
t.Parallel()
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
}
}
// testPrefix runs the given test function for all many prefix possibilities
// followed by all possible 1-byte sequences.
//
// If in -long mode, it then runs a test of all the prefix possibilities followed
// by all possible 2-byte sequences.
func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, concat(manyPrefixes, enum8bit))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
}
}
func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX))
}
}
func caller(skip int) string {
pc, _, _, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
name := "?"
if f != nil {
name = f.Name()
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
}
return name
}
func isValidREX(x []byte) bool {
i := 0
for i < len(x) && isPrefixByte(x[i]) {
i++
}
if i < len(x) && Prefix(x[i]).IsREX() {
i++
if i < len(x) {
return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
}
}
return true
}
func isPrefixByte(b byte) bool {
switch b {
case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
return true
}
return false
}

68
vendor/golang.org/x/arch/x86/x86asm/format_test.go generated vendored Normal file

@ -0,0 +1,68 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"encoding/hex"
"testing"
)
func testFormattingSymname(addr uint64) (string, uint64) {
switch addr {
case 0x424080:
return "runtime.printint", 0x424080
case 0x4c8068:
return "main.A", 0x4c8068
}
return "", 0
}
func TestFormatting(t *testing.T) {
testCases := []struct {
PC uint64
bytes string
goSyntax, intelSyntax, gnuSyntax string
}{
{0x4816b2, "0f8677010000",
"JBE 0x48182f",
"jbe 0x48182f",
"jbe 0x48182f"},
{0x45065b, "488b442408",
"MOVQ 0x8(SP), AX",
"mov rax, qword ptr [rsp+0x8]",
"mov 0x8(%rsp),%rax"},
{0x450678, "488b05e9790700",
"MOVQ main.A(SB), AX",
"mov rax, qword ptr [main.A]",
"mov main.A,%rax"},
{0x450664, "e8173afdff",
"CALL runtime.printint(SB)",
"call runtime.printint",
"callq runtime.printint"},
{0x45069b, "488d0575d90100",
"LEAQ 0x1d975(IP), AX",
"lea rax, ptr [rip+0x1d975]",
"lea 0x1d975(%rip),%rax"},
}
for _, testCase := range testCases {
t.Logf("%#x %s %s", testCase.PC, testCase.bytes, testCase.goSyntax)
bs, _ := hex.DecodeString(testCase.bytes)
inst, err := Decode(bs, 64)
if err != nil {
t.Errorf("decode error %v", err)
}
if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax {
t.Errorf("GoSyntax: %q", out)
}
if out := IntelSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.intelSyntax {
t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax)
}
if out := GNUSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.gnuSyntax {
t.Errorf("GNUSyntax: %q expected: %q", out, testCase.gnuSyntax)
}
}
}

@ -11,12 +11,16 @@ import (
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This general form is often called ``AT&T syntax'' as a reference to AT&T System V Unix.
func GNUSyntax(inst Inst) string {
func GNUSyntax(inst Inst, pc uint64, symname SymLookup) string {
// Rewrite instruction to mimic GNU peculiarities.
// Note that inst has been passed by value and contains
// no pointers, so any changes we make here are local
// and will not propagate back out to the caller.
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
// Adjust opcode [sic].
switch inst.Op {
case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
@ -403,7 +407,7 @@ SuffixLoop:
if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
continue
}
args = append(args, gnuArg(&inst, a, &usedPrefixes))
args = append(args, gnuArg(&inst, pc, symname, a, &usedPrefixes))
}
// The default is to print the arguments in reverse Intel order.
@ -513,7 +517,7 @@ SuffixLoop:
// gnuArg returns the GNU syntax for the argument x from the instruction inst.
// If *usedPrefixes is false and x is a Mem, then the formatting
// includes any segment prefixes and sets *usedPrefixes to true.
func gnuArg(inst *Inst, x Arg, usedPrefixes *bool) string {
func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool) string {
if x == nil {
return "<nil>"
}
@ -535,6 +539,13 @@ func gnuArg(inst *Inst, x Arg, usedPrefixes *bool) string {
}
return gccRegName[x]
case Mem:
if s, disp := memArgToSymbol(x, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s", s, suffix)
}
seg := ""
var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool
switch x.Segment {
@ -644,8 +655,25 @@ func gnuArg(inst *Inst, x Arg, usedPrefixes *bool) string {
}
return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale)
case Rel:
return fmt.Sprintf(".%+#x", int32(x))
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(x))
} else {
addr := pc + uint64(inst.Len) + uint64(x)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(x)
return fmt.Sprintf("%#x", addr)
}
}
case Imm:
if s, base := symname(uint64(x)); s != "" {
suffix := ""
if uint64(x) != base {
suffix = fmt.Sprintf("%+d", uint64(x)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(x))
}

20
vendor/golang.org/x/arch/x86/x86asm/inst_test.go generated vendored Normal file

@ -0,0 +1,20 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestRegString(t *testing.T) {
for r := Reg(1); r <= regMax; r++ {
if regNames[r] == "" {
t.Errorf("regNames[%d] is missing", int(r))
} else if s := r.String(); strings.Contains(s, "Reg(") {
t.Errorf("Reg(%d).String() = %s, want proper name", int(r), s)
}
}
}

@ -10,7 +10,11 @@ import (
)
// IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
func IntelSyntax(inst Inst) string {
func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var iargs []Arg
for _, a := range inst.Args {
if a == nil {
@ -256,7 +260,7 @@ func IntelSyntax(inst Inst) string {
if a == nil {
break
}
args = append(args, intelArg(&inst, a))
args = append(args, intelArg(&inst, pc, symname, a))
}
var op string
@ -334,9 +338,16 @@ func IntelSyntax(inst Inst) string {
return prefix + op
}
func intelArg(inst *Inst, arg Arg) string {
func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
switch a := arg.(type) {
case Imm:
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
suffix = fmt.Sprintf("%+d", uint64(a)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("%#x", uint32(a))
}
@ -417,18 +428,25 @@ func intelArg(inst *Inst, arg Arg) string {
}
prefix += "ptr "
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return prefix + fmt.Sprintf("[%s%s]", s, suffix)
}
if a.Segment != 0 {
prefix += strings.ToLower(a.Segment.String()) + ":"
}
prefix += "["
if a.Base != 0 {
prefix += intelArg(inst, a.Base)
prefix += intelArg(inst, pc, symname, a.Base)
}
if a.Scale != 0 && a.Index != 0 {
if a.Base != 0 {
prefix += "+"
}
prefix += fmt.Sprintf("%s*%d", intelArg(inst, a.Index), a.Scale)
prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
}
if a.Disp != 0 {
if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
@ -440,7 +458,17 @@ func intelArg(inst *Inst, arg Arg) string {
prefix += "]"
return prefix
case Rel:
return fmt.Sprintf(".%+#x", int64(a))
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(a))
} else {
addr := pc + uint64(inst.Len) + uint64(a)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(a)
return fmt.Sprintf("%#x", addr)
}
}
case Reg:
if int(a) < len(intelReg) && intelReg[a] != "" {
switch inst.Op {

385
vendor/golang.org/x/arch/x86/x86asm/objdump_test.go generated vendored Normal file

@ -0,0 +1,385 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestObjdump32Manual(t *testing.T) { testObjdump32(t, hexCases(t, objdumpManualTests)) }
func TestObjdump32Testdata(t *testing.T) { testObjdump32(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump32ModRM(t *testing.T) { testObjdump32(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump32OneByte(t *testing.T) { testBasic(t, testObjdump32) }
func TestObjdump320F(t *testing.T) { testBasic(t, testObjdump32, 0x0F) }
func TestObjdump320F38(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x38) }
func TestObjdump320F3A(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x3A) }
func TestObjdump32Prefix(t *testing.T) { testPrefix(t, testObjdump32) }
func TestObjdump64Manual(t *testing.T) { testObjdump64(t, hexCases(t, objdumpManualTests)) }
func TestObjdump64Testdata(t *testing.T) { testObjdump64(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump64ModRM(t *testing.T) { testObjdump64(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump64OneByte(t *testing.T) { testBasic(t, testObjdump64) }
func TestObjdump640F(t *testing.T) { testBasic(t, testObjdump64, 0x0F) }
func TestObjdump640F38(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump640F3A(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64Prefix(t *testing.T) { testPrefix(t, testObjdump64) }
func TestObjdump64REXTestdata(t *testing.T) {
testObjdump64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestObjdump64REXModRM(t *testing.T) {
testObjdump64(t, concat3(basicPrefixes, rexPrefixes, enumModRM))
}
func TestObjdump64REXOneByte(t *testing.T) { testBasicREX(t, testObjdump64) }
func TestObjdump64REX0F(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F) }
func TestObjdump64REX0F38(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump64REX0F3A(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64REXPrefix(t *testing.T) { testPrefixREX(t, testObjdump64) }
// objdumpManualTests holds test cases that will be run by TestObjdumpManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=ObjdumpManual, particularly with tracing enabled.
var objdumpManualTests = `
4883FE017413
488DFC2500000000
488D3D00000000
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
if size == 15 && dec.nenc == 15 && contains(text, "truncated") && contains(dec.text, "(bad)") {
return true
}
if i := strings.LastIndex(dec.text, " "); isPrefix(dec.text[i+1:]) && size == 1 && isPrefix(text) {
return true
}
if size == dec.nenc && contains(dec.text, "movupd") && contains(dec.text, "data32") {
s := strings.Replace(dec.text, "data32 ", "", -1)
if text == s {
return true
}
}
// Simplify our invalid instruction text.
if text == "error: unrecognized instruction" {
text = "BAD"
}
// Invalid instructions for which libopcodes prints %? register.
// FF E8 11 22 33 44:
// Invalid instructions for which libopcodes prints "internal disassembler error".
// Invalid instructions for which libopcodes prints 8087 only (e.g., DB E0)
// or prints 287 only (e.g., DB E4).
if contains(dec.text, "%?", "<internal disassembler error>", "(8087 only)", "(287 only)") {
dec.text = "(bad)"
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: libopcodes says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps libopcodes is reporting an older encoding.
i := bytes.IndexByte(dec.enc[:], 0x0F)
if contains(dec.text, "nop") && i >= 0 && i+2 < len(dec.enc) && dec.enc[i+1]&^7 == 0x18 && (dec.enc[i+1] != 0x1F || (dec.enc[i+2]>>3)&7 != 0) {
dec.text = "(bad)"
}
// Any invalid instruction.
if text == "BAD" && contains(dec.text, "(bad)") {
return true
}
// Instructions libopcodes knows but we do not (e.g., 0F 19 11).
if (text == "BAD" || size == 1 && isPrefix(text)) && hasPrefix(dec.text, unsupported...) {
return true
}
// Instructions we know but libopcodes does not (e.g., 0F D0 11).
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && hasPrefix(text, libopcodesUnsupported...) {
return true
}
// Libopcodes rejects F2 90 as NOP. Not sure why.
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && inst.Opcode>>24 == 0x90 && countPrefix(inst, 0xF2) > 0 {
return true
}
// 0F 20 11, 0F 21 11, 0F 22 11, 0F 23 11, 0F 24 11:
// Moves into and out of some control registers seem to be unsupported by libopcodes.
// TODO(rsc): Are they invalid somehow?
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && contains(text, "%cr", "%db", "%tr") {
return true
}
if contains(dec.text, "fwait") && dec.nenc == 1 && dec.enc[0] != 0x9B {
return true
}
// 9B D9 11: libopcodes reports FSTSW instead of FWAIT + FNSTSW.
// This is correct in that FSTSW is a pseudo-op for the pair, but it really
// is a pair of instructions: execution can stop between them.
// Our decoder chooses to separate them.
if (text == "fwait" || strings.HasSuffix(text, " fwait")) && dec.nenc >= len(strings.Fields(text)) && dec.enc[len(strings.Fields(text))-1] == 0x9B {
return true
}
// 0F 18 77 11:
// Invalid instructions for which libopcodes prints "nop/reserved".
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "nop/reserved") {
return true
}
// 0F C7 B0 11 22 33 44: libopcodes says vmptrld 0x44332211(%eax); we say rdrand %eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// DD C8: libopcodes says FNOP but the Intel manual is clear FNOP is only D9 D0.
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "fnop") && (dec.enc[0] != 0xD9 || dec.enc[1] != 0xD0) {
return true
}
// 66 90: libopcodes says xchg %ax,%ax; we say 'data16 nop'.
// The 16-bit swap will preserve the high bits of the register,
// so they are the same.
if contains(text, "nop") && contains(dec.text, "xchg %ax,%ax") {
return true
}
// If there are multiple prefixes, allow libopcodes to use an alternate name.
if size == 1 && dec.nenc == 1 && prefixByte[text] > 0 && prefixByte[text] == prefixByte[dec.text] {
return true
}
// 26 9B: libopcodes reports "fwait"/1, ignoring segment prefix.
// https://sourceware.org/bugzilla/show_bug.cgi?id=16891
// F0 82: Decode="lock"/1 but libopcodes="lock (bad)"/2.
if size == 1 && dec.nenc >= 1 && prefixByte[text] == dec.enc[0] && contains(dec.text, "(bad)", "fwait", "fnop") {
return true
}
// libopcodes interprets 660f801122 as taking a rel16 but
// truncating the address at 16 bits. Not sure what is correct.
if contains(text, ".+0x2211", ".+0x11") && contains(dec.text, " .-") {
return true
}
// 66 F3 0F D6 C5, 66 F2 0F D6 C0: libopcodes reports use of XMM register instead of MMX register,
// but only when the instruction has a 66 prefix. Maybe they know something we don't.
if countPrefix(inst, 0x66) > 0 && contains(dec.text, "movdq2q", "movq2dq") && !contains(dec.text, "%mm") {
return true
}
// 0F 01 F8, 0F 05, 0F 07: these are 64-bit instructions but libopcodes accepts them.
if (text == "BAD" || size == 1 && isPrefix(text)) && contains(dec.text, "swapgs", "syscall", "sysret", "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") {
return true
}
return false
}
// Instructions known to libopcodes (or xed) but not to us.
// Most of these come from supplementary manuals of one form or another.
var unsupported = strings.Fields(`
bndc
bndl
bndm
bnds
clac
clgi
femms
fldln
fldz
getsec
invlpga
kmov
montmul
pavg
pf2i
pfacc
pfadd
pfcmp
pfmax
pfmin
pfmul
pfna
pfpnac
pfrc
pfrs
pfsub
phadd
phsub
pi2f
pmulhr
prefetch
pswap
ptest
rdseed
sha1
sha256
skinit
stac
stgi
vadd
vand
vcmp
vcomis
vcvt
vcvt
vdiv
vhadd
vhsub
vld
vmax
vmcall
vmfunc
vmin
vmlaunch
vmload
vmmcall
vmov
vmov
vmov
vmptrld
vmptrst
vmread
vmresume
vmrun
vmsave
vmul
vmwrite
vmxoff
vor
vpack
vpadd
vpand
vpavg
vpcmp
vpcmp
vpins
vpmadd
vpmax
vpmin
vpmul
vpmul
vpor
vpsad
vpshuf
vpsll
vpsra
vpsrad
vpsrl
vpsub
vpunp
vpxor
vrcp
vrsqrt
vshuf
vsqrt
vsub
vucomis
vunp
vxor
vzero
xcrypt
xsha1
xsha256
xstore-rng
insertq
extrq
vmclear
invvpid
adox
vmxon
invept
adcx
vmclear
prefetchwt1
enclu
encls
salc
fstpnce
fdisi8087_nop
fsetpm287_nop
feni8087_nop
syscall
sysret
`)
// Instructions known to us but not to libopcodes (at least in binutils 2.24).
var libopcodesUnsupported = strings.Fields(`
addsubps
aes
blend
cvttpd2dq
dpp
extract
haddps
hsubps
insert
invpcid
lddqu
movmsk
movnt
movq2dq
mps
pack
pblend
pclmul
pcmp
pext
phmin
pins
pmax
pmin
pmov
pmovmsk
pmul
popcnt
pslld
psllq
psllw
psrad
psraw
psrl
ptest
punpck
round
xrstor
xsavec
xsaves
comis
ucomis
movhps
movntps
rsqrt
rcpp
puncpck
bsf
movq2dq
cvttpd2dq
movq
hsubpd
movdqa
movhpd
addsubpd
movd
haddpd
cvtps2dq
bsr
cvtdq2ps
rdrand
maskmov
movq2dq
movlhps
movbe
movlpd
`)

313
vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go generated vendored Normal file

@ -0,0 +1,313 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// Apologies for the proprietary path, but we need objdump 2.24 + some committed patches that will land in 2.25.
const objdumpPath = "/Users/rsc/bin/objdump2"
func testObjdump32(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 32)
}
func testObjdump64(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 64)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch int) {
if testing.Short() {
t.Skip("skipping objdump test in short mode")
}
if _, err := os.Stat(objdumpPath); err != nil {
t.Skip(err)
}
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == 32 {
if err := writeELF32(ext.File, ext.Size); err != nil {
return err
}
} else {
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint32 = start
addr uint32
encbuf [32]byte
enc []byte
text string
)
flush := func() {
if addr == next {
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
text = strings.Replace(text, "0x0(", "(", -1)
text = strings.Replace(text, "%st(0)", "%st", -1)
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok := parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = trimSpace(line[i:])
if i := bytes.IndexByte(line, '#'); i >= 0 {
line = trimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF32 writes an ELF32 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF32(f *os.File, size int) error {
f.Seek(0, 0)
var hdr elf.Header32
var prog elf.Prog32
var sect elf.Section32
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header32{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
Type: 2,
Machine: uint16(elf.EM_386),
Version: 1,
Entry: start,
Phoff: uint32(off1),
Shoff: uint32(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog32{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint32(size),
Memsz: uint32(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section32{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint32(size),
Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section32{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint32(off2 + (off3-off2)*3),
Size: uint32(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
// writeELF64 writes an ELF64 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, 0)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_X86_64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}

119
vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go generated vendored Normal file

@ -0,0 +1,119 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"testing"
)
const plan9Path = "testdata/libmach8db"
func testPlan9Arch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping libmach test in short mode")
}
if _, err := os.Stat(plan9Path); err != nil {
t.Skip(err)
}
testExtDis(t, "plan9", arch, plan9, generate, allowedMismatchPlan9)
}
func testPlan932(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 32, generate)
}
func testPlan964(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 64, generate)
}
func plan9(ext *ExtDis) error {
flag := "-8"
if ext.Arch == 64 {
flag = "-6"
}
b, err := ext.Run(plan9Path, flag, ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading libmach8db output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
nmatch++
addr, enc, text = parseLinePlan9(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLinePlan9(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
i := bytes.IndexByte(line, ' ')
if i < 0 || line[0] != '0' || line[1] != 'x' {
log.Fatalf("cannot parse disassembly: %q", line)
}
j := bytes.IndexByte(line[i+1:], ' ')
if j < 0 {
log.Fatalf("cannot parse disassembly: %q", line)
}
j += i + 1
x, err := strconv.ParseUint(string(trimSpace(line[2:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", line)
}
addr = uint32(x)
enc, ok := parseHex(line[i+1:j], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", line)
}
return addr, enc, string(fixSpace(line[j+1:]))
}

@ -9,6 +9,8 @@ import (
"strings"
)
type SymLookup func(uint64) (string, uint64)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for expanding
@ -16,7 +18,7 @@ import (
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name and base
// address of the symbol containing the target, if any; otherwise it returns "", 0.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
@ -119,14 +121,12 @@ func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg
}
return fmt.Sprintf("$%#x", uint64(a))
case Mem:
if a.Segment == 0 && a.Disp != 0 && a.Base == 0 && (a.Index == 0 || a.Scale == 0) {
if s, base := symname(uint64(a.Disp)); s != "" {
suffix := ""
if uint64(a.Disp) != base {
suffix = fmt.Sprintf("%+d", uint64(a.Disp)-base)
}
return fmt.Sprintf("%s%s(SB)", s, suffix)
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s(SB)", s, suffix)
}
s := ""
if a.Segment != 0 {
@ -148,6 +148,25 @@ func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg
return arg.String()
}
func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) {
if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 {
return "", 0
}
var disp uint64
switch a.Base {
case IP, EIP, RIP:
disp = uint64(a.Disp + int64(pc) + int64(instrLen))
case 0:
disp = uint64(a.Disp)
default:
return "", 0
}
s, base := symname(disp)
return s, int64(disp) - int64(base)
}
var plan9Suffix = [maxOp + 1]bool{
ADC: true,
ADD: true,

54
vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go generated vendored Normal file

@ -0,0 +1,54 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestPlan932Manual(t *testing.T) { testPlan932(t, hexCases(t, plan9ManualTests)) }
func TestPlan932Testdata(t *testing.T) { testPlan932(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan932ModRM(t *testing.T) { testPlan932(t, concat(basicPrefixes, enumModRM)) }
func TestPlan932OneByte(t *testing.T) { testBasic(t, testPlan932) }
func TestPlan9320F(t *testing.T) { testBasic(t, testPlan932, 0x0F) }
func TestPlan9320F38(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x38) }
func TestPlan9320F3A(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x3A) }
func TestPlan932Prefix(t *testing.T) { testPrefix(t, testPlan932) }
func TestPlan964Manual(t *testing.T) { testPlan964(t, hexCases(t, plan9ManualTests)) }
func TestPlan964Testdata(t *testing.T) { testPlan964(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan964ModRM(t *testing.T) { testPlan964(t, concat(basicPrefixes, enumModRM)) }
func TestPlan964OneByte(t *testing.T) { testBasic(t, testPlan964) }
func TestPlan9640F(t *testing.T) { testBasic(t, testPlan964, 0x0F) }
func TestPlan9640F38(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x38) }
func TestPlan9640F3A(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964Prefix(t *testing.T) { testPrefix(t, testPlan964) }
func TestPlan964REXTestdata(t *testing.T) {
testPlan964(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestPlan964REXModRM(t *testing.T) { testPlan964(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) }
func TestPlan964REXOneByte(t *testing.T) { testBasicREX(t, testPlan964) }
func TestPlan964REX0F(t *testing.T) { testBasicREX(t, testPlan964, 0x0F) }
func TestPlan964REX0F38(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x38) }
func TestPlan964REX0F3A(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964REXPrefix(t *testing.T) { testPrefixREX(t, testPlan964) }
// plan9ManualTests holds test cases that will be run by TestPlan9Manual32 and TestPlan9Manual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Plan9Manual, particularly with tracing enabled.
var plan9ManualTests = `
`
// allowedMismatchPlan9 reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchPlan9(text string, size int, inst *Inst, dec ExtInst) bool {
return false
}
// Instructions known to us but not to plan9.
var plan9Unsupported = strings.Fields(`
`)

12
vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile generated vendored Normal file

@ -0,0 +1,12 @@
libmach8db: libmach8db.c
9c libmach8db.c && 9l -o libmach8db libmach8db.o; rm libmach8db.o
newdecode.txt:
cd ..; go test -cover -run 'Objdump.*32' -v -timeout 10h -printtests 2>&1 | tee log
cd ..; go test -cover -run 'Objdump.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
egrep ' (gnu|intel|plan9) ' ../log |sort >newdecode.txt

2075
vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c generated vendored Normal file

File diff suppressed because it is too large Load Diff

211
vendor/golang.org/x/arch/x86/x86asm/xed_test.go generated vendored Normal file

@ -0,0 +1,211 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestXed32Manual(t *testing.T) { testXed32(t, hexCases(t, xedManualTests)) }
func TestXed32Testdata(t *testing.T) { testXed32(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed32ModRM(t *testing.T) { testXed32(t, concat(basicPrefixes, enumModRM)) }
func TestXed32OneByte(t *testing.T) { testBasic(t, testXed32) }
func TestXed320F(t *testing.T) { testBasic(t, testXed32, 0x0F) }
func TestXed320F38(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x38) }
func TestXed320F3A(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x3A) }
func TestXed32Prefix(t *testing.T) { testPrefix(t, testXed32) }
func TestXed64Manual(t *testing.T) { testXed64(t, hexCases(t, xedManualTests)) }
func TestXed64Testdata(t *testing.T) { testXed64(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed64ModRM(t *testing.T) { testXed64(t, concat(basicPrefixes, enumModRM)) }
func TestXed64OneByte(t *testing.T) { testBasic(t, testXed64) }
func TestXed640F(t *testing.T) { testBasic(t, testXed64, 0x0F) }
func TestXed640F38(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x38) }
func TestXed640F3A(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x3A) }
func TestXed64Prefix(t *testing.T) { testPrefix(t, testXed64) }
func TestXed64REXTestdata(t *testing.T) {
testXed64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestXed64REXModRM(t *testing.T) { testXed64(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) }
func TestXed64REXOneByte(t *testing.T) { testBasicREX(t, testXed64) }
func TestXed64REX0F(t *testing.T) { testBasicREX(t, testXed64, 0x0F) }
func TestXed64REX0F38(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x38) }
func TestXed64REX0F3A(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x3A) }
func TestXed64REXPrefix(t *testing.T) { testPrefixREX(t, testXed64) }
// xedManualTests holds test cases that will be run by TestXedManual32 and TestXedManual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=XedManual, particularly with tracing enabled.
var xedManualTests = `
6690
`
// allowedMismatchXed reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchXed(text string, size int, inst *Inst, dec ExtInst) bool {
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "GENERAL_ERROR", "INSTR_TOO_LONG", "BAD_LOCK_PREFIX") {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX") && countExactPrefix(inst, PrefixLOCK|PrefixInvalid) > 0 {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX", "GENERAL_ERROR") && countExactPrefix(inst, PrefixLOCK|PrefixImplicit) > 0 {
return true
}
if text == "lock" && size == 1 && contains(dec.text, "BAD_LOCK_PREFIX") {
return true
}
// Instructions not known to us.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, unsupported...) {
return true
}
// Instructions not known to xed.
if contains(text, xedUnsupported...) && contains(dec.text, "ERROR") {
return true
}
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "shl ") && (inst.Opcode>>16)&0xEC38 == 0xC030 {
return true
}
// 82 11 22: xed says 'adc byte ptr [ecx], 0x22' but there is no justification in the manuals for that.
// C0 30 11: xed says 'shl byte ptr [eax], 0x11' but there is no justification in the manuals for that.
// F6 08 11: xed says 'test byte ptr [eax], 0x11' but there is no justification in the manuals for that.
if (contains(text, "error:") || isPrefix(text) && size == 1) && hasByte(dec.enc[:dec.nenc], 0x82, 0xC0, 0xC1, 0xD0, 0xD1, 0xD2, 0xD3, 0xF6, 0xF7) {
return true
}
// F3 11 22 and many others: xed allows and drops misused rep/repn prefix.
if (text == "rep" && dec.enc[0] == 0xF3 || (text == "repn" || text == "repne") && dec.enc[0] == 0xF2) && (!contains(dec.text, "ins", "outs", "movs", "lods", "cmps", "scas") || contains(dec.text, "xmm")) {
return true
}
// 0F C7 30: xed says vmptrld qword ptr [eax]; we say rdrand eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// F3 0F AE 00: we say 'rdfsbase dword ptr [eax]' but RDFSBASE needs a register.
// Also, this is a 64-bit only instruction.
// TODO(rsc): Fix to reject this encoding.
if contains(text, "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") && contains(dec.text, "ERROR") {
return true
}
// 0F 01 F8: we say swapgs but that's only valid in 64-bit mode.
// TODO(rsc): Fix.
if contains(text, "swapgs") {
return true
}
// 0F 24 11: 'mov ecx, tr2' except there is no TR2.
// Or maybe the MOV to TR registers doesn't use RMF.
if contains(text, "cr1", "cr5", "cr6", "cr7", "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7") && contains(dec.text, "ERROR") {
return true
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: xed says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps xed is reporting an older encoding.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "nop ") && (inst.Opcode>>8)&0xFFFF38 != 0x0F1F00 {
return true
}
// 66 0F AE 38: clflushopt but we only know clflush
if contains(text, "clflush") && contains(dec.text, "clflushopt") {
return true
}
// 0F 20 04 11: MOV SP, CR0 but has mod!=3 despite register argument.
// (This encoding ignores the mod bits.) The decoder sees the non-register
// mod and reads farther ahead to decode the memory reference that
// isn't really there, causing the size to be too large.
// TODO(rsc): Fix.
if text == dec.text && size > dec.nenc && contains(text, " cr", " dr", " tr") {
return true
}
// 0F AE E9: xed says lfence, which is wrong (only 0F AE E8 is lfence). And so on.
if contains(dec.text, "fence") && hasByte(dec.enc[:dec.nenc], 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF) {
return true
}
// DD C9, DF C9: xed says 'fxch st0, st1' but that instruction is D9 C9.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fxch ") && hasByte(dec.enc[:dec.nenc], 0xDD, 0xDF) {
return true
}
// DC D4: xed says 'fcom st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcom ") && hasByte(dec.enc[:dec.nenc], 0xD8, 0xDC) {
return true
}
// DE D4: xed says 'fcomp st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcomp ") && hasByte(dec.enc[:dec.nenc], 0xDC, 0xDE) {
return true
}
// DF D4: xed says 'fstp st4, st0' but that instruction is DD D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fstp ") && hasByte(dec.enc[:dec.nenc], 0xDF) {
return true
}
return false
}
func countExactPrefix(inst *Inst, target Prefix) int {
n := 0
for _, p := range inst.Prefix {
if p == target {
n++
}
}
return n
}
func hasByte(src []byte, target ...byte) bool {
for _, b := range target {
if bytes.IndexByte(src, b) >= 0 {
return true
}
}
return false
}
// Instructions known to us but not to xed.
var xedUnsupported = strings.Fields(`
xrstor
xsave
xsave
ud1
xgetbv
xsetbv
fxsave
fxrstor
clflush
lfence
mfence
sfence
rsqrtps
rcpps
emms
ldmxcsr
stmxcsr
movhpd
movnti
rdrand
movbe
movlpd
sysret
`)

205
vendor/golang.org/x/arch/x86/x86asm/xedext_test.go generated vendored Normal file

@ -0,0 +1,205 @@
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// xed binary from Intel sde-external-6.22.0-2014-03-06.
const xedPath = "/Users/rsc/bin/xed"
func testXedArch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping xed test in short mode")
}
if _, err := os.Stat(xedPath); err != nil {
t.Skip(err)
}
testExtDis(t, "intel", arch, xed, generate, allowedMismatchXed)
}
func testXed32(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 32, generate)
}
func testXed64(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 64, generate)
}
func xed(ext *ExtDis) error {
b, err := ext.Run(xedPath, fmt.Sprintf("-%d", ext.Arch), "-n", "1G", "-ir", ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
var xedEnd = []byte("# end of text section")
var xedEnd1 = []byte("# Errors")
eof := false
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
if bytes.HasPrefix(line, xedEnd) || bytes.HasPrefix(line, xedEnd1) {
eof = true
}
if eof {
continue
}
nmatch++
addr, enc, text = parseLineXed(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
xedInRaw = []byte("In raw...")
xedDots = []byte("...")
xdis = []byte("XDIS ")
xedError = []byte("ERROR: ")
xedNoDecode = []byte("Could not decode at offset: 0x")
)
func parseLineXed(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
if bytes.HasPrefix(line, xedInRaw) || bytes.HasPrefix(line, xedDots) {
return 0, nil, ""
}
if bytes.HasPrefix(line, xedError) {
i := bytes.IndexByte(line[len(xedError):], ' ')
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
errstr := string(line[len(xedError):])
i = bytes.Index(line, xedNoDecode)
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
i += len(xedNoDecode)
j := bytes.IndexByte(line[i:], ' ')
if j < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[i:i+j])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
return addr, nil, errstr
}
if !bytes.HasPrefix(line, xdis) {
log.Fatalf("cannot parse disassembly: %q", oline)
}
i := bytes.IndexByte(line, ':')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[len(xdis):i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
// spaces
i++
for i < len(line) && line[i] == ' ' {
i++
}
// instruction class, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// instruction set, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// hex
hexStart := i
for i < len(line) && line[i] != ' ' {
i++
}
hexEnd := i
for i < len(line) && line[i] == ' ' {
i++
}
// text
textStart := i
for i < len(line) && line[i] != '\n' {
i++
}
textEnd := i
enc, ok := parseHex(line[hexStart:hexEnd], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
return addr, enc, string(fixSpace(line[textStart:textEnd]))
}