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