mirror of https://github.com/golang/go.git
cmd: add golang.org/x/arch/ppc64/ppc64asm for disassembly
For #9039. Change-Id: I2b1bcd76857ff332411ca21a0cc5def3097a8eaf Reviewed-on: https://go-review.googlesource.com/30936 Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
daa121167b
commit
ed0a956746
|
|
@ -48,6 +48,10 @@ go src=..
|
||||||
x86asm
|
x86asm
|
||||||
testdata
|
testdata
|
||||||
+
|
+
|
||||||
|
ppc64
|
||||||
|
ppc64asm
|
||||||
|
testdata
|
||||||
|
+
|
||||||
archive
|
archive
|
||||||
tar
|
tar
|
||||||
testdata
|
testdata
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debugDecode = false
|
||||||
|
|
||||||
|
// instFormat is a decoding rule for one specific instruction form.
|
||||||
|
// a uint32 instruction ins matches the rule if ins&Mask == Value
|
||||||
|
// DontCare bits should be zero, but the machine might not reject
|
||||||
|
// ones in those bits, they are mainly reserved for future expansion
|
||||||
|
// of the instruction set.
|
||||||
|
// The Args are stored in the same order as the instruction manual.
|
||||||
|
type instFormat struct {
|
||||||
|
Op Op
|
||||||
|
Mask uint32
|
||||||
|
Value uint32
|
||||||
|
DontCare uint32
|
||||||
|
Args [5]*argField
|
||||||
|
}
|
||||||
|
|
||||||
|
// argField indicate how to decode an argument to an instruction.
|
||||||
|
// First parse the value from the BitFields, shift it left by Shift
|
||||||
|
// bits to get the actual numerical value.
|
||||||
|
type argField struct {
|
||||||
|
Type ArgType
|
||||||
|
Shift uint8
|
||||||
|
BitFields
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the Arg out from the given binary instruction i.
|
||||||
|
func (a argField) Parse(i uint32) Arg {
|
||||||
|
switch a.Type {
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
case TypeUnknown:
|
||||||
|
return nil
|
||||||
|
case TypeReg:
|
||||||
|
return R0 + Reg(a.BitFields.Parse(i))
|
||||||
|
case TypeCondRegBit:
|
||||||
|
return Cond0LT + CondReg(a.BitFields.Parse(i))
|
||||||
|
case TypeCondRegField:
|
||||||
|
return CR0 + CondReg(a.BitFields.Parse(i))
|
||||||
|
case TypeFPReg:
|
||||||
|
return F0 + Reg(a.BitFields.Parse(i))
|
||||||
|
case TypeVecReg:
|
||||||
|
return V0 + Reg(a.BitFields.Parse(i))
|
||||||
|
case TypeSpReg:
|
||||||
|
return SpReg(a.BitFields.Parse(i))
|
||||||
|
case TypeImmSigned:
|
||||||
|
return Imm(a.BitFields.ParseSigned(i) << a.Shift)
|
||||||
|
case TypeImmUnsigned:
|
||||||
|
return Imm(a.BitFields.Parse(i) << a.Shift)
|
||||||
|
case TypePCRel:
|
||||||
|
return PCRel(a.BitFields.ParseSigned(i) << a.Shift)
|
||||||
|
case TypeLabel:
|
||||||
|
return Label(a.BitFields.ParseSigned(i) << a.Shift)
|
||||||
|
case TypeOffset:
|
||||||
|
return Offset(a.BitFields.ParseSigned(i) << a.Shift)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArgType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeUnknown ArgType = iota
|
||||||
|
TypePCRel // PC-relative address
|
||||||
|
TypeLabel // absolute address
|
||||||
|
TypeReg // integer register
|
||||||
|
TypeCondRegBit // conditional register bit (0-31)
|
||||||
|
TypeCondRegField // conditional register field (0-7)
|
||||||
|
TypeFPReg // floating point register
|
||||||
|
TypeVecReg // vector register
|
||||||
|
TypeSpReg // special register (depends on Op)
|
||||||
|
TypeImmSigned // signed immediate
|
||||||
|
TypeImmUnsigned // unsigned immediate/flag/mask, this is the catch-all type
|
||||||
|
TypeOffset // signed offset in load/store
|
||||||
|
TypeLast // must be the last one
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t ArgType) String() string {
|
||||||
|
switch t {
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("ArgType(%d)", int(t))
|
||||||
|
case TypeUnknown:
|
||||||
|
return "Unknown"
|
||||||
|
case TypeReg:
|
||||||
|
return "Reg"
|
||||||
|
case TypeCondRegBit:
|
||||||
|
return "CondRegBit"
|
||||||
|
case TypeCondRegField:
|
||||||
|
return "CondRegField"
|
||||||
|
case TypeFPReg:
|
||||||
|
return "FPReg"
|
||||||
|
case TypeVecReg:
|
||||||
|
return "VecReg"
|
||||||
|
case TypeSpReg:
|
||||||
|
return "SpReg"
|
||||||
|
case TypeImmSigned:
|
||||||
|
return "ImmSigned"
|
||||||
|
case TypeImmUnsigned:
|
||||||
|
return "ImmUnsigned"
|
||||||
|
case TypePCRel:
|
||||||
|
return "PCRel"
|
||||||
|
case TypeLabel:
|
||||||
|
return "Label"
|
||||||
|
case TypeOffset:
|
||||||
|
return "Offset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ArgType) GoString() string {
|
||||||
|
s := t.String()
|
||||||
|
if t > 0 && t < TypeLast {
|
||||||
|
return "Type" + s
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Errors
|
||||||
|
errShort = fmt.Errorf("truncated instruction")
|
||||||
|
errUnknown = fmt.Errorf("unknown instruction")
|
||||||
|
)
|
||||||
|
|
||||||
|
var decoderCover []bool
|
||||||
|
|
||||||
|
// Decode decodes the leading bytes in src as a single instruction using
|
||||||
|
// byte order ord.
|
||||||
|
func Decode(src []byte, ord binary.ByteOrder) (inst Inst, err error) {
|
||||||
|
if len(src) < 4 {
|
||||||
|
return inst, errShort
|
||||||
|
}
|
||||||
|
if decoderCover == nil {
|
||||||
|
decoderCover = make([]bool, len(instFormats))
|
||||||
|
}
|
||||||
|
inst.Len = 4 // only 4-byte instructions are supported
|
||||||
|
ui := ord.Uint32(src[:inst.Len])
|
||||||
|
inst.Enc = ui
|
||||||
|
for i, iform := range instFormats {
|
||||||
|
if ui&iform.Mask != iform.Value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ui&iform.DontCare != 0 {
|
||||||
|
if debugDecode {
|
||||||
|
log.Printf("Decode(%#x): unused bit is 1 for Op %s", ui, iform.Op)
|
||||||
|
}
|
||||||
|
// to match GNU objdump (libopcodes), we ignore don't care bits
|
||||||
|
}
|
||||||
|
for i, argfield := range iform.Args {
|
||||||
|
if argfield == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
inst.Args[i] = argfield.Parse(ui)
|
||||||
|
}
|
||||||
|
inst.Op = iform.Op
|
||||||
|
if debugDecode {
|
||||||
|
log.Printf("%#x: search entry %d", ui, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if inst.Op == 0 {
|
||||||
|
return inst, errUnknown
|
||||||
|
}
|
||||||
|
return inst, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"io/ioutil"
|
||||||
|
"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", 3)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
syntax, asm := f[1], f[2]
|
||||||
|
inst, err := Decode(code, binary.BigEndian)
|
||||||
|
var out string
|
||||||
|
if err != nil {
|
||||||
|
out = "error: " + err.Error()
|
||||||
|
} else {
|
||||||
|
switch syntax {
|
||||||
|
case "gnu":
|
||||||
|
out = GNUSyntax(inst)
|
||||||
|
//case "plan9":
|
||||||
|
// out = GoSyntax(inst, 0, nil, nil)
|
||||||
|
default:
|
||||||
|
t.Errorf("unknown syntax %q", syntax)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if out != asm || inst.Len != size {
|
||||||
|
t.Errorf("Decode(%s) [%s] = %s want %s", f[0], syntax, out, asm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// 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 ppc64asm implements decoding of 64-bit PowerPC machine code.
|
||||||
|
package ppc64asm
|
||||||
|
|
@ -0,0 +1,535 @@
|
||||||
|
// 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.
|
||||||
|
// Copied and simplified from rsc.io/arm/armasm/ext_test.go.
|
||||||
|
|
||||||
|
package ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"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
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ExtInst represents a single decoded instruction parsed
|
||||||
|
// from an external disassembler's output.
|
||||||
|
type ExtInst struct {
|
||||||
|
addr uint32
|
||||||
|
enc [4]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 {
|
||||||
|
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,
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
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, 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.Fatal("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("", "ppc64asm")
|
||||||
|
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) > 4 {
|
||||||
|
x = x[:4]
|
||||||
|
}
|
||||||
|
if debug {
|
||||||
|
fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
|
||||||
|
}
|
||||||
|
w.Write(x)
|
||||||
|
w.Write(zeros[len(x):])
|
||||||
|
size += len(zeros)
|
||||||
|
})
|
||||||
|
return file, f, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var zeros = []byte{0, 0, 0, 0}
|
||||||
|
|
||||||
|
// pad pads the code sequence with pops.
|
||||||
|
func pad(enc []byte) []byte {
|
||||||
|
if len(enc) < 4 {
|
||||||
|
enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// disasm returns the decoded instruction and text
|
||||||
|
// for the given source bytes, using the given syntax and mode.
|
||||||
|
func disasm(syntax string, 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 := Decode(src, binary.BigEndian)
|
||||||
|
if err != nil {
|
||||||
|
text = "error: " + err.Error()
|
||||||
|
} else {
|
||||||
|
text = inst.String()
|
||||||
|
switch syntax {
|
||||||
|
//case "arm":
|
||||||
|
// text = ARMSyntax(inst)
|
||||||
|
case "gnu":
|
||||||
|
text = GNUSyntax(inst)
|
||||||
|
//case "plan9":
|
||||||
|
// text = GoSyntax(inst, 0, nil)
|
||||||
|
default:
|
||||||
|
text = "error: unknown syntax " + syntax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *printTests {
|
||||||
|
cover += coverage()
|
||||||
|
if cover > 0 {
|
||||||
|
max := len(src)
|
||||||
|
if max > 4 && inst.Len <= 4 {
|
||||||
|
max = 4
|
||||||
|
}
|
||||||
|
fmt.Printf("%x|%x\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], 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 {
|
||||||
|
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.
|
||||||
|
|
||||||
|
// 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 matches instructions using relative addressing mode.
|
||||||
|
var (
|
||||||
|
pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bc)[^ac ]* (?:(?:[0-9]{1,2},)|(?:[0-7]\*)|\+|lt|gt|eq|so|cr[0-7]|,)*)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.
|
||||||
|
|
||||||
|
// randomCases generates random instructions.
|
||||||
|
func randomCases(t *testing.T) func(func([]byte)) {
|
||||||
|
return func(try func([]byte)) {
|
||||||
|
// All the strides are relatively prime to 2 and therefore to 2²⁸,
|
||||||
|
// so we will not repeat any instructions until we have tried all 2²⁸.
|
||||||
|
// Using a stride other than 1 is meant to visit the instructions in a
|
||||||
|
// pseudorandom order, which gives better variety in the set of
|
||||||
|
// test cases chosen by -printtests.
|
||||||
|
stride := uint32(10007)
|
||||||
|
n := 1 << 28 / 7
|
||||||
|
if testing.Short() {
|
||||||
|
stride = 100003
|
||||||
|
n = 1 << 28 / 1001
|
||||||
|
} else if *longTest {
|
||||||
|
stride = 2000033
|
||||||
|
n = 1 << 29
|
||||||
|
}
|
||||||
|
x := uint32(0)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
enc := (x%15)<<28 | x&(1<<28-1)
|
||||||
|
try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
|
||||||
|
x += stride
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A BitField is a bit-field in a 32-bit word.
|
||||||
|
// Bits are counted from 0 from the MSB to 31 as the LSB.
|
||||||
|
type BitField struct {
|
||||||
|
Offs uint8 // the offset of the left-most bit.
|
||||||
|
Bits uint8 // length in bits.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BitField) String() string {
|
||||||
|
if b.Bits > 1 {
|
||||||
|
return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1)
|
||||||
|
} else if b.Bits == 1 {
|
||||||
|
return fmt.Sprintf("[%d]", b.Offs)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("[%d, len=0]", b.Offs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse extracts the bitfield b from i, and return it as an unsigned integer.
|
||||||
|
// Parse will panic if b is invalid.
|
||||||
|
func (b BitField) Parse(i uint32) uint32 {
|
||||||
|
if b.Bits > 32 || b.Bits == 0 || b.Offs > 31 || b.Offs+b.Bits > 32 {
|
||||||
|
panic(fmt.Sprintf("invalid bitfiled %v", b))
|
||||||
|
}
|
||||||
|
return (i >> (32 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSigned extracts the bitfield b from i, and return it as a signed integer.
|
||||||
|
// ParseSigned will panic if b is invalid.
|
||||||
|
func (b BitField) ParseSigned(i uint32) int32 {
|
||||||
|
u := int32(b.Parse(i))
|
||||||
|
return u << (32 - b.Bits) >> (32 - b.Bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BitFields is a series of BitFields representing a single number.
|
||||||
|
type BitFields []BitField
|
||||||
|
|
||||||
|
func (bs BitFields) String() string {
|
||||||
|
ss := make([]string, len(bs))
|
||||||
|
for i, bf := range bs {
|
||||||
|
ss[i] = bf.String()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<%s>", strings.Join(ss, "|"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *BitFields) Append(b BitField) {
|
||||||
|
*bs = append(*bs, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse extracts the bitfields from i, concatenate them and return the result
|
||||||
|
// as an unsigned integer and the total length of all the bitfields.
|
||||||
|
// parse will panic if any bitfield in b is invalid, but it doesn't check if
|
||||||
|
// the sequence of bitfields is reasonable.
|
||||||
|
func (bs BitFields) parse(i uint32) (u uint32, Bits uint8) {
|
||||||
|
for _, b := range bs {
|
||||||
|
u = (u << b.Bits) | b.Parse(i)
|
||||||
|
Bits += b.Bits
|
||||||
|
}
|
||||||
|
return u, Bits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse extracts the bitfields from i, concatenate them and return the result
|
||||||
|
// as an unsigned integer. Parse will panic if any bitfield in b is invalid.
|
||||||
|
func (bs BitFields) Parse(i uint32) uint32 {
|
||||||
|
u, _ := bs.parse(i)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse extracts the bitfields from i, concatenate them and return the result
|
||||||
|
// as a signed integer. Parse will panic if any bitfield in b is invalid.
|
||||||
|
func (bs BitFields) ParseSigned(i uint32) int32 {
|
||||||
|
u, l := bs.parse(i)
|
||||||
|
return int32(u) << (32 - l) >> (32 - l)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func panicOrNot(f func()) (panicked bool) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
panicked = true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBitField(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
b BitField
|
||||||
|
i uint32 // input
|
||||||
|
u uint32 // unsigned output
|
||||||
|
s int32 // signed output
|
||||||
|
fail bool // if the check should panic
|
||||||
|
}{
|
||||||
|
{BitField{0, 0}, 0, 0, 0, true},
|
||||||
|
{BitField{31, 2}, 0, 0, 0, true},
|
||||||
|
{BitField{31, 1}, 1, 1, -1, false},
|
||||||
|
{BitField{29, 2}, 0 << 1, 0, 0, false},
|
||||||
|
{BitField{29, 2}, 1 << 1, 1, 1, false},
|
||||||
|
{BitField{29, 2}, 2 << 1, 2, -2, false},
|
||||||
|
{BitField{29, 2}, 3 << 1, 3, -1, false},
|
||||||
|
{BitField{0, 32}, 1<<32 - 1, 1<<32 - 1, -1, false},
|
||||||
|
{BitField{16, 3}, 1 << 15, 4, -4, false},
|
||||||
|
}
|
||||||
|
for i, tst := range tests {
|
||||||
|
var (
|
||||||
|
ou uint32
|
||||||
|
os int32
|
||||||
|
)
|
||||||
|
failed := panicOrNot(func() {
|
||||||
|
ou = tst.b.Parse(tst.i)
|
||||||
|
os = tst.b.ParseSigned(tst.i)
|
||||||
|
})
|
||||||
|
if failed != tst.fail {
|
||||||
|
t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ou != tst.u {
|
||||||
|
t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if os != tst.s {
|
||||||
|
t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
|
||||||
|
// This form typically matches the syntax defined in the Power ISA Reference Manual.
|
||||||
|
func GNUSyntax(inst Inst) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if inst.Op == 0 {
|
||||||
|
return "error: unkown instruction"
|
||||||
|
}
|
||||||
|
buf.WriteString(inst.Op.String())
|
||||||
|
sep := " "
|
||||||
|
for i, arg := range inst.Args[:] {
|
||||||
|
if arg == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
text := gnuArg(&inst, i, arg)
|
||||||
|
if text == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf.WriteString(sep)
|
||||||
|
sep = ","
|
||||||
|
buf.WriteString(text)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
|
||||||
|
// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
|
||||||
|
// of inst, it's ok to modify inst.Args here.
|
||||||
|
func gnuArg(inst *Inst, argIndex int, arg Arg) string {
|
||||||
|
// special cases for load/store instructions
|
||||||
|
if _, ok := arg.(Offset); ok {
|
||||||
|
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
|
||||||
|
panic(fmt.Errorf("wrong table: offset not followed by register"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case Reg:
|
||||||
|
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
return arg.String()
|
||||||
|
case CondReg:
|
||||||
|
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
|
||||||
|
return "" // don't show cr0 for cmp instructions
|
||||||
|
} else if arg >= CR0 {
|
||||||
|
return fmt.Sprintf("cr%d", int(arg-CR0))
|
||||||
|
}
|
||||||
|
bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4]
|
||||||
|
if arg <= Cond0SO {
|
||||||
|
return bit
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
|
||||||
|
case Imm:
|
||||||
|
return fmt.Sprintf("%d", arg)
|
||||||
|
case SpReg:
|
||||||
|
return fmt.Sprintf("%d", int(arg))
|
||||||
|
case PCRel:
|
||||||
|
return fmt.Sprintf(".%+#x", int(arg))
|
||||||
|
case Label:
|
||||||
|
return fmt.Sprintf("%#x", uint32(arg))
|
||||||
|
case Offset:
|
||||||
|
reg := inst.Args[argIndex+1].(Reg)
|
||||||
|
removeArg(inst, argIndex+1)
|
||||||
|
if reg == R0 {
|
||||||
|
return fmt.Sprintf("%d(0)", int(arg))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("???(%v)", arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeArg removes the arg in inst.Args[index].
|
||||||
|
func removeArg(inst *Inst, index int) {
|
||||||
|
for i := index; i < len(inst.Args); i++ {
|
||||||
|
if i+1 < len(inst.Args) {
|
||||||
|
inst.Args[i] = inst.Args[i+1]
|
||||||
|
} else {
|
||||||
|
inst.Args[i] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLoadStoreOp returns true if op is a load or store instruction
|
||||||
|
func isLoadStoreOp(op Op) bool {
|
||||||
|
switch op {
|
||||||
|
case LBZ, LBZU, LBZX, LBZUX:
|
||||||
|
return true
|
||||||
|
case LHZ, LHZU, LHZX, LHZUX:
|
||||||
|
return true
|
||||||
|
case LHA, LHAU, LHAX, LHAUX:
|
||||||
|
return true
|
||||||
|
case LWZ, LWZU, LWZX, LWZUX:
|
||||||
|
return true
|
||||||
|
case LWA, LWAX, LWAUX:
|
||||||
|
return true
|
||||||
|
case LD, LDU, LDX, LDUX:
|
||||||
|
return true
|
||||||
|
case LQ:
|
||||||
|
return true
|
||||||
|
case STB, STBU, STBX, STBUX:
|
||||||
|
return true
|
||||||
|
case STH, STHU, STHX, STHUX:
|
||||||
|
return true
|
||||||
|
case STW, STWU, STWX, STWUX:
|
||||||
|
return true
|
||||||
|
case STD, STDU, STDX, STDUX:
|
||||||
|
return true
|
||||||
|
case STQ:
|
||||||
|
return true
|
||||||
|
case LHBRX, LWBRX, STHBRX, STWBRX:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,310 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Inst struct {
|
||||||
|
Op Op // Opcode mnemonic
|
||||||
|
Enc uint32 // Raw encoding bits
|
||||||
|
Len int // Length of encoding in bytes.
|
||||||
|
Args Args // Instruction arguments, in Power ISA manual order.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Inst) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(i.Op.String())
|
||||||
|
for j, arg := range i.Args {
|
||||||
|
if arg == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if j == 0 {
|
||||||
|
buf.WriteString(" ")
|
||||||
|
} else {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(arg.String())
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Op is an instruction operation.
|
||||||
|
type Op uint16
|
||||||
|
|
||||||
|
func (o Op) String() string {
|
||||||
|
if int(o) >= len(opstr) || opstr[o] == "" {
|
||||||
|
return fmt.Sprintf("Op(%d)", int(o))
|
||||||
|
}
|
||||||
|
return opstr[o]
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Arg is a single instruction argument, one of these types: Reg, CondReg, SpReg, Imm, PCRel, Label, or Offset.
|
||||||
|
type Arg interface {
|
||||||
|
IsArg()
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Args holds the instruction arguments.
|
||||||
|
// If an instruction has fewer than 4 arguments,
|
||||||
|
// the final elements in the array are nil.
|
||||||
|
type Args [5]Arg
|
||||||
|
|
||||||
|
// A Reg is a single register. The zero value means R0, not the absence of a register.
|
||||||
|
// It also includes special registers.
|
||||||
|
type Reg uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ Reg = iota
|
||||||
|
R0
|
||||||
|
R1
|
||||||
|
R2
|
||||||
|
R3
|
||||||
|
R4
|
||||||
|
R5
|
||||||
|
R6
|
||||||
|
R7
|
||||||
|
R8
|
||||||
|
R9
|
||||||
|
R10
|
||||||
|
R11
|
||||||
|
R12
|
||||||
|
R13
|
||||||
|
R14
|
||||||
|
R15
|
||||||
|
R16
|
||||||
|
R17
|
||||||
|
R18
|
||||||
|
R19
|
||||||
|
R20
|
||||||
|
R21
|
||||||
|
R22
|
||||||
|
R23
|
||||||
|
R24
|
||||||
|
R25
|
||||||
|
R26
|
||||||
|
R27
|
||||||
|
R28
|
||||||
|
R29
|
||||||
|
R30
|
||||||
|
R31
|
||||||
|
F0
|
||||||
|
F1
|
||||||
|
F2
|
||||||
|
F3
|
||||||
|
F4
|
||||||
|
F5
|
||||||
|
F6
|
||||||
|
F7
|
||||||
|
F8
|
||||||
|
F9
|
||||||
|
F10
|
||||||
|
F11
|
||||||
|
F12
|
||||||
|
F13
|
||||||
|
F14
|
||||||
|
F15
|
||||||
|
F16
|
||||||
|
F17
|
||||||
|
F18
|
||||||
|
F19
|
||||||
|
F20
|
||||||
|
F21
|
||||||
|
F22
|
||||||
|
F23
|
||||||
|
F24
|
||||||
|
F25
|
||||||
|
F26
|
||||||
|
F27
|
||||||
|
F28
|
||||||
|
F29
|
||||||
|
F30
|
||||||
|
F31
|
||||||
|
V0 // VSX extension, F0 is V0[0:63].
|
||||||
|
V1
|
||||||
|
V2
|
||||||
|
V3
|
||||||
|
V4
|
||||||
|
V5
|
||||||
|
V6
|
||||||
|
V7
|
||||||
|
V8
|
||||||
|
V9
|
||||||
|
V10
|
||||||
|
V11
|
||||||
|
V12
|
||||||
|
V13
|
||||||
|
V14
|
||||||
|
V15
|
||||||
|
V16
|
||||||
|
V17
|
||||||
|
V18
|
||||||
|
V19
|
||||||
|
V20
|
||||||
|
V21
|
||||||
|
V22
|
||||||
|
V23
|
||||||
|
V24
|
||||||
|
V25
|
||||||
|
V26
|
||||||
|
V27
|
||||||
|
V28
|
||||||
|
V29
|
||||||
|
V30
|
||||||
|
V31
|
||||||
|
V32
|
||||||
|
V33
|
||||||
|
V34
|
||||||
|
V35
|
||||||
|
V36
|
||||||
|
V37
|
||||||
|
V38
|
||||||
|
V39
|
||||||
|
V40
|
||||||
|
V41
|
||||||
|
V42
|
||||||
|
V43
|
||||||
|
V44
|
||||||
|
V45
|
||||||
|
V46
|
||||||
|
V47
|
||||||
|
V48
|
||||||
|
V49
|
||||||
|
V50
|
||||||
|
V51
|
||||||
|
V52
|
||||||
|
V53
|
||||||
|
V54
|
||||||
|
V55
|
||||||
|
V56
|
||||||
|
V57
|
||||||
|
V58
|
||||||
|
V59
|
||||||
|
V60
|
||||||
|
V61
|
||||||
|
V62
|
||||||
|
V63
|
||||||
|
)
|
||||||
|
|
||||||
|
func (Reg) IsArg() {}
|
||||||
|
func (r Reg) String() string {
|
||||||
|
switch {
|
||||||
|
case R0 <= r && r <= R31:
|
||||||
|
return fmt.Sprintf("r%d", int(r-R0))
|
||||||
|
case F0 <= r && r <= F31:
|
||||||
|
return fmt.Sprintf("f%d", int(r-F0))
|
||||||
|
case V0 <= r && r <= V63:
|
||||||
|
return fmt.Sprintf("v%d", int(r-V0))
|
||||||
|
default:
|
||||||
|
return fmt.Sprint("Reg(%d)", int(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CondReg is a bit or field in the conditon register.
|
||||||
|
type CondReg int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ CondReg = iota
|
||||||
|
// Condition Regster bits
|
||||||
|
Cond0LT
|
||||||
|
Cond0GT
|
||||||
|
Cond0EQ
|
||||||
|
Cond0SO
|
||||||
|
Cond1LT
|
||||||
|
Cond1GT
|
||||||
|
Cond1EQ
|
||||||
|
Cond1SO
|
||||||
|
Cond2LT
|
||||||
|
Cond2GT
|
||||||
|
Cond2EQ
|
||||||
|
Cond2SO
|
||||||
|
Cond3LT
|
||||||
|
Cond3GT
|
||||||
|
Cond3EQ
|
||||||
|
Cond3SO
|
||||||
|
Cond4LT
|
||||||
|
Cond4GT
|
||||||
|
Cond4EQ
|
||||||
|
Cond4SO
|
||||||
|
Cond5LT
|
||||||
|
Cond5GT
|
||||||
|
Cond5EQ
|
||||||
|
Cond5SO
|
||||||
|
Cond6LT
|
||||||
|
Cond6GT
|
||||||
|
Cond6EQ
|
||||||
|
Cond6SO
|
||||||
|
Cond7LT
|
||||||
|
Cond7GT
|
||||||
|
Cond7EQ
|
||||||
|
Cond7SO
|
||||||
|
// Condition Register Fields
|
||||||
|
CR0
|
||||||
|
CR1
|
||||||
|
CR2
|
||||||
|
CR3
|
||||||
|
CR4
|
||||||
|
CR5
|
||||||
|
CR6
|
||||||
|
CR7
|
||||||
|
)
|
||||||
|
|
||||||
|
func (CondReg) IsArg() {}
|
||||||
|
func (c CondReg) String() string {
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("CondReg(%d)", int(c))
|
||||||
|
case c >= CR0:
|
||||||
|
return fmt.Sprintf("CR%d", int(c-CR0))
|
||||||
|
case c >= Cond0LT && c < CR0:
|
||||||
|
return fmt.Sprintf("Cond%d%s", int((c-Cond0LT)/4), [4]string{"LT", "GT", "EQ", "SO"}[(c-Cond0LT)%4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpReg is a special register, its meaning depends on Op.
|
||||||
|
type SpReg uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
SpRegZero SpReg = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func (SpReg) IsArg() {}
|
||||||
|
func (s SpReg) String() string {
|
||||||
|
return fmt.Sprintf("SpReg(%d)", int(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PCRel is a PC-relative offset, used only in branch instructions.
|
||||||
|
type PCRel int32
|
||||||
|
|
||||||
|
func (PCRel) IsArg() {}
|
||||||
|
func (r PCRel) String() string {
|
||||||
|
return fmt.Sprintf("PC%+#x", int32(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Label is a code (text) address, used only in absolute branch instructions.
|
||||||
|
type Label uint32
|
||||||
|
|
||||||
|
func (Label) IsArg() {}
|
||||||
|
func (l Label) String() string {
|
||||||
|
return fmt.Sprintf("%#x", uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imm represents an immediate number.
|
||||||
|
type Imm int32
|
||||||
|
|
||||||
|
func (Imm) IsArg() {}
|
||||||
|
func (i Imm) String() string {
|
||||||
|
return fmt.Sprintf("%d", int32(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset represents a memory offset immediate.
|
||||||
|
type Offset int32
|
||||||
|
|
||||||
|
func (Offset) IsArg() {}
|
||||||
|
func (o Offset) String() string {
|
||||||
|
return fmt.Sprintf("%+d", int32(o))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
// 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestObjdumpPowerTestdata(t *testing.T) { testObjdump(t, testdataCases(t)) }
|
||||||
|
func TestObjdumpPowerManual(t *testing.T) { testObjdump(t, hexCases(t, objdumpManualTests)) }
|
||||||
|
func TestObjdumpPowerRandom(t *testing.T) { testObjdump(t, randomCases(t)) }
|
||||||
|
|
||||||
|
// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
|
||||||
|
// 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=Manual, particularly with tracing enabled.
|
||||||
|
// Note that these are byte sequences, so they must be reversed from the usual
|
||||||
|
// word presentation.
|
||||||
|
var objdumpManualTests = `
|
||||||
|
6d746162
|
||||||
|
4c040000
|
||||||
|
88000017
|
||||||
|
`
|
||||||
|
|
||||||
|
// 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 hasPrefix(dec.text, deleted...) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// we support more instructions than binutils
|
||||||
|
if strings.Contains(dec.text, ".long") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPrefix(text, "error:") {
|
||||||
|
if hasPrefix(dec.text, unsupported...) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch inst.Op {
|
||||||
|
case BC, BCA, BL, BLA, BCL, BCLA, TDI, TWI, TW, TD:
|
||||||
|
return true // TODO(minux): we lack the support for extended opcodes here
|
||||||
|
case RLWNM, RLWNM_, RLDICL, RLDICL_, RLWINM, RLWINM_, RLDCL, RLDCL_:
|
||||||
|
return true // TODO(minux): we lack the support for extended opcodes here
|
||||||
|
case DCBTST, DCBT:
|
||||||
|
return true // objdump uses the embedded argument order, we use the server argument order
|
||||||
|
case MTFSF, MTFSF_: // objdump doesn't show the last two arguments
|
||||||
|
return true
|
||||||
|
case VSPLTB, VSPLTH, VSPLTW: // objdump generates unreasonable result "vspltw v6,v19,4" for 10c49a8c, the last 4 should be 0.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasPrefix(text, "evm", "evl", "efs") { // objdump will disassemble them wrong (e.g. evmhoumia as vsldoi)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dec.enc) >= 4 {
|
||||||
|
_ = binary.BigEndian.Uint32(dec.enc[:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instructions known to libopcodes (or xed) but not to us.
|
||||||
|
// TODO(minux): those single precision instructions are missing from ppc64.csv
|
||||||
|
// those data cache instructions are deprecated, but must be treated as no-ops, see 4.3.2.1 pg. 774.
|
||||||
|
var unsupported = strings.Fields(`
|
||||||
|
fmsubs
|
||||||
|
fmsubs.
|
||||||
|
fnmadds
|
||||||
|
fnmadds.
|
||||||
|
fnmsubs
|
||||||
|
fnmsubs.
|
||||||
|
fmuls
|
||||||
|
fmuls.
|
||||||
|
fdivs
|
||||||
|
fdivs.
|
||||||
|
fadds
|
||||||
|
fadds.
|
||||||
|
fsubs
|
||||||
|
fsubs.
|
||||||
|
dst
|
||||||
|
dstst
|
||||||
|
dssall
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Instructions explicitly dropped in Power ISA that were in POWER architecture.
|
||||||
|
// See A.30 Deleted Instructions and A.31 Discontiued Opcodes
|
||||||
|
var deleted = strings.Fields(`
|
||||||
|
abs
|
||||||
|
clcs
|
||||||
|
clf
|
||||||
|
cli
|
||||||
|
dclst
|
||||||
|
div
|
||||||
|
divs
|
||||||
|
doz
|
||||||
|
dozi
|
||||||
|
lscbx
|
||||||
|
maskg
|
||||||
|
maskir
|
||||||
|
mfsri
|
||||||
|
mul
|
||||||
|
nabs
|
||||||
|
rac
|
||||||
|
rfi
|
||||||
|
rfsvc
|
||||||
|
rlmi
|
||||||
|
rrib
|
||||||
|
sle
|
||||||
|
sleq
|
||||||
|
sliq
|
||||||
|
slliq
|
||||||
|
sllq
|
||||||
|
slq
|
||||||
|
sraiq
|
||||||
|
sraq
|
||||||
|
sre
|
||||||
|
srea
|
||||||
|
sreq
|
||||||
|
sriq
|
||||||
|
srliq
|
||||||
|
srlq
|
||||||
|
srq
|
||||||
|
maskg`)
|
||||||
251
src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdumpext_test.go
generated
vendored
Normal file
251
src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdumpext_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Copied and simplified from rsc.io/arm/armasm/objdumpext_test.go.
|
||||||
|
|
||||||
|
package ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const objdumpPath = "/usr/local/bin/powerpc64-unknown-linux-gnu-objdump"
|
||||||
|
|
||||||
|
func testObjdump(t *testing.T, generate func(func([]byte))) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping objdump test in short mode")
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(objdumpPath); err != nil {
|
||||||
|
t.Skip(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testExtDis(t, "gnu", objdump, generate, allowedMismatchObjdump)
|
||||||
|
}
|
||||||
|
|
||||||
|
func objdump(ext *ExtDis) error {
|
||||||
|
// File already written with instructions; add ELF header.
|
||||||
|
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 [4]byte
|
||||||
|
enc []byte
|
||||||
|
text string
|
||||||
|
)
|
||||||
|
flush := func() {
|
||||||
|
if addr == next {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(text, "stmia") {
|
||||||
|
text = "stm" + text[5:]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(text, "stmfd") {
|
||||||
|
text = "stmdb" + text[5:]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(text, "ldmfd") {
|
||||||
|
text = "ldm" + text[5:]
|
||||||
|
}
|
||||||
|
text = strings.Replace(text, "#0.0", "#0", -1)
|
||||||
|
if text == "undefined" && len(enc) == 4 {
|
||||||
|
text = "error: unknown instruction"
|
||||||
|
enc = nil
|
||||||
|
}
|
||||||
|
if len(enc) == 4 {
|
||||||
|
// prints as word but we want to record bytes
|
||||||
|
enc[0], enc[3] = enc[3], enc[0]
|
||||||
|
enc[1], enc[2] = enc[2], enc[1]
|
||||||
|
}
|
||||||
|
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
|
||||||
|
encbuf = [4]byte{}
|
||||||
|
enc = nil
|
||||||
|
next += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
undefined = []byte("<UNDEFINED>")
|
||||||
|
unpredictable = []byte("<UNPREDICTABLE>")
|
||||||
|
illegalShifter = []byte("<illegal shifter operand>")
|
||||||
|
)
|
||||||
|
|
||||||
|
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 bytes.Contains(line, undefined) {
|
||||||
|
text = "undefined"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.Contains(line, illegalShifter) {
|
||||||
|
text = "undefined"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if false && bytes.Contains(line, unpredictable) {
|
||||||
|
text = "unpredictable"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.BigEndian, &hdr)
|
||||||
|
off1 := buf.Len()
|
||||||
|
binary.Write(&buf, binary.BigEndian, &prog)
|
||||||
|
off2 := buf.Len()
|
||||||
|
binary.Write(&buf, binary.BigEndian, §)
|
||||||
|
off3 := buf.Len()
|
||||||
|
buf.Reset()
|
||||||
|
data := byte(elf.ELFDATA2MSB)
|
||||||
|
hdr = elf.Header64{
|
||||||
|
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
|
||||||
|
Type: 2,
|
||||||
|
Machine: uint16(elf.EM_PPC64),
|
||||||
|
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.BigEndian, &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.BigEndian, &prog)
|
||||||
|
binary.Write(&buf, binary.BigEndian, §) // 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.BigEndian, §) // .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.BigEndian, §)
|
||||||
|
buf.WriteString("\x00.text\x00.shstrtab\x00")
|
||||||
|
f.Write(buf.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright 2015 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 ppc64asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoSyntax returns the Go assembler syntax for the instruction.
|
||||||
|
// The pc is the program counter of the first instruction, used for expanding
|
||||||
|
// PC-relative addresses into absolute ones.
|
||||||
|
// The symname function queries the symbol table for the program
|
||||||
|
// being disassembled. It returns the name and base address of the symbol
|
||||||
|
// containing the target, if any; otherwise it returns "", 0.
|
||||||
|
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
|
||||||
|
if symname == nil {
|
||||||
|
symname = func(uint64) (string, uint64) { return "", 0 }
|
||||||
|
}
|
||||||
|
if inst.Op == 0 {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
var args []string
|
||||||
|
for i, a := range inst.Args[:] {
|
||||||
|
if a == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
|
||||||
|
args = append(args, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var op string
|
||||||
|
op = plan9OpMap[inst.Op]
|
||||||
|
if op == "" {
|
||||||
|
op = strings.ToUpper(inst.Op.String())
|
||||||
|
}
|
||||||
|
// laid out the instruction
|
||||||
|
switch inst.Op {
|
||||||
|
default: // dst, sA, sB, ...
|
||||||
|
if len(args) == 0 {
|
||||||
|
return op
|
||||||
|
} else if len(args) == 1 {
|
||||||
|
return fmt.Sprintf("%s %s", op, args[0])
|
||||||
|
}
|
||||||
|
args = append(args, args[0])
|
||||||
|
return op + " " + strings.Join(args[1:], ", ")
|
||||||
|
// store instructions always have the memory operand at the end, no need to reorder
|
||||||
|
case STB, STBU, STBX, STBUX,
|
||||||
|
STH, STHU, STHX, STHUX,
|
||||||
|
STW, STWU, STWX, STWUX,
|
||||||
|
STD, STDU, STDX, STDUX,
|
||||||
|
STQ,
|
||||||
|
STHBRX, STWBRX:
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
// branch instructions needs additional handling
|
||||||
|
case BCLR:
|
||||||
|
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||||
|
return "RET"
|
||||||
|
}
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
case BC:
|
||||||
|
if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set
|
||||||
|
return fmt.Sprintf("B%s %s", args[1], args[2])
|
||||||
|
} else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set
|
||||||
|
return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2])
|
||||||
|
}
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
case BCCTR:
|
||||||
|
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||||
|
return "BR (CTR)"
|
||||||
|
}
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
case BCCTRL:
|
||||||
|
if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
|
||||||
|
return "BL (CTR)"
|
||||||
|
}
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
|
||||||
|
return op + " " + strings.Join(args, ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
|
||||||
|
// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
|
||||||
|
// of inst, it's ok to modify inst.Args here.
|
||||||
|
func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
|
||||||
|
// special cases for load/store instructions
|
||||||
|
if _, ok := arg.(Offset); ok {
|
||||||
|
if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
|
||||||
|
panic(fmt.Errorf("wrong table: offset not followed by register"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case Reg:
|
||||||
|
if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
if arg == R30 {
|
||||||
|
return "g"
|
||||||
|
}
|
||||||
|
return strings.ToUpper(arg.String())
|
||||||
|
case CondReg:
|
||||||
|
if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
|
||||||
|
return "" // don't show cr0 for cmp instructions
|
||||||
|
} else if arg >= CR0 {
|
||||||
|
return fmt.Sprintf("CR%d", int(arg-CR0))
|
||||||
|
}
|
||||||
|
bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4]
|
||||||
|
if arg <= Cond0SO {
|
||||||
|
return bit
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit)
|
||||||
|
case Imm:
|
||||||
|
return fmt.Sprintf("$%d", arg)
|
||||||
|
case SpReg:
|
||||||
|
switch arg {
|
||||||
|
case 8:
|
||||||
|
return "LR"
|
||||||
|
case 9:
|
||||||
|
return "CTR"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("SPR(%d)", int(arg))
|
||||||
|
case PCRel:
|
||||||
|
addr := pc + uint64(int64(arg))
|
||||||
|
if s, base := symname(addr); s != "" && base == addr {
|
||||||
|
return fmt.Sprintf("%s(SB)", s)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%#x", addr)
|
||||||
|
case Label:
|
||||||
|
return fmt.Sprintf("%#x", int(arg))
|
||||||
|
case Offset:
|
||||||
|
reg := inst.Args[argIndex+1].(Reg)
|
||||||
|
removeArg(inst, argIndex+1)
|
||||||
|
if reg == R0 {
|
||||||
|
return fmt.Sprintf("%d(0)", int(arg))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d(R%d)", int(arg), reg-R0)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("???(%v)", arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// revCondMap maps a conditional register bit to its inverse, if possible.
|
||||||
|
var revCondMap = map[string]string{
|
||||||
|
"LT": "GE", "GT": "LE", "EQ": "NE",
|
||||||
|
}
|
||||||
|
|
||||||
|
// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics.
|
||||||
|
var plan9OpMap = map[Op]string{
|
||||||
|
LBZ: "MOVBZ", STB: "MOVB",
|
||||||
|
LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled
|
||||||
|
LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH",
|
||||||
|
LHZU: "MOVHZU", STHU: "MOVHU",
|
||||||
|
LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW",
|
||||||
|
LWZU: "MOVWZU", STWU: "MOVWU",
|
||||||
|
LD: "MOVD", STD: "MOVD",
|
||||||
|
LDU: "MOVDU", STDU: "MOVDU",
|
||||||
|
MTSPR: "MOV", MFSPR: "MOV", // the width is ambiguous for SPRs
|
||||||
|
B: "BR",
|
||||||
|
CMPLD: "CMPU", CMPLW: "CMPWU",
|
||||||
|
CMPD: "CMP", CMPW: "CMPW",
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
25
src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/testdata/decode.txt
generated
vendored
Normal file
25
src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/testdata/decode.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
6d746162| gnu xoris r20,r11,24930
|
||||||
|
4c040000| gnu mcrf cr0,cr1
|
||||||
|
88000017| gnu lbz r0,23(0)
|
||||||
|
4abaa88a| gnu ba 0xfebaa888
|
||||||
|
7d8fc2a6| gnu mfspr r12,783
|
||||||
|
00000000| gnu error: unknown instruction
|
||||||
|
a1841e80| gnu lhz r12,7808(r4)
|
||||||
|
42093d10| gnu bc 16,4*cr2+gt,.+0x3d10
|
||||||
|
e38d5b90| gnu lq r28,23440(r13)
|
||||||
|
84127a20| gnu lwzu r0,31264(r18)
|
||||||
|
c61bb730| gnu lfsu f16,-18640(r27)
|
||||||
|
0825f440| gnu tdi 1,r5,-3008
|
||||||
|
a9a912c1| gnu lha r13,4801(r9)
|
||||||
|
ebb24fd1| gnu ldu r29,20432(r18)
|
||||||
|
b1ce0612| gnu sth r14,1554(r14)
|
||||||
|
f3d74322| gnu xvcvdpuxws v30,v40
|
||||||
|
945c62a2| gnu stwu r2,25250(r28)
|
||||||
|
9c8156e3| gnu stbu r4,22243(r1)
|
||||||
|
f91b9c7a| gnu stq r8,-25480(r27)
|
||||||
|
2c1c81b4| gnu cmpwi r28,-32332
|
||||||
|
f87b904d| gnu stdu r3,-28596(r27)
|
||||||
|
eab3c832| gnu lwa r21,-14288(r19)
|
||||||
|
4320336b| gnu bcla 25,lt,0x3368
|
||||||
|
7e40092e| gnu stwx r18,0,r1
|
||||||
|
7c103c2c| gnu lwbrx r0,r16,r7
|
||||||
|
|
@ -11,6 +11,12 @@
|
||||||
"local": "golang.org/x/arch/arm/armasm",
|
"local": "golang.org/x/arch/arm/armasm",
|
||||||
"revision": "ad6a463afcf9bd5b38c81fa9ba612dae11859d40",
|
"revision": "ad6a463afcf9bd5b38c81fa9ba612dae11859d40",
|
||||||
"revisionTime": "2015-08-28T15:42:14Z"
|
"revisionTime": "2015-08-28T15:42:14Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "golang.org/x/arch/ppc64/ppc64asm",
|
||||||
|
"local": "golang.org/x/arch/ppc64/ppc64asm",
|
||||||
|
"revision": "4831b0a617f7a819d4bf3c877d8e827d0283542c",
|
||||||
|
"revisionTime": "2016-10-12T18:28:04Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue