AMDGPU: Fix verifier assert with out of bounds subregister indexes (#119799)
[llvm-project.git] / lldb / packages / Python / lldbsuite / test / gdbclientutils.py
blob1784487323ad6bef63f19f825f94c6fdac54728e
1 import ctypes
2 import errno
3 import io
4 import threading
5 import socket
6 import traceback
7 from lldbsuite.support import seven
10 def checksum(message):
11 """
12 Calculate the GDB server protocol checksum of the message.
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
22 def frame_packet(message):
23 """
24 Create a framed packet that's ready to send over the GDB connection
25 channel.
27 Framing includes surrounding the message between $ and #, and appending
28 a two character hex checksum.
29 """
30 return "$%s#%02x" % (message, checksum(message))
33 def escape_binary(message):
34 """
35 Escape the binary message using the process described in the GDB server
36 protocol documentation.
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
52 def 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
63 def 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
77 class MockGDBServerResponder:
78 """
79 A base class for handling client packets and issuing server responses for
80 GDB tests.
82 This handles many typical situations, while still allowing subclasses to
83 completely customize their responses.
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 """
89 registerCount = 40
90 packetLog = None
92 class RESPONSE_DISCONNECT:
93 pass
95 def __init__(self):
96 self.packetLog = []
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.
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()
214 return self.other(packet)
216 def qsProcessInfo(self):
217 return "E04"
219 def qfProcessInfo(self, packet):
220 return "E04"
222 def jGetLoadedDynamicLibrariesInfos(self, packet):
223 return ""
225 def qGetWorkingDir(self):
226 return "2f"
228 def qOffsets(self):
229 return ""
231 def qProcessInfo(self):
232 return ""
234 def qHostInfo(self):
235 return "ptrsize:8;endian:little;"
237 def qQueryGDBServer(self):
238 return "E04"
240 def interrupt(self):
241 raise self.UnexpectedPacketException()
243 def cont(self):
244 raise self.UnexpectedPacketException()
246 def vCont(self, packet):
247 raise self.UnexpectedPacketException()
249 def A(self, packet):
250 return ""
252 def D(self, packet):
253 return "OK"
255 def readRegisters(self):
256 return "00000000" * self.registerCount
258 def readRegister(self, register):
259 return "00000000"
261 def writeRegisters(self, registers_hex):
262 return "OK"
264 def writeRegister(self, register, value_hex):
265 return "OK"
267 def readMemory(self, addr, length):
268 return "00" * length
270 def writeMemory(self, addr, data_hex):
271 return "OK"
273 def qSymbol(self, symbol_args):
274 return "OK"
276 def qSupported(self, client_supported):
277 return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+"
279 def qfThreadInfo(self):
280 return "l"
282 def qsThreadInfo(self):
283 return "l"
285 def qC(self):
286 return "QC0"
288 def QEnableErrorStrings(self):
289 return "OK"
291 def haltReason(self):
292 # SIGINT is 2, return type is 2 digit hex string
293 return "S02"
295 def qXferRead(self, obj, annex, offset, length):
296 return None, False
298 def _qXferResponse(self, data, has_more):
299 return "%s%s" % ("m" if has_more else "l", escape_binary(data))
301 def vAttach(self, pid):
302 raise self.UnexpectedPacketException()
304 def selectThread(self, op, thread_id):
305 return "OK"
307 def setBreakpoint(self, packet):
308 raise self.UnexpectedPacketException()
310 def threadStopInfo(self, threadnum):
311 return ""
313 def other(self, packet):
314 # empty string means unsupported
315 return ""
317 def QThreadSuffixSupported(self):
318 return ""
320 def QListThreadsInStopReply(self):
321 return ""
323 def qMemoryRegionInfo(self, addr):
324 return ""
326 def qPathComplete(self):
327 return ""
329 def vFile(self, packet):
330 return ""
332 def vRun(self, packet):
333 return ""
335 def qLaunchGDBServer(self, host):
336 raise self.UnexpectedPacketException()
338 def qLaunchSuccess(self):
339 return ""
341 def QEnvironment(self, packet):
342 return "OK"
344 def QEnvironmentHexEncoded(self, packet):
345 return "OK"
347 def qRegisterInfo(self, num):
348 return ""
350 def k(self):
351 return ["W01", self.RESPONSE_DISCONNECT]
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.
359 class UnexpectedPacketException(Exception):
360 pass
363 class ServerChannel:
365 A wrapper class for TCP or pty-based server.
368 def get_connect_address(self):
369 """Get address for the client to connect to."""
371 def get_connect_url(self):
372 """Get URL suitable for process connect command."""
374 def close_server(self):
375 """Close all resources used by the server."""
377 def accept(self):
378 """Accept a single client connection to the server."""
380 def close_connection(self):
381 """Close all resources used by the accepted connection."""
383 def recv(self):
384 """Receive a data packet from the connected client."""
386 def sendall(self, data):
387 """Send the data to the connected client."""
390 class ServerSocket(ServerChannel):
391 def __init__(self, family, type, proto, addr):
392 self._server_socket = socket.socket(family, type, proto)
393 self._connection = None
395 self._server_socket.bind(addr)
396 self._server_socket.listen(1)
398 def close_server(self):
399 self._server_socket.close()
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
412 def close_connection(self):
413 assert self._connection is not None
414 self._connection.close()
415 self._connection = None
417 def recv(self):
418 assert self._connection is not None
419 return self._connection.recv(4096)
421 def sendall(self, data):
422 assert self._connection is not None
423 return self._connection.sendall(data)
426 class 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)
433 def get_connect_address(self):
434 return "[{}]:{}".format(*self._server_socket.getsockname())
436 def get_connect_url(self):
437 return "connect://" + self.get_connect_address()
440 class UnixServerSocket(ServerSocket):
441 def __init__(self, addr):
442 super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr)
444 def get_connect_address(self):
445 return self._server_socket.getsockname()
447 def get_connect_url(self):
448 return "unix-connect://" + self.get_connect_address()
451 class PtyServerSocket(ServerChannel):
452 def __init__(self):
453 import pty
454 import tty
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")
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()
467 def get_connect_url(self):
468 return "serial://" + self.get_connect_address()
470 def close_server(self):
471 self._secondary.close()
472 self._primary.close()
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
483 def sendall(self, data):
484 return self._primary.write(data)
487 class MockGDBServer:
489 A simple TCP-based GDB server that can test client behavior by receiving
490 commands and issuing custom-tailored responses.
492 Responses are generated via the .responder property, which should be an
493 instance of a class based on MockGDBServerResponder.
496 responder = None
497 _socket = None
498 _thread = None
499 _receivedData = None
500 _receivedDataOffset = None
501 _shouldSendAck = True
503 def __init__(self, socket):
504 self._socket = socket
505 self.responder = MockGDBServerResponder()
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()
512 def stop(self):
513 self._thread.join()
514 self._thread = None
516 def get_connect_address(self):
517 return self._socket.get_connect_address()
519 def get_connect_url(self):
520 return self._socket.get_connect_url()
522 def run(self):
523 # For testing purposes, we only need to worry about one client
524 # connecting just one time.
525 try:
526 self._socket.accept()
527 except:
528 traceback.print_exc()
529 return
530 self._shouldSendAck = True
531 self._receivedData = ""
532 self._receivedDataOffset = 0
533 data = None
534 try:
535 while True:
536 data = seven.bitcast_to_string(self._socket.recv())
537 if data is None or len(data) == 0:
538 break
539 self._receive(data)
540 except self.TerminateConnectionException:
541 pass
542 except Exception as e:
543 print(
544 "An exception happened when receiving the response from the gdb server. Closing the client..."
546 traceback.print_exc()
547 finally:
548 self._socket.close_connection()
549 self._socket.close_server()
551 def _receive(self, data):
553 Collects data, parses and responds to as many packets as exist.
554 Any leftover data is kept for parsing the next time around.
556 self._receivedData += data
557 packet = self._parsePacket()
558 while packet is not None:
559 self._handlePacket(packet)
560 packet = self._parsePacket()
562 def _parsePacket(self):
564 Reads bytes from self._receivedData, returning:
565 - a packet's contents if a valid packet is found
566 - the PACKET_ACK unique object if we got an ack
567 - None if we only have a partial packet
569 Raises an InvalidPacketException if unexpected data is received
570 or if checksums fail.
572 Once a complete packet is found at the front of self._receivedData,
573 its data is removed form self._receivedData.
575 data = self._receivedData
576 i = self._receivedDataOffset
577 data_len = len(data)
578 if data_len == 0:
579 return None
580 if i == 0:
581 # If we're looking at the start of the received data, that means
582 # we're looking for the start of a new packet, denoted by a $.
583 # It's also possible we'll see an ACK here, denoted by a +
584 if data[0] == "+":
585 self._receivedData = data[1:]
586 return self.PACKET_ACK
587 if ord(data[0]) == 3:
588 self._receivedData = data[1:]
589 return self.PACKET_INTERRUPT
590 if data[0] == "$":
591 i += 1
592 else:
593 raise self.InvalidPacketException(
594 "Unexpected leading byte: %s" % data[0]
597 # If we're looking beyond the start of the received data, then we're
598 # looking for the end of the packet content, denoted by a #.
599 # Note that we pick up searching from where we left off last time
600 while i < data_len and data[i] != "#":
601 i += 1
603 # If there isn't enough data left for a checksum, just remember where
604 # we left off so we can pick up there the next time around
605 if i > data_len - 3:
606 self._receivedDataOffset = i
607 return None
609 # If we have enough data remaining for the checksum, extract it and
610 # compare to the packet contents
611 packet = data[1:i]
612 i += 1
613 try:
614 check = int(data[i : i + 2], 16)
615 except ValueError:
616 raise self.InvalidPacketException("Checksum is not valid hex")
617 i += 2
618 if check != checksum(packet):
619 raise self.InvalidPacketException(
620 "Checksum %02x does not match content %02x" % (check, checksum(packet))
622 # remove parsed bytes from _receivedData and reset offset so parsing
623 # can start on the next packet the next time around
624 self._receivedData = data[i:]
625 self._receivedDataOffset = 0
626 return packet
628 def _sendPacket(self, packet):
629 self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet)))
631 def _handlePacket(self, packet):
632 if packet is self.PACKET_ACK:
633 # Ignore ACKs from the client. For the future, we can consider
634 # adding validation code to make sure the client only sends ACKs
635 # when it's supposed to.
636 return
637 response = ""
638 # We'll handle the ack stuff here since it's not something any of the
639 # tests will be concerned about, and it'll get turned off quickly anyway.
640 if self._shouldSendAck:
641 self._socket.sendall(seven.bitcast_to_bytes("+"))
642 if packet == "QStartNoAckMode":
643 self._shouldSendAck = False
644 response = "OK"
645 elif self.responder is not None:
646 # Delegate everything else to our responder
647 response = self.responder.respond(packet)
648 if not isinstance(response, list):
649 response = [response]
650 for part in response:
651 if part is MockGDBServerResponder.RESPONSE_DISCONNECT:
652 raise self.TerminateConnectionException()
653 self._sendPacket(part)
655 PACKET_ACK = object()
656 PACKET_INTERRUPT = object()
658 class TerminateConnectionException(Exception):
659 pass
661 class InvalidPacketException(Exception):
662 pass