golang.org/x/arch@v0.17.0/arm64/arm64asm/objdumpext_test.go (about) 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, §) 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, §) // 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, §) // .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, §) 296 buf.WriteString("\x00.text\x00.shstrtab\x00") 297 f.Write(buf.Bytes()) 298 return nil 299 }