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): 465 self._socket = socket 466 self.responder = MockGDBServerResponder() 467 468 def start(self): 469 # Start a thread that waits for a client connection. 470 self._thread = threading.Thread(target=self.run) 471 self._thread.start() 472 473 def stop(self): 474 self._thread.join() 475 self._thread = None 476 477 def get_connect_address(self): 478 return self._socket.get_connect_address() 479 480 def get_connect_url(self): 481 return self._socket.get_connect_url() 482 483 def run(self): 484 # For testing purposes, we only need to worry about one client 485 # connecting just one time. 486 try: 487 self._socket.accept() 488 except: 489 return 490 self._shouldSendAck = True 491 self._receivedData = "" 492 self._receivedDataOffset = 0 493 data = None 494 try: 495 while True: 496 data = seven.bitcast_to_string(self._socket.recv()) 497 if data is None or len(data) == 0: 498 break 499 self._receive(data) 500 except self.TerminateConnectionException: 501 pass 502 except Exception as e: 503 print("An exception happened when receiving the response from the gdb server. Closing the client...") 504 traceback.print_exc() 505 finally: 506 self._socket.close_connection() 507 self._socket.close_server() 508 509 def _receive(self, data): 510 """ 511 Collects data, parses and responds to as many packets as exist. 512 Any leftover data is kept for parsing the next time around. 513 """ 514 self._receivedData += data 515 packet = self._parsePacket() 516 while packet is not None: 517 self._handlePacket(packet) 518 packet = self._parsePacket() 519 520 def _parsePacket(self): 521 """ 522 Reads bytes from self._receivedData, returning: 523 - a packet's contents if a valid packet is found 524 - the PACKET_ACK unique object if we got an ack 525 - None if we only have a partial packet 526 527 Raises an InvalidPacketException if unexpected data is received 528 or if checksums fail. 529 530 Once a complete packet is found at the front of self._receivedData, 531 its data is removed form self._receivedData. 532 """ 533 data = self._receivedData 534 i = self._receivedDataOffset 535 data_len = len(data) 536 if data_len == 0: 537 return None 538 if i == 0: 539 # If we're looking at the start of the received data, that means 540 # we're looking for the start of a new packet, denoted by a $. 541 # It's also possible we'll see an ACK here, denoted by a + 542 if data[0] == '+': 543 self._receivedData = data[1:] 544 return self.PACKET_ACK 545 if ord(data[0]) == 3: 546 self._receivedData = data[1:] 547 return self.PACKET_INTERRUPT 548 if data[0] == '$': 549 i += 1 550 else: 551 raise self.InvalidPacketException( 552 "Unexpected leading byte: %s" % data[0]) 553 554 # If we're looking beyond the start of the received data, then we're 555 # looking for the end of the packet content, denoted by a #. 556 # Note that we pick up searching from where we left off last time 557 while i < data_len and data[i] != '#': 558 i += 1 559 560 # If there isn't enough data left for a checksum, just remember where 561 # we left off so we can pick up there the next time around 562 if i > data_len - 3: 563 self._receivedDataOffset = i 564 return None 565 566 # If we have enough data remaining for the checksum, extract it and 567 # compare to the packet contents 568 packet = data[1:i] 569 i += 1 570 try: 571 check = int(data[i:i + 2], 16) 572 except ValueError: 573 raise self.InvalidPacketException("Checksum is not valid hex") 574 i += 2 575 if check != checksum(packet): 576 raise self.InvalidPacketException( 577 "Checksum %02x does not match content %02x" % 578 (check, checksum(packet))) 579 # remove parsed bytes from _receivedData and reset offset so parsing 580 # can start on the next packet the next time around 581 self._receivedData = data[i:] 582 self._receivedDataOffset = 0 583 return packet 584 585 def _sendPacket(self, packet): 586 self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet))) 587 588 def _handlePacket(self, packet): 589 if packet is self.PACKET_ACK: 590 # Ignore ACKs from the client. For the future, we can consider 591 # adding validation code to make sure the client only sends ACKs 592 # when it's supposed to. 593 return 594 response = "" 595 # We'll handle the ack stuff here since it's not something any of the 596 # tests will be concerned about, and it'll get turned off quickly anyway. 597 if self._shouldSendAck: 598 self._socket.sendall(seven.bitcast_to_bytes('+')) 599 if packet == "QStartNoAckMode": 600 self._shouldSendAck = False 601 response = "OK" 602 elif self.responder is not None: 603 # Delegate everything else to our responder 604 response = self.responder.respond(packet) 605 if not isinstance(response, list): 606 response = [response] 607 for part in response: 608 if part is MockGDBServerResponder.RESPONSE_DISCONNECT: 609 raise self.TerminateConnectionException() 610 self._sendPacket(part) 611 612 PACKET_ACK = object() 613 PACKET_INTERRUPT = object() 614 615 class TerminateConnectionException(Exception): 616 pass 617 618 class InvalidPacketException(Exception): 619 pass 620