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