Skip to content

Commit e633e62

Browse files
committed
Rearrange as a utility class to allow patching XML
1 parent e8b8ca5 commit e633e62

File tree

2 files changed

+106
-77
lines changed

2 files changed

+106
-77
lines changed

qiling/debugger/gdb/gdb.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from qiling import Qiling
2929
from qiling.const import QL_ARCH, QL_ENDIAN, QL_OS
3030
from qiling.debugger import QlDebugger
31-
from qiling.debugger.gdb import xmlregs
31+
from qiling.debugger.gdb.xmlregs import QlGdbFeatures
3232
from qiling.debugger.gdb.utils import QlGdbUtils
3333

3434
# gdb logging prompt
@@ -98,7 +98,8 @@ def __init__(self, ql: Qiling, ip: str = '127.0.01', port: int = 9999):
9898

9999
self.gdb = QlGdbUtils(ql, entry_point, exit_point)
100100

101-
self.regsmap = xmlregs.load_regsmap(self.ql.arch.type)
101+
self.features = QlGdbFeatures(self.ql.arch.type, self.ql.os.type)
102+
self.regsmap = self.features.regsmap
102103

103104
def run(self):
104105
server = GdbSerialConn(self.ip, self.port, self.ql.log)
@@ -451,22 +452,12 @@ def handle_q(subcmd: str) -> Reply:
451452
offset, length = (int(p, 16) for p in params.split(','))
452453

453454
if feature == 'features' and op == 'read':
454-
xfercmd_abspath = os.path.dirname(os.path.abspath(__file__))
455-
xml_folder = self.ql.arch.type.name.lower()
456-
xfercmd_file = os.path.join(xfercmd_abspath, 'xml', xml_folder, annex)
457-
458-
if self.ql.os.type == QL_OS.WINDOWS:
459-
self.ql.log.info(f'{PROMPT} Qiling does not support XML for this platform yet')
460-
content = ''
461-
462-
elif not os.path.exists(xfercmd_file):
463-
self.ql.log.info(f'{PROMPT} XML file not found: "{xfercmd_file}"')
464-
content = ''
455+
if annex == r'target.xml':
456+
content = self.features.tostring()[offset:offset + length]
465457

466458
else:
467-
with open(xfercmd_file, 'r') as f:
468-
f.seek(offset, os.SEEK_SET)
469-
content = f.read(length)
459+
self.ql.log.info(f'{PROMPT} did not expect "{annex}" here')
460+
content = ''
470461

471462
return f'{"l" if len(content) < length else "m"}{content}'
472463

qiling/debugger/gdb/xmlregs.py

Lines changed: 99 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
from typing import Iterator, Mapping, Optional, Sequence, Tuple
77
from pathlib import PurePath
8+
from xml.etree import ElementTree, ElementInclude
89

910
from qiling.arch.arm_const import reg_map as arm_regs
1011
from qiling.arch.arm_const import reg_vfp as arm_regs_vfp
1112
from qiling.arch.arm64_const import reg_map as arm64_regs
1213
from qiling.arch.arm64_const import reg_map_v as arm64_regs_v
1314
from qiling.arch.mips_const import reg_map as mips_regs_gpr
1415
from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu
15-
from qiling.arch.x86_const import reg_map_16 as x86_regs_16
1616
from qiling.arch.x86_const import reg_map_32 as x86_regs_32
1717
from qiling.arch.x86_const import reg_map_64 as x86_regs_64
1818
from qiling.arch.x86_const import reg_map_misc as x86_regs_misc
@@ -21,92 +21,130 @@
2121
from qiling.arch.x86_const import reg_map_xmm as x86_regs_xmm
2222
from qiling.arch.x86_const import reg_map_ymm as x86_regs_ymm
2323

24-
from qiling.const import QL_ARCH
24+
from qiling.const import QL_ARCH, QL_OS
2525

2626
RegEntry = Tuple[Optional[int], int, int]
2727

28-
# define a local dummy function to let us reference this module
29-
__anchor__ = lambda x: x
28+
class QlGdbFeatures:
29+
def __init__(self, archtype: QL_ARCH, ostype: QL_OS):
30+
xmltree = QlGdbFeatures.__load_target_xml(archtype, ostype)
31+
regsmap = QlGdbFeatures.__load_regsmap(archtype, xmltree)
3032

31-
def __get_xml_path(archtype: QL_ARCH) -> Tuple[str, PurePath]:
32-
import inspect
33+
self.xmltree = xmltree
34+
self.regsmap = regsmap
3335

34-
p = PurePath(inspect.getfile(__anchor__))
35-
basedir = p.parent / 'xml' / archtype.name.lower()
36-
filename = basedir / 'target.xml'
36+
def tostring(self) -> str:
37+
root = self.xmltree.getroot()
3738

38-
return str(filename), basedir
39+
return ElementTree.tostring(root, encoding='unicode', xml_declaration=True)
3940

40-
def __walk_xml_regs(filename: str, base_url: PurePath) -> Iterator[Tuple[int, str, int]]:
41-
from xml.etree import ElementTree, ElementInclude
41+
@staticmethod
42+
def __get_xml_path(archtype: QL_ARCH) -> Tuple[str, PurePath]:
43+
import inspect
4244

43-
tree = ElementTree.parse(filename)
44-
root = tree.getroot()
45+
p = PurePath(inspect.getfile(QlGdbFeatures))
46+
basedir = p.parent / 'xml' / archtype.name.lower()
47+
filename = basedir / 'target.xml'
4548

46-
# NOTE: this is needed to load xinclude hrefs relative to the main xml file. starting
47-
# from python 3.9 ElementInclude.include has an argument for that called 'base_url'.
48-
# this is a workaround for earlier python versions such as 3.8
49+
return str(filename), basedir
4950

50-
def my_loader(base: PurePath):
51-
def __wrapped(href: str, parse, encoding=None):
52-
abshref = base / href
51+
@staticmethod
52+
def __load_target_xml(archtype: QL_ARCH, ostype: QL_OS) -> ElementTree.ElementTree:
53+
filename, base_url = QlGdbFeatures.__get_xml_path(archtype)
5354

54-
return ElementInclude.default_loader(str(abshref), parse, encoding)
55+
tree = ElementTree.parse(filename)
5556

56-
return __wrapped
57+
# NOTE: this is needed to load xinclude hrefs relative to the main xml file. starting
58+
# from python 3.9 ElementInclude.include has an argument for that called 'base_url'.
59+
# this is a workaround for earlier python versions such as 3.8
5760

58-
ElementInclude.include(root, loader=my_loader(base_url))
61+
# <WORKAROUND>
62+
def my_loader(base: PurePath):
63+
def __wrapped(href: str, parse, encoding=None):
64+
abshref = base / href
5965

60-
regnum = -1
66+
return ElementInclude.default_loader(str(abshref), parse, encoding)
6167

62-
for reg in root.iter('reg'):
63-
# if regnum is not specified, assume it follows the previous one
64-
regnum = int(reg.get('regnum', regnum + 1))
68+
return __wrapped
69+
# </WORKAROUND>
6570

66-
name = reg.attrib['name']
67-
bitsize = reg.attrib['bitsize']
71+
# inline all xi:include elements
72+
ElementInclude.include(tree.getroot(), loader=my_loader(base_url))
6873

69-
yield regnum, name, int(bitsize)
74+
# patch xml osabi element with the appropriate abi tag
75+
osabi = tree.find('osabi')
7076

71-
def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]:
72-
"""Initialize registers map using available target XML files.
77+
if osabi is not None:
78+
# NOTE: the 'Windows' abi tag is supported starting from gdb 10.
79+
# earlier gdb versions use 'Cygwin' instead
7380

74-
Args:
75-
archtype: target architecture type
81+
abitag = {
82+
QL_OS.LINUX : 'GNU/Linux',
83+
QL_OS.FREEBSD : 'FreeBSD',
84+
QL_OS.MACOS : 'Darwin',
85+
QL_OS.WINDOWS : 'Windows',
86+
QL_OS.UEFI : 'Windows',
87+
QL_OS.DOS : 'Windows',
88+
QL_OS.QNX : 'QNX-Neutrino'
89+
}.get(ostype, 'unknown')
7690

77-
Returns: a list representing registers data
78-
"""
91+
osabi.text = abitag
7992

80-
# retreive the relevant set of registers; their order of appearance is not
81-
# important as it is determined by the info read from the xml files
82-
ucregs: Mapping[str, int] = {
83-
QL_ARCH.A8086 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st),
84-
QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm),
85-
QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm),
86-
QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp),
87-
QL_ARCH.CORTEX_M : arm_regs,
88-
QL_ARCH.ARM64 : dict(**arm64_regs, **arm64_regs_v),
89-
QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu)
90-
}[archtype]
93+
return tree
9194

92-
xmlpath = __get_xml_path(archtype)
93-
regsinfo = sorted(__walk_xml_regs(*xmlpath))
95+
@staticmethod
96+
def __walk_xml_regs(xmltree: ElementTree.ElementTree) -> Iterator[Tuple[int, str, int]]:
97+
regnum = -1
9498

95-
# pre-allocate regmap and occupy it with null entries
96-
last_regnum = regsinfo[-1][0]
97-
regmap: Sequence[RegEntry] = [(None, 0, 0)] * (last_regnum + 1)
99+
for reg in xmltree.iter('reg'):
100+
# if regnum is not specified, assume it follows the previous one
101+
regnum = int(reg.get('regnum', regnum + 1))
98102

99-
pos = 0
103+
name = reg.attrib['name']
104+
bitsize = reg.attrib['bitsize']
100105

101-
for regnum, name, bitsize in sorted(regsinfo):
102-
# reg value size in nibbles
103-
nibbles = bitsize // 4
106+
yield regnum, name, int(bitsize)
104107

105-
regmap[regnum] = (ucregs.get(name), pos, nibbles)
108+
@staticmethod
109+
def __load_regsmap(archtype: QL_ARCH, xmltree: ElementTree.ElementTree) -> Sequence[RegEntry]:
110+
"""Initialize registers map using available target XML files.
106111
107-
# value position of next reg
108-
pos += nibbles
112+
Args:
113+
archtype: target architecture type
109114
110-
return regmap
115+
Returns: a list representing registers data
116+
"""
111117

112-
__all__ = ['RegEntry', 'load_regsmap']
118+
# retreive the relevant set of registers; their order of appearance is not
119+
# important as it is determined by the info read from the xml files
120+
ucregs: Mapping[str, int] = {
121+
QL_ARCH.A8086 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st),
122+
QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm),
123+
QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm),
124+
QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp),
125+
QL_ARCH.CORTEX_M : arm_regs,
126+
QL_ARCH.ARM64 : dict(**arm64_regs, **arm64_regs_v),
127+
QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu)
128+
}[archtype]
129+
130+
regsinfo = sorted(QlGdbFeatures.__walk_xml_regs(xmltree))
131+
132+
# pre-allocate regmap and occupy it with null entries
133+
last_regnum = regsinfo[-1][0]
134+
regmap: Sequence[RegEntry] = [(None, 0, 0)] * (last_regnum + 1)
135+
136+
pos = 0
137+
138+
for regnum, name, bitsize in sorted(regsinfo):
139+
# reg value size in nibbles
140+
nibbles = bitsize // 4
141+
142+
regmap[regnum] = (ucregs.get(name), pos, nibbles)
143+
144+
# value position of next reg
145+
pos += nibbles
146+
147+
return regmap
148+
149+
150+
__all__ = ['RegEntry', 'QlGdbFeatures']

0 commit comments

Comments
 (0)