gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/gdbserial/gdbserver_conn.go (about) 1 package gdbserial 2 3 import ( 4 "bufio" 5 "bytes" 6 "debug/macho" 7 "encoding/json" 8 "encoding/xml" 9 "errors" 10 "fmt" 11 "io" 12 "net" 13 "os" 14 "strconv" 15 "strings" 16 "time" 17 18 "gitlab.com/Raven-IO/raven-delve/pkg/logflags" 19 "gitlab.com/Raven-IO/raven-delve/pkg/proc" 20 ) 21 22 type gdbConn struct { 23 conn net.Conn 24 rdr *bufio.Reader 25 26 inbuf []byte 27 outbuf bytes.Buffer 28 29 running bool 30 31 direction proc.Direction // direction of execution 32 33 packetSize int // maximum packet size supported by stub 34 regsInfo []gdbRegisterInfo // list of registers 35 36 workaroundReg *gdbRegisterInfo // used to work-around a register setting bug in debugserver, see use in gdbserver.go 37 38 pid int // cache process id 39 40 ack bool // when ack is true acknowledgment packets are enabled 41 multiprocess bool // multiprocess extensions are active 42 maxTransmitAttempts int // maximum number of transmit or receive attempts when bad checksums are read 43 threadSuffixSupported bool // thread suffix supported by stub 44 isDebugserver bool // true if the stub is debugserver 45 xcmdok bool // x command can be used to transfer memory 46 goarch string 47 goos string 48 49 useXcmd bool // forces writeMemory to use the 'X' command 50 51 log logflags.Logger 52 } 53 54 var ErrTooManyAttempts = errors.New("too many transmit attempts") 55 56 // GdbProtocolError is an error response (Exx) of Gdb Remote Serial Protocol 57 // or an "unsupported command" response (empty packet). 58 type GdbProtocolError struct { 59 context string 60 cmd string 61 code string 62 } 63 64 func (err *GdbProtocolError) Error() string { 65 cmd := err.cmd 66 if len(cmd) > 20 { 67 cmd = cmd[:20] + "..." 68 } 69 if err.code == "" { 70 return fmt.Sprintf("unsupported packet %s during %s", cmd, err.context) 71 } 72 return fmt.Sprintf("protocol error %s during %s for packet %s", err.code, err.context, cmd) 73 } 74 75 func isProtocolErrorUnsupported(err error) bool { 76 gdberr, ok := err.(*GdbProtocolError) 77 if !ok { 78 return false 79 } 80 return gdberr.code == "" 81 } 82 83 // GdbMalformedThreadIDError is returned when the stub responds with a 84 // thread ID that does not conform with the Gdb Remote Serial Protocol 85 // specification. 86 type GdbMalformedThreadIDError struct { 87 tid string 88 } 89 90 func (err *GdbMalformedThreadIDError) Error() string { 91 return fmt.Sprintf("malformed thread ID %q", err.tid) 92 } 93 94 const ( 95 qSupportedSimple = "$qSupported:swbreak+;hwbreak+;no-resumed+;xmlRegisters=i386" 96 qSupportedMultiprocess = "$qSupported:multiprocess+;swbreak+;hwbreak+;no-resumed+;xmlRegisters=i386" 97 ) 98 99 func (conn *gdbConn) handshake(regnames *gdbRegnames) error { 100 conn.ack = true 101 conn.packetSize = 256 102 conn.rdr = bufio.NewReader(conn.conn) 103 104 // This first ack packet is needed to start up the connection 105 conn.sendack('+') 106 107 conn.disableAck() 108 109 // Try to enable thread suffixes for the command 'g' and 'p' 110 if _, err := conn.exec([]byte("$QThreadSuffixSupported"), "init"); err != nil { 111 if isProtocolErrorUnsupported(err) { 112 conn.threadSuffixSupported = false 113 } else { 114 return err 115 } 116 } else { 117 conn.threadSuffixSupported = true 118 } 119 120 if !conn.threadSuffixSupported { 121 features, err := conn.qSupported(true) 122 if err != nil { 123 return err 124 } 125 conn.multiprocess = features["multiprocess"] 126 127 // for some reason gdbserver won't let us read target.xml unless first we 128 // select a thread. 129 if conn.multiprocess { 130 conn.exec([]byte("$Hgp0.0"), "init") 131 } else { 132 conn.exec([]byte("$Hgp0"), "init") 133 } 134 } else { 135 // execute qSupported with the multiprocess feature disabled (the 136 // interaction of thread suffixes and multiprocess is not documented), we 137 // only need this call to configure conn.packetSize. 138 if _, err := conn.qSupported(false); err != nil { 139 return err 140 } 141 } 142 143 // Attempt to figure out the name of the processor register. 144 // We either need qXfer:features:read (gdbserver/rr) or qRegisterInfo (lldb) 145 regFound := map[string]bool{ 146 regnames.PC: false, 147 regnames.SP: false, 148 regnames.BP: false, 149 regnames.CX: false, 150 } 151 if err := conn.readRegisterInfo(regFound); err != nil { 152 if isProtocolErrorUnsupported(err) { 153 if err := conn.readTargetXml(regFound); err != nil { 154 return err 155 } 156 } else { 157 return err 158 } 159 } 160 for n := range regFound { 161 if n == "" { 162 continue 163 } 164 if !regFound[n] { 165 return fmt.Errorf("could not find %s register", n) 166 } 167 } 168 169 // We either need: 170 // * QListThreadsInStopReply + qThreadStopInfo (i.e. lldb-server/debugserver), 171 // * or a stub that runs the inferior in single threaded mode (i.e. rr). 172 // Otherwise we'll have problems handling breakpoints in multithreaded programs. 173 if _, err := conn.exec([]byte("$QListThreadsInStopReply"), "init"); err != nil { 174 gdberr, ok := err.(*GdbProtocolError) 175 if !ok { 176 return err 177 } 178 if gdberr.code != "" { 179 return err 180 } 181 } 182 183 if resp, err := conn.exec([]byte("$x0,0"), "init"); err == nil && string(resp) == "OK" { 184 conn.xcmdok = true 185 } 186 187 return nil 188 } 189 190 // qSupported interprets qSupported responses. 191 func (conn *gdbConn) qSupported(multiprocess bool) (features map[string]bool, err error) { 192 q := qSupportedSimple 193 if multiprocess { 194 q = qSupportedMultiprocess 195 } 196 respBuf, err := conn.exec([]byte(q), "init/qSupported") 197 if err != nil { 198 return nil, err 199 } 200 resp := strings.Split(string(respBuf), ";") 201 features = make(map[string]bool) 202 for _, stubfeature := range resp { 203 if len(stubfeature) == 0 { 204 continue 205 } else if equal := strings.Index(stubfeature, "="); equal >= 0 { 206 if stubfeature[:equal] == "PacketSize" { 207 if n, err := strconv.ParseInt(stubfeature[equal+1:], 16, 64); err == nil { 208 conn.packetSize = int(n) 209 } 210 } 211 } else if stubfeature[len(stubfeature)-1] == '+' { 212 features[stubfeature[:len(stubfeature)-1]] = true 213 } 214 } 215 return features, nil 216 } 217 218 // disableAck disables protocol acks. 219 func (conn *gdbConn) disableAck() error { 220 _, err := conn.exec([]byte("$QStartNoAckMode"), "init/disableAck") 221 if err == nil { 222 conn.ack = false 223 } 224 return err 225 } 226 227 // gdbTarget is a struct type used to parse target.xml 228 type gdbTarget struct { 229 Includes []gdbTargetInclude `xml:"xi include"` 230 Registers []gdbRegisterInfo `xml:"reg"` 231 } 232 233 type gdbTargetInclude struct { 234 Href string `xml:"href,attr"` 235 } 236 237 type gdbRegisterInfo struct { 238 Name string `xml:"name,attr"` 239 Bitsize int `xml:"bitsize,attr"` 240 Offset int 241 Regnum int `xml:"regnum,attr"` 242 Group string `xml:"group,attr"` 243 244 ignoreOnWrite bool 245 } 246 247 func setRegFound(regFound map[string]bool, name string) { 248 for n := range regFound { 249 if name == n { 250 regFound[n] = true 251 } 252 } 253 } 254 255 // readTargetXml reads target.xml file from stub using qXfer:features:read, 256 // then parses it requesting any additional files. 257 // The schema of target.xml is described by: 258 // 259 // https://github.com/bminor/binutils-gdb/blob/61baf725eca99af2569262d10aca03dcde2698f6/gdb/features/gdb-target.dtd 260 func (conn *gdbConn) readTargetXml(regFound map[string]bool) (err error) { 261 conn.regsInfo, err = conn.readAnnex("target.xml") 262 if err != nil { 263 return err 264 } 265 var offset int 266 regnum := 0 267 for i := range conn.regsInfo { 268 if conn.regsInfo[i].Regnum == 0 { 269 conn.regsInfo[i].Regnum = regnum 270 } else { 271 regnum = conn.regsInfo[i].Regnum 272 } 273 conn.regsInfo[i].Offset = offset 274 offset += conn.regsInfo[i].Bitsize / 8 275 276 setRegFound(regFound, conn.regsInfo[i].Name) 277 regnum++ 278 } 279 280 return nil 281 } 282 283 // readRegisterInfo uses qRegisterInfo to read register information (used 284 // when qXfer:feature:read is not supported). 285 func (conn *gdbConn) readRegisterInfo(regFound map[string]bool) (err error) { 286 regnum := 0 287 for { 288 conn.outbuf.Reset() 289 fmt.Fprintf(&conn.outbuf, "$qRegisterInfo%x", regnum) 290 respbytes, err := conn.exec(conn.outbuf.Bytes(), "register info") 291 if err != nil { 292 if regnum == 0 { 293 return err 294 } 295 break 296 } 297 298 var regname string 299 var offset int 300 var bitsize int 301 var contained bool 302 var ignoreOnWrite bool 303 304 resp := string(respbytes) 305 for { 306 semicolon := strings.Index(resp, ";") 307 keyval := resp 308 if semicolon >= 0 { 309 keyval = resp[:semicolon] 310 } 311 312 colon := strings.Index(keyval, ":") 313 if colon >= 0 { 314 name := keyval[:colon] 315 value := keyval[colon+1:] 316 317 switch name { 318 case "name": 319 regname = value 320 case "offset": 321 offset, _ = strconv.Atoi(value) 322 case "bitsize": 323 bitsize, _ = strconv.Atoi(value) 324 case "container-regs": 325 contained = true 326 case "set": 327 if value == "Exception State Registers" || value == "AMX Registers" { 328 // debugserver doesn't like it if we try to write these 329 ignoreOnWrite = true 330 } 331 } 332 } 333 334 if semicolon < 0 { 335 break 336 } 337 resp = resp[semicolon+1:] 338 } 339 340 if contained { 341 if regname == "xmm0" { 342 conn.workaroundReg = &gdbRegisterInfo{Regnum: regnum, Name: regname, Bitsize: bitsize, Offset: offset, ignoreOnWrite: ignoreOnWrite} 343 } 344 regnum++ 345 continue 346 } 347 348 setRegFound(regFound, regname) 349 350 conn.regsInfo = append(conn.regsInfo, gdbRegisterInfo{Regnum: regnum, Name: regname, Bitsize: bitsize, Offset: offset, ignoreOnWrite: ignoreOnWrite}) 351 352 regnum++ 353 } 354 355 return nil 356 } 357 358 func (conn *gdbConn) readAnnex(annex string) ([]gdbRegisterInfo, error) { 359 tgtbuf, err := conn.qXfer("features", annex, false) 360 if err != nil { 361 return nil, err 362 } 363 var tgt gdbTarget 364 if err := xml.Unmarshal(tgtbuf, &tgt); err != nil { 365 return nil, err 366 } 367 368 for _, incl := range tgt.Includes { 369 regs, err := conn.readAnnex(incl.Href) 370 if err != nil { 371 return nil, err 372 } 373 tgt.Registers = append(tgt.Registers, regs...) 374 } 375 return tgt.Registers, nil 376 } 377 378 func (conn *gdbConn) readExecFile() (string, error) { 379 outbuf, err := conn.qXfer("exec-file", "", true) 380 if err != nil { 381 return "", err 382 } 383 return string(outbuf), nil 384 } 385 386 func (conn *gdbConn) readAuxv() ([]byte, error) { 387 return conn.qXfer("auxv", "", true) 388 } 389 390 // qXfer executes a 'qXfer' read with the specified kind (i.e. feature, 391 // exec-file, etc...) and annex. 392 func (conn *gdbConn) qXfer(kind, annex string, binary bool) ([]byte, error) { 393 out := []byte{} 394 for { 395 cmd := []byte(fmt.Sprintf("$qXfer:%s:read:%s:%x,fff", kind, annex, len(out))) 396 err := conn.send(cmd) 397 if err != nil { 398 return nil, err 399 } 400 buf, err := conn.recv(cmd, "target features transfer", binary) 401 if err != nil { 402 return nil, err 403 } 404 405 out = append(out, buf[1:]...) 406 if buf[0] == 'l' { 407 break 408 } 409 } 410 return out, nil 411 } 412 413 // qXferWrite executes a 'qXfer' write with the specified kind and annex. 414 func (conn *gdbConn) qXferWrite(kind, annex string) error { 415 conn.outbuf.Reset() 416 fmt.Fprintf(&conn.outbuf, "$qXfer:%s:write:%s:0:", kind, annex) 417 //TODO(aarzilli): if we ever actually need to write something with qXfer, 418 //this will need to be implemented properly. At the moment it is only used 419 //for a fake write to the siginfo kind, to end a diversion in 'rr'. 420 _, err := conn.exec(conn.outbuf.Bytes(), "qXfer") 421 return err 422 } 423 424 type breakpointType uint8 425 426 const ( 427 swBreakpoint breakpointType = 0 428 hwBreakpoint breakpointType = 1 429 writeWatchpoint breakpointType = 2 430 readWatchpoint breakpointType = 3 431 accessWatchpoint breakpointType = 4 432 ) 433 434 // setBreakpoint executes a 'Z' (insert breakpoint) command of type '0' and kind '1' or '4' 435 func (conn *gdbConn) setBreakpoint(addr uint64, typ breakpointType, kind int) error { 436 conn.outbuf.Reset() 437 fmt.Fprintf(&conn.outbuf, "$Z%d,%x,%d", typ, addr, kind) 438 _, err := conn.exec(conn.outbuf.Bytes(), "set breakpoint") 439 return err 440 } 441 442 // clearBreakpoint executes a 'z' (remove breakpoint) command of type '0' and kind '1' or '4' 443 func (conn *gdbConn) clearBreakpoint(addr uint64, typ breakpointType, kind int) error { 444 conn.outbuf.Reset() 445 fmt.Fprintf(&conn.outbuf, "$z%d,%x,%d", typ, addr, kind) 446 _, err := conn.exec(conn.outbuf.Bytes(), "clear breakpoint") 447 return err 448 } 449 450 // kill executes a 'k' (kill) command. 451 func (conn *gdbConn) kill() error { 452 resp, err := conn.exec([]byte{'$', 'k'}, "kill") 453 if err == io.EOF { 454 // The stub is allowed to shut the connection on us immediately after a 455 // kill. This is not an error. 456 conn.conn.Close() 457 conn.conn = nil 458 return proc.ErrProcessExited{Pid: conn.pid} 459 } 460 if err != nil { 461 return err 462 } 463 _, _, err = conn.parseStopPacket(resp, "", nil) 464 return err 465 } 466 467 // detach executes a 'D' (detach) command. 468 func (conn *gdbConn) detach() error { 469 if conn.conn == nil { 470 // Already detached 471 return nil 472 } 473 _, err := conn.exec([]byte{'$', 'D'}, "detach") 474 conn.conn.Close() 475 conn.conn = nil 476 return err 477 } 478 479 // readRegisters executes a 'g' (read registers) command. 480 func (conn *gdbConn) readRegisters(threadID string, data []byte) error { 481 if !conn.threadSuffixSupported { 482 if err := conn.selectThread('g', threadID, "registers read"); err != nil { 483 return err 484 } 485 } 486 conn.outbuf.Reset() 487 conn.outbuf.WriteString("$g") 488 conn.appendThreadSelector(threadID) 489 resp, err := conn.exec(conn.outbuf.Bytes(), "registers read") 490 if err != nil { 491 return err 492 } 493 494 for i := 0; i < len(resp); i += 2 { 495 n, _ := strconv.ParseUint(string(resp[i:i+2]), 16, 8) 496 data[i/2] = uint8(n) 497 } 498 499 return nil 500 } 501 502 // writeRegisters executes a 'G' (write registers) command. 503 func (conn *gdbConn) writeRegisters(threadID string, data []byte) error { 504 if !conn.threadSuffixSupported { 505 if err := conn.selectThread('g', threadID, "registers write"); err != nil { 506 return err 507 } 508 } 509 conn.outbuf.Reset() 510 conn.outbuf.WriteString("$G") 511 512 for _, b := range data { 513 fmt.Fprintf(&conn.outbuf, "%02x", b) 514 } 515 conn.appendThreadSelector(threadID) 516 _, err := conn.exec(conn.outbuf.Bytes(), "registers write") 517 return err 518 } 519 520 // readRegister executes 'p' (read register) command. 521 func (conn *gdbConn) readRegister(threadID string, regnum int, data []byte) error { 522 if !conn.threadSuffixSupported { 523 if err := conn.selectThread('g', threadID, "registers write"); err != nil { 524 return err 525 } 526 } 527 conn.outbuf.Reset() 528 fmt.Fprintf(&conn.outbuf, "$p%x", regnum) 529 conn.appendThreadSelector(threadID) 530 resp, err := conn.exec(conn.outbuf.Bytes(), "register read") 531 if err != nil { 532 return err 533 } 534 535 for i := 0; i < len(resp); i += 2 { 536 n, _ := strconv.ParseUint(string(resp[i:i+2]), 16, 8) 537 data[i/2] = uint8(n) 538 } 539 540 return nil 541 } 542 543 // writeRegister executes 'P' (write register) command. 544 func (conn *gdbConn) writeRegister(threadID string, regnum int, data []byte) error { 545 if !conn.threadSuffixSupported { 546 if err := conn.selectThread('g', threadID, "registers write"); err != nil { 547 return err 548 } 549 } 550 conn.outbuf.Reset() 551 fmt.Fprintf(&conn.outbuf, "$P%x=", regnum) 552 for _, b := range data { 553 fmt.Fprintf(&conn.outbuf, "%02x", b) 554 } 555 conn.appendThreadSelector(threadID) 556 _, err := conn.exec(conn.outbuf.Bytes(), "register write") 557 return err 558 } 559 560 // resume execution of the target process. 561 // If the current direction is proc.Backward this is done with the 'bc' command. 562 // If the current direction is proc.Forward this is done with the vCont command. 563 // The threads argument will be used to determine which signal to use to 564 // resume each thread. If a thread has sig == 0 the 'c' action will be used, 565 // otherwise the 'C' action will be used and the value of sig will be passed 566 // to it. 567 func (conn *gdbConn) resume(cctx *proc.ContinueOnceContext, threads map[int]*gdbThread, tu *threadUpdater) (stopPacket, error) { 568 if conn.direction == proc.Forward { 569 conn.outbuf.Reset() 570 fmt.Fprintf(&conn.outbuf, "$vCont") 571 for _, th := range threads { 572 if th.sig != 0 { 573 fmt.Fprintf(&conn.outbuf, ";C%02x:%s", th.sig, th.strID) 574 } 575 } 576 fmt.Fprintf(&conn.outbuf, ";c") 577 } else { 578 if err := conn.selectThread('c', "p-1.-1", "resume"); err != nil { 579 return stopPacket{}, err 580 } 581 conn.outbuf.Reset() 582 fmt.Fprint(&conn.outbuf, "$bc") 583 } 584 cctx.StopMu.Lock() 585 if err := conn.send(conn.outbuf.Bytes()); err != nil { 586 cctx.StopMu.Unlock() 587 return stopPacket{}, err 588 } 589 conn.running = true 590 cctx.StopMu.Unlock() 591 defer func() { 592 cctx.StopMu.Lock() 593 conn.running = false 594 cctx.StopMu.Unlock() 595 }() 596 if cctx.ResumeChan != nil { 597 close(cctx.ResumeChan) 598 cctx.ResumeChan = nil 599 } 600 return conn.waitForvContStop("resume", "-1", tu) 601 } 602 603 // step executes a 'vCont' command on the specified thread with 's' action. 604 func (conn *gdbConn) step(th *gdbThread, tu *threadUpdater, ignoreFaultSignal bool) error { 605 threadID := th.strID 606 if conn.direction != proc.Forward { 607 if err := conn.selectThread('c', threadID, "step"); err != nil { 608 return err 609 } 610 conn.outbuf.Reset() 611 fmt.Fprint(&conn.outbuf, "$bs") 612 if err := conn.send(conn.outbuf.Bytes()); err != nil { 613 return err 614 } 615 _, err := conn.waitForvContStop("singlestep", threadID, tu) 616 return err 617 } 618 619 var _SIGBUS uint8 620 switch conn.goos { 621 case "linux": 622 _SIGBUS = 0x7 623 case "darwin": 624 _SIGBUS = 0xa 625 default: 626 panic(fmt.Errorf("unknown GOOS %s", conn.goos)) 627 } 628 629 var sig uint8 = 0 630 for { 631 conn.outbuf.Reset() 632 if sig == 0 { 633 fmt.Fprintf(&conn.outbuf, "$vCont;s:%s", threadID) 634 } else { 635 fmt.Fprintf(&conn.outbuf, "$vCont;S%02x:%s", sig, threadID) 636 } 637 if err := conn.send(conn.outbuf.Bytes()); err != nil { 638 return err 639 } 640 if tu != nil { 641 tu.Reset() 642 } 643 sp, err := conn.waitForvContStop("singlestep", threadID, tu) 644 sig = sp.sig 645 if err != nil { 646 return err 647 } 648 switch sig { 649 case faultSignal: 650 if ignoreFaultSignal { // we attempting to read the TLS, a fault here should be ignored 651 return nil 652 } 653 if conn.isDebugserver { 654 // For some reason trying to deliver a signal in vCont step makes 655 // debugserver lockup (no errors, it just gets stuck), store the signal 656 // to deliver it later with the vCont;c 657 th.sig = sig 658 return nil 659 } 660 case _SIGILL, _SIGBUS, _SIGFPE: 661 if conn.isDebugserver { 662 // See comment above 663 th.sig = sig 664 return nil 665 } 666 // otherwise propagate these signals to inferior immediately 667 case interruptSignal, breakpointSignal, stopSignal: 668 return nil 669 case childSignal: // stop on debugserver but SIGCHLD on lldb-server/linux 670 if conn.isDebugserver { 671 return nil 672 } 673 case debugServerTargetExcBadAccess, debugServerTargetExcBadInstruction, debugServerTargetExcArithmetic, debugServerTargetExcEmulation, debugServerTargetExcSoftware, debugServerTargetExcBreakpoint: 674 if ignoreFaultSignal { 675 return nil 676 } 677 return machTargetExcToError(sig) 678 default: 679 // delay propagation of any other signal to until after the stepping is done 680 th.sig = sig 681 sig = 0 682 } 683 } 684 } 685 686 var errThreadBlocked = errors.New("thread blocked") 687 688 func (conn *gdbConn) waitForvContStop(context, threadID string, tu *threadUpdater) (stopPacket, error) { 689 count := 0 690 failed := false 691 for { 692 conn.conn.SetReadDeadline(time.Now().Add(heartbeatInterval)) 693 resp, err := conn.recv(nil, context, false) 694 conn.conn.SetReadDeadline(time.Time{}) 695 if neterr, isneterr := err.(net.Error); isneterr && neterr.Timeout() { 696 // Debugserver sometimes forgets to inform us that inferior stopped, 697 // sending this status request after a timeout helps us get unstuck. 698 // Debugserver will not respond to this request unless inferior is 699 // already stopped. 700 if conn.isDebugserver { 701 conn.send([]byte("$?")) 702 } 703 if count > 1 && context == "singlestep" { 704 failed = true 705 conn.sendCtrlC() 706 } 707 count++ 708 } else if failed { 709 return stopPacket{}, errThreadBlocked 710 } else if err != nil { 711 return stopPacket{}, err 712 } else { 713 repeat, sp, err := conn.parseStopPacket(resp, threadID, tu) 714 if !repeat { 715 return sp, err 716 } 717 } 718 } 719 } 720 721 type stopPacket struct { 722 threadID string 723 sig uint8 724 reason string 725 watchAddr uint64 726 } 727 728 // Mach exception codes used to decode metype/medata keys in stop packets (necessary to support watchpoints with debugserver). 729 // See: 730 // 731 // https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/mach/exception_types.h.auto.html 732 // https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/mach/i386/exception.h.auto.html 733 // https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/mach/arm/exception.h.auto.html 734 const ( 735 _EXC_BREAKPOINT = 6 // mach exception type for hardware breakpoints 736 _EXC_I386_SGL = 1 // mach exception code for single step on x86, for some reason this is also used for watchpoints 737 _EXC_ARM_DA_DEBUG = 0x102 // mach exception code for debug fault on arm/arm64 738 ) 739 740 // executes 'vCont' (continue/step) command 741 func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpdater) (repeat bool, sp stopPacket, err error) { 742 switch resp[0] { 743 case 'T': 744 if len(resp) < 3 { 745 return false, stopPacket{}, fmt.Errorf("malformed response for vCont %s", string(resp)) 746 } 747 748 sig, err := strconv.ParseUint(string(resp[1:3]), 16, 8) 749 if err != nil { 750 return false, stopPacket{}, fmt.Errorf("malformed stop packet: %s", string(resp)) 751 } 752 sp.sig = uint8(sig) 753 754 if logflags.GdbWire() && gdbWireFullStopPacket { 755 conn.log.Debugf("full stop packet: %s", string(resp)) 756 } 757 758 var metype int 759 var medata = make([]uint64, 0, 10) 760 761 csp := colonSemicolonParser{buf: resp[3:]} 762 for csp.next() { 763 key, value := csp.key, csp.value 764 765 switch string(key) { 766 case "thread": 767 sp.threadID = string(value) 768 case "threads": 769 if tu != nil { 770 tu.Add(strings.Split(string(value), ",")) 771 tu.Finish() 772 } 773 case "reason": 774 sp.reason = string(value) 775 case "watch", "awatch", "rwatch": 776 sp.watchAddr, err = strconv.ParseUint(string(value), 16, 64) 777 if err != nil { 778 return false, stopPacket{}, fmt.Errorf("malformed stop packet: %s (wrong watch address)", string(resp)) 779 } 780 case "metype": 781 // mach exception type (debugserver extension) 782 metype, _ = strconv.Atoi(string(value)) 783 case "medata": 784 // mach exception data (debugserver extension) 785 d, _ := strconv.ParseUint(string(value), 16, 64) 786 medata = append(medata, d) 787 } 788 } 789 790 // Debugserver does not report watchpoint stops in the standard way preferring 791 // instead the semi-undocumented metype/medata keys. 792 // These values also have different meanings depending on the CPU architecture. 793 switch conn.goarch { 794 case "amd64": 795 if metype == _EXC_BREAKPOINT && len(medata) >= 2 && medata[0] == _EXC_I386_SGL { 796 sp.watchAddr = medata[1] // this should be zero if this is really a single step stop and non-zero for watchpoints 797 } 798 case "arm64": 799 if metype == _EXC_BREAKPOINT && len(medata) >= 2 && medata[0] == _EXC_ARM_DA_DEBUG { 800 sp.watchAddr = medata[1] 801 } 802 } 803 804 return false, sp, nil 805 806 case 'W', 'X': 807 // process exited, next two character are exit code 808 809 semicolon := bytes.Index(resp, []byte{';'}) 810 811 if semicolon < 0 { 812 semicolon = len(resp) 813 } 814 status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8) 815 return false, stopPacket{}, proc.ErrProcessExited{Pid: conn.pid, Status: int(status)} 816 817 case 'N': 818 // we were singlestepping the thread and the thread exited 819 sp.threadID = threadID 820 return false, sp, nil 821 822 case 'O': 823 data := make([]byte, 0, len(resp[1:])/2) 824 for i := 1; i < len(resp); i += 2 { 825 n, _ := strconv.ParseUint(string(resp[i:i+2]), 16, 8) 826 data = append(data, uint8(n)) 827 } 828 os.Stdout.Write(data) 829 return true, sp, nil 830 831 default: 832 return false, sp, fmt.Errorf("unexpected response for vCont %c", resp[0]) 833 } 834 } 835 836 const ctrlC = 0x03 // the ASCII character for ^C 837 838 // executes a ctrl-C on the line 839 func (conn *gdbConn) sendCtrlC() error { 840 conn.log.Debug("<- interrupt") 841 _, err := conn.conn.Write([]byte{ctrlC}) 842 return err 843 } 844 845 // queryProcessInfo executes a qProcessInfoPID (if pid != 0) or a qProcessInfo (if pid == 0) 846 func (conn *gdbConn) queryProcessInfo(pid int) (map[string]string, error) { 847 conn.outbuf.Reset() 848 if pid != 0 { 849 fmt.Fprintf(&conn.outbuf, "$qProcessInfoPID:%d", pid) 850 } else { 851 fmt.Fprint(&conn.outbuf, "$qProcessInfo") 852 } 853 resp, err := conn.exec(conn.outbuf.Bytes(), "process info for pid") 854 if err != nil { 855 return nil, err 856 } 857 858 pi := make(map[string]string) 859 860 csp := colonSemicolonParser{buf: resp} 861 for csp.next() { 862 key, value := string(csp.key), string(csp.value) 863 864 switch key { 865 case "name": 866 name := make([]byte, len(value)/2) 867 for i := 0; i < len(value); i += 2 { 868 n, _ := strconv.ParseUint(value[i:i+2], 16, 8) 869 name[i/2] = byte(n) 870 } 871 pi[key] = string(name) 872 873 default: 874 pi[key] = value 875 } 876 } 877 return pi, nil 878 } 879 880 // executes qfThreadInfo/qsThreadInfo commands 881 func (conn *gdbConn) queryThreads(first bool) (threads []string, err error) { 882 // https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html 883 conn.outbuf.Reset() 884 if first { 885 conn.outbuf.WriteString("$qfThreadInfo") 886 } else { 887 conn.outbuf.WriteString("$qsThreadInfo") 888 } 889 890 resp, err := conn.exec(conn.outbuf.Bytes(), "thread info") 891 if err != nil { 892 return nil, err 893 } 894 895 switch resp[0] { 896 case 'l': 897 return nil, nil 898 case 'm': 899 // parse list... 900 default: 901 return nil, errors.New("malformed qfThreadInfo response") 902 } 903 904 var pid int 905 resp = resp[1:] 906 for { 907 tidbuf := resp 908 comma := bytes.Index(tidbuf, []byte{','}) 909 if comma >= 0 { 910 tidbuf = tidbuf[:comma] 911 } 912 if conn.multiprocess && pid == 0 { 913 dot := bytes.Index(tidbuf, []byte{'.'}) 914 if dot >= 0 { 915 pid, _ = strconv.Atoi(string(tidbuf[1:dot])) 916 } 917 } 918 threads = append(threads, string(tidbuf)) 919 if comma < 0 { 920 break 921 } 922 resp = resp[comma+1:] 923 } 924 925 if conn.multiprocess && pid > 0 { 926 conn.pid = pid 927 } 928 return threads, nil 929 } 930 931 func (conn *gdbConn) selectThread(kind byte, threadID string, context string) error { 932 if conn.threadSuffixSupported { 933 panic("selectThread when thread suffix is supported") 934 } 935 conn.outbuf.Reset() 936 fmt.Fprintf(&conn.outbuf, "$H%c%s", kind, threadID) 937 _, err := conn.exec(conn.outbuf.Bytes(), context) 938 return err 939 } 940 941 func (conn *gdbConn) appendThreadSelector(threadID string) { 942 if !conn.threadSuffixSupported { 943 return 944 } 945 fmt.Fprintf(&conn.outbuf, ";thread:%s;", threadID) 946 } 947 948 func (conn *gdbConn) readMemory(data []byte, addr uint64) error { 949 if conn.xcmdok && len(data) > conn.packetSize { 950 return conn.readMemoryBinary(data, addr) 951 } 952 return conn.readMemoryHex(data, addr) 953 } 954 955 // executes 'm' (read memory) command 956 func (conn *gdbConn) readMemoryHex(data []byte, addr uint64) error { 957 size := len(data) 958 data = data[:0] 959 960 for size > 0 { 961 conn.outbuf.Reset() 962 963 // gdbserver will crash if we ask too many bytes... not return an error, actually crash 964 sz := size 965 if dataSize := (conn.packetSize - 4) / 2; sz > dataSize { 966 sz = dataSize 967 } 968 size = size - sz 969 970 fmt.Fprintf(&conn.outbuf, "$m%x,%x", addr+uint64(len(data)), sz) 971 resp, err := conn.exec(conn.outbuf.Bytes(), "memory read") 972 if err != nil { 973 return err 974 } 975 976 for i := 0; i < len(resp); i += 2 { 977 n, _ := strconv.ParseUint(string(resp[i:i+2]), 16, 8) 978 data = append(data, uint8(n)) 979 } 980 } 981 return nil 982 } 983 984 // executes 'x' (binary read memory) command 985 func (conn *gdbConn) readMemoryBinary(data []byte, addr uint64) error { 986 size := len(data) 987 data = data[:0] 988 989 for len(data) < size { 990 conn.outbuf.Reset() 991 992 sz := size - len(data) 993 994 fmt.Fprintf(&conn.outbuf, "$x%x,%x", addr+uint64(len(data)), sz) 995 if err := conn.send(conn.outbuf.Bytes()); err != nil { 996 return err 997 } 998 resp, err := conn.recv(conn.outbuf.Bytes(), "binary memory read", true) 999 if err != nil { 1000 return err 1001 } 1002 data = append(data, resp...) 1003 } 1004 return nil 1005 } 1006 1007 func writeAsciiBytes(w io.Writer, data []byte) { 1008 for _, b := range data { 1009 fmt.Fprintf(w, "%02x", b) 1010 } 1011 } 1012 1013 // writeMemory writes memory using either 'M' or 'X' 1014 func (conn *gdbConn) writeMemory(addr uint64, data []byte) (written int, err error) { 1015 if conn.useXcmd { 1016 return conn.writeMemoryBinary(addr, data) 1017 } 1018 return conn.writeMemoryHex(addr, data) 1019 } 1020 1021 // executes 'M' (write memory) command 1022 func (conn *gdbConn) writeMemoryHex(addr uint64, data []byte) (written int, err error) { 1023 if len(data) == 0 { 1024 // LLDB can't parse requests for 0-length writes and hangs if we emit them 1025 return 0, nil 1026 } 1027 conn.outbuf.Reset() 1028 //TODO(aarzilli): do not send packets larger than conn.PacketSize 1029 fmt.Fprintf(&conn.outbuf, "$M%x,%x:", addr, len(data)) 1030 1031 writeAsciiBytes(&conn.outbuf, data) 1032 1033 _, err = conn.exec(conn.outbuf.Bytes(), "memory write") 1034 if err != nil { 1035 return 0, err 1036 } 1037 return len(data), nil 1038 } 1039 1040 func (conn *gdbConn) writeMemoryBinary(addr uint64, data []byte) (written int, err error) { 1041 conn.outbuf.Reset() 1042 fmt.Fprintf(&conn.outbuf, "$X%x,%x:", addr, len(data)) 1043 1044 for _, b := range data { 1045 switch b { 1046 case '#', '$', '}': 1047 conn.outbuf.WriteByte('}') 1048 conn.outbuf.WriteByte(b ^ escapeXor) 1049 default: 1050 conn.outbuf.WriteByte(b) 1051 } 1052 } 1053 1054 _, err = conn.exec(conn.outbuf.Bytes(), "memory write") 1055 if err != nil { 1056 return 0, err 1057 } 1058 return len(data), nil 1059 } 1060 1061 func (conn *gdbConn) allocMemory(sz uint64) (uint64, error) { 1062 conn.outbuf.Reset() 1063 fmt.Fprintf(&conn.outbuf, "$_M%x,rwx", sz) 1064 resp, err := conn.exec(conn.outbuf.Bytes(), "memory allocation") 1065 if err != nil { 1066 return 0, err 1067 } 1068 return strconv.ParseUint(string(resp), 16, 64) 1069 } 1070 1071 // threadStopInfo executes a 'qThreadStopInfo' and returns the reason the 1072 // thread stopped. 1073 func (conn *gdbConn) threadStopInfo(threadID string) (sp stopPacket, err error) { 1074 conn.outbuf.Reset() 1075 fmt.Fprintf(&conn.outbuf, "$qThreadStopInfo%s", threadID) 1076 resp, err := conn.exec(conn.outbuf.Bytes(), "thread stop info") 1077 if err != nil { 1078 return stopPacket{}, err 1079 } 1080 _, sp, err = conn.parseStopPacket(resp, "", nil) 1081 if err != nil { 1082 return stopPacket{}, err 1083 } 1084 if sp.threadID != threadID { 1085 // When we send a ^C (manual stop request) and the process is close to 1086 // stopping anyway, sometimes, debugserver will send back two stop 1087 // packets. We need to ignore this spurious stop packet. Because the first 1088 // thing we do after the stop is updateThreadList, which calls this 1089 // function, this is relatively painless. We simply need to check that the 1090 // stop packet we receive is for the thread we requested, if it isn't we 1091 // can assume it is the spurious extra stop packet and simply ignore it. 1092 // An example of a problematic interaction is in the commit message for 1093 // this change. 1094 // See https://gitlab.com/Raven-IO/raven-delve/issues/3013. 1095 1096 conn.conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) 1097 resp, err = conn.recv(conn.outbuf.Bytes(), "thread stop info", false) 1098 conn.conn.SetReadDeadline(time.Time{}) 1099 if err != nil { 1100 if neterr, isneterr := err.(net.Error); isneterr && neterr.Timeout() { 1101 return stopPacket{}, fmt.Errorf("qThreadStopInfo mismatch, requested %s got %s", sp.threadID, threadID) 1102 } 1103 return stopPacket{}, err 1104 } 1105 _, sp, err = conn.parseStopPacket(resp, "", nil) 1106 if err != nil { 1107 return stopPacket{}, err 1108 } 1109 if sp.threadID != threadID { 1110 return stopPacket{}, fmt.Errorf("qThreadStopInfo mismatch, requested %s got %s", sp.threadID, threadID) 1111 } 1112 } 1113 1114 return sp, nil 1115 } 1116 1117 // restart executes a 'vRun' command. 1118 func (conn *gdbConn) restart(pos string) error { 1119 conn.outbuf.Reset() 1120 fmt.Fprint(&conn.outbuf, "$vRun;") 1121 if pos != "" { 1122 fmt.Fprint(&conn.outbuf, ";") 1123 writeAsciiBytes(&conn.outbuf, []byte(pos)) 1124 } 1125 _, err := conn.exec(conn.outbuf.Bytes(), "restart") 1126 return err 1127 } 1128 1129 // qRRCmd executes a qRRCmd command 1130 func (conn *gdbConn) qRRCmd(args ...string) (string, error) { 1131 if len(args) == 0 { 1132 panic("must specify at least one argument for qRRCmd") 1133 } 1134 conn.outbuf.Reset() 1135 fmt.Fprint(&conn.outbuf, "$qRRCmd") 1136 for _, arg := range args { 1137 fmt.Fprint(&conn.outbuf, ":") 1138 writeAsciiBytes(&conn.outbuf, []byte(arg)) 1139 } 1140 resp, err := conn.exec(conn.outbuf.Bytes(), "qRRCmd") 1141 if err != nil { 1142 return "", err 1143 } 1144 data := make([]byte, 0, len(resp)/2) 1145 for i := 0; i < len(resp); i += 2 { 1146 n, _ := strconv.ParseUint(string(resp[i:i+2]), 16, 8) 1147 data = append(data, uint8(n)) 1148 } 1149 return string(data), nil 1150 } 1151 1152 type imageList struct { 1153 Images []imageDescription `json:"images"` 1154 } 1155 1156 type imageDescription struct { 1157 LoadAddress uint64 `json:"load_address"` 1158 Pathname string `json:"pathname"` 1159 MachHeader machHeader `json:"mach_header"` 1160 } 1161 1162 type machHeader struct { 1163 FileType macho.Type `json:"filetype"` 1164 } 1165 1166 // getLoadedDynamicLibraries executes jGetLoadedDynamicLibrariesInfos which 1167 // returns the list of loaded dynamic libraries 1168 func (conn *gdbConn) getLoadedDynamicLibraries() ([]imageDescription, error) { 1169 cmd := []byte("$jGetLoadedDynamicLibrariesInfos:{\"fetch_all_solibs\":true}") 1170 if err := conn.send(cmd); err != nil { 1171 return nil, err 1172 } 1173 resp, err := conn.recv(cmd, "get dynamic libraries", true) 1174 if err != nil { 1175 return nil, err 1176 } 1177 var images imageList 1178 err = json.Unmarshal(resp, &images) 1179 return images.Images, err 1180 } 1181 1182 type memoryRegionInfo struct { 1183 start uint64 1184 size uint64 1185 permissions string 1186 name string 1187 } 1188 1189 func decodeHexString(in []byte) (string, bool) { 1190 out := make([]byte, 0, len(in)/2) 1191 for i := 0; i < len(in); i += 2 { 1192 v, err := strconv.ParseUint(string(in[i:i+2]), 16, 8) 1193 if err != nil { 1194 return "", false 1195 } 1196 out = append(out, byte(v)) 1197 } 1198 return string(out), true 1199 } 1200 1201 func (conn *gdbConn) memoryRegionInfo(addr uint64) (*memoryRegionInfo, error) { 1202 conn.outbuf.Reset() 1203 fmt.Fprintf(&conn.outbuf, "$qMemoryRegionInfo:%x", addr) 1204 resp, err := conn.exec(conn.outbuf.Bytes(), "qMemoryRegionInfo") 1205 if err != nil { 1206 return nil, err 1207 } 1208 1209 mri := &memoryRegionInfo{} 1210 1211 csp := colonSemicolonParser{buf: resp} 1212 for csp.next() { 1213 key, value := csp.key, csp.value 1214 1215 switch string(key) { 1216 case "start": 1217 start, err := strconv.ParseUint(string(value), 16, 64) 1218 if err != nil { 1219 return nil, fmt.Errorf("malformed qMemoryRegionInfo response packet (start): %v in %s", err, string(resp)) 1220 } 1221 mri.start = start 1222 case "size": 1223 size, err := strconv.ParseUint(string(value), 16, 64) 1224 if err != nil { 1225 return nil, fmt.Errorf("malformed qMemoryRegionInfo response packet (size): %v in %s", err, string(resp)) 1226 } 1227 mri.size = size 1228 case "permissions": 1229 mri.permissions = string(value) 1230 case "name": 1231 namestr, ok := decodeHexString(value) 1232 if !ok { 1233 return nil, fmt.Errorf("malformed qMemoryRegionInfo response packet (name): %s", string(resp)) 1234 } 1235 mri.name = namestr 1236 case "error": 1237 errstr, ok := decodeHexString(value) 1238 if !ok { 1239 return nil, fmt.Errorf("malformed qMemoryRegionInfo response packet (error): %s", string(resp)) 1240 } 1241 return nil, fmt.Errorf("qMemoryRegionInfo error: %s", errstr) 1242 } 1243 } 1244 1245 return mri, nil 1246 } 1247 1248 // exec executes a message to the stub and reads a response. 1249 // The details of the wire protocol are described here: 1250 // 1251 // https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview 1252 func (conn *gdbConn) exec(cmd []byte, context string) ([]byte, error) { 1253 if err := conn.send(cmd); err != nil { 1254 return nil, err 1255 } 1256 return conn.recv(cmd, context, false) 1257 } 1258 1259 const hexdigit = "0123456789abcdef" 1260 1261 func (conn *gdbConn) send(cmd []byte) error { 1262 if len(cmd) == 0 || cmd[0] != '$' { 1263 panic("gdb protocol error: command doesn't start with '$'") 1264 } 1265 1266 // append checksum to packet 1267 cmd = append(cmd, '#') 1268 sum := checksum(cmd) 1269 cmd = append(cmd, hexdigit[sum>>4], hexdigit[sum&0xf]) 1270 1271 attempt := 0 1272 for { 1273 if logflags.GdbWire() { 1274 if len(cmd) > gdbWireMaxLen { 1275 conn.log.Debugf("<- %s...", string(cmd[:gdbWireMaxLen])) 1276 } else { 1277 conn.log.Debugf("<- %s", string(cmd)) 1278 } 1279 } 1280 _, err := conn.conn.Write(cmd) 1281 if err != nil { 1282 return err 1283 } 1284 1285 if !conn.ack { 1286 break 1287 } 1288 1289 if conn.readack() { 1290 break 1291 } 1292 if attempt > conn.maxTransmitAttempts { 1293 return ErrTooManyAttempts 1294 } 1295 attempt++ 1296 } 1297 return nil 1298 } 1299 1300 func (conn *gdbConn) recv(cmd []byte, context string, binary bool) (resp []byte, err error) { 1301 attempt := 0 1302 for { 1303 var err error 1304 resp, err = conn.rdr.ReadBytes('#') 1305 if err != nil { 1306 return nil, err 1307 } 1308 1309 // read checksum 1310 _, err = io.ReadFull(conn.rdr, conn.inbuf[:2]) 1311 if err != nil { 1312 return nil, err 1313 } 1314 if logflags.GdbWire() { 1315 out := resp 1316 partial := false 1317 if !binary { 1318 if idx := bytes.Index(out, []byte{'\n'}); idx >= 0 { 1319 out = resp[:idx] 1320 partial = true 1321 } 1322 } 1323 if len(out) > gdbWireMaxLen { 1324 out = out[:gdbWireMaxLen] 1325 partial = true 1326 } 1327 if !partial { 1328 if binary { 1329 conn.log.Debugf("-> %q%s", string(resp), string(conn.inbuf[:2])) 1330 } else { 1331 conn.log.Debugf("-> %s%s", string(resp), string(conn.inbuf[:2])) 1332 } 1333 } else { 1334 if binary { 1335 conn.log.Debugf("-> %q...", string(out)) 1336 } else { 1337 conn.log.Debugf("-> %s...", string(out)) 1338 } 1339 } 1340 } 1341 1342 if !conn.ack { 1343 break 1344 } 1345 1346 if resp[0] == '%' { 1347 // If the first character is a % (instead of $) the stub sent us a 1348 // notification packet, this is weird since we specifically claimed that 1349 // we don't support notifications of any kind, but it should be safe to 1350 // ignore regardless. 1351 continue 1352 } 1353 1354 if checksumok(resp, conn.inbuf[:2]) { 1355 conn.sendack('+') 1356 break 1357 } 1358 if attempt > conn.maxTransmitAttempts { 1359 conn.sendack('+') 1360 return nil, ErrTooManyAttempts 1361 } 1362 attempt++ 1363 conn.sendack('-') 1364 } 1365 1366 if binary { 1367 conn.inbuf, resp = binarywiredecode(resp, conn.inbuf) 1368 } else { 1369 conn.inbuf, resp = wiredecode(resp, conn.inbuf) 1370 } 1371 1372 if len(resp) == 0 || (resp[0] == 'E' && !binary) || (resp[0] == 'E' && len(resp) == 3) { 1373 cmdstr := "" 1374 if cmd != nil { 1375 cmdstr = string(cmd) 1376 } 1377 return nil, &GdbProtocolError{context, cmdstr, string(resp)} 1378 } 1379 1380 return resp, nil 1381 } 1382 1383 // Readack reads one byte from stub, returns true if the byte is '+' 1384 func (conn *gdbConn) readack() bool { 1385 b, err := conn.rdr.ReadByte() 1386 if err != nil { 1387 return false 1388 } 1389 conn.log.Debugf("-> %s", string(b)) 1390 return b == '+' 1391 } 1392 1393 // Sendack executes an ack character, c must be either '+' or '-' 1394 func (conn *gdbConn) sendack(c byte) { 1395 if c != '+' && c != '-' { 1396 panic(fmt.Errorf("sendack(%c)", c)) 1397 } 1398 conn.conn.Write([]byte{c}) 1399 conn.log.Debugf("<- %s", string(c)) 1400 } 1401 1402 // escapeXor is the value mandated by the specification to escape characters 1403 const escapeXor byte = 0x20 1404 1405 // wiredecode decodes the contents of in into buf. 1406 // If buf is nil it will be allocated ex-novo, if the size of buf is not 1407 // enough to hold the decoded contents it will be grown. 1408 // Returns the newly allocated buffer as newbuf and the message contents as 1409 // msg. 1410 func wiredecode(in, buf []byte) (newbuf, msg []byte) { 1411 if buf != nil { 1412 buf = buf[:0] 1413 } else { 1414 buf = make([]byte, 0, 256) 1415 } 1416 1417 start := 1 1418 1419 for i := 0; i < len(in); i++ { 1420 switch ch := in[i]; ch { 1421 case '{': // escape 1422 if i+1 >= len(in) { 1423 buf = append(buf, ch) 1424 } else { 1425 buf = append(buf, in[i+1]^escapeXor) 1426 i++ 1427 } 1428 case ':': 1429 buf = append(buf, ch) 1430 if i == 3 { 1431 // we just read the sequence identifier 1432 start = i + 1 1433 } 1434 case '#': // end of packet 1435 return buf, buf[start:] 1436 case '*': // runlength encoding marker 1437 if i+1 >= len(in) || i == 0 { 1438 buf = append(buf, ch) 1439 } else { 1440 n := in[i+1] - 29 1441 r := buf[len(buf)-1] 1442 for j := uint8(0); j < n; j++ { 1443 buf = append(buf, r) 1444 } 1445 i++ 1446 } 1447 default: 1448 buf = append(buf, ch) 1449 } 1450 } 1451 return buf, buf[start:] 1452 } 1453 1454 // binarywiredecode is like wiredecode but decodes the wire encoding for 1455 // binary packets, such as the 'x' and 'X' packets as well as all the json 1456 // packets used by lldb/debugserver. 1457 func binarywiredecode(in, buf []byte) (newbuf, msg []byte) { 1458 if buf != nil { 1459 buf = buf[:0] 1460 } else { 1461 buf = make([]byte, 0, 256) 1462 } 1463 1464 start := 1 1465 1466 for i := 0; i < len(in); i++ { 1467 switch ch := in[i]; ch { 1468 case '}': // escape 1469 if i+1 >= len(in) { 1470 buf = append(buf, ch) 1471 } else { 1472 buf = append(buf, in[i+1]^escapeXor) 1473 i++ 1474 } 1475 case '#': // end of packet 1476 return buf, buf[start:] 1477 default: 1478 buf = append(buf, ch) 1479 } 1480 } 1481 return buf, buf[start:] 1482 } 1483 1484 // checksumok checks that checksum is a valid checksum for packet. 1485 func checksumok(packet, checksumBuf []byte) bool { 1486 if packet[0] != '$' { 1487 return false 1488 } 1489 1490 sum := checksum(packet) 1491 tgt, err := strconv.ParseUint(string(checksumBuf), 16, 8) 1492 if err != nil { 1493 return false 1494 } 1495 1496 tgt8 := uint8(tgt) 1497 1498 return sum == tgt8 1499 } 1500 1501 func checksum(packet []byte) (sum uint8) { 1502 for i := 1; i < len(packet); i++ { 1503 if packet[i] == '#' { 1504 return sum 1505 } 1506 sum += packet[i] 1507 } 1508 return sum 1509 } 1510 1511 // colonSemicolonParser parses a string in the form: 1512 // 1513 // key1:value1;key2:value2;...;keyN:valueN 1514 type colonSemicolonParser struct { 1515 buf []byte 1516 key, value []byte 1517 } 1518 1519 func (csp *colonSemicolonParser) next() bool { 1520 if len(csp.buf) == 0 { 1521 return false 1522 } 1523 colon := bytes.IndexByte(csp.buf, ':') 1524 if colon < 0 { 1525 return false 1526 } 1527 csp.key = csp.buf[:colon] 1528 csp.buf = csp.buf[colon+1:] 1529 1530 semicolon := bytes.IndexByte(csp.buf, ';') 1531 if semicolon < 0 { 1532 csp.value = csp.buf 1533 csp.buf = nil 1534 } else { 1535 csp.value = csp.buf[:semicolon] 1536 csp.buf = csp.buf[semicolon+1:] 1537 } 1538 return true 1539 }