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