pcode-graph 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcode_graph/__init__.py +0 -0
- pcode_graph/__main__.py +3 -0
- pcode_graph/analysis.py +395 -0
- pcode_graph/arch.py +10 -0
- pcode_graph/asm.py +60 -0
- pcode_graph/charac.py +97 -0
- pcode_graph/checker.py +120 -0
- pcode_graph/cli.py +175 -0
- pcode_graph/emulator.py +419 -0
- pcode_graph/gnn_exporter.py +208 -0
- pcode_graph/graph.py +234 -0
- pcode_graph/lief_importer.py +299 -0
- pcode_graph/log.py +36 -0
- pcode_graph/maker.py +369 -0
- pcode_graph/nx_exporter.py +20 -0
- pcode_graph/pcode.py +187 -0
- pcode_graph/registers.py +2929 -0
- pcode_graph/simplifier.py +185 -0
- pcode_graph/torch_utils.py +30 -0
- pcode_graph/translator.py +235 -0
- pcode_graph/utils.py +15 -0
- pcode_graph/view.py +65 -0
- pcode_graph/visitor.py +103 -0
- pcode_graph-1.0.0.dist-info/METADATA +205 -0
- pcode_graph-1.0.0.dist-info/RECORD +28 -0
- pcode_graph-1.0.0.dist-info/WHEEL +4 -0
- pcode_graph-1.0.0.dist-info/entry_points.txt +2 -0
- pcode_graph-1.0.0.dist-info/licenses/LICENSE +21 -0
pcode_graph/cli.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Dumps Control and Data Graph (CDG) and various data from a binary or assembly file.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
cdg ( asm | functions | html | md | pcode | table ) <input_path> [options]
|
|
7
|
+
|
|
8
|
+
Dump commands:
|
|
9
|
+
asm Assembly dump
|
|
10
|
+
functions Functions from symbol table (using LIEF, binary must not be stripped)
|
|
11
|
+
html CDG in HTML format
|
|
12
|
+
md CDG in markdown/mermaid format
|
|
13
|
+
pcode P-Code operations
|
|
14
|
+
table Markdown table of the P-Code enriched by static analysis results
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
-f, --function <name> Take only the function with given symbol name.
|
|
18
|
+
-c, --chunk <begin:end> Pass "0xbeaf:0xdead" to lift only the chunk started at 0xbeaf and ending before 0xdead.
|
|
19
|
+
-a, --arch <arch> Use the given architecture (x86|arm|mips)_(64|32) or auto [Default: auto].
|
|
20
|
+
-b, --base-address <int> Consider the given assembly starts at given address [Default: 0].
|
|
21
|
+
-D, --dataflow-only Only extract dataflow graph (and no control flow).
|
|
22
|
+
-o, --output <path> Where to write the output.
|
|
23
|
+
-h, --help This help.
|
|
24
|
+
-d, --debug Debug mode: do no catch exceptions.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
import sys
|
|
29
|
+
from docopt import docopt
|
|
30
|
+
from lief import Binary
|
|
31
|
+
import lief
|
|
32
|
+
from loguru import logger
|
|
33
|
+
import pypcode
|
|
34
|
+
from pcode_graph.arch import Arch
|
|
35
|
+
from pcode_graph.asm import Assembler
|
|
36
|
+
from pcode_graph.lief_importer import (
|
|
37
|
+
get_function,
|
|
38
|
+
iter_code_sections,
|
|
39
|
+
iter_functions,
|
|
40
|
+
get_architecture,
|
|
41
|
+
lookup_chunk,
|
|
42
|
+
parse_binary,
|
|
43
|
+
)
|
|
44
|
+
from pcode_graph.log import setup_logger
|
|
45
|
+
from pcode_graph.translator import Translator
|
|
46
|
+
from pcode_graph.pcode import dump_operation
|
|
47
|
+
from pcode_graph.analysis import RichPcodeList
|
|
48
|
+
from pcode_graph.maker import MakerFlags, make_graph
|
|
49
|
+
from pcode_graph.view import draw_graph
|
|
50
|
+
from magika import Magika
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main():
|
|
54
|
+
args = docopt(__doc__)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
if args["--debug"]:
|
|
58
|
+
setup_logger(stderr_verbosity="DEBUG")
|
|
59
|
+
else:
|
|
60
|
+
lief.logging.disable()
|
|
61
|
+
setup_logger()
|
|
62
|
+
|
|
63
|
+
input_path = Path(args["<input_path>"])
|
|
64
|
+
assert input_path.exists()
|
|
65
|
+
|
|
66
|
+
binary: Binary | None = None
|
|
67
|
+
assert input_path.stat().st_size > 0, "Input file is empty"
|
|
68
|
+
if not Magika().identify_path(input_path).output.is_text:
|
|
69
|
+
binary = parse_binary(input_path)
|
|
70
|
+
|
|
71
|
+
if args["--arch"] != "auto":
|
|
72
|
+
arch: Arch = Arch(args["--arch"])
|
|
73
|
+
else:
|
|
74
|
+
assert binary, "Assembly file requires --arch"
|
|
75
|
+
arch = get_architecture(binary)
|
|
76
|
+
|
|
77
|
+
output_path = args["--output"]
|
|
78
|
+
if output_path:
|
|
79
|
+
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
output = open(output_path, "w")
|
|
81
|
+
else:
|
|
82
|
+
output = sys.stdout
|
|
83
|
+
|
|
84
|
+
if args["functions"]:
|
|
85
|
+
assert binary, "Command 'functions' requires a binary file"
|
|
86
|
+
for f in iter_functions(binary):
|
|
87
|
+
print(
|
|
88
|
+
f"{hex(f.address)}: {f.name}: {len(f.content)} bytes", file=output
|
|
89
|
+
)
|
|
90
|
+
sys.exit(0)
|
|
91
|
+
|
|
92
|
+
translator = Translator(arch)
|
|
93
|
+
|
|
94
|
+
function_name = args["--function"]
|
|
95
|
+
chunk_begin_end = args["--chunk"]
|
|
96
|
+
operations: list[pypcode.PcodeOp] = []
|
|
97
|
+
|
|
98
|
+
if binary:
|
|
99
|
+
if function_name or chunk_begin_end:
|
|
100
|
+
if function_name:
|
|
101
|
+
# Lift function
|
|
102
|
+
function = get_function(binary, function_name)
|
|
103
|
+
start_address = function.address
|
|
104
|
+
code = function.content
|
|
105
|
+
else:
|
|
106
|
+
# Lift piece of code
|
|
107
|
+
start_address, end_address = [
|
|
108
|
+
int(a, 16) for a in chunk_begin_end.split(":")
|
|
109
|
+
]
|
|
110
|
+
code = lookup_chunk(binary, start_address, end_address)
|
|
111
|
+
if args["asm"]:
|
|
112
|
+
print(translator.dump_asm(code, start_address, False), file=output)
|
|
113
|
+
sys.exit(0)
|
|
114
|
+
operations = translator.translate(code, start_address)
|
|
115
|
+
else:
|
|
116
|
+
# Lift everything
|
|
117
|
+
for section in iter_code_sections(binary):
|
|
118
|
+
if args["asm"]:
|
|
119
|
+
print(f"; Section at {hex(section.address)}", file=output)
|
|
120
|
+
print(
|
|
121
|
+
translator.dump_asm(
|
|
122
|
+
section.content, section.address, False
|
|
123
|
+
),
|
|
124
|
+
file=output,
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
operations += translator.translate(
|
|
128
|
+
section.content, section.address
|
|
129
|
+
)
|
|
130
|
+
if args["asm"]:
|
|
131
|
+
sys.exit(0)
|
|
132
|
+
else:
|
|
133
|
+
assert (
|
|
134
|
+
not function_name
|
|
135
|
+
), "Option --function not supported for assembly files."
|
|
136
|
+
assert (
|
|
137
|
+
not chunk_begin_end
|
|
138
|
+
), "Option --chunk not supported for assembly files."
|
|
139
|
+
assembly = input_path.read_text()
|
|
140
|
+
assembler = Assembler(arch)
|
|
141
|
+
start_address = int(args["--base-address"])
|
|
142
|
+
code = assembler.assemble(assembly, start_address)
|
|
143
|
+
if args["asm"]:
|
|
144
|
+
print(translator.dump_asm(code, start_address, False), file=output)
|
|
145
|
+
sys.exit(0)
|
|
146
|
+
operations = translator.translate(code, start_address)
|
|
147
|
+
|
|
148
|
+
if args["pcode"]:
|
|
149
|
+
for op in operations:
|
|
150
|
+
print(dump_operation(op), file=output)
|
|
151
|
+
sys.exit(0)
|
|
152
|
+
|
|
153
|
+
logger.info(f"Analyze {len(operations)} P-Code operations")
|
|
154
|
+
rpl = RichPcodeList(arch, operations)
|
|
155
|
+
|
|
156
|
+
if args["table"]:
|
|
157
|
+
print(rpl.dump())
|
|
158
|
+
sys.exit(0)
|
|
159
|
+
|
|
160
|
+
flags = MakerFlags(build_cfg=not args["--dataflow-only"])
|
|
161
|
+
cdg = make_graph(rpl, flags)
|
|
162
|
+
|
|
163
|
+
if args["md"]:
|
|
164
|
+
print(str(cdg), file=output)
|
|
165
|
+
sys.exit(0)
|
|
166
|
+
|
|
167
|
+
assert output_path, "HTML output file is mandatory"
|
|
168
|
+
output.close()
|
|
169
|
+
draw_graph(cdg, Path(output_path))
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
if args["--debug"]:
|
|
173
|
+
raise
|
|
174
|
+
print(str(e), file=sys.stderr)
|
|
175
|
+
sys.exit(1)
|
pcode_graph/emulator.py
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
from pcode_graph.graph import (
|
|
3
|
+
CDG,
|
|
4
|
+
EdgeKinds,
|
|
5
|
+
Node,
|
|
6
|
+
NodeIndex,
|
|
7
|
+
NodeKinds,
|
|
8
|
+
OperandNumber,
|
|
9
|
+
RegisterId,
|
|
10
|
+
Value,
|
|
11
|
+
)
|
|
12
|
+
from pcode_graph.utils import signed_to_unsigned, unsigned_to_signed
|
|
13
|
+
from pcode_graph.visitor import PcodeVisitor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Emulator(PcodeVisitor):
|
|
17
|
+
"""Emulate the execution of an homogeneous graph"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, graph: CDG) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.graph = graph
|
|
22
|
+
|
|
23
|
+
# Known value of every node
|
|
24
|
+
self.values: dict[NodeIndex, int] = {}
|
|
25
|
+
|
|
26
|
+
# Inputs and outputs node indexes sorted by register id
|
|
27
|
+
self.inputs: dict[RegisterId, NodeIndex] = {}
|
|
28
|
+
self.outputs: dict[RegisterId, NodeIndex] = {}
|
|
29
|
+
|
|
30
|
+
for index, node in enumerate(self.graph.nodes):
|
|
31
|
+
if node.kind == NodeKinds.InputRegister:
|
|
32
|
+
self.inputs[node.register_id] = index
|
|
33
|
+
elif node.kind == NodeKinds.OutputRegister:
|
|
34
|
+
self.outputs[node.register_id] = index
|
|
35
|
+
elif node.kind == NodeKinds.Constant:
|
|
36
|
+
self.values[index] = node.value
|
|
37
|
+
|
|
38
|
+
def clear(self):
|
|
39
|
+
self.values.clear()
|
|
40
|
+
|
|
41
|
+
def set_input(self, index: NodeIndex, value: Value):
|
|
42
|
+
self.values[index] = value
|
|
43
|
+
|
|
44
|
+
def set_input_register(self, register_id: RegisterId, value: Value):
|
|
45
|
+
index = self.inputs[register_id]
|
|
46
|
+
self.set_input(index, value)
|
|
47
|
+
|
|
48
|
+
def set_input_registers(self, values_by_reg_id: dict[RegisterId, Value]):
|
|
49
|
+
for rid, value in values_by_reg_id.items():
|
|
50
|
+
self.set_input_register(rid, value)
|
|
51
|
+
|
|
52
|
+
def emulate(self):
|
|
53
|
+
|
|
54
|
+
# TODO: support CFG and Phi nodes
|
|
55
|
+
proxy = self.graph.compute_edge_proxy()
|
|
56
|
+
propagated = True
|
|
57
|
+
while propagated:
|
|
58
|
+
propagated = False
|
|
59
|
+
|
|
60
|
+
for index, node in enumerate(self.graph.nodes):
|
|
61
|
+
if index in self.values:
|
|
62
|
+
# Already computed
|
|
63
|
+
continue
|
|
64
|
+
p = proxy[index]
|
|
65
|
+
match node.kind:
|
|
66
|
+
|
|
67
|
+
case NodeKinds.Operation:
|
|
68
|
+
operands: dict[
|
|
69
|
+
OperandNumber | None, tuple[NodeIndex, Value]
|
|
70
|
+
] = {}
|
|
71
|
+
|
|
72
|
+
for in_edge in p.in_edges:
|
|
73
|
+
if in_edge.kind != EdgeKinds.Data:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
value = self.values.get(in_edge.source_node)
|
|
77
|
+
if value is None:
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
assert in_edge.operand_number not in operands
|
|
81
|
+
operands[in_edge.operand_number] = (
|
|
82
|
+
in_edge.source_node,
|
|
83
|
+
value,
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
# We can evaluate this node
|
|
87
|
+
self.values[index] = self.visit(node.opcode, node, operands)
|
|
88
|
+
propagated = True
|
|
89
|
+
|
|
90
|
+
case NodeKinds.OutputRegister:
|
|
91
|
+
(in_edge,) = p.get_inputs_edges()
|
|
92
|
+
value = self.values.get(in_edge.source_node)
|
|
93
|
+
if value is not None:
|
|
94
|
+
self.values[index] = value
|
|
95
|
+
# Do not set propagated as OutputRegister nodes are never used as inputs
|
|
96
|
+
|
|
97
|
+
def get_output(self, register_id) -> Value:
|
|
98
|
+
index = self.outputs[register_id]
|
|
99
|
+
return self.values[index]
|
|
100
|
+
|
|
101
|
+
def get_outputs(self) -> dict[RegisterId, Value]:
|
|
102
|
+
out_values = {}
|
|
103
|
+
for register_id, index in self.outputs.items():
|
|
104
|
+
out_values[register_id] = self.values[index]
|
|
105
|
+
return out_values
|
|
106
|
+
|
|
107
|
+
@override
|
|
108
|
+
def visit_bool_and(
|
|
109
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
110
|
+
) -> int:
|
|
111
|
+
i1, i2 = values[0][1], values[1][1]
|
|
112
|
+
return int((i1 != 0) and (i2 != 0))
|
|
113
|
+
|
|
114
|
+
@override
|
|
115
|
+
def visit_bool_negate(
|
|
116
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
117
|
+
) -> int:
|
|
118
|
+
return int(values[0] == 0)
|
|
119
|
+
|
|
120
|
+
@override
|
|
121
|
+
def visit_bool_or(
|
|
122
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
123
|
+
) -> int:
|
|
124
|
+
i1, i2 = values[0][1], values[1][1]
|
|
125
|
+
return int((i1 != 0) or (i2 != 0))
|
|
126
|
+
|
|
127
|
+
@override
|
|
128
|
+
def visit_bool_xor(
|
|
129
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
130
|
+
) -> int:
|
|
131
|
+
i1, i2 = values[0][1], values[1][1]
|
|
132
|
+
return int((i1 != 0) ^ (i2 != 0))
|
|
133
|
+
|
|
134
|
+
@override
|
|
135
|
+
def visit_int_2comp(
|
|
136
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
137
|
+
) -> int:
|
|
138
|
+
i = values[0][1]
|
|
139
|
+
mask = 2**node.size_bits - 1
|
|
140
|
+
return (mask ^ i) + 1
|
|
141
|
+
|
|
142
|
+
@override
|
|
143
|
+
def visit_int_add(
|
|
144
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
145
|
+
) -> int:
|
|
146
|
+
i1, i2 = values[0][1], values[1][1]
|
|
147
|
+
return i1 + i2
|
|
148
|
+
|
|
149
|
+
@override
|
|
150
|
+
def visit_int_and(
|
|
151
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
152
|
+
) -> int:
|
|
153
|
+
i1, i2 = values[0][1], values[1][1]
|
|
154
|
+
return i1 & i2
|
|
155
|
+
|
|
156
|
+
@override
|
|
157
|
+
def visit_int_carry(
|
|
158
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
159
|
+
) -> int:
|
|
160
|
+
i1, i2 = values[0][1], values[1][1]
|
|
161
|
+
return int((i1 + i2) >= 2**node.size_bits)
|
|
162
|
+
|
|
163
|
+
@override
|
|
164
|
+
def visit_int_div(
|
|
165
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
166
|
+
) -> int:
|
|
167
|
+
i1, i2 = values[0][1], values[1][1]
|
|
168
|
+
if i2 == 0:
|
|
169
|
+
return 0
|
|
170
|
+
return i1 // i2
|
|
171
|
+
|
|
172
|
+
@override
|
|
173
|
+
def visit_int_equal(
|
|
174
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
175
|
+
) -> int:
|
|
176
|
+
i1, i2 = values[0][1], values[1][1]
|
|
177
|
+
return int(i1 == i2)
|
|
178
|
+
|
|
179
|
+
@override
|
|
180
|
+
def visit_int_left(
|
|
181
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
182
|
+
) -> int:
|
|
183
|
+
i1, i2 = values[0][1], values[1][1]
|
|
184
|
+
return i1 << i2
|
|
185
|
+
|
|
186
|
+
@override
|
|
187
|
+
def visit_int_less(
|
|
188
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
189
|
+
) -> int:
|
|
190
|
+
i1, i2 = values[0][1], values[1][1]
|
|
191
|
+
return int(i1 < i2)
|
|
192
|
+
|
|
193
|
+
@override
|
|
194
|
+
def visit_int_lessequal(
|
|
195
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
196
|
+
) -> int:
|
|
197
|
+
i1, i2 = values[0][1], values[1][1]
|
|
198
|
+
return int(i1 <= i2)
|
|
199
|
+
|
|
200
|
+
@override
|
|
201
|
+
def visit_int_mult(
|
|
202
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
203
|
+
) -> int:
|
|
204
|
+
i1, i2 = values[0][1], values[1][1]
|
|
205
|
+
return i1 * i2
|
|
206
|
+
|
|
207
|
+
@override
|
|
208
|
+
def visit_int_negate(
|
|
209
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
210
|
+
) -> int:
|
|
211
|
+
i = values[0][1]
|
|
212
|
+
mask = 2 ** (node.size * 8) - 1
|
|
213
|
+
return mask ^ i
|
|
214
|
+
|
|
215
|
+
@override
|
|
216
|
+
def visit_int_notequal(
|
|
217
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
218
|
+
) -> int:
|
|
219
|
+
i1, i2 = values[0][1], values[1][1]
|
|
220
|
+
return int(i1 != i2)
|
|
221
|
+
|
|
222
|
+
@override
|
|
223
|
+
def visit_int_or(
|
|
224
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
225
|
+
) -> int:
|
|
226
|
+
i1, i2 = values[0][1], values[1][1]
|
|
227
|
+
return i1 | i2
|
|
228
|
+
|
|
229
|
+
@override
|
|
230
|
+
def visit_int_rem(
|
|
231
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
232
|
+
) -> int:
|
|
233
|
+
i1, i2 = values[0][1], values[1][1]
|
|
234
|
+
if i2 == 0:
|
|
235
|
+
return 0
|
|
236
|
+
q = i1 // i2
|
|
237
|
+
return i1 - q * i2
|
|
238
|
+
|
|
239
|
+
@override
|
|
240
|
+
def visit_int_right(
|
|
241
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
242
|
+
) -> int:
|
|
243
|
+
i1, i2 = values[0][1], values[1][1]
|
|
244
|
+
return i1 >> i2
|
|
245
|
+
|
|
246
|
+
@override
|
|
247
|
+
def visit_int_sborrow(
|
|
248
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
249
|
+
) -> int:
|
|
250
|
+
i1, i2 = values[0][1], values[1][1]
|
|
251
|
+
input_size = self.graph.nodes[values[0][0]].size
|
|
252
|
+
max = 2 ** (input_size * 8 - 1)
|
|
253
|
+
return int(
|
|
254
|
+
not (
|
|
255
|
+
-max
|
|
256
|
+
<= (
|
|
257
|
+
unsigned_to_signed(i1, input_size)
|
|
258
|
+
- unsigned_to_signed(i2, input_size)
|
|
259
|
+
< max
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
@override
|
|
265
|
+
def visit_int_scarry(
|
|
266
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
267
|
+
) -> int:
|
|
268
|
+
i1, i2 = values[0][1], values[1][1]
|
|
269
|
+
input_size = self.graph.nodes[values[0][0]].size
|
|
270
|
+
max = 2 ** (input_size * 8 - 1)
|
|
271
|
+
return int(
|
|
272
|
+
not (
|
|
273
|
+
-max
|
|
274
|
+
<= (
|
|
275
|
+
unsigned_to_signed(i1, input_size)
|
|
276
|
+
+ unsigned_to_signed(i2, input_size)
|
|
277
|
+
< max
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
@override
|
|
283
|
+
def visit_int_sdiv(
|
|
284
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
285
|
+
) -> int:
|
|
286
|
+
i1, i2 = values[0][1], values[1][1]
|
|
287
|
+
if i2 == 0:
|
|
288
|
+
return 0
|
|
289
|
+
s1, s2 = unsigned_to_signed(i1, node.size), unsigned_to_signed(i2, node.size)
|
|
290
|
+
return signed_to_unsigned(int(s1 / s2), node.size)
|
|
291
|
+
|
|
292
|
+
@override
|
|
293
|
+
def visit_int_sext(
|
|
294
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
295
|
+
) -> int:
|
|
296
|
+
i = values[0][1]
|
|
297
|
+
input_size = self.graph.nodes[values[0][0]].size
|
|
298
|
+
s = unsigned_to_signed(i, input_size)
|
|
299
|
+
return signed_to_unsigned(s, node.size)
|
|
300
|
+
|
|
301
|
+
@override
|
|
302
|
+
def visit_int_sless(
|
|
303
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
304
|
+
) -> int:
|
|
305
|
+
i1, i2 = values[0][1], values[1][1]
|
|
306
|
+
input_size = self.graph.nodes[values[0][0]].size
|
|
307
|
+
return int(
|
|
308
|
+
unsigned_to_signed(i1, input_size) < unsigned_to_signed(i2, input_size)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
@override
|
|
312
|
+
def visit_int_slessequal(
|
|
313
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
314
|
+
) -> int:
|
|
315
|
+
i1, i2 = values[0][1], values[1][1]
|
|
316
|
+
input_size = self.graph.nodes[values[0][0]].size
|
|
317
|
+
return int(
|
|
318
|
+
unsigned_to_signed(i1, input_size) <= unsigned_to_signed(i2, input_size)
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
@override
|
|
322
|
+
def visit_int_srem(
|
|
323
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
324
|
+
) -> int:
|
|
325
|
+
i1, i2 = values[0][1], values[1][1]
|
|
326
|
+
if i2 == 0:
|
|
327
|
+
return 0
|
|
328
|
+
s1, s2 = unsigned_to_signed(i1, node.size), unsigned_to_signed(i2, node.size)
|
|
329
|
+
q = int(s1 / s2)
|
|
330
|
+
return signed_to_unsigned(s1 - q * s2, node.size)
|
|
331
|
+
|
|
332
|
+
@override
|
|
333
|
+
def visit_int_sright(
|
|
334
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
335
|
+
) -> int:
|
|
336
|
+
i1, i2 = values[0][1], values[1][1]
|
|
337
|
+
s1, s2 = unsigned_to_signed(i1, node.size), unsigned_to_signed(i2, node.size)
|
|
338
|
+
return signed_to_unsigned(s1 >> s2, node.size)
|
|
339
|
+
|
|
340
|
+
@override
|
|
341
|
+
def visit_int_sub(
|
|
342
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
343
|
+
) -> int:
|
|
344
|
+
i1, i2 = values[0][1], values[1][1]
|
|
345
|
+
return i1 - i2
|
|
346
|
+
|
|
347
|
+
@override
|
|
348
|
+
def visit_int_xor(
|
|
349
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
350
|
+
) -> int:
|
|
351
|
+
i1, i2 = values[0][1], values[1][1]
|
|
352
|
+
return i1 ^ i2
|
|
353
|
+
|
|
354
|
+
@override
|
|
355
|
+
def visit_int_zext(
|
|
356
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
357
|
+
) -> int:
|
|
358
|
+
return values[0][1]
|
|
359
|
+
|
|
360
|
+
@override
|
|
361
|
+
def visit_load(
|
|
362
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
363
|
+
) -> None:
|
|
364
|
+
raise NotImplementedError
|
|
365
|
+
|
|
366
|
+
@override
|
|
367
|
+
def visit_lzcount(
|
|
368
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
369
|
+
) -> int:
|
|
370
|
+
value = values[0][1]
|
|
371
|
+
max_bits = node.size * 8
|
|
372
|
+
top_bit = 1 << (max_bits - 1)
|
|
373
|
+
count = 0
|
|
374
|
+
while not (value & top_bit):
|
|
375
|
+
count += 1
|
|
376
|
+
value <<= 1
|
|
377
|
+
return count
|
|
378
|
+
|
|
379
|
+
@override
|
|
380
|
+
def visit_piece(
|
|
381
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
382
|
+
) -> int:
|
|
383
|
+
# Consider the virtual machine as big endian
|
|
384
|
+
i1, i2 = values[0][1], values[1][1]
|
|
385
|
+
return (i1 << self.graph.nodes[values[1][0]].size_bits) | i2
|
|
386
|
+
|
|
387
|
+
@override
|
|
388
|
+
def visit_popcount(
|
|
389
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
390
|
+
) -> int:
|
|
391
|
+
i = values[0][1]
|
|
392
|
+
return i.bit_count()
|
|
393
|
+
|
|
394
|
+
@override
|
|
395
|
+
def visit_segmentop(
|
|
396
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
397
|
+
) -> int:
|
|
398
|
+
# TODO: opcode not documented in Pcode spec
|
|
399
|
+
raise NotImplementedError
|
|
400
|
+
|
|
401
|
+
@override
|
|
402
|
+
def visit_store(
|
|
403
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
404
|
+
) -> None:
|
|
405
|
+
raise NotImplementedError
|
|
406
|
+
|
|
407
|
+
@override
|
|
408
|
+
def visit_subpiece(
|
|
409
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
410
|
+
) -> int:
|
|
411
|
+
# Consider the machine as big endian (see piece)
|
|
412
|
+
i1, i2 = values[0][1], values[1][1]
|
|
413
|
+
return (i1 >> (i2 * 8)) & (2**node.size_bits - 1)
|
|
414
|
+
|
|
415
|
+
@override
|
|
416
|
+
def visit_copy(
|
|
417
|
+
self, node: Node, values: dict[OperandNumber, tuple[NodeIndex, Value]]
|
|
418
|
+
) -> int:
|
|
419
|
+
return values[0][1]
|