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