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