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