...

Source file src/golang.org/x/arch/arm64/arm64asm/objdumpext_test.go

Documentation: golang.org/x/arch/arm64/arm64asm

     1  // Copyright 2017 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 ../../arm/armasm/objdumpext_test.go.
     6  
     7  package arm64asm
     8  
     9  import (
    10  	"bytes"
    11  	"debug/elf"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"io"
    15  	"log"
    16  	"os"
    17  	"os/exec"
    18  	"strconv"
    19  	"strings"
    20  	"testing"
    21  )
    22  
    23  const objdumpPath = "/usr/bin/objdump"
    24  
    25  func testObjdumpARM64(t *testing.T, generate func(func([]byte))) {
    26  	testObjdumpArch(t, generate, ModeARM64)
    27  }
    28  
    29  func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) {
    30  	checkObjdumpAarch64(t)
    31  	testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
    32  	testExtDis(t, "plan9", arch, objdump, generate, allowedMismatchObjdump)
    33  }
    34  
    35  func checkObjdumpAarch64(t *testing.T) {
    36  	out, err := exec.Command(objdumpPath, "-i").Output()
    37  	if err != nil {
    38  		t.Skipf("cannot run objdump: %v\n%s", err, out)
    39  	}
    40  	if !strings.Contains(string(out), "aarch64") {
    41  		t.Skip("objdump does not have aarch64 support")
    42  	}
    43  }
    44  
    45  func objdump(ext *ExtDis) error {
    46  	// File already written with instructions; add ELF header.
    47  	if ext.Arch == ModeARM64 {
    48  		if err := writeELF64(ext.File, ext.Size); err != nil {
    49  			return err
    50  		}
    51  	} else {
    52  		panic("unknown arch")
    53  	}
    54  
    55  	b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	var (
    61  		nmatch  int
    62  		reading bool
    63  		next    uint64 = start
    64  		addr    uint64
    65  		encbuf  [4]byte
    66  		enc     []byte
    67  		text    string
    68  	)
    69  	flush := func() {
    70  		if addr == next {
    71  			// PC-relative addresses are translated to absolute addresses based on PC by GNU objdump
    72  			// Following logical rewrites the absolute addresses back to PC-relative ones for comparing
    73  			// with our disassembler output which are PC-relative
    74  
    75  			if m := pcrelprfmim.FindStringSubmatch(text); m != nil {
    76  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    77  				text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
    78  			}
    79  			if m := pcrelprfm.FindStringSubmatch(text); m != nil {
    80  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    81  				text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
    82  			}
    83  			if m := pcrelim.FindStringSubmatch(text); m != nil {
    84  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    85  				text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
    86  			}
    87  			if m := pcrelimzr.FindStringSubmatch(text); m != nil {
    88  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    89  				text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
    90  			}
    91  			if m := pcrelr.FindStringSubmatch(text); m != nil {
    92  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    93  				if strings.Contains(m[1], "adrp") {
    94  					text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
    95  				} else {
    96  					text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
    97  				}
    98  			}
    99  			if m := pcrelrzr.FindStringSubmatch(text); m != nil {
   100  				targ, _ := strconv.ParseUint(m[2], 16, 64)
   101  				if strings.Contains(m[1], "adrp") {
   102  					text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr&0xfffff000)))
   103  				} else {
   104  					text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
   105  				}
   106  			}
   107  			if m := pcrel.FindStringSubmatch(text); m != nil {
   108  				targ, _ := strconv.ParseUint(m[2], 16, 64)
   109  				text = fmt.Sprintf("%s .%+#x", m[1], uint64(targ-uint64(addr)))
   110  			}
   111  			if strings.HasPrefix(text, "mov") && strings.Contains(text, "//") {
   112  				s := strings.Split(text, " //")
   113  				text = s[0]
   114  			}
   115  			text = strings.Replace(text, "#0.0", "#0", -1)
   116  			if text == "undefined" && len(enc) == 4 {
   117  				text = "error: unknown instruction"
   118  				enc = nil
   119  			}
   120  			if len(enc) == 4 {
   121  				// prints as word but we want to record bytes
   122  				enc[0], enc[3] = enc[3], enc[0]
   123  				enc[1], enc[2] = enc[2], enc[1]
   124  			}
   125  			ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
   126  			encbuf = [4]byte{}
   127  			enc = nil
   128  			next += 4
   129  		}
   130  	}
   131  	var textangle = []byte("<.text>:")
   132  	for {
   133  		line, err := b.ReadSlice('\n')
   134  		if err != nil {
   135  			if err == io.EOF {
   136  				break
   137  			}
   138  			return fmt.Errorf("reading objdump output: %v", err)
   139  		}
   140  		if bytes.Contains(line, textangle) {
   141  			reading = true
   142  			continue
   143  		}
   144  		if !reading {
   145  			continue
   146  		}
   147  		if debug {
   148  			os.Stdout.Write(line)
   149  		}
   150  		if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
   151  			enc = enc1
   152  			continue
   153  		}
   154  		flush()
   155  		nmatch++
   156  		addr, enc, text = parseLine(line, encbuf[:0])
   157  		if addr > next {
   158  			return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
   159  		}
   160  	}
   161  	flush()
   162  	if next != start+uint64(ext.Size) {
   163  		return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
   164  	}
   165  	if err := ext.Wait(); err != nil {
   166  		return fmt.Errorf("exec: %v", err)
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  var (
   173  	undefined     = []byte("undefined")
   174  	unpredictable = []byte("unpredictable")
   175  	slashslash    = []byte("//")
   176  )
   177  
   178  func parseLine(line []byte, encstart []byte) (addr uint64, enc []byte, text string) {
   179  	ok := false
   180  	oline := line
   181  	i := index(line, ":\t")
   182  	if i < 0 {
   183  		log.Fatalf("cannot parse disassembly: %q", oline)
   184  	}
   185  	x, err := strconv.ParseUint(string(bytes.TrimSpace(line[:i])), 16, 32)
   186  	if err != nil {
   187  		log.Fatalf("cannot parse disassembly: %q", oline)
   188  	}
   189  	addr = uint64(x)
   190  	line = line[i+2:]
   191  	i = bytes.IndexByte(line, '\t')
   192  	if i < 0 {
   193  		log.Fatalf("cannot parse disassembly: %q", oline)
   194  	}
   195  	enc, ok = parseHex(line[:i], encstart)
   196  	if !ok {
   197  		log.Fatalf("cannot parse disassembly: %q", oline)
   198  	}
   199  	line = bytes.TrimSpace(line[i:])
   200  	if bytes.Contains(line, undefined) {
   201  		text = "undefined"
   202  		return
   203  	}
   204  	if false && bytes.Contains(line, unpredictable) {
   205  		text = "unpredictable"
   206  		return
   207  	}
   208  	// Strip trailing comment starting with ';'
   209  	//   e.g: "csinv x23, x2, x19, cc ; xxx"
   210  	if i := bytes.IndexByte(line, ';'); i >= 0 {
   211  		line = bytes.TrimSpace(line[:i])
   212  	}
   213  	// Strip trailing comment starting with "//"
   214  	//   e.g:  "fccmpe s2, s9, #0x7, ne // xxx"
   215  	if i := bytes.Index(line, slashslash); i >= 0 {
   216  		line = bytes.TrimSpace(line[:i])
   217  	}
   218  	text = string(fixSpace(line))
   219  	return
   220  }
   221  
   222  func parseContinuation(line []byte, enc []byte) []byte {
   223  	i := index(line, ":\t")
   224  	if i < 0 {
   225  		return nil
   226  	}
   227  	line = line[i+1:]
   228  	enc, _ = parseHex(line, enc)
   229  	return enc
   230  }
   231  
   232  // writeELF64 writes an ELF64 header to the file, describing a text
   233  // segment that starts at start (0x8000) and extends for size bytes.
   234  func writeELF64(f *os.File, size int) error {
   235  	f.Seek(0, io.SeekStart)
   236  	var hdr elf.Header64
   237  	var prog elf.Prog64
   238  	var sect elf.Section64
   239  	var buf bytes.Buffer
   240  	binary.Write(&buf, binary.LittleEndian, &hdr)
   241  	off1 := buf.Len()
   242  	binary.Write(&buf, binary.LittleEndian, &prog)
   243  	off2 := buf.Len()
   244  	binary.Write(&buf, binary.LittleEndian, &sect)
   245  	off3 := buf.Len()
   246  	buf.Reset()
   247  	data := byte(elf.ELFDATA2LSB)
   248  	hdr = elf.Header64{
   249  		Ident:     [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
   250  		Type:      2,
   251  		Machine:   uint16(elf.EM_AARCH64),
   252  		Version:   1,
   253  		Entry:     start,
   254  		Phoff:     uint64(off1),
   255  		Shoff:     uint64(off2),
   256  		Flags:     0x05000002,
   257  		Ehsize:    uint16(off1),
   258  		Phentsize: uint16(off2 - off1),
   259  		Phnum:     1,
   260  		Shentsize: uint16(off3 - off2),
   261  		Shnum:     3,
   262  		Shstrndx:  2,
   263  	}
   264  	binary.Write(&buf, binary.LittleEndian, &hdr)
   265  	prog = elf.Prog64{
   266  		Type:   1,
   267  		Off:    start,
   268  		Vaddr:  start,
   269  		Paddr:  start,
   270  		Filesz: uint64(size),
   271  		Memsz:  uint64(size),
   272  		Flags:  5,
   273  		Align:  start,
   274  	}
   275  	binary.Write(&buf, binary.LittleEndian, &prog)
   276  	binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
   277  	sect = elf.Section64{
   278  		Name:      1,
   279  		Type:      uint32(elf.SHT_PROGBITS),
   280  		Addr:      start,
   281  		Off:       start,
   282  		Size:      uint64(size),
   283  		Flags:     uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
   284  		Addralign: 4,
   285  	}
   286  	binary.Write(&buf, binary.LittleEndian, &sect) // .text
   287  	sect = elf.Section64{
   288  		Name:      uint32(len("\x00.text\x00")),
   289  		Type:      uint32(elf.SHT_STRTAB),
   290  		Addr:      0,
   291  		Off:       uint64(off2 + (off3-off2)*3),
   292  		Size:      uint64(len("\x00.text\x00.shstrtab\x00")),
   293  		Addralign: 1,
   294  	}
   295  	binary.Write(&buf, binary.LittleEndian, &sect)
   296  	buf.WriteString("\x00.text\x00.shstrtab\x00")
   297  	f.Write(buf.Bytes())
   298  	return nil
   299  }
   300  

View as plain text