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 if self._thread is not None: 514 self._thread.join() 515 self._thread = None 516 517 def get_connect_address(self): 518 return self._socket.get_connect_address() 519 520 def get_connect_url(self): 521 return self._socket.get_connect_url() 522 523 def run(self): 524 # For testing purposes, we only need to worry about one client 525 # connecting just one time. 526 try: 527 self._socket.accept() 528 except: 529 traceback.print_exc() 530 return 531 self._shouldSendAck = True 532 self._receivedData = "" 533 self._receivedDataOffset = 0 534 data = None 535 try: 536 while True: 537 data = seven.bitcast_to_string(self._socket.recv()) 538 if data is None or len(data) == 0: 539 break 540 self._receive(data) 541 except self.TerminateConnectionException: 542 pass 543 except Exception as e: 544 print( 545 "An exception happened when receiving the response from the gdb server. Closing the client..." 546 ) 547 traceback.print_exc() 548 finally: 549 self._socket.close_connection() 550 self._socket.close_server() 551 552 def _receive(self, data): 553 """ 554 Collects data, parses and responds to as many packets as exist. 555 Any leftover data is kept for parsing the next time around. 556 """ 557 self._receivedData += data 558 packet = self._parsePacket() 559 while packet is not None: 560 self._handlePacket(packet) 561 packet = self._parsePacket() 562 563 def _parsePacket(self): 564 """ 565 Reads bytes from self._receivedData, returning: 566 - a packet's contents if a valid packet is found 567 - the PACKET_ACK unique object if we got an ack 568 - None if we only have a partial packet 569 570 Raises an InvalidPacketException if unexpected data is received 571 or if checksums fail. 572 573 Once a complete packet is found at the front of self._receivedData, 574 its data is removed form self._receivedData. 575 """ 576 data = self._receivedData 577 i = self._receivedDataOffset 578 data_len = len(data) 579 if data_len == 0: 580 return None 581 if i == 0: 582 # If we're looking at the start of the received data, that means 583 # we're looking for the start of a new packet, denoted by a $. 584 # It's also possible we'll see an ACK here, denoted by a + 585 if data[0] == "+": 586 self._receivedData = data[1:] 587 return self.PACKET_ACK 588 if ord(data[0]) == 3: 589 self._receivedData = data[1:] 590 return self.PACKET_INTERRUPT 591 if data[0] == "$": 592 i += 1 593 else: 594 raise self.InvalidPacketException( 595 "Unexpected leading byte: %s" % data[0] 596 ) 597 598 # If we're looking beyond the start of the received data, then we're 599 # looking for the end of the packet content, denoted by a #. 600 # Note that we pick up searching from where we left off last time 601 while i < data_len and data[i] != "#": 602 i += 1 603 604 # If there isn't enough data left for a checksum, just remember where 605 # we left off so we can pick up there the next time around 606 if i > data_len - 3: 607 self._receivedDataOffset = i 608 return None 609 610 # If we have enough data remaining for the checksum, extract it and 611 # compare to the packet contents 612 packet = data[1:i] 613 i += 1 614 try: 615 check = int(data[i : i + 2], 16) 616 except ValueError: 617 raise self.InvalidPacketException("Checksum is not valid hex") 618 i += 2 619 if check != checksum(packet): 620 raise self.InvalidPacketException( 621 "Checksum %02x does not match content %02x" % (check, checksum(packet)) 622 ) 623 # remove parsed bytes from _receivedData and reset offset so parsing 624 # can start on the next packet the next time around 625 self._receivedData = data[i:] 626 self._receivedDataOffset = 0 627 return packet 628 629 def _sendPacket(self, packet): 630 self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet))) 631 632 def _handlePacket(self, packet): 633 if packet is self.PACKET_ACK: 634 # Ignore ACKs from the client. For the future, we can consider 635 # adding validation code to make sure the client only sends ACKs 636 # when it's supposed to. 637 return 638 response = "" 639 # We'll handle the ack stuff here since it's not something any of the 640 # tests will be concerned about, and it'll get turned off quickly anyway. 641 if self._shouldSendAck: 642 self._socket.sendall(seven.bitcast_to_bytes("+")) 643 if packet == "QStartNoAckMode": 644 self._shouldSendAck = False 645 response = "OK" 646 elif self.responder is not None: 647 # Delegate everything else to our responder 648 response = self.responder.respond(packet) 649 if not isinstance(response, list): 650 response = [response] 651 for part in response: 652 if part is MockGDBServerResponder.RESPONSE_DISCONNECT: 653 raise self.TerminateConnectionException() 654 self._sendPacket(part) 655 656 PACKET_ACK = object() 657 PACKET_INTERRUPT = object() 658 659 class TerminateConnectionException(Exception): 660 pass 661 662 class InvalidPacketException(Exception): 663 pass 664