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