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