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