1import errno 2import os 3import os.path 4import threading 5import socket 6import lldb 7import binascii 8import traceback 9from lldbsuite.support import seven 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbtest_config 12 13 14def checksum(message): 15 """ 16 Calculate the GDB server protocol checksum of the message. 17 18 The GDB server protocol uses a simple modulo 256 sum. 19 """ 20 check = 0 21 for c in message: 22 check += ord(c) 23 return check % 256 24 25 26def frame_packet(message): 27 """ 28 Create a framed packet that's ready to send over the GDB connection 29 channel. 30 31 Framing includes surrounding the message between $ and #, and appending 32 a two character hex checksum. 33 """ 34 return "$%s#%02x" % (message, checksum(message)) 35 36 37def escape_binary(message): 38 """ 39 Escape the binary message using the process described in the GDB server 40 protocol documentation. 41 42 Most bytes are sent through as-is, but $, #, and { are escaped by writing 43 a { followed by the original byte mod 0x20. 44 """ 45 out = "" 46 for c in message: 47 d = ord(c) 48 if d in (0x23, 0x24, 0x7d): 49 out += chr(0x7d) 50 out += chr(d ^ 0x20) 51 else: 52 out += c 53 return out 54 55 56def hex_encode_bytes(message): 57 """ 58 Encode the binary message by converting each byte into a two-character 59 hex string. 60 """ 61 out = "" 62 for c in message: 63 out += "%02x" % ord(c) 64 return out 65 66 67def hex_decode_bytes(hex_bytes): 68 """ 69 Decode the hex string into a binary message by converting each two-character 70 hex string into a single output byte. 71 """ 72 out = "" 73 hex_len = len(hex_bytes) 74 while i < hex_len - 1: 75 out += chr(int(hex_bytes[i:i + 2]), 16) 76 i += 2 77 return out 78 79 80class MockGDBServerResponder: 81 """ 82 A base class for handling client packets and issuing server responses for 83 GDB tests. 84 85 This handles many typical situations, while still allowing subclasses to 86 completely customize their responses. 87 88 Most subclasses will be interested in overriding the other() method, which 89 handles any packet not recognized in the common packet handling code. 90 """ 91 92 registerCount = 40 93 packetLog = None 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] == "g": 113 return self.readRegisters() 114 if packet[0] == "G": 115 # Gxxxxxxxxxxx 116 # Gxxxxxxxxxxx;thread:1234; 117 return self.writeRegisters(packet[1:].split(';')[0]) 118 if packet[0] == "p": 119 regnum = packet[1:].split(';')[0] 120 return self.readRegister(int(regnum, 16)) 121 if packet[0] == "P": 122 register, value = packet[1:].split("=") 123 return self.writeRegister(int(register, 16), value) 124 if packet[0] == "m": 125 addr, length = [int(x, 16) for x in packet[1:].split(',')] 126 return self.readMemory(addr, length) 127 if packet[0] == "M": 128 location, encoded_data = packet[1:].split(":") 129 addr, length = [int(x, 16) for x in location.split(',')] 130 return self.writeMemory(addr, encoded_data) 131 if packet[0:7] == "qSymbol": 132 return self.qSymbol(packet[8:]) 133 if packet[0:10] == "qSupported": 134 return self.qSupported(packet[11:].split(";")) 135 if packet == "qfThreadInfo": 136 return self.qfThreadInfo() 137 if packet == "qsThreadInfo": 138 return self.qsThreadInfo() 139 if packet == "qC": 140 return self.qC() 141 if packet == "QEnableErrorStrings": 142 return self.QEnableErrorStrings() 143 if packet == "?": 144 return self.haltReason() 145 if packet == "s": 146 return self.haltReason() 147 if packet[0] == "H": 148 return self.selectThread(packet[1], int(packet[2:], 16)) 149 if packet[0:6] == "qXfer:": 150 obj, read, annex, location = packet[6:].split(":") 151 offset, length = [int(x, 16) for x in location.split(',')] 152 data, has_more = self.qXferRead(obj, annex, offset, length) 153 if data is not None: 154 return self._qXferResponse(data, has_more) 155 return "" 156 if packet.startswith("vAttach;"): 157 pid = packet.partition(';')[2] 158 return self.vAttach(int(pid, 16)) 159 if packet[0] == "Z": 160 return self.setBreakpoint(packet) 161 if packet.startswith("qThreadStopInfo"): 162 threadnum = int (packet[15:], 16) 163 return self.threadStopInfo(threadnum) 164 if packet == "QThreadSuffixSupported": 165 return self.QThreadSuffixSupported() 166 if packet == "QListThreadsInStopReply": 167 return self.QListThreadsInStopReply() 168 if packet.startswith("qMemoryRegionInfo:"): 169 return self.qMemoryRegionInfo() 170 if packet == "qQueryGDBServer": 171 return self.qQueryGDBServer() 172 if packet == "qHostInfo": 173 return self.qHostInfo() 174 if packet == "qGetWorkingDir": 175 return self.qGetWorkingDir() 176 if packet == "qOffsets": 177 return self.qOffsets(); 178 if packet == "qsProcessInfo": 179 return self.qsProcessInfo() 180 if packet.startswith("qfProcessInfo"): 181 return self.qfProcessInfo(packet) 182 if packet.startswith("qPathComplete:"): 183 return self.qPathComplete() 184 185 return self.other(packet) 186 187 def qsProcessInfo(self): 188 return "E04" 189 190 def qfProcessInfo(self, packet): 191 return "E04" 192 193 def qGetWorkingDir(self): 194 return "2f" 195 196 def qOffsets(self): 197 return "" 198 199 def qHostInfo(self): 200 return "ptrsize:8;endian:little;" 201 202 def qQueryGDBServer(self): 203 return "E04" 204 205 def interrupt(self): 206 raise self.UnexpectedPacketException() 207 208 def cont(self): 209 raise self.UnexpectedPacketException() 210 211 def vCont(self, packet): 212 raise self.UnexpectedPacketException() 213 214 def A(self, packet): 215 return "" 216 217 def readRegisters(self): 218 return "00000000" * self.registerCount 219 220 def readRegister(self, register): 221 return "00000000" 222 223 def writeRegisters(self, registers_hex): 224 return "OK" 225 226 def writeRegister(self, register, value_hex): 227 return "OK" 228 229 def readMemory(self, addr, length): 230 return "00" * length 231 232 def writeMemory(self, addr, data_hex): 233 return "OK" 234 235 def qSymbol(self, symbol_args): 236 return "OK" 237 238 def qSupported(self, client_supported): 239 return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+" 240 241 def qfThreadInfo(self): 242 return "l" 243 244 def qsThreadInfo(self): 245 return "l" 246 247 def qC(self): 248 return "QC0" 249 250 def QEnableErrorStrings(self): 251 return "OK" 252 253 def haltReason(self): 254 # SIGINT is 2, return type is 2 digit hex string 255 return "S02" 256 257 def qXferRead(self, obj, annex, offset, length): 258 return None, False 259 260 def _qXferResponse(self, data, has_more): 261 return "%s%s" % ("m" if has_more else "l", escape_binary(data)) 262 263 def vAttach(self, pid): 264 raise self.UnexpectedPacketException() 265 266 def selectThread(self, op, thread_id): 267 return "OK" 268 269 def setBreakpoint(self, packet): 270 raise self.UnexpectedPacketException() 271 272 def threadStopInfo(self, threadnum): 273 return "" 274 275 def other(self, packet): 276 # empty string means unsupported 277 return "" 278 279 def QThreadSuffixSupported(self): 280 return "" 281 282 def QListThreadsInStopReply(self): 283 return "" 284 285 def qMemoryRegionInfo(self): 286 return "" 287 288 def qPathComplete(self): 289 return "" 290 291 """ 292 Raised when we receive a packet for which there is no default action. 293 Override the responder class to implement behavior suitable for the test at 294 hand. 295 """ 296 class UnexpectedPacketException(Exception): 297 pass 298 299 300class MockGDBServer: 301 """ 302 A simple TCP-based GDB server that can test client behavior by receiving 303 commands and issuing custom-tailored responses. 304 305 Responses are generated via the .responder property, which should be an 306 instance of a class based on MockGDBServerResponder. 307 """ 308 309 responder = None 310 _socket = None 311 _client = None 312 _thread = None 313 _receivedData = None 314 _receivedDataOffset = None 315 _shouldSendAck = True 316 317 def __init__(self): 318 self.responder = MockGDBServerResponder() 319 320 def start(self): 321 family, type, proto, _, addr = socket.getaddrinfo("localhost", 0, 322 proto=socket.IPPROTO_TCP)[0] 323 self._socket = socket.socket(family, type, proto) 324 325 326 self._socket.bind(addr) 327 self._socket.listen(1) 328 329 # Start a thread that waits for a client connection. 330 self._thread = threading.Thread(target=self._run) 331 self._thread.start() 332 333 def stop(self): 334 self._socket.close() 335 self._thread.join() 336 self._thread = None 337 338 def get_connect_address(self): 339 return "[{}]:{}".format(*self._socket.getsockname()) 340 341 def _run(self): 342 # For testing purposes, we only need to worry about one client 343 # connecting just one time. 344 try: 345 # accept() is stubborn and won't fail even when the socket is 346 # shutdown, so we'll use a timeout 347 self._socket.settimeout(30.0) 348 client, client_addr = self._socket.accept() 349 self._client = client 350 # The connected client inherits its timeout from self._socket, 351 # but we'll use a blocking socket for the client 352 self._client.settimeout(None) 353 except: 354 return 355 self._shouldSendAck = True 356 self._receivedData = "" 357 self._receivedDataOffset = 0 358 data = None 359 while True: 360 try: 361 data = seven.bitcast_to_string(self._client.recv(4096)) 362 if data is None or len(data) == 0: 363 break 364 self._receive(data) 365 except Exception as e: 366 print("An exception happened when receiving the response from the gdb server. Closing the client...") 367 traceback.print_exc() 368 self._client.close() 369 break 370 371 def _receive(self, data): 372 """ 373 Collects data, parses and responds to as many packets as exist. 374 Any leftover data is kept for parsing the next time around. 375 """ 376 self._receivedData += data 377 try: 378 packet = self._parsePacket() 379 while packet is not None: 380 self._handlePacket(packet) 381 packet = self._parsePacket() 382 except self.InvalidPacketException: 383 self._client.close() 384 385 def _parsePacket(self): 386 """ 387 Reads bytes from self._receivedData, returning: 388 - a packet's contents if a valid packet is found 389 - the PACKET_ACK unique object if we got an ack 390 - None if we only have a partial packet 391 392 Raises an InvalidPacketException if unexpected data is received 393 or if checksums fail. 394 395 Once a complete packet is found at the front of self._receivedData, 396 its data is removed form self._receivedData. 397 """ 398 data = self._receivedData 399 i = self._receivedDataOffset 400 data_len = len(data) 401 if data_len == 0: 402 return None 403 if i == 0: 404 # If we're looking at the start of the received data, that means 405 # we're looking for the start of a new packet, denoted by a $. 406 # It's also possible we'll see an ACK here, denoted by a + 407 if data[0] == '+': 408 self._receivedData = data[1:] 409 return self.PACKET_ACK 410 if ord(data[0]) == 3: 411 self._receivedData = data[1:] 412 return self.PACKET_INTERRUPT 413 if data[0] == '$': 414 i += 1 415 else: 416 raise self.InvalidPacketException( 417 "Unexpected leading byte: %s" % data[0]) 418 419 # If we're looking beyond the start of the received data, then we're 420 # looking for the end of the packet content, denoted by a #. 421 # Note that we pick up searching from where we left off last time 422 while i < data_len and data[i] != '#': 423 i += 1 424 425 # If there isn't enough data left for a checksum, just remember where 426 # we left off so we can pick up there the next time around 427 if i > data_len - 3: 428 self._receivedDataOffset = i 429 return None 430 431 # If we have enough data remaining for the checksum, extract it and 432 # compare to the packet contents 433 packet = data[1:i] 434 i += 1 435 try: 436 check = int(data[i:i + 2], 16) 437 except ValueError: 438 raise self.InvalidPacketException("Checksum is not valid hex") 439 i += 2 440 if check != checksum(packet): 441 raise self.InvalidPacketException( 442 "Checksum %02x does not match content %02x" % 443 (check, checksum(packet))) 444 # remove parsed bytes from _receivedData and reset offset so parsing 445 # can start on the next packet the next time around 446 self._receivedData = data[i:] 447 self._receivedDataOffset = 0 448 return packet 449 450 def _handlePacket(self, packet): 451 if packet is self.PACKET_ACK: 452 # Ignore ACKs from the client. For the future, we can consider 453 # adding validation code to make sure the client only sends ACKs 454 # when it's supposed to. 455 return 456 response = "" 457 # We'll handle the ack stuff here since it's not something any of the 458 # tests will be concerned about, and it'll get turned off quickly anyway. 459 if self._shouldSendAck: 460 self._client.sendall(seven.bitcast_to_bytes('+')) 461 if packet == "QStartNoAckMode": 462 self._shouldSendAck = False 463 response = "OK" 464 elif self.responder is not None: 465 # Delegate everything else to our responder 466 response = self.responder.respond(packet) 467 # Handle packet framing since we don't want to bother tests with it. 468 if response is not None: 469 framed = frame_packet(response) 470 self._client.sendall(seven.bitcast_to_bytes(framed)) 471 472 PACKET_ACK = object() 473 PACKET_INTERRUPT = object() 474 475 class InvalidPacketException(Exception): 476 pass 477 478class GDBRemoteTestBase(TestBase): 479 """ 480 Base class for GDB client tests. 481 482 This class will setup and start a mock GDB server for the test to use. 483 It also provides assertPacketLogContains, which simplifies the checking 484 of packets sent by the client. 485 """ 486 487 NO_DEBUG_INFO_TESTCASE = True 488 mydir = TestBase.compute_mydir(__file__) 489 server = None 490 491 def setUp(self): 492 TestBase.setUp(self) 493 self.server = MockGDBServer() 494 self.server.start() 495 496 def tearDown(self): 497 # TestBase.tearDown will kill the process, but we need to kill it early 498 # so its client connection closes and we can stop the server before 499 # finally calling the base tearDown. 500 if self.process() is not None: 501 self.process().Kill() 502 self.server.stop() 503 TestBase.tearDown(self) 504 505 def createTarget(self, yaml_path): 506 """ 507 Create a target by auto-generating the object based on the given yaml 508 instructions. 509 510 This will track the generated object so it can be automatically removed 511 during tearDown. 512 """ 513 yaml_base, ext = os.path.splitext(yaml_path) 514 obj_path = self.getBuildArtifact(yaml_base) 515 self.yaml2obj(yaml_path, obj_path) 516 return self.dbg.CreateTarget(obj_path) 517 518 def connect(self, target): 519 """ 520 Create a process by connecting to the mock GDB server. 521 522 Includes assertions that the process was successfully created. 523 """ 524 listener = self.dbg.GetListener() 525 error = lldb.SBError() 526 process = target.ConnectRemote(listener, 527 "connect://" + self.server.get_connect_address(), "gdb-remote", error) 528 self.assertTrue(error.Success(), error.description) 529 self.assertTrue(process, PROCESS_IS_VALID) 530 return process 531 532 def assertPacketLogContains(self, packets): 533 """ 534 Assert that the mock server's packet log contains the given packets. 535 536 The packet log includes all packets sent by the client and received 537 by the server. This fuction makes it easy to verify that the client 538 sent the expected packets to the server. 539 540 The check does not require that the packets be consecutive, but does 541 require that they are ordered in the log as they ordered in the arg. 542 """ 543 i = 0 544 j = 0 545 log = self.server.responder.packetLog 546 547 while i < len(packets) and j < len(log): 548 if log[j] == packets[i]: 549 i += 1 550 j += 1 551 if i < len(packets): 552 self.fail(u"Did not receive: %s\nLast 10 packets:\n\t%s" % 553 (packets[i], u'\n\t'.join(log))) 554