1
2
3
4
5 package x86asm
6
7 import (
8 "bytes"
9 "debug/elf"
10 "encoding/binary"
11 "fmt"
12 "io"
13 "log"
14 "os"
15 "strconv"
16 "strings"
17 "testing"
18 )
19
20
21 const objdumpPath = "/Users/rsc/bin/objdump2"
22
23 func testObjdump32(t *testing.T, generate func(func([]byte))) {
24 testObjdumpArch(t, generate, 32)
25 }
26
27 func testObjdump64(t *testing.T, generate func(func([]byte))) {
28 testObjdumpArch(t, generate, 64)
29 }
30
31 func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch int) {
32 if testing.Short() {
33 t.Skip("skipping objdump test in short mode")
34 }
35 if _, err := os.Stat(objdumpPath); err != nil {
36 t.Skip(err)
37 }
38
39 testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
40 }
41
42 func objdump(ext *ExtDis) error {
43
44 if ext.Arch == 32 {
45 if err := writeELF32(ext.File, ext.Size); err != nil {
46 return err
47 }
48 } else {
49 if err := writeELF64(ext.File, ext.Size); err != nil {
50 return err
51 }
52 }
53
54 b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
55 if err != nil {
56 return err
57 }
58
59 var (
60 nmatch int
61 reading bool
62 next uint32 = start
63 addr uint32
64 encbuf [32]byte
65 enc []byte
66 text string
67 )
68 flush := func() {
69 if addr == next {
70 switch text {
71 case "repz":
72 text = "rep"
73 case "repnz":
74 text = "repn"
75 default:
76 text = strings.Replace(text, "repz ", "rep ", -1)
77 text = strings.Replace(text, "repnz ", "repn ", -1)
78 }
79 if m := pcrelw.FindStringSubmatch(text); m != nil {
80 targ, _ := strconv.ParseUint(m[2], 16, 64)
81 text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
82 }
83 if m := pcrel.FindStringSubmatch(text); m != nil {
84 targ, _ := strconv.ParseUint(m[2], 16, 64)
85 text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
86 }
87 text = strings.Replace(text, "0x0(", "(", -1)
88 text = strings.Replace(text, "%st(0)", "%st", -1)
89
90 ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
91 encbuf = [32]byte{}
92 enc = nil
93 next += 32
94 }
95 }
96 var textangle = []byte("<.text>:")
97 for {
98 line, err := b.ReadSlice('\n')
99 if err != nil {
100 if err == io.EOF {
101 break
102 }
103 return fmt.Errorf("reading objdump output: %v", err)
104 }
105 if bytes.Contains(line, textangle) {
106 reading = true
107 continue
108 }
109 if !reading {
110 continue
111 }
112 if debug {
113 os.Stdout.Write(line)
114 }
115 if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
116 enc = enc1
117 continue
118 }
119 flush()
120 nmatch++
121 addr, enc, text = parseLine(line, encbuf[:0])
122 if addr > next {
123 return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
124 }
125 }
126 flush()
127 if next != start+uint32(ext.Size) {
128 return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
129 }
130 if err := ext.Wait(); err != nil {
131 return fmt.Errorf("exec: %v", err)
132 }
133
134 return nil
135 }
136
137 func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
138 oline := line
139 i := index(line, ":\t")
140 if i < 0 {
141 log.Fatalf("cannot parse disassembly: %q", oline)
142 }
143 x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
144 if err != nil {
145 log.Fatalf("cannot parse disassembly: %q", oline)
146 }
147 addr = uint32(x)
148 line = line[i+2:]
149 i = bytes.IndexByte(line, '\t')
150 if i < 0 {
151 log.Fatalf("cannot parse disassembly: %q", oline)
152 }
153 enc, ok := parseHex(line[:i], encstart)
154 if !ok {
155 log.Fatalf("cannot parse disassembly: %q", oline)
156 }
157 line = trimSpace(line[i:])
158 if i := bytes.IndexByte(line, '#'); i >= 0 {
159 line = trimSpace(line[:i])
160 }
161 text = string(fixSpace(line))
162 return
163 }
164
165 func parseContinuation(line []byte, enc []byte) []byte {
166 i := index(line, ":\t")
167 if i < 0 {
168 return nil
169 }
170 line = line[i+1:]
171 enc, _ = parseHex(line, enc)
172 return enc
173 }
174
175
176
177
178 func writeELF32(f *os.File, size int) error {
179 f.Seek(0, io.SeekStart)
180 var hdr elf.Header32
181 var prog elf.Prog32
182 var sect elf.Section32
183 var buf bytes.Buffer
184 binary.Write(&buf, binary.LittleEndian, &hdr)
185 off1 := buf.Len()
186 binary.Write(&buf, binary.LittleEndian, &prog)
187 off2 := buf.Len()
188 binary.Write(&buf, binary.LittleEndian, §)
189 off3 := buf.Len()
190 buf.Reset()
191 data := byte(elf.ELFDATA2LSB)
192 hdr = elf.Header32{
193 Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
194 Type: 2,
195 Machine: uint16(elf.EM_386),
196 Version: 1,
197 Entry: start,
198 Phoff: uint32(off1),
199 Shoff: uint32(off2),
200 Flags: 0x05000002,
201 Ehsize: uint16(off1),
202 Phentsize: uint16(off2 - off1),
203 Phnum: 1,
204 Shentsize: uint16(off3 - off2),
205 Shnum: 3,
206 Shstrndx: 2,
207 }
208 binary.Write(&buf, binary.LittleEndian, &hdr)
209 prog = elf.Prog32{
210 Type: 1,
211 Off: start,
212 Vaddr: start,
213 Paddr: start,
214 Filesz: uint32(size),
215 Memsz: uint32(size),
216 Flags: 5,
217 Align: start,
218 }
219 binary.Write(&buf, binary.LittleEndian, &prog)
220 binary.Write(&buf, binary.LittleEndian, §)
221 sect = elf.Section32{
222 Name: 1,
223 Type: uint32(elf.SHT_PROGBITS),
224 Addr: start,
225 Off: start,
226 Size: uint32(size),
227 Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
228 Addralign: 4,
229 }
230 binary.Write(&buf, binary.LittleEndian, §)
231 sect = elf.Section32{
232 Name: uint32(len("\x00.text\x00")),
233 Type: uint32(elf.SHT_STRTAB),
234 Addr: 0,
235 Off: uint32(off2 + (off3-off2)*3),
236 Size: uint32(len("\x00.text\x00.shstrtab\x00")),
237 Addralign: 1,
238 }
239 binary.Write(&buf, binary.LittleEndian, §)
240 buf.WriteString("\x00.text\x00.shstrtab\x00")
241 f.Write(buf.Bytes())
242 return nil
243 }
244
245
246
247
248 func writeELF64(f *os.File, size int) error {
249 f.Seek(0, io.SeekStart)
250 var hdr elf.Header64
251 var prog elf.Prog64
252 var sect elf.Section64
253 var buf bytes.Buffer
254 binary.Write(&buf, binary.LittleEndian, &hdr)
255 off1 := buf.Len()
256 binary.Write(&buf, binary.LittleEndian, &prog)
257 off2 := buf.Len()
258 binary.Write(&buf, binary.LittleEndian, §)
259 off3 := buf.Len()
260 buf.Reset()
261 data := byte(elf.ELFDATA2LSB)
262 hdr = elf.Header64{
263 Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
264 Type: 2,
265 Machine: uint16(elf.EM_X86_64),
266 Version: 1,
267 Entry: start,
268 Phoff: uint64(off1),
269 Shoff: uint64(off2),
270 Flags: 0x05000002,
271 Ehsize: uint16(off1),
272 Phentsize: uint16(off2 - off1),
273 Phnum: 1,
274 Shentsize: uint16(off3 - off2),
275 Shnum: 3,
276 Shstrndx: 2,
277 }
278 binary.Write(&buf, binary.LittleEndian, &hdr)
279 prog = elf.Prog64{
280 Type: 1,
281 Off: start,
282 Vaddr: start,
283 Paddr: start,
284 Filesz: uint64(size),
285 Memsz: uint64(size),
286 Flags: 5,
287 Align: start,
288 }
289 binary.Write(&buf, binary.LittleEndian, &prog)
290 binary.Write(&buf, binary.LittleEndian, §)
291 sect = elf.Section64{
292 Name: 1,
293 Type: uint32(elf.SHT_PROGBITS),
294 Addr: start,
295 Off: start,
296 Size: uint64(size),
297 Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
298 Addralign: 4,
299 }
300 binary.Write(&buf, binary.LittleEndian, §)
301 sect = elf.Section64{
302 Name: uint32(len("\x00.text\x00")),
303 Type: uint32(elf.SHT_STRTAB),
304 Addr: 0,
305 Off: uint64(off2 + (off3-off2)*3),
306 Size: uint64(len("\x00.text\x00.shstrtab\x00")),
307 Addralign: 1,
308 }
309 binary.Write(&buf, binary.LittleEndian, §)
310 buf.WriteString("\x00.text\x00.shstrtab\x00")
311 f.Write(buf.Bytes())
312 return nil
313 }
314
View as plain text