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