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 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 // Apologies for the proprietary path, but we need objdump 2.24 + some committed patches that will land in 2.25. 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 // File already written with instructions; add ELF header. 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 // writeELF32 writes an ELF32 header to the file, 176 // describing a text segment that starts at start 177 // and extends for size bytes. 178 func writeELF32(f *os.File, size int) error { 179 f.Seek(0, 0) 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, §) // NULL section 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, §) // .text 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 // writeELF64 writes an ELF64 header to the file, 246 // describing a text segment that starts at start 247 // and extends for size bytes. 248 func writeELF64(f *os.File, size int) error { 249 f.Seek(0, 0) 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, §) // NULL section 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, §) // .text 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