207 lines
5.5 KiB
Python
207 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2020 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import abc
|
|
import binascii
|
|
import logging
|
|
|
|
|
|
logger = logging.getLogger("gdbstub")
|
|
|
|
|
|
class GdbStub(abc.ABC):
|
|
def __init__(self, logfile, elffile):
|
|
self.logfile = logfile
|
|
self.elffile = elffile
|
|
self.socket = None
|
|
self.gdb_signal = None
|
|
|
|
mem_regions = list()
|
|
|
|
for r in logfile.get_memory_regions():
|
|
mem_regions.append(r)
|
|
|
|
for r in elffile.get_memory_regions():
|
|
mem_regions.append(r)
|
|
|
|
self.mem_regions = mem_regions
|
|
|
|
def get_gdb_packet(self):
|
|
socket = self.socket
|
|
if socket is None:
|
|
return None
|
|
|
|
data = b''
|
|
checksum = 0
|
|
# Wait for '$'
|
|
while True:
|
|
ch = socket.recv(1)
|
|
if ch == b'$':
|
|
break
|
|
|
|
# Get a full packet
|
|
while True:
|
|
ch = socket.recv(1)
|
|
if ch == b'#':
|
|
# End of packet
|
|
break
|
|
|
|
checksum += ord(ch)
|
|
data += ch
|
|
|
|
# Get checksum (2-bytes)
|
|
ch = socket.recv(2)
|
|
in_chksum = ord(binascii.unhexlify(ch))
|
|
|
|
logger.debug(f"Received GDB packet: {data}")
|
|
|
|
if (checksum % 256) == in_chksum:
|
|
# ACK
|
|
logger.debug("ACK")
|
|
socket.send(b'+')
|
|
|
|
return data
|
|
else:
|
|
# NACK
|
|
logger.debug(f"NACK (checksum {in_chksum} != {checksum}")
|
|
socket.send(b'-')
|
|
|
|
return None
|
|
|
|
def put_gdb_packet(self, data):
|
|
socket = self.socket
|
|
if socket is None:
|
|
return
|
|
|
|
checksum = 0
|
|
for d in data:
|
|
checksum += d
|
|
|
|
pkt = b'$' + data + b'#'
|
|
|
|
checksum = checksum % 256
|
|
pkt += format(checksum, "02X").encode()
|
|
|
|
logger.debug(f"Sending GDB packet: {pkt}")
|
|
|
|
socket.send(pkt)
|
|
|
|
def handle_signal_query_packet(self):
|
|
# the '?' packet
|
|
pkt = b'S'
|
|
pkt += format(self.gdb_signal, "02X").encode()
|
|
|
|
self.put_gdb_packet(pkt)
|
|
|
|
@abc.abstractmethod
|
|
def handle_register_group_read_packet(self):
|
|
# the 'g' packet for reading a group of registers
|
|
pass
|
|
|
|
def handle_register_group_write_packet(self):
|
|
# the 'G' packet for writing to a group of registers
|
|
#
|
|
# We don't support writing so return error
|
|
self.put_gdb_packet(b"E01")
|
|
|
|
def handle_register_single_read_packet(self, pkt):
|
|
# the 'p' packet for reading a single register
|
|
self.put_gdb_packet(b"E01")
|
|
|
|
def handle_register_single_write_packet(self, pkt):
|
|
# the 'P' packet for writing to registers
|
|
#
|
|
# We don't support writing so return error
|
|
self.put_gdb_packet(b"E01")
|
|
|
|
def handle_memory_read_packet(self, pkt):
|
|
# the 'm' packet for reading memory: m<addr>,<len>
|
|
|
|
def get_mem_region(addr):
|
|
for r in self.mem_regions:
|
|
if r['start'] <= addr < r['end']:
|
|
return r
|
|
|
|
return None
|
|
|
|
# extract address and length from packet
|
|
# and convert them into usable integer values
|
|
str_addr, str_length = pkt[1:].split(b',')
|
|
s_addr = int(b'0x' + str_addr, 16)
|
|
length = int(b'0x' + str_length, 16)
|
|
|
|
# FIXME: Need more efficient way of extracting memory content
|
|
remaining = length
|
|
addr = s_addr
|
|
barray = b''
|
|
r = get_mem_region(addr)
|
|
while remaining > 0:
|
|
if r is None:
|
|
barray = None
|
|
break
|
|
|
|
if addr > r['end']:
|
|
r = get_mem_region(addr)
|
|
continue
|
|
|
|
offset = addr - r['start']
|
|
barray += r['data'][offset:offset+1]
|
|
|
|
addr += 1
|
|
remaining -= 1
|
|
|
|
if barray is not None:
|
|
pkt = binascii.hexlify(barray)
|
|
self.put_gdb_packet(pkt)
|
|
else:
|
|
self.put_gdb_packet(b"E01")
|
|
|
|
def handle_memory_write_packet(self, pkt):
|
|
# the 'M' packet for writing to memory
|
|
#
|
|
# We don't support writing so return error
|
|
self.put_gdb_packet(b"E02")
|
|
|
|
def handle_general_query_packet(self, pkt):
|
|
self.put_gdb_packet(b'')
|
|
|
|
def run(self, socket):
|
|
self.socket = socket
|
|
|
|
while True:
|
|
pkt = self.get_gdb_packet()
|
|
if pkt is None:
|
|
continue
|
|
|
|
pkt_type = pkt[0:1]
|
|
logger.debug(f"Got packet type: {pkt_type}")
|
|
|
|
if pkt_type == b'?':
|
|
self.handle_signal_query_packet()
|
|
elif pkt_type in (b'C', b'S'):
|
|
# Continue/stepping execution, which is not supported.
|
|
# So signal exception again
|
|
self.handle_signal_query_packet()
|
|
elif pkt_type == b'g':
|
|
self.handle_register_group_read_packet()
|
|
elif pkt_type == b'G':
|
|
self.handle_register_group_write_packet()
|
|
elif pkt_type == b'p':
|
|
self.handle_register_single_read_packet(pkt)
|
|
elif pkt_type == b'P':
|
|
self.handle_register_single_write_packet(pkt)
|
|
elif pkt_type == b'm':
|
|
self.handle_memory_read_packet(pkt)
|
|
elif pkt_type == b'M':
|
|
self.handle_memory_write_packet(pkt)
|
|
elif pkt_type == b'q':
|
|
self.handle_general_query_packet(pkt)
|
|
elif pkt_type == b'k':
|
|
# GDB quits
|
|
break
|
|
else:
|
|
self.put_gdb_packet(b'')
|