1
2
3
4
5
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
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
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
192
193
194 func writeELF32(f *os.File, size int) error {
195 f.Seek(0, io.SeekStart)
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, §)
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, §)
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
View as plain text