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, §) 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, §) // 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, §) // .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, §) 252 buf.WriteString("\x00.text\x00.shstrtab\x00") 253 f.Write(buf.Bytes()) 254 return nil 255 } 256