1
2
3
4
5
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
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
72
73
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
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
209
210 if i := bytes.IndexByte(line, ';'); i >= 0 {
211 line = bytes.TrimSpace(line[:i])
212 }
213
214
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
233
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, §)
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, §)
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 }
300
View as plain text