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