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:
parent
84ce278352
commit
ec8dc3a10d
2
glide.lock
generated
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
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
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
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
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
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
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
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
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
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)
|
||||
}
|
||||
}
|
||||
}
|
36
vendor/golang.org/x/arch/x86/x86asm/gnu.go
generated
vendored
36
vendor/golang.org/x/arch/x86/x86asm/gnu.go
generated
vendored
@ -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
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)
|
||||
}
|
||||
}
|
||||
}
|
40
vendor/golang.org/x/arch/x86/x86asm/intel.go
generated
vendored
40
vendor/golang.org/x/arch/x86/x86asm/intel.go
generated
vendored
@ -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
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
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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, §)
|
||||
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, §) // 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, §) // .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, §)
|
||||
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
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:]))
|
||||
}
|
35
vendor/golang.org/x/arch/x86/x86asm/plan9x.go
generated
vendored
35
vendor/golang.org/x/arch/x86/x86asm/plan9x.go
generated
vendored
@ -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
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
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
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
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
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]))
|
||||
}
|
Loading…
Reference in New Issue
Block a user