Home | History | Annotate | Download | only in ppc64asm
      1 // Copyright 2014 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Copied and simplified from rsc.io/arm/armasm/objdumpext_test.go.
      6 
      7 package ppc64asm
      8 
      9 import (
     10 	"bytes"
     11 	"debug/elf"
     12 	"encoding/binary"
     13 	"fmt"
     14 	"io"
     15 	"log"
     16 	"os"
     17 	"runtime"
     18 	"strconv"
     19 	"strings"
     20 	"testing"
     21 )
     22 
     23 const objdumpPath = "/usr/bin/objdump"
     24 
     25 func testObjdump(t *testing.T, generate func(func([]byte))) {
     26 	if testing.Short() {
     27 		t.Skip("skipping objdump test in short mode")
     28 	}
     29 	if runtime.GOARCH != "ppc64le" && runtime.GOARCH != "ppc64" {
     30 		t.Skip("skipping; test requires host tool objdump for ppc64 or ppc64le")
     31 	}
     32 	if _, err := os.Stat(objdumpPath); err != nil {
     33 		t.Skip(err)
     34 	}
     35 
     36 	testExtDis(t, "gnu", objdump, generate, allowedMismatchObjdump)
     37 }
     38 
     39 func objdump(ext *ExtDis) error {
     40 	// File already written with instructions; add ELF header.
     41 	if err := writeELF64(ext.File, ext.Size); err != nil {
     42 		return err
     43 	}
     44 
     45 	b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
     46 	if err != nil {
     47 		return err
     48 	}
     49 
     50 	var (
     51 		nmatch  int
     52 		reading bool
     53 		next    uint32 = start
     54 		addr    uint32
     55 		encbuf  [4]byte
     56 		enc     []byte
     57 		text    string
     58 	)
     59 	flush := func() {
     60 		if addr == next {
     61 			if m := pcrel.FindStringSubmatch(text); m != nil {
     62 				targ, _ := strconv.ParseUint(m[2], 16, 64)
     63 				text = fmt.Sprintf("%s.%+#x", m[1], int32(uint32(targ)-addr))
     64 			}
     65 			if strings.HasPrefix(text, "stmia") {
     66 				text = "stm" + text[5:]
     67 			}
     68 			if strings.HasPrefix(text, "stmfd") {
     69 				text = "stmdb" + text[5:]
     70 			}
     71 			if strings.HasPrefix(text, "ldmfd") {
     72 				text = "ldm" + text[5:]
     73 			}
     74 			text = strings.Replace(text, "#0.0", "#0", -1)
     75 			if text == "undefined" && len(enc) == 4 {
     76 				text = "error: unknown instruction"
     77 				enc = nil
     78 			}
     79 			if len(enc) == 4 {
     80 				// prints as word but we want to record bytes
     81 				enc[0], enc[3] = enc[3], enc[0]
     82 				enc[1], enc[2] = enc[2], enc[1]
     83 			}
     84 			ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
     85 			encbuf = [4]byte{}
     86 			enc = nil
     87 			next += 4
     88 		}
     89 	}
     90 	var textangle = []byte("<.text>:")
     91 	for {
     92 		line, err := b.ReadSlice('\n')
     93 		if err != nil {
     94 			if err == io.EOF {
     95 				break
     96 			}
     97 			return fmt.Errorf("reading objdump output: %v", err)
     98 		}
     99 		if bytes.Contains(line, textangle) {
    100 			reading = true
    101 			continue
    102 		}
    103 		if !reading {
    104 			continue
    105 		}
    106 		if debug {
    107 			os.Stdout.Write(line)
    108 		}
    109 		if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
    110 			enc = enc1
    111 			continue
    112 		}
    113 		flush()
    114 		nmatch++
    115 		addr, enc, text = parseLine(line, encbuf[:0])
    116 		if addr > next {
    117 			return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
    118 		}
    119 	}
    120 	flush()
    121 	if next != start+uint32(ext.Size) {
    122 		return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
    123 	}
    124 	if err := ext.Wait(); err != nil {
    125 		return fmt.Errorf("exec: %v", err)
    126 	}
    127 
    128 	return nil
    129 }
    130 
    131 var (
    132 	undefined      = []byte("<UNDEFINED>")
    133 	unpredictable  = []byte("<UNPREDICTABLE>")
    134 	illegalShifter = []byte("<illegal shifter operand>")
    135 )
    136 
    137 func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
    138 	oline := line
    139 	i := index(line, ":\t")
    140 	if i < 0 {
    141 		log.Fatalf("cannot parse disassembly: %q", oline)
    142 	}
    143 	x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
    144 	if err != nil {
    145 		log.Fatalf("cannot parse disassembly: %q", oline)
    146 	}
    147 	addr = uint32(x)
    148 	line = line[i+2:]
    149 	i = bytes.IndexByte(line, '\t')
    150 	if i < 0 {
    151 		log.Fatalf("cannot parse disassembly: %q", oline)
    152 	}
    153 	enc, ok := parseHex(line[:i], encstart)
    154 	if !ok {
    155 		log.Fatalf("cannot parse disassembly: %q", oline)
    156 	}
    157 	line = trimSpace(line[i:])
    158 	if bytes.Contains(line, undefined) {
    159 		text = "undefined"
    160 		return
    161 	}
    162 	if bytes.Contains(line, illegalShifter) {
    163 		text = "undefined"
    164 		return
    165 	}
    166 	if false && bytes.Contains(line, unpredictable) {
    167 		text = "unpredictable"
    168 		return
    169 	}
    170 	if i := bytes.IndexByte(line, ';'); i >= 0 {
    171 		line = trimSpace(line[:i])
    172 	}
    173 	text = string(fixSpace(line))
    174 	return
    175 }
    176 
    177 func parseContinuation(line []byte, enc []byte) []byte {
    178 	i := index(line, ":\t")
    179 	if i < 0 {
    180 		return nil
    181 	}
    182 	line = line[i+1:]
    183 	enc, _ = parseHex(line, enc)
    184 	return enc
    185 }
    186 
    187 // writeELF64 writes an ELF64 header to the file,
    188 // describing a text segment that starts at start
    189 // and extends for size bytes.
    190 func writeELF64(f *os.File, size int) error {
    191 	f.Seek(0, 0)
    192 	var hdr elf.Header64
    193 	var prog elf.Prog64
    194 	var sect elf.Section64
    195 	var buf bytes.Buffer
    196 	binary.Write(&buf, binary.BigEndian, &hdr)
    197 	off1 := buf.Len()
    198 	binary.Write(&buf, binary.BigEndian, &prog)
    199 	off2 := buf.Len()
    200 	binary.Write(&buf, binary.BigEndian, &sect)
    201 	off3 := buf.Len()
    202 	buf.Reset()
    203 	data := byte(elf.ELFDATA2MSB)
    204 	hdr = elf.Header64{
    205 		Ident:     [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
    206 		Type:      2,
    207 		Machine:   uint16(elf.EM_PPC64),
    208 		Version:   1,
    209 		Entry:     start,
    210 		Phoff:     uint64(off1),
    211 		Shoff:     uint64(off2),
    212 		Flags:     0x05000002,
    213 		Ehsize:    uint16(off1),
    214 		Phentsize: uint16(off2 - off1),
    215 		Phnum:     1,
    216 		Shentsize: uint16(off3 - off2),
    217 		Shnum:     3,
    218 		Shstrndx:  2,
    219 	}
    220 	binary.Write(&buf, binary.BigEndian, &hdr)
    221 	prog = elf.Prog64{
    222 		Type:   1,
    223 		Off:    start,
    224 		Vaddr:  start,
    225 		Paddr:  start,
    226 		Filesz: uint64(size),
    227 		Memsz:  uint64(size),
    228 		Flags:  5,
    229 		Align:  start,
    230 	}
    231 	binary.Write(&buf, binary.BigEndian, &prog)
    232 	binary.Write(&buf, binary.BigEndian, &sect) // NULL section
    233 	sect = elf.Section64{
    234 		Name:      1,
    235 		Type:      uint32(elf.SHT_PROGBITS),
    236 		Addr:      start,
    237 		Off:       start,
    238 		Size:      uint64(size),
    239 		Flags:     uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
    240 		Addralign: 4,
    241 	}
    242 	binary.Write(&buf, binary.BigEndian, &sect) // .text
    243 	sect = elf.Section64{
    244 		Name:      uint32(len("\x00.text\x00")),
    245 		Type:      uint32(elf.SHT_STRTAB),
    246 		Addr:      0,
    247 		Off:       uint64(off2 + (off3-off2)*3),
    248 		Size:      uint64(len("\x00.text\x00.shstrtab\x00")),
    249 		Addralign: 1,
    250 	}
    251 	binary.Write(&buf, binary.BigEndian, &sect)
    252 	buf.WriteString("\x00.text\x00.shstrtab\x00")
    253 	f.Write(buf.Bytes())
    254 	return nil
    255 }
    256