1import ctypes 2import errno 3import io 4import threading 5import socket 6import traceback 7from lldbsuite.support import seven 8 9 10def checksum(message): 11 """ 12 Calculate the GDB server protocol checksum of the message. 13 14 The GDB server protocol uses a simple modulo 256 sum. 15 """ 16 check = 0 17 for c in message: 18 check += ord(c) 19 return check % 256 20 21 22def frame_packet(message): 23 """ 24 Create a framed packet that's ready to send over the GDB connection 25 channel. 26 27 Framing includes surrounding the message between $ and #, and appending 28 a two character hex checksum. 29 """ 30 return "$%s#%02x" % (message, checksum(message)) 31 32 33def escape_binary(message): 34 """ 35 Escape the binary message using the process described in the GDB server 36 protocol documentation. 37 38 Most bytes are sent through as-is, but $, #, and { are escaped by writing 39 a { followed by the original byte mod 0x20. 40 """ 41 out = "" 42 for c in message: 43 d = ord(c) 44 if d in (0x23, 0x24, 0x7D): 45 out += chr(0x7D) 46 out += chr(d ^ 0x20) 47 else: 48 out += c 49 return out 50 51 52def hex_encode_bytes(message): 53 """ 54 Encode the binary message by converting each byte into a two-character 55 hex string. 56 """ 57 out = "" 58 for c in message: 59 out += "%02x" % ord(c) 60 return out 61 62 63def hex_decode_bytes(hex_bytes): 64 """ 65 Decode the hex string into a binary message by converting each two-character 66 hex string into a single output byte. 67 """ 68 out = "" 69 hex_len = len(hex_bytes) 70 i = 0 71 while i < hex_len - 1: 72 out += chr(int(hex_bytes[i : i + 2], 16)) 73 i += 2 74 return out 75 76 77class MockGDBServerResponder: 78 """ 79 A base class for handling client packets and issuing server responses for 80 GDB tests. 81 82 This handles many typical situations, while still allowing subclasses to 83 completely customize their responses. 84 85 Most subclasses will be interested in overriding the other() method, which 86 handles any packet not recognized in the common packet handling code. 87 """ 88 89 registerCount = 40 90 packetLog = None 91 92 class RESPONSE_DISCONNECT: 93 pass 94 95 def __init__(self): 96 self.packetLog = [] 97 98 def respond(self, packet): 99 """ 100 Return the unframed packet data that the server should issue in response 101 to the given packet received from the client. 102 """ 103 self.packetLog.append(packet) 104 if packet is MockGDBServer.PACKET_INTERRUPT: 105 return self.interrupt() 106 if packet == "c": 107 return self.cont() 108 if packet.startswith("vCont;c"): 109 return self.vCont(packet) 110 if packet[0] == "A": 111 return self.A(packet) 112 if packet[0] == "D": 113 return self.D(packet) 114 if packet[0] == "g": 115 return self.readRegisters() 116 if packet[0] == "G": 117 # Gxxxxxxxxxxx 118 # Gxxxxxxxxxxx;thread:1234; 119 return self.writeRegisters(packet[1:].split(";")[0]) 120 if packet[0] == "p": 121 regnum = packet[1:].split(";")[0] 122 return self.readRegister(int(regnum, 16)) 123 if packet[0] == "P": 124 register, value = packet[1:].split("=") 125 return self.writeRegister(int(register, 16), value) 126 if packet[0] == "m": 127 addr, length = [int(x, 16) for x in packet[1:].split(",")] 128 return self.readMemory(addr, length) 129 if packet[0] == "M": 130 location, encoded_data = packet[1:].split(":") 131 addr, length = [int(x, 16) for x in location.split(",")] 132 return self.writeMemory(addr, encoded_data) 133 if packet[0:7] == "qSymbol": 134 return self.qSymbol(packet[8:]) 135 if packet[0:10] == "qSupported": 136 return self.qSupported(packet[11:].split(";")) 137 if packet == "qfThreadInfo": 138 return self.qfThreadInfo() 139 if packet == "qsThreadInfo": 140 return self.qsThreadInfo() 141 if packet == "qC": 142 return self.qC() 143 if packet == "QEnableErrorStrings": 144 return self.QEnableErrorStrings() 145 if packet == "?": 146 return self.haltReason() 147 if packet == "s": 148 return self.haltReason() 149 if packet[0] == "H": 150 tid = packet[2:] 151 if "." in tid: 152 assert tid.startswith("p") 153 # TODO: do we want to do anything with PID? 154 tid = tid.split(".", 1)[1] 155 return self.selectThread(packet[1], int(tid, 16)) 156 if packet[0:6] == "qXfer:": 157 obj, read, annex, location = packet[6:].split(":") 158 offset, length = [int(x, 16) for x in location.split(",")] 159 data, has_more = self.qXferRead(obj, annex, offset, length) 160 if data is not None: 161 return self._qXferResponse(data, has_more) 162 return "" 163 if packet.startswith("vAttach;"): 164 pid = packet.partition(";")[2] 165 return self.vAttach(int(pid, 16)) 166 if packet[0] == "Z": 167 return self.setBreakpoint(packet) 168 if packet.startswith("qThreadStopInfo"): 169 threadnum = int(packet[15:], 16) 170 return self.threadStopInfo(threadnum) 171 if packet == "QThreadSuffixSupported": 172 return self.QThreadSuffixSupported() 173 if packet == "QListThreadsInStopReply": 174 return self.QListThreadsInStopReply() 175 if packet.startswith("qMemoryRegionInfo:"): 176 return self.qMemoryRegionInfo(int(packet.split(":")[1], 16)) 177 if packet == "qQueryGDBServer": 178 return self.qQueryGDBServer() 179 if packet == "qHostInfo": 180 return self.qHostInfo() 181 if packet == "qGetWorkingDir": 182 return self.qGetWorkingDir() 183 if packet == "qOffsets": 184 return self.qOffsets() 185 if packet == "qProcessInfo": 186 return self.qProcessInfo() 187 if packet == "qsProcessInfo": 188 return self.qsProcessInfo() 189 if packet.startswith("qfProcessInfo"): 190 return self.qfProcessInfo(packet) 191 if packet.startswith("jGetLoadedDynamicLibrariesInfos"): 192 return self.jGetLoadedDynamicLibrariesInfos(packet) 193 if packet.startswith("qPathComplete:"): 194 return self.qPathComplete() 195 if packet.startswith("vFile:"): 196 return self.vFile(packet) 197 if packet.startswith("vRun;"): 198 return self.vRun(packet) 199 if packet.startswith("qLaunchGDBServer;"): 200 _, host = packet.partition(";")[2].split(":") 201 return self.qLaunchGDBServer(host) 202 if packet.startswith("qLaunchSuccess"): 203 return self.qLaunchSuccess() 204 if packet.startswith("QEnvironment:"): 205 return self.QEnvironment(packet) 206 if packet.startswith("QEnvironmentHexEncoded:"): 207 return self.QEnvironmentHexEncoded(packet) 208 if packet.startswith("qRegisterInfo"): 209 regnum = int(packet[len("qRegisterInfo") :], 16) 210 return self.qRegisterInfo(regnum) 211 if packet == "k": 212 return self.k() 213 214 return self.other(packet) 215 216 def qsProcessInfo(self): 217 return "E04" 218 219 def qfProcessInfo(self, packet): 220 return "E04" 221 222 def jGetLoadedDynamicLibrariesInfos(self, packet): 223 return "" 224 225 def qGetWorkingDir(self): 226 return "2f" 227 228 def qOffsets(self): 229 return "" 230 231 def qProcessInfo(self): 232 return "" 233 234 def qHostInfo(self): 235 return "ptrsize:8;endian:little;" 236 237 def qQueryGDBServer(self): 238 return "E04" 239 240 def interrupt(self): 241 raise self.UnexpectedPacketException() 242 243 def cont(self): 244 raise self.UnexpectedPacketException() 245 246 def vCont(self, packet): 247 raise self.UnexpectedPacketException() 248 249 def A(self, packet): 250 return "" 251 252 def D(self, packet): 253 return "OK" 254 255 def readRegisters(self): 256 return "00000000" * self.registerCount 257 258 def readRegister(self, register): 259 return "00000000" 260 261 def writeRegisters(self, registers_hex): 262 return "OK" 263 264 def writeRegister(self, register, value_hex): 265 return "OK" 266 267 def readMemory(self, addr, length): 268 return "00" * length 269 270 def writeMemory(self, addr, data_hex): 271 return "OK" 272 273 def qSymbol(self, symbol_args): 274 return "OK" 275 276 def qSupported(self, client_supported): 277 return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+" 278 279 def qfThreadInfo(self): 280 return "l" 281 282 def qsThreadInfo(self): 283 return "l" 284 285 def qC(self): 286 return "QC0" 287 288 def QEnableErrorStrings(self): 289 return "OK" 290 291 def haltReason(self): 292 # SIGINT is 2, return type is 2 digit hex string 293 return "S02" 294 295 def qXferRead(self, obj, annex, offset, length): 296 return None, False 297 298 def _qXferResponse(self, data, has_more): 299 return "%s%s" % ("m" if has_more else "l", escape_binary(data)) 300 301 def vAttach(self, pid): 302 raise self.UnexpectedPacketException() 303 304 def selectThread(self, op, thread_id): 305 return "OK" 306 307 def setBreakpoint(self, packet): 308 raise self.UnexpectedPacketException() 309 310 def threadStopInfo(self, threadnum): 311 return "" 312 313 def other(self, packet): 314 # empty string means unsupported 315 return "" 316 317 def QThreadSuffixSupported(self): 318 return "" 319 320 def QListThreadsInStopReply(self): 321 return "" 322 323 def qMemoryRegionInfo(self, addr): 324 return "" 325 326 def qPathComplete(self): 327 return "" 328 329 def vFile(self, packet): 330 return "" 331 332 def vRun(self, packet): 333 return "" 334 335 def qLaunchGDBServer(self, host): 336 raise self.UnexpectedPacketException() 337 338 def qLaunchSuccess(self): 339 return "" 340 341 def QEnvironment(self, packet): 342 return "OK" 343 344 def QEnvironmentHexEncoded(self, packet): 345 return "OK" 346 347 def qRegisterInfo(self, num): 348 return "" 349 350 def k(self): 351 return ["W01", self.RESPONSE_DISCONNECT] 352 353 """ 354 Raised when we receive a packet for which there is no default action. 355 Override the responder class to implement behavior suitable for the test at 356 hand. 357 """ 358 359 class UnexpectedPacketException(Exception): 360 pass 361 362 363class ServerChannel: 364 """ 365 A wrapper class for TCP or pty-based server. 366 """ 367 368 def get_connect_address(self): 369 """Get address for the client to connect to.""" 370 371 def get_connect_url(self): 372 """Get URL suitable for process connect command.""" 373 374 def close_server(self): 375 """Close all resources used by the server.""" 376 377 def accept(self): 378 """Accept a single client connection to the server.""" 379 380 def close_connection(self): 381 """Close all resources used by the accepted connection.""" 382 383 def recv(self): 384 """Receive a data packet from the connected client.""" 385 386 def sendall(self, data): 387 """Send the data to the connected client.""" 388 389 390class ServerSocket(ServerChannel): 391 def __init__(self, family, type, proto, addr): 392 self._server_socket = socket.socket(family, type, proto) 393 self._connection = None 394 395 self._server_socket.bind(addr) 396 self._server_socket.listen(1) 397 398 def close_server(self): 399 self._server_socket.close() 400 401 def accept(self): 402 assert self._connection is None 403 # accept() is stubborn and won't fail even when the socket is 404 # shutdown, so we'll use a timeout 405 self._server_socket.settimeout(30.0) 406 client, client_addr = self._server_socket.accept() 407 # The connected client inherits its timeout from self._socket, 408 # but we'll use a blocking socket for the client 409 client.settimeout(None) 410 self._connection = client 411 412 def close_connection(self): 413 assert self._connection is not None 414 self._connection.close() 415 self._connection = None 416 417 def recv(self): 418 assert self._connection is not None 419 return self._connection.recv(4096) 420 421 def sendall(self, data): 422 assert self._connection is not None 423 return self._connection.sendall(data) 424 425 426class TCPServerSocket(ServerSocket): 427 def __init__(self): 428 family, type, proto, _, addr = socket.getaddrinfo( 429 "localhost", 0, proto=socket.IPPROTO_TCP 430 )[0] 431 super().__init__(family, type, proto, addr) 432 433 def get_connect_address(self): 434 return "[{}]:{}".format(*self._server_socket.getsockname()) 435 436 def get_connect_url(self): 437 return "connect://" + self.get_connect_address() 438 439 440class UnixServerSocket(ServerSocket): 441 def __init__(self, addr): 442 super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr) 443 444 def get_connect_address(self): 445 return self._server_socket.getsockname() 446 447 def get_connect_url(self): 448 return "unix-connect://" + self.get_connect_address() 449 450 451class PtyServerSocket(ServerChannel): 452 def __init__(self): 453 import pty 454 import tty 455 456 primary, secondary = pty.openpty() 457 tty.setraw(primary) 458 self._primary = io.FileIO(primary, "r+b") 459 self._secondary = io.FileIO(secondary, "r+b") 460 461 def get_connect_address(self): 462 libc = ctypes.CDLL(None) 463 libc.ptsname.argtypes = (ctypes.c_int,) 464 libc.ptsname.restype = ctypes.c_char_p 465 return libc.ptsname(self._primary.fileno()).decode() 466 467 def get_connect_url(self): 468 return "serial://" + self.get_connect_address() 469 470 def close_server(self): 471 self._secondary.close() 472 self._primary.close() 473 474 def recv(self): 475 try: 476 return self._primary.read(4096) 477 except OSError as e: 478 # closing the pty results in EIO on Linux, convert it to EOF 479 if e.errno == errno.EIO: 480 return b"" 481 raise 482 483 def sendall(self, data): 484 return self._primary.write(data) 485 486 487class MockGDBServer: 488 """ 489 A simple TCP-based GDB server that can test client behavior by receiving 490 commands and issuing custom-tailored responses. 491 492 Responses are generated via the .responder property, which should be an 493 instance of a class based on MockGDBServerResponder. 494 """ 495 496 responder = None 497 _socket = None 498 _thread = None 499 _receivedData = None 500 _receivedDataOffset = None 501 _shouldSendAck = True 502 503 def __init__(self, socket): 504 self._socket = socket 505 self.responder = MockGDBServerResponder() 506 507 def start(self): 508 # Start a thread that waits for a client connection. 509 self._thread = threading.Thread(target=self.run) 510 self._thread.start() 511 512 def stop(self): 513 self._thread.join() 514 self._thread = None 515 516 def get_connect_address(self): 517 return self._socket.get_connect_address() 518 519 def get_connect_url(self): 520 return self._socket.get_connect_url() 521 522 def run(self): 523 # For testing purposes, we only need to worry about one client 524 # connecting just one time. 525 try: 526 self._socket.accept() 527 except: 528 traceback.print_exc() 529 return 530 self._shouldSendAck = True 531 self._receivedData = "" 532 self._receivedDataOffset = 0 533 data = None 534 try: 535 while True: 536 data = seven.bitcast_to_string(self._socket.recv()) 537 if data is None or len(data) == 0: 538 break 539 self._receive(data) 540 except self.TerminateConnectionException: 541 pass 542 except Exception as e: 543 print( 544 "An exception happened when receiving the response from the gdb server. Closing the client..." 545 ) 546 traceback.print_exc() 547 finally: 548 self._socket.close_connection() 549 self._socket.close_server() 550 551 def _receive(self, data): 552 """ 553 Collects data, parses and responds to as many packets as exist. 554 Any leftover data is kept for parsing the next time around. 555 """ 556 self._receivedData += data 557 packet = self._parsePacket() 558 while packet is not None: 559 self._handlePacket(packet) 560 packet = self._parsePacket() 561 562 def _parsePacket(self): 563 """ 564 Reads bytes from self._receivedData, returning: 565 - a packet's contents if a valid packet is found 566 - the PACKET_ACK unique object if we got an ack 567 - None if we only have a partial packet 568 569 Raises an InvalidPacketException if unexpected data is received 570 or if checksums fail. 571 572 Once a complete packet is found at the front of self._receivedData, 573 its data is removed form self._receivedData. 574 """ 575 data = self._receivedData 576 i = self._receivedDataOffset 577 data_len = len(data) 578 if data_len == 0: 579 return None 580 if i == 0: 581 # If we're looking at the start of the received data, that means 582 # we're looking for the start of a new packet, denoted by a $. 583 # It's also possible we'll see an ACK here, denoted by a + 584 if data[0] == "+": 585 self._receivedData = data[1:] 586 return self.PACKET_ACK 587 if ord(data[0]) == 3: 588 self._receivedData = data[1:] 589 return self.PACKET_INTERRUPT 590 if data[0] == "$": 591 i += 1 592 else: 593 raise self.InvalidPacketException( 594 "Unexpected leading byte: %s" % data[0] 595 ) 596 597 # If we're looking beyond the start of the received data, then we're 598 # looking for the end of the packet content, denoted by a #. 599 # Note that we pick up searching from where we left off last time 600 while i < data_len and data[i] != "#": 601 i += 1 602 603 # If there isn't enough data left for a checksum, just remember where 604 # we left off so we can pick up there the next time around 605 if i > data_len - 3: 606 self._receivedDataOffset = i 607 return None 608 609 # If we have enough data remaining for the checksum, extract it and 610 # compare to the packet contents 611 packet = data[1:i] 612 i += 1 613 try: 614 check = int(data[i : i + 2], 16) 615 except ValueError: 616 raise self.InvalidPacketException("Checksum is not valid hex") 617 i += 2 618 if check != checksum(packet): 619 raise self.InvalidPacketException( 620 "Checksum %02x does not match content %02x" % (check, checksum(packet)) 621 ) 622 # remove parsed bytes from _receivedData and reset offset so parsing 623 # can start on the next packet the next time around 624 self._receivedData = data[i:] 625 self._receivedDataOffset = 0 626 return packet 627 628 def _sendPacket(self, packet): 629 self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet))) 630 631 def _handlePacket(self, packet): 632 if packet is self.PACKET_ACK: 633 # Ignore ACKs from the client. For the future, we can consider 634 # adding validation code to make sure the client only sends ACKs 635 # when it's supposed to. 636 return 637 response = "" 638 # We'll handle the ack stuff here since it's not something any of the 639 # tests will be concerned about, and it'll get turned off quickly anyway. 640 if self._shouldSendAck: 641 self._socket.sendall(seven.bitcast_to_bytes("+")) 642 if packet == "QStartNoAckMode": 643 self._shouldSendAck = False 644 response = "OK" 645 elif self.responder is not None: 646 # Delegate everything else to our responder 647 response = self.responder.respond(packet) 648 if not isinstance(response, list): 649 response = [response] 650 for part in response: 651 if part is MockGDBServerResponder.RESPONSE_DISCONNECT: 652 raise self.TerminateConnectionException() 653 self._sendPacket(part) 654 655 PACKET_ACK = object() 656 PACKET_INTERRUPT = object() 657 658 class TerminateConnectionException(Exception): 659 pass 660 661 class InvalidPacketException(Exception): 662 pass 663