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
|
- name: github.com/spf13/pflag
|
||||||
version: 9e4c21054fa135711121b557932b1887f2405d92
|
version: 9e4c21054fa135711121b557932b1887f2405d92
|
||||||
- name: golang.org/x/arch
|
- name: golang.org/x/arch
|
||||||
version: 58ea1a195b1a354bcd572b7ef6bbbd264dc63732
|
version: 077ac972c2e48fdb75168a5ba36a387f0c72d8aa
|
||||||
subpackages:
|
subpackages:
|
||||||
- x86/x86asm
|
- x86/x86asm
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
|
@ -17,7 +17,7 @@ import:
|
|||||||
- package: github.com/spf13/pflag
|
- package: github.com/spf13/pflag
|
||||||
version: 9e4c21054fa135711121b557932b1887f2405d92
|
version: 9e4c21054fa135711121b557932b1887f2405d92
|
||||||
- package: golang.org/x/arch
|
- package: golang.org/x/arch
|
||||||
version: 58ea1a195b1a354bcd572b7ef6bbbd264dc63732
|
version: 077ac972c2e48fdb75168a5ba36a387f0c72d8aa
|
||||||
subpackages:
|
subpackages:
|
||||||
- x86/x86asm
|
- x86/x86asm
|
||||||
- package: golang.org/x/sys
|
- package: golang.org/x/sys
|
||||||
|
@ -38,10 +38,10 @@ type BinaryInfo struct {
|
|||||||
loclist loclistReader
|
loclist loclistReader
|
||||||
compileUnits []*compileUnit
|
compileUnits []*compileUnit
|
||||||
types map[string]dwarf.Offset
|
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
|
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
|
Functions []Function
|
||||||
// Sources is a list of all source files found in debug_line.
|
// Sources is a list of all source files found in debug_line.
|
||||||
Sources []string
|
Sources []string
|
||||||
@ -149,6 +149,15 @@ type constantValue struct {
|
|||||||
singleBit bool
|
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 {
|
type loclistReader struct {
|
||||||
data []byte
|
data []byte
|
||||||
cur int
|
cur int
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
type AsmInstruction struct {
|
type AsmInstruction struct {
|
||||||
Loc Location
|
Loc Location
|
||||||
DestLoc *Location
|
DestLoc *Location
|
||||||
@ -14,6 +16,7 @@ type AssemblyFlavour int
|
|||||||
const (
|
const (
|
||||||
GNUFlavour = AssemblyFlavour(iota)
|
GNUFlavour = AssemblyFlavour(iota)
|
||||||
IntelFlavour
|
IntelFlavour
|
||||||
|
GoFlavour
|
||||||
)
|
)
|
||||||
|
|
||||||
// Disassemble disassembles target memory between startPC and endPC, marking
|
// Disassemble disassembles target memory between startPC and endPC, marking
|
||||||
@ -83,3 +86,31 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
|
|||||||
}
|
}
|
||||||
return r, nil
|
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 {
|
if inst.Inst == nil {
|
||||||
return "?"
|
return "?"
|
||||||
}
|
}
|
||||||
@ -43,15 +43,13 @@ func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
|
|||||||
|
|
||||||
switch flavour {
|
switch flavour {
|
||||||
case GNUFlavour:
|
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:
|
case IntelFlavour:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
|
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
|
||||||
}
|
|
||||||
|
|
||||||
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
|
|
||||||
text += " " + inst.DestLoc.Fn.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
@ -18,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||||
|
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"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) 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] }
|
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) {
|
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup) {
|
||||||
if wg != nil {
|
if wg != nil {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
}
|
}
|
||||||
bi.types = make(map[string]dwarf.Offset)
|
bi.types = make(map[string]dwarf.Offset)
|
||||||
bi.packageVars = make(map[string]dwarf.Offset)
|
bi.packageVars = []packageVar{}
|
||||||
bi.Functions = []Function{}
|
bi.Functions = []Function{}
|
||||||
bi.compileUnits = []*compileUnit{}
|
bi.compileUnits = []*compileUnit{}
|
||||||
bi.consts = make(map[dwarf.Offset]*constantType)
|
bi.consts = make(map[dwarf.Offset]*constantType)
|
||||||
@ -226,7 +234,13 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
|||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
n = "C." + n
|
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:
|
case dwarf.TagConstant:
|
||||||
@ -271,6 +285,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
|||||||
}
|
}
|
||||||
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
|
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
|
||||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||||
|
sort.Sort(packageVarsByAddr(bi.packageVars))
|
||||||
|
|
||||||
bi.LookupFunc = make(map[string]*Function)
|
bi.LookupFunc = make(map[string]*Function)
|
||||||
for i := range bi.Functions {
|
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) {
|
func (scope *EvalScope) findGlobal(name string) (*Variable, error) {
|
||||||
for n, off := range scope.BinInfo.packageVars {
|
for _, pkgvar := range scope.BinInfo.packageVars {
|
||||||
if n == name || strings.HasSuffix(n, "/"+name) {
|
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
|
||||||
reader := scope.DwarfReader()
|
reader := scope.DwarfReader()
|
||||||
reader.Seek(off)
|
reader.Seek(pkgvar.offset)
|
||||||
entry, err := reader.Next()
|
entry, err := reader.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -944,7 +944,7 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
|||||||
disass := make(api.AsmInstructions, len(insts))
|
disass := make(api.AsmInstructions, len(insts))
|
||||||
|
|
||||||
for i := range 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
|
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.
|
// 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.
|
// 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.
|
// Rewrite instruction to mimic GNU peculiarities.
|
||||||
// Note that inst has been passed by value and contains
|
// Note that inst has been passed by value and contains
|
||||||
// no pointers, so any changes we make here are local
|
// no pointers, so any changes we make here are local
|
||||||
// and will not propagate back out to the caller.
|
// and will not propagate back out to the caller.
|
||||||
|
|
||||||
|
if symname == nil {
|
||||||
|
symname = func(uint64) (string, uint64) { return "", 0 }
|
||||||
|
}
|
||||||
|
|
||||||
// Adjust opcode [sic].
|
// Adjust opcode [sic].
|
||||||
switch inst.Op {
|
switch inst.Op {
|
||||||
case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
|
case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
|
||||||
@ -403,7 +407,7 @@ SuffixLoop:
|
|||||||
if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
|
if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
|
||||||
continue
|
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.
|
// 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.
|
// 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
|
// If *usedPrefixes is false and x is a Mem, then the formatting
|
||||||
// includes any segment prefixes and sets *usedPrefixes to true.
|
// 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 {
|
if x == nil {
|
||||||
return "<nil>"
|
return "<nil>"
|
||||||
}
|
}
|
||||||
@ -535,6 +539,13 @@ func gnuArg(inst *Inst, x Arg, usedPrefixes *bool) string {
|
|||||||
}
|
}
|
||||||
return gccRegName[x]
|
return gccRegName[x]
|
||||||
case Mem:
|
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 := ""
|
seg := ""
|
||||||
var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool
|
var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool
|
||||||
switch x.Segment {
|
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)
|
return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale)
|
||||||
case Rel:
|
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:
|
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 {
|
if inst.Mode == 32 {
|
||||||
return fmt.Sprintf("$%#x", uint32(x))
|
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.
|
// 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
|
var iargs []Arg
|
||||||
for _, a := range inst.Args {
|
for _, a := range inst.Args {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
@ -256,7 +260,7 @@ func IntelSyntax(inst Inst) string {
|
|||||||
if a == nil {
|
if a == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
args = append(args, intelArg(&inst, a))
|
args = append(args, intelArg(&inst, pc, symname, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
var op string
|
var op string
|
||||||
@ -334,9 +338,16 @@ func IntelSyntax(inst Inst) string {
|
|||||||
return prefix + op
|
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) {
|
switch a := arg.(type) {
|
||||||
case Imm:
|
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 {
|
if inst.Mode == 32 {
|
||||||
return fmt.Sprintf("%#x", uint32(a))
|
return fmt.Sprintf("%#x", uint32(a))
|
||||||
}
|
}
|
||||||
@ -417,18 +428,25 @@ func intelArg(inst *Inst, arg Arg) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prefix += "ptr "
|
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 {
|
if a.Segment != 0 {
|
||||||
prefix += strings.ToLower(a.Segment.String()) + ":"
|
prefix += strings.ToLower(a.Segment.String()) + ":"
|
||||||
}
|
}
|
||||||
prefix += "["
|
prefix += "["
|
||||||
if a.Base != 0 {
|
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.Scale != 0 && a.Index != 0 {
|
||||||
if a.Base != 0 {
|
if a.Base != 0 {
|
||||||
prefix += "+"
|
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 a.Disp != 0 {
|
||||||
if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
|
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 += "]"
|
prefix += "]"
|
||||||
return prefix
|
return prefix
|
||||||
case Rel:
|
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:
|
case Reg:
|
||||||
if int(a) < len(intelReg) && intelReg[a] != "" {
|
if int(a) < len(intelReg) && intelReg[a] != "" {
|
||||||
switch inst.Op {
|
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"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SymLookup func(uint64) (string, uint64)
|
||||||
|
|
||||||
// GoSyntax returns the Go assembler syntax for the instruction.
|
// GoSyntax returns the Go assembler syntax for the instruction.
|
||||||
// The syntax was originally defined by Plan 9.
|
// The syntax was originally defined by Plan 9.
|
||||||
// The pc is the program counter of the instruction, used for expanding
|
// 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
|
// The symname function queries the symbol table for the program
|
||||||
// being disassembled. Given a target address it returns the name and base
|
// 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.
|
// 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 {
|
if symname == nil {
|
||||||
symname = func(uint64) (string, uint64) { return "", 0 }
|
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))
|
return fmt.Sprintf("$%#x", uint64(a))
|
||||||
case Mem:
|
case Mem:
|
||||||
if a.Segment == 0 && a.Disp != 0 && a.Base == 0 && (a.Index == 0 || a.Scale == 0) {
|
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
|
||||||
if s, base := symname(uint64(a.Disp)); s != "" {
|
suffix := ""
|
||||||
suffix := ""
|
if disp != 0 {
|
||||||
if uint64(a.Disp) != base {
|
suffix = fmt.Sprintf("%+d", disp)
|
||||||
suffix = fmt.Sprintf("%+d", uint64(a.Disp)-base)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s%s(SB)", s, suffix)
|
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("%s%s(SB)", s, suffix)
|
||||||
}
|
}
|
||||||
s := ""
|
s := ""
|
||||||
if a.Segment != 0 {
|
if a.Segment != 0 {
|
||||||
@ -148,6 +148,25 @@ func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg
|
|||||||
return arg.String()
|
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{
|
var plan9Suffix = [maxOp + 1]bool{
|
||||||
ADC: true,
|
ADC: true,
|
||||||
ADD: 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