Skip to content

Commit

Permalink
add aarch64 support
Browse files Browse the repository at this point in the history
  • Loading branch information
0ddc0de committed Nov 2, 2024
1 parent 56a90cf commit d9ee288
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 33 deletions.
30 changes: 27 additions & 3 deletions avatar2/archs/architecture.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from avatar2.installer.config import AvatarConfig
import distutils
from os import environ
import string
from os import environ, path
from re import sub

class Architecture(object):
Expand All @@ -14,13 +15,36 @@ def _resolve_executable(exec_name):
is installed on the system
"""

def sanitize_env_var_name(name):
"""
Replaces all non-letter and non-digit characters with underscores (_).
"""

new_name = []

for char in name:
if char in string.ascii_letters + string.digits:
new_name.append(char)
else:
new_name.append("_")

return "".join(new_name)

env_var_name = 'AVATAR2_%s_EXECUTABLE' % sub(r'avatar-|\W\(.*\)',
'', exec_name).upper()
env_exec = environ.get( env_var_name )
env_var_name = sanitize_env_var_name(env_var_name)

env_exec = environ.get(env_var_name)

if env_exec is not None:
target_path = distutils.spawn.find_executable(env_exec)
if path.isfile(env_exec):
target_path = env_exec
else:
target_path = distutils.spawn.find_executable(env_exec)

else:
target_path = AvatarConfig().get_target_path(exec_name)

if target_path is None:
raise Exception(("Couldn't find executable for %s\n"
"Have you tried running the avatar2-installer "
Expand Down
79 changes: 78 additions & 1 deletion avatar2/archs/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .architecture import Architecture
import avatar2

from avatar2.installer.config import QEMU, PANDA, OPENOCD, GDB_MULTI
from avatar2.installer.config import QEMU, QEMU_AARCH64, PANDA, OPENOCD, GDB_ARM, GDB_AARCH64, GDB_MULTI


class ARM(Architecture):
Expand Down Expand Up @@ -37,6 +37,83 @@ class ARM(Architecture):
keystone_mode = KS_MODE_ARM
unicorn_arch = UC_ARCH_ARM
unicorn_mode = UC_MODE_ARM

class AARCH64(Architecture):

get_qemu_executable = Architecture.resolve(QEMU_AARCH64)
#get_panda_executable = Architecture.resolve(PANDA)
get_gdb_executable = Architecture.resolve(GDB_AARCH64)
#get_oocd_executable = Architecture.resolve(OPENOCD)



qemu_name = 'aarch64'
gdb_name = 'aarch64'

# note that many of these registers are read-only
# registers that may only be written to using msr can not be written from gdb
registers = {'x0': 0, 'x1': 1, 'x2': 2, 'x3': 3, 'x4': 4, 'x5': 5, 'x6': 6,
'x7': 7, 'x8': 8, 'x9': 9, 'x10': 10, 'x11': 11, 'x12': 12,
'x13': 13, 'x14': 14, 'x15': 15, 'x16': 16, 'x17': 17, 'x18': 18,
'x19': 19, 'x20': 20, 'x21': 21, 'x22': 22, 'x23': 23, 'x24': 24,
'x25': 25, 'x26': 26, 'x27': 27, 'x28': 28, 'x29': 29, 'lr': 30,
'sp': 31, 'pc': 32, 'cpsr': 33, 'fpsr': 66, 'fpcr': 67,
'MVFR6_EL1_RESERVED': 68, 'ESR_EL2': 69, 'MVFR7_EL1_RESERVED': 70, 'TPIDR_EL3': 71, 'MAIR_EL3': 72,
'ID_AA64PFR1_EL1': 73, 'ID_AA64PFR2_EL1_RESERVED': 74, 'AFSR0_EL3': 75,
'ID_AA64PFR3_EL1_RESERVED': 76, 'SCTLR': 77, 'AFSR1_EL3': 78, 'ID_AA64ZFR0_EL1': 79, 'CNTKCTL': 80,
'DACR32_EL2': 81, 'ID_AA64PFR5_EL1_RESERVED': 82, 'CPACR': 83, 'ID_AA64PFR6_EL1_RESERVED': 84,
'FPEXC32_EL2': 85, 'ACTLR_EL1': 86, 'ID_AA64PFR7_EL1_RESERVED': 87, 'ID_AA64DFR0_EL1': 88,
'AMAIR_EL3': 89, 'ID_AA64DFR1_EL1': 90, 'ID_AA64DFR2_EL1_RESERVED': 91, 'ESR_EL3': 92,
'ID_AA64DFR3_EL1_RESERVED': 93, 'ID_AA64AFR0_EL1': 94, 'ID_AA64AFR1_EL1': 95,
'ID_AA64AFR2_EL1_RESERVED': 96, 'CNTFRQ_EL0': 97, 'ID_AA64AFR3_EL1_RESERVED': 98, 'SPSR_EL1': 99,
'ID_AA64ISAR0_EL1': 100, 'DBGBVR': 197, 'ELR_EL1': 102, 'ID_AA64ISAR1_EL1': 103, 'FAR_EL2': 104,
'PMEVTYPER0_EL0': 105, 'DBGBCR': 199, 'ID_AA64ISAR2_EL1_RESERVED': 107, 'PMEVTYPER1_EL0': 108,
'DBGWVR': 170, 'ID_AA64ISAR3_EL1_RESERVED': 110, 'DBGWCR': 173, 'PMEVTYPER2_EL0': 112,
'ID_AA64ISAR4_EL1_RESERVED': 113, 'PMEVTYPER3_EL0': 114, 'MDCCSR_EL0': 115,
'ID_AA64ISAR5_EL1_RESERVED': 116, 'HPFAR_EL2': 117, 'ID_AA64ISAR6_EL1_RESERVED': 118,
'ID_AA64ISAR7_EL1_RESERVED': 119, 'CNTVOFF_EL2': 120, 'SP_EL0': 121, 'ID_AA64MMFR0_EL1': 122,
'ID_AA64MMFR1_EL1': 124, 'ID_AA64MMFR2_EL1': 126, 'PMINTENSET_EL1': 127,
'ID_AA64MMFR3_EL1_RESERVED': 129, 'SCTLR_EL2': 130, 'ID_AA64MMFR4_EL1_RESERVED': 132,
'PMCNTENSET_EL0': 133, 'CNTHCTL_EL2': 134, 'PMCR_EL0': 135, 'ID_AA64MMFR5_EL1_RESERVED': 136,
'PMCNTENCLR_EL0': 137, 'FAR_EL3': 138, 'ID_AA64MMFR6_EL1_RESERVED': 139, 'ACTLR_EL2': 140,
'PMOVSCLR_EL0': 141, 'MDSCR_EL1': 142, 'ID_AA64MMFR7_EL1_RESERVED': 143, 'CNTP_CTL_EL0': 144,
'PMSELR_EL0': 145, 'PMCEID1_EL0': 148, 'PMCEID0_EL0': 149, 'HCR_EL2': 151, 'PMCCNTR_EL0': 152,
'CNTP_CVAL_EL0': 153, 'MDCR_EL2': 155, 'CNTHP_TVAL_EL2': 156, 'CPTR_EL2': 157, 'CNTHP_CTL_EL2': 158,
'L2ACTLR': 159, 'HSTR_EL2': 160, 'TTBR0_EL1': 161, 'CNTHP_CVAL_EL2': 162, 'SCTLR_EL3': 163,
'TTBR1_EL1': 164, 'TCR_EL1': 165, 'HACR_EL2': 168, 'VBAR_EL2': 169, 'PMUSERENR_EL0': 171,
'CNTV_CTL_EL0': 172, 'VBAR': 174, 'ACTLR_EL3': 175, 'CNTV_CVAL_EL0': 176, 'PMOVSSET_EL0': 177,
'SCR_EL3': 178, 'SP_EL1': 179, 'MDRAR_EL1': 180, 'SDER32_EL3': 181, 'PMCCFILTR_EL0': 182,
'CPTR_EL3': 184, 'SPSR_EL3': 186, 'ELR_EL3': 187, 'CPUACTLR_EL1': 188, 'CPUECTLR_EL1': 189,
'VBAR_EL3': 190, 'CONTEXTIDR_EL1': 191, 'CNTPS_CTL_EL1': 192, 'CPUMERRSR_EL1': 193, 'RVBAR_EL3': 194,
'CNTPS_CVAL_EL1': 195, 'L2MERRSR_EL1': 196, 'MAIR_EL1': 198, 'TPIDR_EL1': 200, 'AFSR0_EL1': 201,
'OSLSR_EL1': 202, 'AFSR1_EL1': 203, 'PAR_EL1': 204, 'CBAR_EL1': 205, 'TTBR0_EL2': 206,
'SPSR_IRQ': 207, 'MDCR_EL3': 208, 'TCR_EL2': 209, 'SPSR_ABT': 210, 'SPSR_UND': 211, 'FPCR': 212,
'AMAIR0': 213, 'FPSR': 214, 'SPSR_FIQ': 215, 'ESR_EL1': 216, 'REVIDR_EL1': 217, 'CLIDR': 218,
'ID_PFR0': 219, 'VTTBR_EL2': 220, 'ID_DFR0': 221, 'ID_AFR0': 222, 'VTCR_EL2': 223, 'ID_MMFR0': 224,
'CSSELR': 225, 'ID_MMFR1': 226, 'TPIDR_EL0': 227, 'AIDR': 228, 'TTBR0_EL3': 229, 'ID_MMFR2': 230,
'TPIDRRO_EL0': 231, 'ID_MMFR3': 232, 'IFSR32_EL2': 233, 'TCR_EL3': 234, 'ID_ISAR0': 235,
'ID_ISAR1': 236, 'PMEVCNTR0_EL0': 237, 'ID_ISAR2': 238, 'PMEVCNTR1_EL0': 239, 'ID_ISAR3': 240,
'CTR_EL0': 241, 'TPIDR_EL2': 242, 'PMEVCNTR2_EL0': 243, 'ID_ISAR4': 244, 'PMEVCNTR3_EL0': 245,
'ID_ISAR5': 246, 'MAIR_EL2': 247, 'ID_MMFR4': 248, 'AFSR0_EL2': 249, 'ID_ISAR6': 250,
'AFSR1_EL2': 251, 'L2CTLR_EL1': 252, 'VPIDR_EL2': 253, 'MVFR0_EL1': 254, 'L2ECTLR_EL1': 255,
'FAR_EL1': 256, 'MVFR1_EL1': 257, 'MVFR2_EL1': 258, 'MVFR3_EL1_RESERVED': 259,
'MVFR4_EL1_RESERVED': 260, 'AMAIR_EL2': 261, 'MVFR5_EL1_RESERVED': 262}

"""unicorn_registers = {'r0': UC_ARM_REG_R0, 'r1': UC_ARM_REG_R1, 'r2': UC_ARM_REG_R2,
'r3': UC_ARM_REG_R3, 'r4': UC_ARM_REG_R4, 'r5': UC_ARM_REG_R5,
'r6': UC_ARM_REG_R6, 'r7': UC_ARM_REG_R7, 'r8': UC_ARM_REG_R8,
'r9': UC_ARM_REG_R9, 'r10': UC_ARM_REG_R10, 'r11': UC_ARM_REG_R11,
'r12': UC_ARM_REG_R12, 'sp': UC_ARM_REG_SP, 'lr': UC_ARM_REG_LR,
'pc': UC_ARM_REG_PC, 'cpsr': UC_ARM_REG_CPSR}"""
pc_name = 'pc'
sr_name = 'cpsr'
#unemulated_instructions = ['mcr', 'mrc']
#capstone_arch = CS_ARCH_ARM
#capstone_mode = CS_MODE_LITTLE_ENDIAN
#keystone_arch = KS_ARCH_ARM
#keystone_mode = KS_MODE_ARM
#unicorn_arch = UC_ARCH_ARM
#unicorn_mode = UC_MODE_ARM


class ARM_CORTEX_M3(ARM):
Expand Down
6 changes: 3 additions & 3 deletions avatar2/avatar2.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from .memory_range import MemoryRange
from .message import *
from .peripherals import AvatarPeripheral
from .targets.target import TargetStates # TargetStates
from .targets.target import Target, TargetStates
from .watchmen import watch, Watchmen


Expand Down Expand Up @@ -194,8 +194,8 @@ def load_plugin(self, name, local=False, *args, **kwargs):
plugin.load_plugin(self, *args, **kwargs)
self.loaded_plugins += [name]

@watch("AddTarget")
def add_target(self, backend, *args, **kwargs):
@watch('AddTarget')
def add_target(self, backend, *args, **kwargs) -> Target:
"""
Adds a new target to the analyses
Expand Down
21 changes: 17 additions & 4 deletions avatar2/installer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
# Constant names for the different targets
OPENOCD = 'openocd'
QEMU = 'avatar-qemu'
PANDA = 'panda'
GDB_MULTI = 'gdb (multiarch)'
GDB_X86 = 'gdb (x86)'
QEMU_AARCH64 = 'avatar-qemu-aarch64'
PANDA = 'avatar-panda'
GDB_ARM = 'gdb (ARM)'
GDB_AARCH64 = 'gdb (AARCH64)'
GDB_X86 = 'gdb (x86)'


TARGETS = OrderedDict(
Expand All @@ -32,6 +35,13 @@
'install_cmd': ['git submodule update --init dtc',
'./configure', 'make']
}),
(QEMU_AARCH64, { 'git': 'https://github.com/avatartwo/avatar-qemu',
'configure': '--disable-sdl --target-list=aarch64-softmmu',
'make': '',
'rel_path': 'aarch64-softmmu/qemu-system-aarch64',
'install_cmd': ['git submodule update --init dtc',
'./configure', 'make'],
}),
(PANDA, {'git': 'https://github.com/panda-re/panda',
'configure': '--disable-sdl --target-list=arm-softmmu',
'make': '',
Expand All @@ -42,13 +52,16 @@
'install_script': 'panda/scripts/install_ubuntu.sh'
}),
(GDB_X86, { 'apt_name': 'gdb' }),
(GDB_MULTI, { 'apt_name': 'gdb-multiarch'})
(GDB_MULTI, { 'apt_name': 'gdb-multiarch'}),
(GDB_ARM, { 'apt_name': 'gdb-arm-none-eabi',
'sys_name': 'arm-none-eabi-gdb'}),
(GDB_AARCH64, {'apt_name': 'gdb-multiarch',}) #TODO: put the path to specific gdb
]
)


class AvatarConfig(ConfigParser):


def __init__(self):
super(AvatarConfig, self).__init__()
Expand Down
95 changes: 74 additions & 21 deletions avatar2/plugins/gdbserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
from time import sleep
from struct import pack
from types import MethodType
from typing import Iterable, NamedTuple, Type
from threading import Thread, Event
from socket import AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
from os.path import dirname

from avatar2.targets import TargetStates
from avatar2.archs import AARCH64, Architecture
from avatar2 import Target

l = logging.getLogger('avatar2.gdbplugin')

Expand All @@ -25,10 +28,14 @@
TIMEOUT_TIME = 1.0


class GDBRSPServer(Thread):
class Register(NamedTuple):
name: str
bitsize: int
num: int


def __init__(self, avatar, target, port=3333, xml_file=None,
do_forwarding=False):
class GDBRSPServer(Thread):
def __init__(self, avatar, target, port=3333, do_forwarding=False):
super().__init__()
self.daemon=True
self.sock = socket.socket(AF_INET, SOCK_STREAM)
Expand All @@ -37,18 +44,15 @@ def __init__(self, avatar, target, port=3333, xml_file=None,
self.avatar = avatar
self.target = target
self.port = port
self.xml_file = xml_file
self.do_forwarding = do_forwarding

self._packetsize=0x47FF
self.running = False
self.bps = {}
self._do_shutdown = Event()


xml_regs = ET.parse(self.xml_file).getroot().find('feature')
self.registers = [reg.attrib for reg in xml_regs if reg.tag == 'reg']
assert(len(self.registers))
self.registers = list(self._fetch_registers(self.avatar.arch, self.target))
assert(self.registers)

self.handlers = {
'q' : self.query,
Expand All @@ -69,6 +73,62 @@ def __init__(self, avatar, target, port=3333, xml_file=None,
'D' : self.detach,
}

@staticmethod
def _make_gdb_xml(arch: Type[Architecture], registers: Iterable[Register]):
target = ET.Element("target")

architecture = ET.Element("architecture")
architecture.text = arch.gdb_name
target.append(architecture)

feature = ET.Element("feature", attrib={"name": f"org.gnu.gdb.{arch.gdb_name}.core"})

for register in registers:
# v* registers are used for floating point and SIMD in AArch64, the others map to those
# they use a different size, which we can send to gdb, but gdb does not like registers with a size of 128
# therefore we skip them
if re.match(r"[bhsdqv][0-9]+", register.name):
continue

reg = ET.Element("reg", attrib={
"name": register.name,
"bitsize": str(register.bitsize),
"regnum": str(register.num),
})

if register.name in ["pc"]:
reg.attrib["type"] = "data_ptr"

elif register.name in ["sp"]:
reg.attrib["type"] = "code_ptr"

feature.append(reg)

target.append(feature)

return ET.tostring(target)

@staticmethod
def _fetch_registers(arch: Type[Architecture], target: Target) -> Iterable[Register]:
register_names = target.protocols.registers.get_register_names()

# we do not support different-size registers at the moment
# gdb for instance has complained about the 128 bit v* registers (see also the filter below)
if arch == AARCH64:
bitsize = 64
arch_name = "aarch64"
else:
raise NotImplementedError(f"architecture {repr(arch)} not implemented yet")

for num, name in enumerate(register_names):
# v* registers are used for floating point and SIMD in AArch64, the others map to those
# they use a different size, which we can send to gdb, but gdb does not like registers with a size of 128
# therefore we skip them
if arch == AARCH64 and re.match(r"[bhsdqv][0-9]+", name):
continue

yield Register(name, bitsize, num)

def shutdown(self):
self._do_shutdown.set()
sleep(TIMEOUT_TIME*2)
Expand Down Expand Up @@ -125,8 +185,7 @@ def query(self, pkt):
off, length = match_hex('qXfer:features:read:target.xml:(.*),(.*)',
pkt.decode())

with open(self.xml_file, 'rb') as f:
data = f.read()
data = self._make_gdb_xml(self.avatar.arch, self.registers)
resp_data = data[off:off+length]
if len(resp_data) < length:
prefix = b'l'
Expand Down Expand Up @@ -181,11 +240,9 @@ def halt_reason(self, pkt):
def read_registers(self, pkt):
resp = ''
for reg in self.registers:

bitsize = int(reg['bitsize'])
assert( bitsize % 8 == 0)
r_len = int(bitsize / 8)
r_val = self.target.read_register(reg['name'])
assert(reg.bitsize % 8 == 0)
r_len = reg.bitsize / 8
r_val = self.target.read_register(reg.name)
#l.debug(f'{reg["name"]}, {r_val}, {r_len}')

resp += r_val.to_bytes(r_len, 'little').hex()
Expand Down Expand Up @@ -349,12 +406,8 @@ def receive_packet(self):
pkt += c


def spawn_gdb_server(self, target, port, do_forwarding=True, xml_file=None):
if xml_file is None:
# default for now: use ARM
xml_file = f'{dirname(__file__)}/gdb/arm-target.xml'

server = GDBRSPServer(self, target, port, xml_file, do_forwarding)
def spawn_gdb_server(self, target, port, do_forwarding=True):
server = GDBRSPServer(self, target, port, do_forwarding)
server.start()
self._gdb_servers.append(server)
return server
Expand Down
12 changes: 12 additions & 0 deletions avatar2/protocols/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,18 @@ def get_symbol(self, symbol):
self.log.debug("Attempt to resolve the symbol %s. " +
"Received: %s" % resp)
return ret, resp

def get_address(self, address):
self._communicator.start_console_collection()
req = ['info', 'symbol', '0x%x' % address]
#print(req)
ret, resp = self._sync_request(req, GDB_PROT_DONE)
self._communicator.stop_console_collection()
if ret:
resp = self._communicator._console_output
self.log.debug("Attempt to resolve the address %x. " +
"Received: %s" % resp)
return ret, resp

def set_gdb_variable(self, variable, value):
req = ['-gdb-set', str(variable), str(value)]
Expand Down
Loading

0 comments on commit d9ee288

Please sign in to comment.