Skip to content

Commit 47f987f

Browse files
committed
ARM:ASTI: add lifting support for LDMIB
1 parent caf94a0 commit 47f987f

File tree

2 files changed

+310
-15
lines changed

2 files changed

+310
-15
lines changed

chb/app/CHVersion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
chbversion: str = "0.3.0-20250420"
1+
chbversion: str = "0.3.0-20250502"

chb/arm/opcodes/ARMLoadMultipleIncrementBefore.py

Lines changed: 309 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# ------------------------------------------------------------------------------
55
# The MIT License (MIT)
66
#
7-
# Copyright (c) 2021-2023 Aarno Labs LLC
7+
# Copyright (c) 2021-2025 Aarno Labs LLC
88
#
99
# Permission is hereby granted, free of charge, to any person obtaining a copy
1010
# of this software and associated documentation files (the "Software"), to deal
@@ -25,27 +25,187 @@
2525
# SOFTWARE.
2626
# ------------------------------------------------------------------------------
2727

28-
from typing import List, TYPE_CHECKING
28+
from typing import Iterable, List, Tuple, TYPE_CHECKING
2929

3030
from chb.app.InstrXData import InstrXData
3131

3232
from chb.arm.ARMDictionaryRecord import armregistry
33-
from chb.arm.ARMOpcode import ARMOpcode, simplify_result
33+
from chb.arm.ARMOpcode import ARMOpcode, ARMOpcodeXData, simplify_result
3434
from chb.arm.ARMOperand import ARMOperand
3535

36-
import chb.util.fileutil as UF
36+
import chb.ast.ASTNode as AST
37+
from chb.astinterface.ASTInterface import ASTInterface
38+
39+
import chb.invariants.XXprUtil as XU
3740

41+
import chb.util.fileutil as UF
3842
from chb.util.IndexedTable import IndexedTableValue
43+
from chb.util.loggingutil import chklogger
3944

4045
if TYPE_CHECKING:
4146
from chb.arm.ARMDictionary import ARMDictionary
47+
from chb.invariants.XVariable import XVariable
48+
from chb.invariants.XXpr import XXpr
49+
50+
51+
class ARMLoadMultipleIncrementBeforeXData(ARMOpcodeXData):
52+
"""Data format:
53+
- variables:
54+
0: baselhs
55+
1..n: lhsvars
56+
57+
- expressions:
58+
0: baserhs
59+
1..n: memrhss
60+
n+1..2n: rmemrhss (memrhss, rewritten)
61+
2n+1..3n: xaddrs
62+
63+
- c expressions:
64+
0..n-1: cmemrhss
65+
"""
66+
67+
def __init__(self, xdata: InstrXData) -> None:
68+
ARMOpcodeXData.__init__(self, xdata)
69+
70+
@property
71+
def regcount(self) -> int:
72+
return len(self.xdata.vars_r) - 1
73+
74+
def has_index(self, index: int) -> bool:
75+
"""returns true if index is between 1 and regcount (inclusive)."""
76+
77+
return index > 0 and index <= self.regcount
78+
79+
@property
80+
def memrhs_range(self) -> Iterable[int]:
81+
return range(1, self.regcount + 1)
82+
83+
@property
84+
def rmemrhs_range(self) -> Iterable[int]:
85+
return range(self.regcount + 1, (2 * self.regcount) + 1)
86+
87+
@property
88+
def xaddr_range(self) -> Iterable[int]:
89+
return range((2 * self.regcount) + 1, (3 * self.regcount) + 1)
90+
91+
@property
92+
def baselhs(self) -> "XVariable":
93+
return self.var(0, "baselhs")
94+
95+
@property
96+
def lhsvars(self) -> List["XVariable"]:
97+
return [self.var(i, "lhsvar") for i in range(1, self.regcount + 1)]
98+
99+
def lhsvar(self, index: int) -> "XVariable":
100+
"""returns the lhsvar at (1-based) position index"""
101+
102+
if self.has_index(index):
103+
return self.lhsvars[index]
104+
else:
105+
raise UF.CHBError("LDMIB: index out of bounds: " + str(index))
106+
107+
@property
108+
def baserhs(self) -> "XXpr":
109+
return self.xpr(0, "baserhs")
110+
111+
@property
112+
def memrhss(self) -> List["XXpr"]:
113+
return [self.xpr(i, "memrhs") for i in self.memrhs_range]
114+
115+
@property
116+
def are_memrhss_ok(self) -> bool:
117+
return all(self.is_xpr_ok(i) for i in self.memrhs_range)
118+
119+
def memrhs(self, index) -> "XXpr":
120+
"""returns the rhs expression at (1-based) position index"""
121+
122+
if self.has_index(index):
123+
if self.are_memrhss_ok:
124+
return self.memrhss[index - 1]
125+
else:
126+
raise UF.CHBError("LDMIB: memrhss have error values")
127+
else:
128+
raise UF.CHBError("LDMIB: index out of bounds for memrhs")
129+
130+
@property
131+
def rmemrhss(self) -> List["XXpr"]:
132+
return [self.xpr(i, "rmemrhs") for i in self.rmemrhs_range]
133+
134+
@property
135+
def are_rmemrhss_ok(self) -> bool:
136+
return all(self.is_xpr_ok(i) for i in self.rmemrhs_range)
137+
138+
def rmemrhs(self, index) -> "XXpr":
139+
"""returns the rewritten rhs expr at (1-based) position index"""
140+
141+
if self.has_index(index):
142+
if self.are_rmemrhss_ok:
143+
return self.rmemrhss[index - 1]
144+
else:
145+
raise UF.CHBError("LDMIB: rmemrhss have error values")
146+
else:
147+
raise UF.CHBError("LDMIB: index out of bounds for rmemrhs")
148+
149+
@property
150+
def xaddrs(self) -> List["XXpr"]:
151+
return [self.xpr(i, "xaddr") for i in self.xaddr_range]
152+
153+
@property
154+
def are_xaddrs_ok(self) -> bool:
155+
return all(self.is_xpr_ok(i) for i in self.xaddr_range)
156+
157+
def xaddr(self, index) -> "XXpr":
158+
"""returns the rhs addr at (1-based) position index"""
159+
160+
if self.has_index(index):
161+
if self.are_xaddrs_ok:
162+
return self.xaddrs[index - 1]
163+
else:
164+
raise UF.CHBError("LDMIB: xaddrs have error values")
165+
else:
166+
raise UF.CHBError("LDMIB: index out of bounds for xaddr")
167+
168+
@property
169+
def cmemrhss(self) -> List["XXpr"]:
170+
return [self.cxpr(i, "cmemrhs") for i in range(0, self.regcount)]
171+
172+
@property
173+
def are_cmemrhss_ok(self) -> bool:
174+
return all(self.is_cxpr_ok(i) for i in range(0, self.regcount))
175+
176+
def cmemrhs(self, index) -> "XXpr":
177+
"""returns the rhs addr at (1-based) position index"""
178+
179+
if self.has_index(index):
180+
if self.are_cmemrhss_ok:
181+
return self.cmemrhss[index - 1]
182+
else:
183+
raise UF.CHBError("LDMIB: cmemrhss have error values")
184+
else:
185+
raise UF.CHBError("LDMIB: index out of bounds cmemrhs")
186+
187+
@property
188+
def annotation(self) -> str:
189+
if self.are_rmemrhss_ok:
190+
pairs = list(zip(self.lhsvars, self.rmemrhss))
191+
elif self.are_memrhss_ok:
192+
pairs = list(zip(self.lhsvars, self.memrhss))
193+
else:
194+
pairs = []
195+
if len(pairs) > 0:
196+
assigns = "; ".join(str(v) + " := " + str(x) for (x, v) in pairs)
197+
else:
198+
assigns = "unknown rhs memory"
199+
wbu = self.writeback_update()
200+
return self.add_instruction_condition(assigns + wbu)
201+
42202

43203

44204
@armregistry.register_tag("LDMIB", ARMOpcode)
45205
class ARMLoadMultipleIncrementBefore(ARMOpcode):
46206
"""Loads multiple registers from consecutive memory locations.
47207
48-
LDMDB<c> <Rn>, <registers>
208+
LDMIB<c> <Rn>, <registers>
49209
50210
tags[1]: <c>
51211
args[0]: writeback
@@ -63,19 +223,154 @@ def __init__(
63223

64224
@property
65225
def operands(self) -> List[ARMOperand]:
66-
return [self.armd.arm_operand(i) for i in self.args[1:-1]]
226+
return [self.armd.arm_operand(i) for i in self.args[1:]]
227+
228+
@property
229+
def opargs(self) -> List[ARMOperand]:
230+
return [self.armd.arm_operand(i) for i in self.args[1:]]
231+
232+
@property
233+
def operandstring(self) -> str:
234+
return (
235+
str(self.armd.arm_operand(self.args[1]))
236+
+ ("!" if self.args[0] == 1 else "")
237+
+ ", "
238+
+ str(self.armd.arm_operand(self.args[2])))
239+
240+
@property
241+
def writeback(self) -> bool:
242+
return self.args[0] == 1
67243

68244
def is_load_instruction(self, xdata: InstrXData) -> bool:
69245
return True
70246

71247
def annotation(self, xdata: InstrXData) -> str:
72-
"""xdata format: a:v..x.. .
248+
xd = ARMLoadMultipleIncrementBeforeXData(xdata)
249+
return xd.annotation
73250

74-
vars[0..]: lhs variables
75-
xprs[0..]: rhs expressions
76-
"""
251+
def ast_prov(
252+
self,
253+
astree: ASTInterface,
254+
iaddr: str,
255+
bytestring: str,
256+
xdata: InstrXData) -> Tuple[
257+
List[AST.ASTInstruction], List[AST.ASTInstruction]]:
77258

78-
return (
79-
'; '.join(str(v)
80-
+ " := "
81-
+ str(x) for (v, x) in zip(xdata.vars, xdata.xprs)))
259+
xd = ARMLoadMultipleIncrementBeforeXData(xdata)
260+
261+
if xd.are_cmemrhss_ok:
262+
rhsexprs = xd.cmemrhss
263+
elif xd.are_rmemrhss_ok:
264+
rhsexprs = xd.rmemrhss
265+
elif xd.are_memrhss_ok:
266+
rhsexprs = xd.memrhss
267+
268+
else:
269+
chklogger.logger.error(
270+
"LDMIB: Error value encountered at address %s", iaddr)
271+
return ([], [])
272+
273+
baselhs = xd.baselhs
274+
lhsvars = xd.lhsvars
275+
baserhs = xd.baserhs
276+
277+
baserdef = xdata.reachingdefs[0]
278+
memrdefs = xdata.reachingdefs[1:]
279+
baseuses = xdata.defuses[0]
280+
reguses = xdata.defuses[1:]
281+
baseuseshigh = xdata.defuseshigh[0]
282+
reguseshigh = xdata.defuseshigh[1:]
283+
284+
annotations: List[str] = [iaddr, "LDMIB"]
285+
286+
ll_instrs: List[AST.ASTInstruction] = []
287+
hl_instrs: List[AST.ASTInstruction] = []
288+
289+
# low-level assignments
290+
291+
(baselval, _, _) = self.opargs[0].ast_lvalue(astree)
292+
(baserval, _, _) = self.opargs[0].ast_rvalue(astree)
293+
294+
regsop = self.opargs[1]
295+
registers = regsop.registers
296+
base_offset = 0
297+
for (i, r) in enumerate(registers):
298+
299+
# low-level assignments
300+
301+
base_offset += 4
302+
303+
base_offset_c = astree.mk_integer_constant(base_offset)
304+
addr = astree.mk_binary_op("plus", baserval, base_offset_c)
305+
ll_lhs = astree.mk_variable_lval(r)
306+
ll_rhs = astree.mk_memref_expr(addr)
307+
ll_assign = astree.mk_assign(
308+
ll_lhs,
309+
ll_rhs,
310+
iaddr=iaddr,
311+
bytestring=bytestring,
312+
annotations=annotations)
313+
ll_instrs.append(ll_assign)
314+
315+
# high-level assignments
316+
317+
lhs = lhsvars[i]
318+
rhs = rhsexprs[i]
319+
hl_lhs = XU.xvariable_to_ast_lval(lhs, xdata, iaddr, astree)
320+
hl_rhs = XU.xxpr_to_ast_def_expr(rhs, xdata, iaddr, astree)
321+
hl_assign = astree.mk_assign(
322+
hl_lhs,
323+
hl_rhs,
324+
iaddr=iaddr,
325+
bytestring=bytestring,
326+
annotations=annotations)
327+
hl_instrs.append(hl_assign)
328+
329+
astree.add_instr_mapping(hl_assign, ll_assign)
330+
astree.add_instr_address(hl_assign, [iaddr])
331+
astree.add_expr_mapping(hl_rhs, ll_rhs)
332+
astree.add_lval_mapping(hl_lhs, ll_lhs)
333+
astree.add_expr_reachingdefs(ll_rhs, [memrdefs[i]])
334+
astree.add_lval_defuses(hl_lhs, reguses[i])
335+
astree.add_lval_defuses_high(hl_lhs, reguseshigh[i])
336+
337+
if self.writeback:
338+
339+
# low-level base assignment
340+
341+
baseincr = 4 * xd.regcount
342+
baseincr_c = astree.mk_integer_constant(baseincr)
343+
344+
ll_base_lhs = baselval
345+
ll_base_rhs = astree.mk_binary_op("plus", baserval, baseincr_c)
346+
ll_base_assign = astree.mk_assign(
347+
ll_base_lhs,
348+
ll_base_rhs,
349+
iaddr=iaddr,
350+
bytestring=bytestring,
351+
annotations=annotations)
352+
ll_instrs.append(ll_base_assign)
353+
354+
# high-level base assignment
355+
356+
baseresult = xd.get_base_update_xpr()
357+
hl_base_lhs = XU.xvariable_to_ast_lval(baselhs, xdata, iaddr, astree)
358+
hl_base_rhs = XU.xxpr_to_ast_def_expr(
359+
baseresult, xdata, iaddr, astree)
360+
hl_base_assign = astree.mk_assign(
361+
hl_base_lhs,
362+
hl_base_rhs,
363+
iaddr=iaddr,
364+
bytestring=bytestring,
365+
annotations=annotations)
366+
hl_instrs.append(hl_base_assign)
367+
368+
astree.add_instr_mapping(hl_base_assign, ll_base_assign)
369+
astree.add_instr_address(hl_base_assign, [iaddr])
370+
astree.add_expr_mapping(hl_base_rhs, ll_base_rhs)
371+
astree.add_lval_mapping(hl_base_lhs, ll_base_lhs)
372+
astree.add_expr_reachingdefs(ll_base_rhs, [baserdef])
373+
astree.add_lval_defuses(hl_base_lhs, baseuses)
374+
astree.add_lval_defuses_high(hl_base_lhs, baseuseshigh)
375+
376+
return (hl_instrs, ll_instrs)

0 commit comments

Comments
 (0)