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