github.com/ccccaoqing/test@v0.0.0-20220510085219-3985d23445c0/src/cmd/internal/rsc.io/x86/x86asm/objdumpext_test.go (about)

     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  package x86asm
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"testing"
    18  )
    19  
    20  // Apologies for the proprietary path, but we need objdump 2.24 + some committed patches that will land in 2.25.
    21  const objdumpPath = "/Users/rsc/bin/objdump2"
    22  
    23  func testObjdump32(t *testing.T, generate func(func([]byte))) {
    24  	testObjdumpArch(t, generate, 32)
    25  }
    26  
    27  func testObjdump64(t *testing.T, generate func(func([]byte))) {
    28  	testObjdumpArch(t, generate, 64)
    29  }
    30  
    31  func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch int) {
    32  	if testing.Short() {
    33  		t.Skip("skipping objdump test in short mode")
    34  	}
    35  
    36  	if _, err := os.Stat(objdumpPath); err != nil {
    37  		t.Fatal(err)
    38  	}
    39  
    40  	testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
    41  }
    42  
    43  func objdump(ext *ExtDis) error {
    44  	// File already written with instructions; add ELF header.
    45  	if ext.Arch == 32 {
    46  		if err := writeELF32(ext.File, ext.Size); err != nil {
    47  			return err
    48  		}
    49  	} else {
    50  		if err := writeELF64(ext.File, ext.Size); err != nil {
    51  			return err
    52  		}
    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    uint32 = start
    64  		addr    uint32
    65  		encbuf  [32]byte
    66  		enc     []byte
    67  		text    string
    68  	)
    69  	flush := func() {
    70  		if addr == next {
    71  			switch text {
    72  			case "repz":
    73  				text = "rep"
    74  			case "repnz":
    75  				text = "repn"
    76  			default:
    77  				text = strings.Replace(text, "repz ", "rep ", -1)
    78  				text = strings.Replace(text, "repnz ", "repn ", -1)
    79  			}
    80  			if m := pcrelw.FindStringSubmatch(text); m != nil {
    81  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    82  				text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
    83  			}
    84  			if m := pcrel.FindStringSubmatch(text); m != nil {
    85  				targ, _ := strconv.ParseUint(m[2], 16, 64)
    86  				text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
    87  			}
    88  			text = strings.Replace(text, "0x0(", "(", -1)
    89  			text = strings.Replace(text, "%st(0)", "%st", -1)
    90  
    91  			ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
    92  			encbuf = [32]byte{}
    93  			enc = nil
    94  			next += 32
    95  		}
    96  	}
    97  	var textangle = []byte("<.text>:")
    98  	for {
    99  		line, err := b.ReadSlice('\n')
   100  		if err != nil {
   101  			if err == io.EOF {
   102  				break
   103  			}
   104  			return fmt.Errorf("reading objdump output: %v", err)
   105  		}
   106  		if bytes.Contains(line, textangle) {
   107  			reading = true
   108  			continue
   109  		}
   110  		if !reading {
   111  			continue
   112  		}
   113  		if debug {
   114  			os.Stdout.Write(line)
   115  		}
   116  		if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
   117  			enc = enc1
   118  			continue
   119  		}
   120  		flush()
   121  		nmatch++
   122  		addr, enc, text = parseLine(line, encbuf[:0])
   123  		if addr > next {
   124  			return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
   125  		}
   126  	}
   127  	flush()
   128  	if next != start+uint32(ext.Size) {
   129  		return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
   130  	}
   131  	if err := ext.Wait(); err != nil {
   132  		return fmt.Errorf("exec: %v", err)
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
   139  	oline := line
   140  	i := index(line, ":\t")
   141  	if i < 0 {
   142  		log.Fatalf("cannot parse disassembly: %q", oline)
   143  	}
   144  	x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
   145  	if err != nil {
   146  		log.Fatalf("cannot parse disassembly: %q", oline)
   147  	}
   148  	addr = uint32(x)
   149  	line = line[i+2:]
   150  	i = bytes.IndexByte(line, '\t')
   151  	if i < 0 {
   152  		log.Fatalf("cannot parse disassembly: %q", oline)
   153  	}
   154  	enc, ok := parseHex(line[:i], encstart)
   155  	if !ok {
   156  		log.Fatalf("cannot parse disassembly: %q", oline)
   157  	}
   158  	line = trimSpace(line[i:])
   159  	if i := bytes.IndexByte(line, '#'); i >= 0 {
   160  		line = trimSpace(line[:i])
   161  	}
   162  	text = string(fixSpace(line))
   163  	return
   164  }
   165  
   166  func parseContinuation(line []byte, enc []byte) []byte {
   167  	i := index(line, ":\t")
   168  	if i < 0 {
   169  		return nil
   170  	}
   171  	line = line[i+1:]
   172  	enc, _ = parseHex(line, enc)
   173  	return enc
   174  }
   175  
   176  // writeELF32 writes an ELF32 header to the file,
   177  // describing a text segment that starts at start
   178  // and extends for size bytes.
   179  func writeELF32(f *os.File, size int) error {
   180  	f.Seek(0, 0)
   181  	var hdr elf.Header32
   182  	var prog elf.Prog32
   183  	var sect elf.Section32
   184  	var buf bytes.Buffer
   185  	binary.Write(&buf, binary.LittleEndian, &hdr)
   186  	off1 := buf.Len()
   187  	binary.Write(&buf, binary.LittleEndian, &prog)
   188  	off2 := buf.Len()
   189  	binary.Write(&buf, binary.LittleEndian, &sect)
   190  	off3 := buf.Len()
   191  	buf.Reset()
   192  	data := byte(elf.ELFDATA2LSB)
   193  	hdr = elf.Header32{
   194  		Ident:     [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
   195  		Type:      2,
   196  		Machine:   uint16(elf.EM_386),
   197  		Version:   1,
   198  		Entry:     start,
   199  		Phoff:     uint32(off1),
   200  		Shoff:     uint32(off2),
   201  		Flags:     0x05000002,
   202  		Ehsize:    uint16(off1),
   203  		Phentsize: uint16(off2 - off1),
   204  		Phnum:     1,
   205  		Shentsize: uint16(off3 - off2),
   206  		Shnum:     3,
   207  		Shstrndx:  2,
   208  	}
   209  	binary.Write(&buf, binary.LittleEndian, &hdr)
   210  	prog = elf.Prog32{
   211  		Type:   1,
   212  		Off:    start,
   213  		Vaddr:  start,
   214  		Paddr:  start,
   215  		Filesz: uint32(size),
   216  		Memsz:  uint32(size),
   217  		Flags:  5,
   218  		Align:  start,
   219  	}
   220  	binary.Write(&buf, binary.LittleEndian, &prog)
   221  	binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
   222  	sect = elf.Section32{
   223  		Name:      1,
   224  		Type:      uint32(elf.SHT_PROGBITS),
   225  		Addr:      start,
   226  		Off:       start,
   227  		Size:      uint32(size),
   228  		Flags:     uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
   229  		Addralign: 4,
   230  	}
   231  	binary.Write(&buf, binary.LittleEndian, &sect) // .text
   232  	sect = elf.Section32{
   233  		Name:      uint32(len("\x00.text\x00")),
   234  		Type:      uint32(elf.SHT_STRTAB),
   235  		Addr:      0,
   236  		Off:       uint32(off2 + (off3-off2)*3),
   237  		Size:      uint32(len("\x00.text\x00.shstrtab\x00")),
   238  		Addralign: 1,
   239  	}
   240  	binary.Write(&buf, binary.LittleEndian, &sect)
   241  	buf.WriteString("\x00.text\x00.shstrtab\x00")
   242  	f.Write(buf.Bytes())
   243  	return nil
   244  }
   245  
   246  // writeELF64 writes an ELF64 header to the file,
   247  // describing a text segment that starts at start
   248  // and extends for size bytes.
   249  func writeELF64(f *os.File, size int) error {
   250  	f.Seek(0, 0)
   251  	var hdr elf.Header64
   252  	var prog elf.Prog64
   253  	var sect elf.Section64
   254  	var buf bytes.Buffer
   255  	binary.Write(&buf, binary.LittleEndian, &hdr)
   256  	off1 := buf.Len()
   257  	binary.Write(&buf, binary.LittleEndian, &prog)
   258  	off2 := buf.Len()
   259  	binary.Write(&buf, binary.LittleEndian, &sect)
   260  	off3 := buf.Len()
   261  	buf.Reset()
   262  	data := byte(elf.ELFDATA2LSB)
   263  	hdr = elf.Header64{
   264  		Ident:     [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
   265  		Type:      2,
   266  		Machine:   uint16(elf.EM_X86_64),
   267  		Version:   1,
   268  		Entry:     start,
   269  		Phoff:     uint64(off1),
   270  		Shoff:     uint64(off2),
   271  		Flags:     0x05000002,
   272  		Ehsize:    uint16(off1),
   273  		Phentsize: uint16(off2 - off1),
   274  		Phnum:     1,
   275  		Shentsize: uint16(off3 - off2),
   276  		Shnum:     3,
   277  		Shstrndx:  2,
   278  	}
   279  	binary.Write(&buf, binary.LittleEndian, &hdr)
   280  	prog = elf.Prog64{
   281  		Type:   1,
   282  		Off:    start,
   283  		Vaddr:  start,
   284  		Paddr:  start,
   285  		Filesz: uint64(size),
   286  		Memsz:  uint64(size),
   287  		Flags:  5,
   288  		Align:  start,
   289  	}
   290  	binary.Write(&buf, binary.LittleEndian, &prog)
   291  	binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
   292  	sect = elf.Section64{
   293  		Name:      1,
   294  		Type:      uint32(elf.SHT_PROGBITS),
   295  		Addr:      start,
   296  		Off:       start,
   297  		Size:      uint64(size),
   298  		Flags:     uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
   299  		Addralign: 4,
   300  	}
   301  	binary.Write(&buf, binary.LittleEndian, &sect) // .text
   302  	sect = elf.Section64{
   303  		Name:      uint32(len("\x00.text\x00")),
   304  		Type:      uint32(elf.SHT_STRTAB),
   305  		Addr:      0,
   306  		Off:       uint64(off2 + (off3-off2)*3),
   307  		Size:      uint64(len("\x00.text\x00.shstrtab\x00")),
   308  		Addralign: 1,
   309  	}
   310  	binary.Write(&buf, binary.LittleEndian, &sect)
   311  	buf.WriteString("\x00.text\x00.shstrtab\x00")
   312  	f.Write(buf.Bytes())
   313  	return nil
   314  }