mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-24 13:11:47 +00:00
Compare commits
49 Commits
f4410b0daf
...
4b9ce97754
Author | SHA1 | Date | |
---|---|---|---|
|
4b9ce97754 | ||
|
ac58d20693 | ||
|
22ddbc3f4b | ||
|
55b2016b16 | ||
|
4d96514f1b | ||
|
79b008d7e4 | ||
|
ac22ed69f2 | ||
|
68bb1e182a | ||
|
9421ec0ab0 | ||
|
650c5d8756 | ||
|
58e48456de | ||
|
a7351baba4 | ||
|
329f8b4a19 | ||
|
fc7b0ef327 | ||
|
454d57d754 | ||
|
8abe4d76f0 | ||
|
8f08f2730d | ||
|
b67fab8b51 | ||
|
058ecb5eef | ||
|
81432f75c7 | ||
|
69d6495b60 | ||
|
dc17dcd639 | ||
|
4cbc94d960 | ||
|
b12d39ee3f | ||
|
294f81dfc2 | ||
|
0411db7e88 | ||
|
600c02703a | ||
|
7712224ff1 | ||
|
2390546961 | ||
|
55a026b3ba | ||
|
ab7684a230 | ||
|
eced291c6e | ||
|
306e15a33d | ||
|
c9a51029bb | ||
|
a7c192c58e | ||
|
b536b99e60 | ||
|
83d38b8368 | ||
|
a9c2c6efc7 | ||
|
f7b21f5ea3 | ||
|
2ac3fe7b1b | ||
|
bd9959730f | ||
|
6be0884f18 | ||
|
8e6d269b8e | ||
|
3fb7d914f5 | ||
|
aaf23cf096 | ||
|
54cd023fc9 | ||
|
33646bbcdc | ||
|
38445e387b | ||
|
a11bb2d30d |
@ -10,8 +10,8 @@
|
||||
::@icon icon.debugger
|
||||
::@help TraceRmiLauncherServicePlugin#dbgeng_attach
|
||||
::@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||
::@env OPT_TARGET_PID:int="" "Process id" "The target process id"
|
||||
::@env OPT_ATTACH_FLAGS:int="0" "Attach flags" "Attach flags"
|
||||
::@env OPT_TARGET_PID:int=0 "Process id" "The target process id"
|
||||
::@env OPT_ATTACH_FLAGS:int=0 "Attach flags" "Attach flags"
|
||||
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
||||
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
||||
|
||||
|
@ -208,16 +208,11 @@ class DefaultMemoryMapper(object):
|
||||
self.defaultSpace = defaultSpace
|
||||
|
||||
def map(self, proc: int, offset: int):
|
||||
if proc == 0:
|
||||
space = self.defaultSpace
|
||||
else:
|
||||
space = f'{self.defaultSpace}{proc}'
|
||||
space = self.defaultSpace
|
||||
return self.defaultSpace, Address(space, offset)
|
||||
|
||||
def map_back(self, proc: int, address: Address) -> int:
|
||||
if address.space == self.defaultSpace and proc == 0:
|
||||
return address.offset
|
||||
if address.space == f'{self.defaultSpace}{proc}':
|
||||
if address.space == self.defaultSpace:
|
||||
return address.offset
|
||||
raise ValueError(f"Address {address} is not in process {proc}")
|
||||
|
||||
|
@ -591,8 +591,8 @@ def putreg():
|
||||
regs = util.dbg._base.reg
|
||||
for i in range(0, len(regs)):
|
||||
name = regs._reg.GetDescription(i)[0]
|
||||
value = regs._get_register_by_index(i)
|
||||
try:
|
||||
value = regs._get_register_by_index(i)
|
||||
values.append(mapper.map_value(nproc, name, value))
|
||||
robj.set_value(name, hex(value))
|
||||
except Exception:
|
||||
|
@ -1,17 +1,17 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
from contextlib import redirect_stdout
|
||||
@ -199,9 +199,11 @@ def execute(cmd: str, to_string: bool=False):
|
||||
exec(cmd, shared_globals)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method(action='evaluate', display='Evaluate')
|
||||
# @util.dbg.eng_thread
|
||||
def evaluate(expr: str):
|
||||
def evaluate(
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
"""Evaluate a Python3 expression."""
|
||||
return str(eval(expr, shared_globals))
|
||||
|
||||
@ -323,15 +325,16 @@ def remove_process(process: sch.Schema('Process')):
|
||||
dbg().detach_proc()
|
||||
|
||||
|
||||
@REGISTRY.method(action='connect')
|
||||
@REGISTRY.method(action='connect', display='Connect')
|
||||
@util.dbg.eng_thread
|
||||
def target(process: sch.Schema('Process'), spec: str):
|
||||
def target(
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
"""Connect to a target machine or process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().attach_kernel(spec)
|
||||
dbg().attach_kernel(cmd)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
@REGISTRY.method(action='attach', display='Attach')
|
||||
@util.dbg.eng_thread
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
"""Attach the process to the given target."""
|
||||
@ -339,29 +342,34 @@ def attach_obj(target: sch.Schema('Attachable')):
|
||||
dbg().attach_proc(pid)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
@REGISTRY.method(action='attach', display='Attach by pid')
|
||||
@util.dbg.eng_thread
|
||||
def attach_pid(pid: int):
|
||||
def attach_pid(
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(pid)
|
||||
dbg().attach_proc(int(pid))
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
@REGISTRY.method(action='attach', display='Attach by name')
|
||||
@util.dbg.eng_thread
|
||||
def attach_name(process: sch.Schema('Process'), name: str):
|
||||
def attach_name(
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(name)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method(action='detach', display='Detach')
|
||||
@util.dbg.eng_thread
|
||||
def detach(process: sch.Schema('Process')):
|
||||
"""Detach the process's target."""
|
||||
dbg().detach_proc()
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
@REGISTRY.method(action='launch', display='Launch')
|
||||
def launch_loader(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
@ -373,8 +381,9 @@ def launch_loader(
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
@REGISTRY.method(action='launch', display='LaunchEx')
|
||||
def launch(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')='',
|
||||
initial_break: ParamDesc(bool, display='Initial Break')=True,
|
||||
@ -431,7 +440,7 @@ def step_out(thread: sch.Schema('Thread')):
|
||||
util.dbg.run_async(lambda: dbg().stepout())
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_to')
|
||||
@REGISTRY.method(action='step_to', display='Step To')
|
||||
def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
||||
"""Continue execution up to the given address."""
|
||||
find_thread_by_obj(thread)
|
||||
|
@ -1,17 +1,17 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import re
|
||||
@ -196,8 +196,10 @@ def execute(cmd: str, to_string: bool=False):
|
||||
exec("{}".format(cmd), shared_globals)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def evaluate(expr: str):
|
||||
@REGISTRY.method(action='evaluate', display='Evaluate')
|
||||
def evaluate(
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
"""Execute a CLI command."""
|
||||
return str(eval("{}".format(expr), shared_globals))
|
||||
|
||||
@ -311,40 +313,51 @@ def remove_process(process: sch.Schema('Process')):
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='connect')
|
||||
def target(process: sch.Schema('Process'), spec: str):
|
||||
@REGISTRY.method(action='connect', display='Connect')
|
||||
@util.dbg.eng_thread
|
||||
def target(
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
"""Connect to a target machine or process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().attach(spec)
|
||||
dbg().attach_kernel(cmd)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
@REGISTRY.method(action='attach', display='Attach')
|
||||
@util.dbg.eng_thread
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
"""Attach the process to the given target."""
|
||||
pid = find_availpid_by_obj(target)
|
||||
dbg().attach(pid)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
def attach_pid(pid: int):
|
||||
@REGISTRY.method(action='attach', display='Attach by pid')
|
||||
@util.dbg.eng_thread
|
||||
def attach_pid(
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach(pid)
|
||||
dbg().attach_proc(int(pid))
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
def attach_name(process: sch.Schema('Process'), name: str):
|
||||
@REGISTRY.method(action='attach', display='Attach by name')
|
||||
@util.dbg.eng_thread
|
||||
def attach_name(
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().atach(name)
|
||||
dbg().attach_proc(name)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method(action='detach', display='Detach')
|
||||
@util.dbg.eng_thread
|
||||
def detach(process: sch.Schema('Process')):
|
||||
"""Detach the process's target."""
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
@REGISTRY.method(action='launch', display='Launch')
|
||||
def launch_loader(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
@ -356,11 +369,13 @@ def launch_loader(
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
@REGISTRY.method(action='launch', display='LaunchEx')
|
||||
def launch(
|
||||
timeout: ParamDesc(int, display='Timeout'),
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
args: ParamDesc(str, display='Arguments')='',
|
||||
initial_break: ParamDesc(bool, display='Initial Break')=True,
|
||||
timeout: ParamDesc(int, display='Timeout')=-1):
|
||||
"""
|
||||
Run a native process with the given command line.
|
||||
"""
|
||||
@ -368,7 +383,7 @@ def launch(
|
||||
if args != None:
|
||||
command += " "+args
|
||||
commands.ghidra_trace_create(
|
||||
command, initial_break=False, timeout=timeout, start_trace=False)
|
||||
command, initial_break=initial_break, timeout=timeout, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@ -413,7 +428,7 @@ def step_out(thread: sch.Schema('Thread')):
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_to')
|
||||
@REGISTRY.method(action='step_to', display='Step To')
|
||||
def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
||||
"""Continue execution up to the given address."""
|
||||
find_thread_by_obj(thread)
|
||||
|
@ -98,11 +98,11 @@ class InferiorState(object):
|
||||
frame, util.get_register_descs(frame.architecture(), 'general'))
|
||||
try:
|
||||
commands.putmem("$pc", "1", from_tty=False)
|
||||
except MemoryError as e:
|
||||
except gdb.MemoryError as e:
|
||||
print(f"Couldn't record page with PC: {e}")
|
||||
try:
|
||||
commands.putmem("$sp", "1", from_tty=False)
|
||||
except MemoryError as e:
|
||||
except gdb.MemoryError as e:
|
||||
print(f"Couldn't record page with SP: {e}")
|
||||
self.visited.add(hashable_frame)
|
||||
# NB: These commands (put_modules/put_regions) will fail if the process is running
|
||||
@ -281,6 +281,11 @@ def on_register_changed(event):
|
||||
|
||||
@log_errors
|
||||
def on_cont(event):
|
||||
if gdb.selected_thread() is None:
|
||||
# thread-based state computed in record_continued will
|
||||
# fail in some versions of gdb because the current_thread is None
|
||||
# and gdb fails to test for None before switching
|
||||
return
|
||||
if (HOOK_STATE.check_skip_continue()):
|
||||
return
|
||||
inf = gdb.selected_inferior()
|
||||
|
@ -196,18 +196,12 @@ MODULE_INFO_READER = _choose_module_info_reader()
|
||||
|
||||
|
||||
REGIONS_CMD = 'info proc mappings'
|
||||
REGION_PATTERN_V8 = re.compile("\\s*" +
|
||||
"0x(?P<start>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<end>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<size>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<offset>[0-9,A-F,a-f]+)\\s+" +
|
||||
"(?P<objfile>.*)")
|
||||
REGION_PATTERN_V12 = re.compile("\\s*" +
|
||||
REGION_PATTERN = re.compile("\\s*" +
|
||||
"0x(?P<start>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<end>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<size>[0-9,A-F,a-f]+)\\s+" +
|
||||
"0x(?P<offset>[0-9,A-F,a-f]+)\\s+" +
|
||||
"(?P<perms>[rwsxp\\-]+)\\s+" +
|
||||
"((?P<perms>[rwsxp\\-]+)?\\s+)?" +
|
||||
"(?P<objfile>.*)")
|
||||
|
||||
|
||||
@ -216,6 +210,9 @@ class Region(namedtuple('BaseRegion', ['start', 'end', 'offset', 'perms', 'objfi
|
||||
|
||||
|
||||
class RegionInfoReader(object):
|
||||
cmd = REGIONS_CMD
|
||||
region_pattern = REGION_PATTERN
|
||||
|
||||
def region_from_line(self, line):
|
||||
mat = self.region_pattern.fullmatch(line)
|
||||
if mat is None:
|
||||
@ -253,28 +250,13 @@ class RegionInfoReader(object):
|
||||
return False, None
|
||||
return True, new_regions
|
||||
|
||||
|
||||
class RegionInfoReaderV8(RegionInfoReader):
|
||||
cmd = REGIONS_CMD
|
||||
region_pattern = REGION_PATTERN_V8
|
||||
|
||||
def get_region_perms(self, mat):
|
||||
return None
|
||||
|
||||
|
||||
class RegionInfoReaderV12(RegionInfoReader):
|
||||
cmd = REGIONS_CMD
|
||||
region_pattern = REGION_PATTERN_V12
|
||||
|
||||
def get_region_perms(self, mat):
|
||||
return mat['perms']
|
||||
|
||||
|
||||
def _choose_region_info_reader():
|
||||
if 8 <= GDB_VERSION.major < 12:
|
||||
return RegionInfoReaderV8()
|
||||
elif GDB_VERSION.major >= 12:
|
||||
return RegionInfoReaderV12()
|
||||
if 8 <= GDB_VERSION.major:
|
||||
return RegionInfoReader()
|
||||
else:
|
||||
raise gdb.GdbError(
|
||||
"GDB version not recognized by ghidragdb: " + GDB_VERSION.full)
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -27,17 +27,13 @@ public class IsfComposite extends AbstractIsfObject {
|
||||
public String kind;
|
||||
public Integer size;
|
||||
public JsonObject fields;
|
||||
|
||||
|
||||
public IsfComposite(Composite composite, IsfDataTypeWriter writer, TaskMonitor monitor) {
|
||||
super(composite);
|
||||
size = composite.getLength();
|
||||
size = composite.isZeroLength() ? 0 : composite.getLength();
|
||||
kind = composite instanceof Structure ? "struct" : "union";
|
||||
|
||||
DataTypeComponent[] components = composite.getComponents();
|
||||
if (components.length == 0) {
|
||||
// NB: composite.getLength always returns > 0
|
||||
size = 0;
|
||||
}
|
||||
DataTypeComponent[] components = composite.getDefinedComponents();
|
||||
fields = new JsonObject();
|
||||
for (DataTypeComponent component : components) {
|
||||
if (monitor.isCancelled()) {
|
||||
|
@ -821,7 +821,6 @@ src/main/resources/images/field.header.up.png||GHIDRA||||END|
|
||||
src/main/resources/images/fingerPointer.png||GHIDRA||||END|
|
||||
src/main/resources/images/flag-green.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/flag-yellow.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/folder-downloads-32.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||
src/main/resources/images/format-text-bold.png||Tango Icons - Public Domain|||tango icon set|END|
|
||||
src/main/resources/images/functionDef.png||GHIDRA||||END|
|
||||
src/main/resources/images/function_graph.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
|
@ -115,9 +115,8 @@ font.listing.header = SansSerif-PLAIN-11
|
||||
color.bg.highlight.listing.diff = #4D4D2A
|
||||
|
||||
|
||||
// non-palette colors; these are currently ugly, but bright enough for easy scanning
|
||||
color.bg.listing.highlighter.default = yellow
|
||||
color.bg.listing.highlighter.scoped.read = darkorange
|
||||
color.bg.listing.highlighter.scoped.write = lime
|
||||
color.bg.listing.highlighter.default = #666600
|
||||
color.bg.listing.highlighter.scoped.read = #996600
|
||||
color.bg.listing.highlighter.scoped.write = #009900
|
||||
|
||||
|
||||
|
@ -107,6 +107,7 @@ public class VSCodeProjectScript extends GhidraScript {
|
||||
File settingsFile = new File(vscodeDir, "settings.json");
|
||||
String gradleVersion = Application
|
||||
.getApplicationProperty(ApplicationProperties.APPLICATION_GRADLE_MIN_PROPERTY);
|
||||
String pythonInterpreterPath = System.getProperty("pyghidra.sys.prefix", null);
|
||||
|
||||
// Build settings json object
|
||||
JsonObject json = new JsonObject();
|
||||
@ -138,6 +139,9 @@ public class VSCodeProjectScript extends GhidraScript {
|
||||
|
||||
json.addProperty("python.analysis.stubPath",
|
||||
new File(installDir, "docs/ghidra_stubs/typestubs").getAbsolutePath());
|
||||
if (pythonInterpreterPath != null) {
|
||||
json.addProperty("python.defaultInterpreterPath", pythonInterpreterPath);
|
||||
}
|
||||
|
||||
// Write settings json object
|
||||
if (!FileUtilities.mkdirs(settingsFile.getParentFile())) {
|
||||
@ -183,6 +187,8 @@ public class VSCodeProjectScript extends GhidraScript {
|
||||
json.addProperty("version", "0.2.0");
|
||||
JsonArray configurationsArray = new JsonArray();
|
||||
json.add("configurations", configurationsArray);
|
||||
|
||||
// Ghidra launcher
|
||||
JsonObject ghidraConfigObject = new JsonObject();
|
||||
configurationsArray.add(ghidraConfigObject);
|
||||
ghidraConfigObject.addProperty("type", "java");
|
||||
@ -198,6 +204,33 @@ public class VSCodeProjectScript extends GhidraScript {
|
||||
vmArgsArray.add("-Dghidra.external.modules=${workspaceFolder}");
|
||||
vmArgs.forEach(vmArgsArray::add);
|
||||
|
||||
// PyGhidra launcher
|
||||
JsonObject pyghidraConfigObject = new JsonObject();
|
||||
configurationsArray.add(pyghidraConfigObject);
|
||||
pyghidraConfigObject.addProperty("type", "debugpy");
|
||||
pyghidraConfigObject.addProperty("name", "PyGhidra");
|
||||
pyghidraConfigObject.addProperty("request", "launch");
|
||||
pyghidraConfigObject.addProperty("module", "pyghidra.ghidra_launch");
|
||||
pyghidraConfigObject.addProperty("args", GhidraRun.class.getName());
|
||||
JsonArray argsArray = new JsonArray();
|
||||
pyghidraConfigObject.add("args", argsArray);
|
||||
argsArray.add("--install-dir");
|
||||
argsArray.add(installDir.getAbsolutePath());
|
||||
argsArray.add("-g");
|
||||
argsArray.add(GhidraRun.class.getName());
|
||||
JsonObject envObject = new JsonObject();
|
||||
pyghidraConfigObject.add("env", envObject);
|
||||
envObject.addProperty("PYGHIDRA_DEBUG", "1");
|
||||
|
||||
// PyGhidra Java Attach
|
||||
JsonObject pyghidraAttachObject = new JsonObject();
|
||||
configurationsArray.add(pyghidraAttachObject);
|
||||
pyghidraAttachObject.addProperty("type", "java");
|
||||
pyghidraAttachObject.addProperty("name", "PyGhidra Java Attach");
|
||||
pyghidraAttachObject.addProperty("request", "attach");
|
||||
pyghidraAttachObject.addProperty("hostName", "localhost");
|
||||
pyghidraAttachObject.addProperty("port", 18001);
|
||||
|
||||
// Write launch json object
|
||||
if (!FileUtilities.mkdirs(launchFile.getParentFile())) {
|
||||
throw new IOException("Failed to create: " + launchFile.getParentFile());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -62,6 +62,10 @@ public class AnalysisOptionsDialog extends DialogComponentProvider
|
||||
addCancelButton();
|
||||
addApplyButton();
|
||||
setOkButtonText("Analyze");
|
||||
|
||||
// This allows user to press Enter to launch analysis when the dialog is shown. Without
|
||||
// this, the table takes focus, which consumes Enter key presses.
|
||||
setFocusComponent(okButton);
|
||||
okButton.setMnemonic('A');
|
||||
setOkEnabled(true);
|
||||
setPreferredSize(1000, 600);
|
||||
|
@ -1,293 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.help;
|
||||
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.dnd.GClipboard;
|
||||
import docking.dnd.StringTransferable;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.main.ApplicationLevelPlugin;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.COMMON,
|
||||
shortDescription = "Displays list of installed processor modules",
|
||||
description = "This plugin provides a Help action that displays a list of installed processor modules"
|
||||
)
|
||||
//@formatter:on
|
||||
public class ProcessorListPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
|
||||
private DockingAction processorListAction;
|
||||
|
||||
private ProcessorListDialogProvider dialogProvider;
|
||||
|
||||
public ProcessorListPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
setupActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
tool.removeAction(processorListAction);
|
||||
processorListAction.dispose();
|
||||
|
||||
if (dialogProvider != null) {
|
||||
dialogProvider.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void setupActions() {
|
||||
|
||||
processorListAction = new DockingAction("Installed Processors", this.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showProcessorList();
|
||||
}
|
||||
};
|
||||
|
||||
processorListAction.setEnabled(true);
|
||||
|
||||
processorListAction.setMenuBarData(new MenuData(
|
||||
new String[] { ToolConstants.MENU_HELP, processorListAction.getName() }, null, "AAAZ"));
|
||||
|
||||
processorListAction
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.RUNTIME_INFO, "InstalledProcessors"));
|
||||
processorListAction.setDescription(getPluginDescription().getDescription());
|
||||
tool.addAction(processorListAction);
|
||||
}
|
||||
|
||||
private void dialogClosed() {
|
||||
dialogProvider = null;
|
||||
}
|
||||
|
||||
private void showProcessorList() {
|
||||
if (dialogProvider == null) {
|
||||
dialogProvider = new ProcessorListDialogProvider();
|
||||
}
|
||||
tool.showDialog(dialogProvider);
|
||||
}
|
||||
|
||||
private void copy(boolean asHtml) {
|
||||
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||
Transferable transferable = new StringTransferable(getProcessorList(asHtml));
|
||||
systemClipboard.setContents(transferable, null);
|
||||
}
|
||||
|
||||
private Set<Processor> getProcessors() {
|
||||
TreeSet<Processor> processors = new TreeSet<>();
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
for (LanguageDescription languageDescription : languageService
|
||||
.getLanguageDescriptions(true)) {
|
||||
processors.add(languageDescription.getProcessor());
|
||||
}
|
||||
return processors;
|
||||
}
|
||||
|
||||
private String getProcessorList(boolean asHtml) {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (asHtml) {
|
||||
strBuilder.append("<html><BODY>\n");
|
||||
strBuilder.append("<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n<tr>");
|
||||
}
|
||||
|
||||
Set<Processor> processors = getProcessors();
|
||||
int itemsPerColum = (processors.size() + 2) / 3;
|
||||
int colCnt = 0;
|
||||
|
||||
for (Processor processor : processors) {
|
||||
if (asHtml) {
|
||||
if ((colCnt % itemsPerColum) == 0) {
|
||||
if (colCnt != 0) {
|
||||
strBuilder.append("</ul>\n</td>");
|
||||
}
|
||||
strBuilder.append("<td width=\"33%\">\n<ul>");
|
||||
}
|
||||
strBuilder.append("<li>");
|
||||
}
|
||||
++colCnt;
|
||||
strBuilder.append(processor.toString());
|
||||
if (asHtml) {
|
||||
strBuilder.append("</li>");
|
||||
}
|
||||
strBuilder.append("\n");
|
||||
}
|
||||
if (asHtml) {
|
||||
strBuilder.append("</ul>\n</td></tr>\n</table>");
|
||||
strBuilder.append("</BODY></html>");
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
private class ProcessorListDialogProvider extends ReusableDialogComponentProvider {
|
||||
|
||||
ProcessorListDialogProvider() {
|
||||
super("Installed Processor Modules", false, false, true, false);
|
||||
ProcessorListTableProvider tableProvider =
|
||||
new ProcessorListTableProvider(tool, getName());
|
||||
setRememberLocation(true);
|
||||
addWorkPanel(tableProvider.getComponent());
|
||||
|
||||
setHelpLocation(new HelpLocation(HelpTopics.ABOUT, "ProcessorList"));
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
|
||||
JButton copyButton = new JButton("Copy");
|
||||
copyButton.addActionListener(e -> copy(false));
|
||||
addButton(copyButton);
|
||||
|
||||
JButton copyHtmlButton = new JButton("Copy as HTML");
|
||||
copyHtmlButton.addActionListener(e -> copy(true));
|
||||
addButton(copyHtmlButton);
|
||||
}
|
||||
|
||||
JButton closeButton = new JButton("Close");
|
||||
closeButton.addActionListener(e -> close());
|
||||
addButton(closeButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
super.dialogClosed();
|
||||
ProcessorListPlugin.this.dialogClosed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcessorListTableProvider extends ComponentProviderAdapter {
|
||||
GTable table;
|
||||
private ProcessorListTableModel processorTableModel;
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
public ProcessorListTableProvider(PluginTool tool, String owner) {
|
||||
super(tool, "Processor Table", owner);
|
||||
buildTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
private void buildTable() {
|
||||
|
||||
TreeSet<Processor> processors = new TreeSet<>();
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
for (LanguageDescription languageDescription : languageService
|
||||
.getLanguageDescriptions(true)) {
|
||||
processors.add(languageDescription.getProcessor());
|
||||
}
|
||||
|
||||
processorTableModel = new ProcessorListTableModel(new ArrayList<>(processors));
|
||||
|
||||
table = new GTable(processorTableModel);
|
||||
scrollPane = new JScrollPane(table);
|
||||
table.getSelectionManager().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcessorListTableModel extends AbstractSortedTableModel<Processor> {
|
||||
|
||||
private static final int PROCESSOR_COL = 0;
|
||||
|
||||
private List<Processor> processors;
|
||||
|
||||
public ProcessorListTableModel(List<Processor> processors) {
|
||||
this.processors = processors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(Processor p, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case PROCESSOR_COL:
|
||||
return p.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Processors";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Processor> getModelData() {
|
||||
return processors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return false; // maybe later when we add more columns
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return processors.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
switch (column) {
|
||||
case PROCESSOR_COL:
|
||||
return "Processor";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case PROCESSOR_COL:
|
||||
return String.class;
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -61,7 +61,7 @@ public class LabelMgrPlugin extends Plugin {
|
||||
// Setup list of actions
|
||||
setupActions();
|
||||
|
||||
addEditDialog = new AddEditDialog("", tool);
|
||||
addEditDialog = new AddEditDialog("Add/Edit Label", tool);
|
||||
addEditDialog.setReusable(true);
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ public class LabelMgrPlugin extends Plugin {
|
||||
addr = loc.getAddress();
|
||||
}
|
||||
else if (location instanceof OperandFieldLocation) {
|
||||
Address a = ((OperandFieldLocation) location).getRefAddress();
|
||||
Address a = location.getRefAddress();
|
||||
addr = (a == null) ? addr : a;
|
||||
}
|
||||
|
||||
|
@ -44,41 +44,48 @@ public abstract class DemangledObject implements Demangled {
|
||||
/*
|
||||
The following names probably need to be refactored. Until then, this is how the following
|
||||
fields are used.
|
||||
|
||||
mangled -
|
||||
|
||||
mangled -
|
||||
Source: The original mangled string as seen in the program
|
||||
Usage: Can be used to see if a program symbol has already been demangled
|
||||
|
||||
originalDemangled -
|
||||
|
||||
rawDemangled -
|
||||
Source: The raw demangled string returned from the demangler
|
||||
Usage: for debugging
|
||||
|
||||
originalDemangled -
|
||||
Source: The starting demangled string. This will usually be the same as the raw
|
||||
demangled string. This may have simplifications applied.
|
||||
Usage: for display
|
||||
|
||||
demangledName -
|
||||
|
||||
demangledName -
|
||||
Source: The name as created by the parser which may transform or even replace the
|
||||
string returned from the demangler
|
||||
Usage: for display
|
||||
|
||||
name -
|
||||
|
||||
name -
|
||||
Source: This is derived from the 'demangledName' This is updated to be suitable
|
||||
for use as a symbol name. This may be null while building, but is
|
||||
expected to be non-null when applyTo() is called
|
||||
Usage: The name that will be applied when applyTo() is called.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Future: These variables should be refactored and renamed to be clearer and more cohesive,
|
||||
something like:
|
||||
|
||||
|
||||
mangled
|
||||
rawDemangled
|
||||
escapedDemangled
|
||||
simplifiedDemangled --| These two could be combined into 'transformedDemangled'
|
||||
escapedDemangled --|
|
||||
symbolName
|
||||
|
||||
|
||||
*/
|
||||
protected MangledContext mangledContext; // the mangled context, which includes mangled string
|
||||
protected final String mangled; // original mangled string
|
||||
protected String originalDemangled; // raw demangled string
|
||||
private String demangledName; // updated demangled string
|
||||
protected String rawDemangled; // demangled string from the demangler without any simplifications
|
||||
protected String originalDemangled; // starting demangled string that may have been simplified
|
||||
private String demangledName; // updated demangled string, possibly with changes made while building
|
||||
private String name; // version of demangled name suitable for symbols
|
||||
|
||||
protected String specialPrefix;
|
||||
@ -120,6 +127,7 @@ public abstract class DemangledObject implements Demangled {
|
||||
DemangledObject(String mangled, String originalDemangled) {
|
||||
this.mangled = mangled;
|
||||
this.originalDemangled = originalDemangled;
|
||||
this.rawDemangled = originalDemangled;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,6 +140,7 @@ public abstract class DemangledObject implements Demangled {
|
||||
this.mangledContext = mangledContext;
|
||||
this.mangled = mangledContext.getMangled();
|
||||
this.originalDemangled = originalDemangled;
|
||||
this.rawDemangled = originalDemangled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -280,6 +289,24 @@ public abstract class DemangledObject implements Demangled {
|
||||
this.originalDemangled = originalDemangled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw demangled string. This is the value returned from the demangler before any
|
||||
* simplifications or transformations have been made.
|
||||
* @return the string
|
||||
*/
|
||||
public String getRawDemangled() {
|
||||
return rawDemangled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw demangled string. This is the value returned from the demangler before any
|
||||
* simplifications or transformations have been made.
|
||||
* @param s the string
|
||||
*/
|
||||
public void setRawDemangledString(String s) {
|
||||
this.rawDemangled = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Demangled getNamespace() {
|
||||
return namespace;
|
||||
|
@ -184,7 +184,9 @@ public class SymbolSearcher {
|
||||
return;
|
||||
}
|
||||
Symbol s = program.getSymbolTable().getPrimarySymbol(address);
|
||||
addSymbolIfMatches(s, locations);
|
||||
if (s.isDynamic()) { // non-dynamic symbols have already been searched (ex, FUN_12345678)
|
||||
addSymbolIfMatches(s, locations);
|
||||
}
|
||||
}
|
||||
|
||||
private void searchDefinedSymbols(Program program, List<ProgramLocation> locations) {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -237,6 +237,7 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
|
||||
public void performAction(String actionName, String owner, ComponentProvider contextProvider,
|
||||
boolean wait) {
|
||||
DockingActionIf action = getAction(tool, owner, actionName);
|
||||
assertNotNull("Could not find action: " + actionName + " for owner " + owner, action);
|
||||
performAction(action, contextProvider, wait);
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -225,7 +225,7 @@ public abstract class GhidraScreenShotGenerator extends AbstractScreenShotGenera
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
private void showDialog(final DialogComponentProvider dialogComponent) {
|
||||
private void showDialog(DialogComponentProvider dialogComponent) {
|
||||
runSwing(() -> {
|
||||
DockingDialog dialog = DockingDialog.createDialog(null, dialogComponent, null);
|
||||
dialog.setLocation(WindowUtilities.centerOnScreen(dialog.getSize()));
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -207,8 +207,7 @@ public class StructureEditorUnlockedCellEdit2Test extends AbstractStructureEdito
|
||||
assertEquals(model.getDataTypeColumn(), model.getColumn());
|
||||
|
||||
// Bad value allows escape.
|
||||
escape();
|
||||
escape();
|
||||
escape(); // cancel editing
|
||||
waitForSwing();
|
||||
assertTrue(!model.isEditingField());
|
||||
assertEquals(1, model.getNumSelectedRows());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -630,7 +630,7 @@ public class AutoTableDisassemblerTest extends AbstractGhidraHeadedIntegrationTe
|
||||
}
|
||||
|
||||
private JButton getActionButton(String actionName) {
|
||||
Map<?, ?> actionMap = (Map<?, ?>) getInstanceField("actionMap", dialog);
|
||||
Map<?, ?> actionMap = (Map<?, ?>) getInstanceField("toolbarButtonsByAction", dialog);
|
||||
Set<?> entrySet = actionMap.entrySet();
|
||||
for (Object entry : entrySet) {
|
||||
Map.Entry<?, ?> mapEntry = (Map.Entry<?, ?>) entry;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -19,7 +19,6 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
@ -27,8 +26,6 @@ import javax.swing.table.TableModel;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.cmd.memory.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
@ -93,58 +90,6 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(action.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenProgram() throws Exception {
|
||||
env.close(program);
|
||||
program = buildProgram("sdk");
|
||||
env.open(program);
|
||||
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
|
||||
for (DockingActionIf action : actions) {
|
||||
String name = action.getName();
|
||||
if (name.equals("Add Block") || name.equals("Set Image Base") ||
|
||||
name.equals("Memory Map") || name.equals("Close Window") ||
|
||||
name.contains("Table")) {
|
||||
assertActionEnabled(action, getActionContext(), true);
|
||||
}
|
||||
else {
|
||||
assertActionEnabled(action, getActionContext(), false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseProgram() {
|
||||
env.close(program);
|
||||
JTable table = provider.getTable();
|
||||
assertEquals(0, table.getModel().getRowCount());
|
||||
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
|
||||
for (DockingActionIf action : actions) {
|
||||
String name = action.getName();
|
||||
if (name.equals("Memory Map") || name.equals("Close Window") ||
|
||||
name.equals("Local Menu")) {
|
||||
continue;
|
||||
}
|
||||
assertActionEnabled(action, getActionContext(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertActionEnabled(DockingActionIf action, ActionContext context,
|
||||
boolean shouldBeEnabled) {
|
||||
|
||||
String text = shouldBeEnabled ? "should be enabled" : "should be disabled";
|
||||
assertEquals("Action " + text + ", but is not: '" + action.getFullName() + "'",
|
||||
shouldBeEnabled, action.isEnabledForContext(context));
|
||||
}
|
||||
|
||||
private ActionContext getActionContext() {
|
||||
ActionContext context = provider.getActionContext(null);
|
||||
if (context == null) {
|
||||
return new DefaultActionContext();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockNameChanged() throws Exception {
|
||||
MemoryBlock block = memory.getBlock(program.getMinAddress());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -124,29 +124,6 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiSelection() {
|
||||
|
||||
table.addRowSelectionInterval(0, 1);
|
||||
assertEquals(2, table.getSelectedRowCount());
|
||||
Set<DockingActionIf> actions = getActionsByOwner(tool, plugin.getName());
|
||||
for (DockingActionIf action : actions) {
|
||||
String name = action.getName();
|
||||
if (name.equals("Add Block") || name.equals("Merge Blocks") ||
|
||||
name.equals("Delete Block") || name.equals("Set Image Base") ||
|
||||
name.equals("Memory Map") || name.equals("Close Window") ||
|
||||
name.equals("Make Selection")) {
|
||||
assertTrue("Action should be enabled for a multi-row selection - '" + name + "'",
|
||||
action.isEnabled());
|
||||
}
|
||||
else {
|
||||
assertFalse(
|
||||
"Action should not be enabled for a multi-row selection - '" + name + "'",
|
||||
action.isEnabled());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoToAddress() {
|
||||
Rectangle rect = table.getCellRect(2, MemoryMapModel.START, true);
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -25,7 +25,7 @@ import javax.swing.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.DockingDialog;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.options.editor.OptionsDialog;
|
||||
import docking.options.editor.StringWithChoicesEditor;
|
||||
import docking.widgets.tree.GTree;
|
||||
@ -34,26 +34,22 @@ import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private DockingDialog dialog;
|
||||
private OptionsDialog dialog;
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if (dialog != null) {
|
||||
dialog.setVisible(false);
|
||||
dialog.dispose();
|
||||
close(dialog);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private DockingDialog showEditor(Options options) {
|
||||
private OptionsDialog showEditor(Options options) {
|
||||
OptionsDialog dialogComponent =
|
||||
new OptionsDialog("Test Properties", "Properties", new Options[] { options }, null);
|
||||
final DockingDialog editorDialog =
|
||||
runSwing(() -> DockingDialog.createDialog(null, dialogComponent, null));
|
||||
SwingUtilities.invokeLater(() -> editorDialog.setVisible(true));
|
||||
runSwing(() -> DockingWindowManager.showDialog(dialogComponent), false);
|
||||
|
||||
waitForJDialog("Test Properties");
|
||||
assertNotNull("Dialog failed to launch", editorDialog);
|
||||
OptionsDialog editorDialog = waitForDialogComponent(OptionsDialog.class);
|
||||
waitForSwing();
|
||||
waitForOptionsTree(dialogComponent);
|
||||
return editorDialog;
|
||||
@ -65,10 +61,9 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
waitForTree(tree);
|
||||
}
|
||||
|
||||
private Component findPairedComponent(final Container container, final String labelText) {
|
||||
final Component[] box = new Component[1];
|
||||
runSwing(() -> box[0] = doFindPairedComponent(container, labelText));
|
||||
return box[0];
|
||||
private Component findPairedComponent(final String labelText) {
|
||||
JComponent component = dialog.getComponent();
|
||||
return runSwing(() -> doFindPairedComponent(component, labelText));
|
||||
}
|
||||
|
||||
private Component doFindPairedComponent(Container container, String labelText) {
|
||||
@ -102,7 +97,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestInt");
|
||||
Component editor = findPairedComponent("TestInt");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertyText.class, editor.getClass());
|
||||
final PropertyText textField = (PropertyText) editor;
|
||||
@ -125,7 +120,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestLong");
|
||||
Component editor = findPairedComponent("TestLong");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertyText.class, editor.getClass());
|
||||
final PropertyText textField = (PropertyText) editor;
|
||||
@ -148,7 +143,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestFloat");
|
||||
Component editor = findPairedComponent("TestFloat");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertyText.class, editor.getClass());
|
||||
final PropertyText textField = (PropertyText) editor;
|
||||
@ -173,7 +168,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestDouble");
|
||||
Component editor = findPairedComponent("TestDouble");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertyText.class, editor.getClass());
|
||||
final PropertyText textField = (PropertyText) editor;
|
||||
@ -197,7 +192,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestString");
|
||||
Component editor = findPairedComponent("TestString");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertyText.class, editor.getClass());
|
||||
final PropertyText textField = (PropertyText) editor;
|
||||
@ -222,7 +217,7 @@ public class PropertyEditorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
dialog = showEditor(options);
|
||||
|
||||
Component editor = findPairedComponent(dialog, "TestStringWithChoices");
|
||||
Component editor = findPairedComponent("TestStringWithChoices");
|
||||
assertNotNull("Could not find editor component", editor);
|
||||
assertEquals(PropertySelector.class, editor.getClass());
|
||||
final PropertySelector textSelector = (PropertySelector) editor;
|
||||
|
@ -25,6 +25,7 @@ src/decompile/datatests/divopt.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/doublemove.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/dupptr.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/elseif.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/enum.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/floatcast.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/floatconv.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace ghidra {
|
||||
|
||||
const int4 ArraySequence::MINIMUM_SEQUENCE_LENGTH = 4;
|
||||
const int4 ArraySequence::MAXIMUM_SEQUENCE_LENGTH = 0x20000;
|
||||
|
||||
/// Initialize the sequence with the \b root operation which writes the earliest character in the memory region.
|
||||
/// \param fdata is the function containing the sequence
|
||||
@ -112,7 +113,7 @@ int4 ArraySequence::formByteArray(int4 sz,int4 slot,uint8 rootOff,bool bigEndian
|
||||
int4 elSize = charType->getSize();
|
||||
for(int4 i=0;i<moveOps.size();++i) {
|
||||
int4 bytePos = moveOps[i].offset - rootOff;
|
||||
if (bytePos + elSize > sz) continue;
|
||||
if (bytePos < 0 || bytePos + elSize > sz) continue;
|
||||
uint8 val = moveOps[i].op->getIn(slot)->getOffset();
|
||||
used[bytePos] = (val == 0) ? 2 : 1; // Mark byte as used, a 2 indicates a null terminator
|
||||
if (bigEndian) {
|
||||
@ -470,7 +471,7 @@ void HeapSequence::findBasePointer(Varnode *initPtr)
|
||||
OpCode opc = op->code();
|
||||
if (opc == CPUI_PTRADD) {
|
||||
int8 sz = op->getIn(2)->getOffset();
|
||||
if (sz != charType->getAlignSize()) break;
|
||||
if (sz != ptrAddMult) break;
|
||||
}
|
||||
else if (opc != CPUI_COPY)
|
||||
break;
|
||||
@ -546,7 +547,6 @@ void HeapSequence::findInitialStores(vector<PcodeOp *> &stores)
|
||||
vector<Varnode *> ptradds;
|
||||
findDuplicateBases(ptradds);
|
||||
int4 pos = 0;
|
||||
int4 alignSize = charType->getAlignSize();
|
||||
while(pos < ptradds.size()) {
|
||||
Varnode *vn = ptradds[pos];
|
||||
pos += 1;
|
||||
@ -558,7 +558,7 @@ void HeapSequence::findInitialStores(vector<PcodeOp *> &stores)
|
||||
if (op->getIn(0) != vn) continue;
|
||||
// We only check array element size here, if we checked the data-type, we would
|
||||
// need to take into account different pointer styles to the same element data-type
|
||||
if (op->getIn(2)->getOffset() != alignSize) continue;
|
||||
if (op->getIn(2)->getOffset() != ptrAddMult) continue;
|
||||
ptradds.push_back(op->getOut());
|
||||
}
|
||||
else if (opc == CPUI_COPY) {
|
||||
@ -594,13 +594,13 @@ uint8 HeapSequence::calcAddElements(Varnode *vn,vector<Varnode *> &nonConst,int4
|
||||
return res;
|
||||
}
|
||||
|
||||
/// \brief Calculate the offset and any non-constant additive elements between the given Varnode and the \b basePointer
|
||||
/// \brief Calculate the byte offset and any non-constant additive elements between the given Varnode and the \b basePointer
|
||||
///
|
||||
/// Walk backward from the given Varnode thru PTRADDs and COPYs, summing any offsets encountered.
|
||||
/// Any non-constant Varnodes encountered in the path, that are not themselves a pointer, are passed back in a list.
|
||||
/// \param vn is the given Varnode to trace back to the \b basePointer
|
||||
/// \param nonConst will hold the list of non-constant Varnodes being passed back
|
||||
/// \return the sum off constant offsets on the path
|
||||
/// \return the sum off constant offsets on the path in byte units
|
||||
uint8 HeapSequence::calcPtraddOffset(Varnode *vn,vector<Varnode *> &nonConst)
|
||||
|
||||
{
|
||||
@ -610,7 +610,7 @@ uint8 HeapSequence::calcPtraddOffset(Varnode *vn,vector<Varnode *> &nonConst)
|
||||
OpCode opc = op->code();
|
||||
if (opc == CPUI_PTRADD) {
|
||||
uint8 mult = op->getIn(2)->getOffset();
|
||||
if (mult != charType->getAlignSize())
|
||||
if (mult != ptrAddMult)
|
||||
break;
|
||||
uint8 off = calcAddElements(op->getIn(1),nonConst,3);
|
||||
off *= mult;
|
||||
@ -623,7 +623,7 @@ uint8 HeapSequence::calcPtraddOffset(Varnode *vn,vector<Varnode *> &nonConst)
|
||||
else
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
return AddrSpace::addressToByteInt(res, storeSpace->getWordSize());
|
||||
}
|
||||
|
||||
/// \brief Determine if two sets of Varnodes are equal
|
||||
@ -667,18 +667,21 @@ bool HeapSequence::collectStoreOps(void)
|
||||
findInitialStores(initStores);
|
||||
if (initStores.size() + 1 < MINIMUM_SEQUENCE_LENGTH)
|
||||
return false;
|
||||
uint8 maxSize = MAXIMUM_SEQUENCE_LENGTH * charType->getAlignSize(); // Maximum bytes
|
||||
uint8 wrapMask = calc_mask(storeSpace->getAddrSize());
|
||||
baseOffset = calcPtraddOffset(rootOp->getIn(1), nonConstAdds);
|
||||
vector<Varnode *> nonConstComp;
|
||||
for(int4 i=0;i<initStores.size();++i) {
|
||||
PcodeOp *op = initStores[i];
|
||||
nonConstComp.clear();
|
||||
uint8 curOffset = calcPtraddOffset(op->getIn(1), nonConstComp);
|
||||
uint8 diff = (curOffset - baseOffset) & wrapMask; // Allow wrapping relative to base pointer
|
||||
if (setsEqual(nonConstAdds, nonConstComp)) {
|
||||
if (curOffset < baseOffset)
|
||||
return false; // Root is not the earliest STORE
|
||||
if (diff >= maxSize)
|
||||
return false; // Root is not the earliest STORE, or offsets span range larger then maxSize
|
||||
if (!testValue(op))
|
||||
return false;
|
||||
moveOps.emplace_back(curOffset - baseOffset,op,-1);
|
||||
moveOps.emplace_back(diff,op,-1);
|
||||
}
|
||||
}
|
||||
moveOps.emplace_back(0,rootOp,-1);
|
||||
@ -721,6 +724,7 @@ PcodeOp *HeapSequence::buildStringCopy(void)
|
||||
if (baseOffset != 0) { // Add in any non-zero constant
|
||||
uint8 numEl = baseOffset / charType->getAlignSize();
|
||||
Varnode *cvn = data.newConstant(basePointer->getSize(), numEl);
|
||||
cvn->updateType(intType, false, false);
|
||||
if (indexVn == (Varnode *)0)
|
||||
indexVn = cvn;
|
||||
else {
|
||||
@ -875,13 +879,15 @@ HeapSequence::HeapSequence(Funcdata &fdata,Datatype *ct,PcodeOp *root)
|
||||
: ArraySequence(fdata,ct,root)
|
||||
{
|
||||
baseOffset = 0;
|
||||
storeSpace = root->getIn(0)->getSpaceFromConst();
|
||||
ptrAddMult = AddrSpace::byteToAddressInt(charType->getAlignSize(), storeSpace->getWordSize());
|
||||
findBasePointer(rootOp->getIn(1));
|
||||
if (!collectStoreOps())
|
||||
return;
|
||||
if (!checkInterference())
|
||||
return;
|
||||
int4 arrSize = moveOps.size() * charType->getAlignSize();
|
||||
bool bigEndian = moveOps[0].op->getIn(0)->getSpaceFromConst()->isBigEndian();
|
||||
bool bigEndian = storeSpace->isBigEndian();
|
||||
numElements = formByteArray(arrSize, 2, 0, bigEndian);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ namespace ghidra {
|
||||
class ArraySequence {
|
||||
public:
|
||||
static const int4 MINIMUM_SEQUENCE_LENGTH; ///< Minimum number of sequential characters to trigger replacement with CALLOTHER
|
||||
static const int4 MAXIMUM_SEQUENCE_LENGTH; ///< Maximum number of characters in replacement string
|
||||
/// \brief Helper class holding a data-flow edge and optionally a memory offset being COPYed into or from
|
||||
class WriteNode {
|
||||
public:
|
||||
@ -38,8 +39,6 @@ public:
|
||||
WriteNode(uint8 off,PcodeOp *o,int4 sl) { offset = off; op = o; slot = sl; } ///< Constructor
|
||||
/// \brief Compare two nodes by their order within a basic block
|
||||
bool operator<(const WriteNode &node2) const { return op->getSeqNum().getOrder() < node2.op->getSeqNum().getOrder(); }
|
||||
/// \brief Compare two PcodeOps based on the position of the element they copy within the sequence
|
||||
static bool compareOffset(const WriteNode &a,const WriteNode &b) { return a.offset < b.offset; }
|
||||
};
|
||||
protected:
|
||||
Funcdata &data; ///< The function containing the sequence
|
||||
@ -87,6 +86,8 @@ public:
|
||||
class HeapSequence : public ArraySequence {
|
||||
Varnode *basePointer; ///< Pointer that sequence is stored to
|
||||
uint8 baseOffset; ///< Offset relative to pointer to root STORE
|
||||
AddrSpace *storeSpace; ///< Address space being STOREed to
|
||||
int4 ptrAddMult; ///< Required multiplier for PTRADD ops
|
||||
vector<Varnode *> nonConstAdds; ///< non-constant Varnodes being added into pointer calculation
|
||||
void findBasePointer(Varnode *initPtr); ///< Find the base pointer for the sequence
|
||||
void findDuplicateBases(vector<Varnode *> &duplist); ///< Find any duplicates of \b basePointer
|
||||
|
@ -5600,6 +5600,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
||||
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
|
||||
actcleanup->addRule( new RuleSubRight("cleanup") );
|
||||
actcleanup->addRule( new RuleFloatSignCleanup("cleanup") );
|
||||
actcleanup->addRule( new RuleExpandLoad("cleanup") );
|
||||
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
|
||||
actcleanup->addRule( new RuleExtensionPush("cleanup") );
|
||||
actcleanup->addRule( new RulePieceStructure("cleanup") );
|
||||
|
@ -3112,7 +3112,7 @@ int grammarerror(const char *str)
|
||||
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||
|
||||
{
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
@ -3131,7 +3131,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||
void parse_protopieces(PrototypePieces &pieces,
|
||||
istream &s,Architecture *glb)
|
||||
{
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
@ -3151,7 +3151,7 @@ void parse_protopieces(PrototypePieces &pieces,
|
||||
void parse_C(Architecture *glb,istream &s)
|
||||
|
||||
{ // Load type data straight into datastructures
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
|
@ -1366,7 +1366,7 @@ int grammarerror(const char *str)
|
||||
Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||
|
||||
{
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_parameter_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
@ -1385,7 +1385,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb)
|
||||
void parse_protopieces(PrototypePieces &pieces,
|
||||
istream &s,Architecture *glb)
|
||||
{
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
@ -1405,7 +1405,7 @@ void parse_protopieces(PrototypePieces &pieces,
|
||||
void parse_C(Architecture *glb,istream &s)
|
||||
|
||||
{ // Load type data straight into datastructures
|
||||
CParse parser(glb,1000);
|
||||
CParse parser(glb,4096);
|
||||
|
||||
if (!parser.parseStream(s,CParse::doc_declaration))
|
||||
throw ParseError(parser.getError());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -1252,8 +1252,9 @@ AttributeId ATTRIB_VAL = AttributeId("val",24);
|
||||
AttributeId ATTRIB_VALUE = AttributeId("value",25);
|
||||
AttributeId ATTRIB_WORDSIZE = AttributeId("wordsize",26);
|
||||
AttributeId ATTRIB_STORAGE = AttributeId("storage",149);
|
||||
AttributeId ATTRIB_STACKSPILL = AttributeId("stackspill",150);
|
||||
|
||||
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",150); // Number serves as next open index
|
||||
AttributeId ATTRIB_UNKNOWN = AttributeId("XMLunknown",151); // Number serves as next open index
|
||||
|
||||
ElementId ELEM_DATA = ElementId("data",1);
|
||||
ElementId ELEM_INPUT = ElementId("input",2);
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -736,6 +736,7 @@ extern AttributeId ATTRIB_VAL; ///< Marshaling attribute "val"
|
||||
extern AttributeId ATTRIB_VALUE; ///< Marshaling attribute "value"
|
||||
extern AttributeId ATTRIB_WORDSIZE; ///< Marshaling attribute "wordsize"
|
||||
extern AttributeId ATTRIB_STORAGE; ///< Marshaling attribute "storage"
|
||||
extern AttributeId ATTRIB_STACKSPILL; ///< Marshaling attribute "stackspill"
|
||||
|
||||
extern ElementId ELEM_DATA; ///< Marshaling element \<data>
|
||||
extern ElementId ELEM_INPUT; ///< Marshaling element \<input>
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -689,6 +689,7 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
|
||||
vector<int4> tmpStatus = status;
|
||||
vector<VarnodeData> pieces;
|
||||
int4 sizeLeft = dt->getSize();
|
||||
int4 align = dt->getAlignment();
|
||||
list<ParamEntry>::const_iterator iter = firstIter;
|
||||
list<ParamEntry>::const_iterator endIter = resource->getEntry().end();
|
||||
if (enforceAlignment) {
|
||||
@ -699,7 +700,6 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
|
||||
break; // Reached end of resource list
|
||||
if (entry.getType() == resourceType && entry.getAllGroups().size() == 1) { // Single register
|
||||
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
|
||||
int4 align = dt->getAlignment();
|
||||
int4 regSize = entry.getSize();
|
||||
if (align <= regSize || (resourcesConsumed % align) == 0)
|
||||
break;
|
||||
@ -720,19 +720,20 @@ uint4 MultiSlotAssign::assignAddress(Datatype *dt,const PrototypePieces &proto,i
|
||||
if (tmpStatus[entry.getGroup()] != 0)
|
||||
continue; // Already consumed
|
||||
int4 trialSize = entry.getSize();
|
||||
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,1);
|
||||
Address addr = entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize,align);
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume the register
|
||||
pieces.push_back(VarnodeData());
|
||||
pieces.back().space = addr.getSpace();
|
||||
pieces.back().offset = addr.getOffset();
|
||||
pieces.back().size = trialSize;
|
||||
sizeLeft -= trialSize;
|
||||
align = 1; // Treat remaining partial pieces as having no alignment requirement
|
||||
}
|
||||
if (sizeLeft > 0) { // Have to use stack to get enough bytes
|
||||
if (!consumeFromStack)
|
||||
return fail;
|
||||
int4 grp = stackEntry->getGroup();
|
||||
Address addr = stackEntry->getAddrBySlot(tmpStatus[grp],sizeLeft,1); // Consume all the space we need
|
||||
Address addr = stackEntry->getAddrBySlot(tmpStatus[grp],sizeLeft,align); // Consume all the space we need
|
||||
if (addr.isInvalid())
|
||||
return fail;
|
||||
pieces.push_back(VarnodeData());
|
||||
@ -841,6 +842,9 @@ void MultiSlotAssign::decode(Decoder &decoder)
|
||||
else if (attribId == ATTRIB_ALIGN) {
|
||||
enforceAlignment = decoder.readBool();
|
||||
}
|
||||
else if (attribId == ATTRIB_STACKSPILL) {
|
||||
consumeFromStack = decoder.readBool();
|
||||
}
|
||||
}
|
||||
decoder.closeElement(elemId);
|
||||
initializeEntries(); // Need new firstIter
|
||||
|
@ -959,7 +959,7 @@ void PrintC::opPtrsub(const PcodeOp *op)
|
||||
if (ct->getMetatype() == TYPE_STRUCT || ct->getMetatype() == TYPE_UNION) {
|
||||
int8 suboff = (int4)in1const; // How far into container
|
||||
if (ptrel != (TypePointerRel *)0) {
|
||||
suboff += ptrel->getPointerOffset();
|
||||
suboff += ptrel->getAddressOffset();
|
||||
suboff &= calc_mask(ptype->getSize());
|
||||
if (suboff == 0) {
|
||||
// Special case where we do not print a field
|
||||
@ -1666,22 +1666,23 @@ void PrintC::pushCharConstant(uintb val,const Datatype *ct,tagtype tag,const Var
|
||||
void PrintC::pushEnumConstant(uintb val,const TypeEnum *ct,tagtype tag,
|
||||
const Varnode *vn,const PcodeOp *op)
|
||||
{
|
||||
vector<string> valnames;
|
||||
TypeEnum::Representation rep;
|
||||
|
||||
bool complement = ct->getMatches(val,valnames);
|
||||
if (valnames.size() > 0) {
|
||||
if (complement)
|
||||
ct->getMatches(val,rep);
|
||||
if (rep.matchname.size() > 0) {
|
||||
if (rep.shiftAmount != 0)
|
||||
pushOp(&shift_right,op);
|
||||
if (rep.complement)
|
||||
pushOp(&bitwise_not,op);
|
||||
for(int4 i=valnames.size()-1;i>0;--i)
|
||||
for(int4 i=rep.matchname.size()-1;i>0;--i)
|
||||
pushOp(&enum_cat,op);
|
||||
for(int4 i=0;i<valnames.size();++i)
|
||||
pushAtom(Atom(valnames[i],tag,EmitMarkup::const_color,op,vn,val));
|
||||
for(int4 i=0;i<rep.matchname.size();++i)
|
||||
pushAtom(Atom(rep.matchname[i],tag,EmitMarkup::const_color,op,vn,val));
|
||||
if (rep.shiftAmount != 0)
|
||||
push_integer(rep.shiftAmount,4,false,tag,vn,op);
|
||||
}
|
||||
else {
|
||||
push_integer(val,ct->getSize(),false,tag,vn,op);
|
||||
// ostringstream s;
|
||||
// s << "BAD_ENUM(0x" << hex << val << ")";
|
||||
// pushAtom(Atom(s.str(),vartoken,EmitMarkup::const_color,op,vn));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1793,8 +1794,11 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag,
|
||||
case TYPE_SPACEBASE:
|
||||
case TYPE_CODE:
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_ENUM_INT:
|
||||
case TYPE_ENUM_UINT:
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_PARTIALENUM:
|
||||
case TYPE_PARTIALSTRUCT:
|
||||
case TYPE_PARTIALUNION:
|
||||
break;
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
#include "ruleaction.hh"
|
||||
#include "coreaction.hh"
|
||||
#include "subflow.hh"
|
||||
#include "rangeutil.hh"
|
||||
#include "multiprecision.hh"
|
||||
|
||||
@ -5679,7 +5678,7 @@ void AddTreeState::clear(void)
|
||||
nonmultsum = 0;
|
||||
biggestNonMultCoeff = 0;
|
||||
if (pRelType != (const TypePointerRel *)0) {
|
||||
nonmultsum = ((TypePointerRel *)ct)->getPointerOffset();
|
||||
nonmultsum = ((TypePointerRel *)ct)->getAddressOffset();
|
||||
nonmultsum &= ptrmask;
|
||||
}
|
||||
multiple.clear();
|
||||
@ -5733,7 +5732,7 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot)
|
||||
if (ct->isFormalPointerRel()) {
|
||||
pRelType = (const TypePointerRel *)ct;
|
||||
baseType = pRelType->getParent();
|
||||
nonmultsum = pRelType->getPointerOffset();
|
||||
nonmultsum = pRelType->getAddressOffset();
|
||||
nonmultsum &= ptrmask;
|
||||
}
|
||||
if (baseType->isVariableLength())
|
||||
@ -6012,7 +6011,7 @@ void AddTreeState::calcSubtype(void)
|
||||
extra = AddrSpace::byteToAddressInt(extra, ct->getWordSize()); // Convert back to address units
|
||||
offset = (offset - extra) & ptrmask;
|
||||
correct = (correct - extra) & ptrmask;
|
||||
if (pRelType != (TypePointerRel *)0 && offset == pRelType->getPointerOffset()) {
|
||||
if (pRelType != (TypePointerRel *)0 && offset == pRelType->getAddressOffset()) {
|
||||
// offset falls within basic ptrto
|
||||
if (!pRelType->evaluateThruParent(0)) { // If we are not representing offset 0 through parent
|
||||
valid = false; // Use basic (alternate) form
|
||||
@ -6031,7 +6030,7 @@ void AddTreeState::calcSubtype(void)
|
||||
valid = false; // There is substructure we don't know about
|
||||
}
|
||||
if (pRelType != (const TypePointerRel *)0) {
|
||||
int4 ptrOff = ((TypePointerRel *)ct)->getPointerOffset();
|
||||
int4 ptrOff = ((TypePointerRel *)ct)->getAddressOffset();
|
||||
offset = (offset - ptrOff) & ptrmask;
|
||||
correct = (correct - ptrOff) & ptrmask;
|
||||
}
|
||||
@ -6398,8 +6397,7 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data)
|
||||
baseType = ptRel->getParent();
|
||||
if (baseType->getMetatype() != TYPE_STRUCT)
|
||||
return 0;
|
||||
int8 iOff = ptRel->getPointerOffset();
|
||||
iOff = AddrSpace::addressToByteInt(iOff, ptRel->getWordSize());
|
||||
int8 iOff = ptRel->getByteOffset();
|
||||
if (iOff >= baseType->getSize())
|
||||
return 0;
|
||||
offset = iOff;
|
||||
@ -6864,7 +6862,6 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data)
|
||||
Datatype *dt = constvn->getTypeReadFacing(op);
|
||||
if (dt->getMetatype() != TYPE_UINT) return 0;
|
||||
if (dt->isCharPrint()) return 0; // Only change integer forms
|
||||
if (dt->isEnumType()) return 0;
|
||||
uintb val = constvn->getOffset();
|
||||
uintb mask = calc_mask(constvn->getSize());
|
||||
int4 sa = constvn->getSize() * 6; // 1/4 less than full bitsize
|
||||
@ -6877,8 +6874,14 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data)
|
||||
return 0; // Dont transform a named equate
|
||||
}
|
||||
}
|
||||
uintb negatedVal = (-val) & mask;
|
||||
if (dt->isEnumType()) {
|
||||
TypeEnum *enumType = (TypeEnum *)dt;
|
||||
if (!enumType->hasNamedValue(negatedVal) && enumType->hasNamedValue((~val)&mask))
|
||||
return 0;
|
||||
}
|
||||
data.opSetOpcode(op,CPUI_INT_SUB);
|
||||
Varnode *cvn = data.newConstant(constvn->getSize(), (-val) & mask);
|
||||
Varnode *cvn = data.newConstant(constvn->getSize(), negatedVal);
|
||||
cvn->copySymbol(constvn);
|
||||
data.opSetInput(op,cvn,1);
|
||||
return 1;
|
||||
@ -7369,86 +7372,6 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSplitCopy
|
||||
/// \brief Split COPY ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is copied at once,
|
||||
/// rewrite the COPY operator as multiple COPYs.
|
||||
void RuleSplitCopy::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_COPY);
|
||||
}
|
||||
|
||||
int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *inType = op->getIn(0)->getTypeReadFacing(op);
|
||||
Datatype *outType = op->getOut()->getTypeDefFacing();
|
||||
type_metatype metain = inType->getMetatype();
|
||||
type_metatype metaout = outType->getMetatype();
|
||||
if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT &&
|
||||
metain != TYPE_ARRAY && metaout != TYPE_ARRAY &&
|
||||
metain != TYPE_STRUCT && metaout != TYPE_STRUCT)
|
||||
return false;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitCopy(op, inType, outType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \class RuleSplitLoad
|
||||
/// \brief Split LOAD ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is loaded at once,
|
||||
/// rewrite the LOAD operator as multiple LOADs.
|
||||
void RuleSplitLoad::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_LOAD);
|
||||
}
|
||||
|
||||
int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types);
|
||||
if (inType == (Datatype *)0)
|
||||
return 0;
|
||||
type_metatype metain = inType->getMetatype();
|
||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||
return 0;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitLoad(op, inType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \class RuleSplitStore
|
||||
/// \brief Split STORE ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is stored at once,
|
||||
/// rewrite the STORE operator as multiple STOREs.
|
||||
void RuleSplitStore::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_STORE);
|
||||
}
|
||||
|
||||
int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types);
|
||||
if (outType == (Datatype *)0)
|
||||
return 0;
|
||||
type_metatype metain = outType->getMetatype();
|
||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||
return 0;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitStore(op, outType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \class RuleSubNormal
|
||||
/// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT
|
||||
///
|
||||
@ -8779,138 +8702,6 @@ int4 RuleSegment::applyOp(PcodeOp *op,Funcdata &data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarAnd
|
||||
/// \brief Perform SubVariableFlow analysis triggered by INT_AND
|
||||
void RuleSubvarAnd::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_AND);
|
||||
}
|
||||
|
||||
int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
// if (vn->getSize() != 1) return 0; // Only for bitsize variables
|
||||
if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0;
|
||||
if ((outvn->getConsume() & 1)==0) return 0;
|
||||
uintb cmask;
|
||||
if (outvn->getConsume() == (uintb)1)
|
||||
cmask = (uintb)1;
|
||||
else {
|
||||
cmask = calc_mask(vn->getSize());
|
||||
cmask >>=8;
|
||||
while(cmask != 0) {
|
||||
if (cmask == outvn->getConsume()) break;
|
||||
cmask >>=8;
|
||||
}
|
||||
}
|
||||
if (cmask == 0) return 0;
|
||||
// if (vn->getConsume() == 0) return 0;
|
||||
// if ((vn->getConsume() & 0xff)==0xff) return 0;
|
||||
// if (op->getIn(1)->getOffset() != (uintb)1) return 0;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
SubvariableFlow subflow(&data,vn,cmask,false,false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarSubpiece
|
||||
/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE
|
||||
void RuleSubvarSubpiece::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_SUBPIECE);
|
||||
}
|
||||
|
||||
int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 flowsize = outvn->getSize();
|
||||
uintb mask = calc_mask( flowsize );
|
||||
mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
bool aggressive = outvn->isPtrFlow();
|
||||
if (!aggressive) {
|
||||
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
}
|
||||
bool big = false;
|
||||
if (flowsize >= 8 && vn->isInput()) {
|
||||
// Vector register inputs getting truncated to what actually gets used
|
||||
// happens occasionally. We let SubvariableFlow deal with this special case
|
||||
// to avoid overlapping inputs
|
||||
// TODO: ActionLaneDivide should be handling this
|
||||
if (vn->loneDescend() == op)
|
||||
big = true;
|
||||
}
|
||||
SubvariableFlow subflow(&data,vn,mask,aggressive,false,big);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSplitFlow
|
||||
/// \brief Try to detect and split artificially joined Varnodes
|
||||
///
|
||||
/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL
|
||||
/// Then: check if the input to SUBPIECE can be viewed as two independent pieces
|
||||
/// If so: split the pieces into independent data-flows
|
||||
void RuleSplitFlow::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_SUBPIECE);
|
||||
}
|
||||
|
||||
int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
int4 loSize = (int4)op->getIn(1)->getOffset();
|
||||
if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part
|
||||
return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
if (!vn->isWritten())
|
||||
return 0;
|
||||
if (vn->isPrecisLo() || vn->isPrecisHi())
|
||||
return 0;
|
||||
if (op->getOut()->getSize() + loSize != vn->getSize())
|
||||
return 0; // Make sure SUBPIECE is taking most significant part
|
||||
PcodeOp *concatOp = (PcodeOp *)0;
|
||||
PcodeOp *multiOp = vn->getDef();
|
||||
while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT
|
||||
Varnode *tmpvn = multiOp->getIn(0);
|
||||
if (!tmpvn->isWritten()) return 0;
|
||||
multiOp = tmpvn->getDef();
|
||||
}
|
||||
if (multiOp->code() == CPUI_PIECE) {
|
||||
if (vn->getDef() != multiOp)
|
||||
concatOp = multiOp;
|
||||
}
|
||||
else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL
|
||||
for(int4 i=0;i<multiOp->numInput();++i) {
|
||||
Varnode *invn = multiOp->getIn(i);
|
||||
if (!invn->isWritten()) continue;
|
||||
PcodeOp *tmpOp = invn->getDef();
|
||||
if (tmpOp->code() == CPUI_PIECE) {
|
||||
concatOp = tmpOp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (concatOp == (PcodeOp *)0) // Didn't find the concatenate
|
||||
return 0;
|
||||
if (concatOp->getIn(1)->getSize() != loSize)
|
||||
return 0;
|
||||
SplitFlow splitFlow(&data,vn,loSize);
|
||||
if (!splitFlow.doTrace()) return 0;
|
||||
splitFlow.apply();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RulePtrFlow
|
||||
/// \brief Mark Varnode and PcodeOp objects that are carrying or operating on pointers
|
||||
///
|
||||
@ -9111,178 +8902,6 @@ int4 RulePtrFlow::applyOp(PcodeOp *op,Funcdata &data)
|
||||
return madeChange;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarCompZero
|
||||
/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit
|
||||
///
|
||||
/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant,
|
||||
/// check that input has only 1 bit that can possibly be non-zero
|
||||
/// and that the constant is testing this. This then triggers
|
||||
/// the full SubvariableFlow analysis.
|
||||
void RuleSubvarCompZero::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_NOTEQUAL);
|
||||
oplist.push_back(CPUI_INT_EQUAL);
|
||||
}
|
||||
|
||||
int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
uintb mask = vn->getNZMask();
|
||||
int4 bitnum = leastsigbit_set(mask);
|
||||
if (bitnum == -1) return 0;
|
||||
if ((mask >> bitnum) != 1) return 0; // Check if only one bit active
|
||||
|
||||
// Check if the active bit is getting tested
|
||||
if ((op->getIn(1)->getOffset()!=mask)&&
|
||||
(op->getIn(1)->getOffset()!=0))
|
||||
return 0;
|
||||
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
// We do a basic check that the stream from which it looks like
|
||||
// the bit is getting pulled is not fully consumed
|
||||
if (vn->isWritten()) {
|
||||
PcodeOp *andop = vn->getDef();
|
||||
if (andop->numInput()==0) return 0;
|
||||
Varnode *vn0 = andop->getIn(0);
|
||||
switch(andop->code()) {
|
||||
case CPUI_INT_AND:
|
||||
case CPUI_INT_OR:
|
||||
case CPUI_INT_RIGHT:
|
||||
{
|
||||
if (vn0->isConstant()) return 0;
|
||||
uintb mask0 = vn0->getConsume() & vn0->getNZMask();
|
||||
uintb wholemask = calc_mask(vn0->getSize()) & mask0;
|
||||
// We really need a popcnt here
|
||||
// We want: if the number of bits that are both consumed
|
||||
// and not known to be zero are "big" then don't continue
|
||||
// because it doesn't look like a few bits getting manipulated
|
||||
// within a status register
|
||||
if ((wholemask & 0xff)==0xff) return 0;
|
||||
if ((wholemask & 0xff00)==0xff00) return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||
if (!subflow.doTrace()) {
|
||||
return 0;
|
||||
}
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarShift
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT
|
||||
///
|
||||
/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero
|
||||
/// and it is getting shifted into the least significant bit position,
|
||||
/// trigger the full SubvariableFlow analysis.
|
||||
void RuleSubvarShift::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_RIGHT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getIn(0);
|
||||
if (vn->getSize() != 1) return 0;
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
int4 sa = (int4)op->getIn(1)->getOffset();
|
||||
uintb mask = vn->getNZMask();
|
||||
if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit
|
||||
mask = (mask >> sa) << sa;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarZext
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT
|
||||
void RuleSubvarZext::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_ZEXT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getOut();
|
||||
Varnode *invn = op->getIn(0);
|
||||
uintb mask = calc_mask(invn->getSize());
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleSubvarSext
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT
|
||||
void RuleSubvarSext::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_SEXT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getOut();
|
||||
Varnode *invn = op->getIn(0);
|
||||
uintb mask = calc_mask(invn->getSize());
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarSext::reset(Funcdata &data)
|
||||
|
||||
{
|
||||
isaggressive = data.getArch()->aggressive_ext_trim;
|
||||
}
|
||||
|
||||
/// \class RuleSubfloatConvert
|
||||
/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT
|
||||
void RuleSubfloatConvert::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT);
|
||||
}
|
||||
|
||||
int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *invn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 insize = invn->getSize();
|
||||
int4 outsize = outvn->getSize();
|
||||
if (outsize > insize) {
|
||||
SubfloatFlow subflow(&data,outvn,insize);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.apply();
|
||||
}
|
||||
else {
|
||||
SubfloatFlow subflow(&data,invn,outsize);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.apply();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \class RuleNegateNegate
|
||||
/// \brief Simplify INT_NEGATE chains: `~~V => V`
|
||||
void RuleNegateNegate::getOpList(vector<uint4> &oplist) const
|
||||
@ -10926,4 +10545,145 @@ int4 RuleOrCompare::applyOp(PcodeOp *op,Funcdata &data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \brief Check that all uses of given Varnode are of the form `(V & C) == D`
|
||||
///
|
||||
/// \param vn is the given Varnode
|
||||
/// \return \b true if all uses match the INT_AND form
|
||||
bool RuleExpandLoad::checkAndComparison(Varnode *vn)
|
||||
|
||||
{
|
||||
list<PcodeOp *>::const_iterator iter;
|
||||
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
||||
PcodeOp *op = *iter;
|
||||
if (op->code() != CPUI_INT_AND) return false;
|
||||
if (!op->getIn(1)->isConstant()) return false;
|
||||
PcodeOp *compOp = op->getOut()->loneDescend();
|
||||
if (compOp == (PcodeOp *)0) return false;
|
||||
OpCode opc = compOp->code();
|
||||
if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL) return false;
|
||||
if (!compOp->getIn(1)->isConstant()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Expand the constants in the previously scanned forms: `(V & C) == D`
|
||||
///
|
||||
/// The method checkAndComparison() must have returned \b true for \b oldVn. Change the size and
|
||||
/// data-type of all the constants in these expressions.
|
||||
/// \param data is the function containing the expressions
|
||||
/// \param oldVn is incoming variable in all the expressions
|
||||
/// \param newVn is the new bigger variable
|
||||
/// \param dt is the data-type to associate with the constants
|
||||
/// \param offset is the number of least significant (zero) bytes to add to each constant
|
||||
void RuleExpandLoad::modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset)
|
||||
|
||||
{
|
||||
offset = 8*offset; // Convert to shift amount
|
||||
list<PcodeOp *>::const_iterator iter = oldVn->beginDescend();
|
||||
while(iter != oldVn->endDescend()) {
|
||||
PcodeOp *andOp = *iter;
|
||||
++iter; // Advance iterator before modifying op
|
||||
PcodeOp *compOp = andOp->getOut()->loneDescend();
|
||||
uintb newOff = andOp->getIn(1)->getOffset();
|
||||
newOff <<= offset;
|
||||
Varnode *vn = data.newConstant(dt->getSize(), newOff);
|
||||
vn->updateType(dt, false, false);
|
||||
data.opSetInput(andOp, newVn, 0);
|
||||
data.opSetInput(andOp, vn, 1);
|
||||
newOff = compOp->getIn(1)->getOffset();
|
||||
newOff <<= offset;
|
||||
vn = data.newConstant(dt->getSize(), newOff);
|
||||
vn->updateType(dt, false, false);
|
||||
data.opSetInput(compOp,vn,1);
|
||||
}
|
||||
}
|
||||
|
||||
/// \class RuleExpandLoad
|
||||
/// \brief Convert LOAD size to match pointer data-type
|
||||
///
|
||||
/// Change LOAD output to a larger size if used for INT_AND comparisons or if a truncation is natural
|
||||
void RuleExpandLoad::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_LOAD);
|
||||
}
|
||||
|
||||
int4 RuleExpandLoad::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *outVn = op->getOut();
|
||||
int4 outSize = outVn->getSize();
|
||||
Varnode *rootPtr = op->getIn(1);
|
||||
PcodeOp *addOp = (PcodeOp *)0;
|
||||
int4 offset = 0;
|
||||
Datatype *elType;
|
||||
if (rootPtr->isWritten()) {
|
||||
PcodeOp *defOp = rootPtr->getDef();
|
||||
if (defOp->code() == CPUI_INT_ADD && defOp->getIn(1)->isConstant()) {
|
||||
addOp = defOp;
|
||||
rootPtr = defOp->getIn(0);
|
||||
offset = defOp->getIn(1)->getOffset();
|
||||
if (offset > 16) return 0; // INT_ADD offset must be small
|
||||
if (defOp->getOut()->loneDescend() == (PcodeOp *)0) return 0; // INT_ADD must be used only once
|
||||
elType = rootPtr->getTypeReadFacing(defOp);
|
||||
}
|
||||
else
|
||||
elType = rootPtr->getTypeReadFacing(op);
|
||||
}
|
||||
else
|
||||
elType = rootPtr->getTypeReadFacing(op);
|
||||
if (elType->getMetatype() != TYPE_PTR) return 0;
|
||||
elType = ((TypePointer *)elType)->getPtrTo();
|
||||
if (elType->getSize() <= outSize) return 0; // Pointer data-type must be bigger than LOAD
|
||||
if (elType->getSize() < outSize + offset) return 0;
|
||||
|
||||
type_metatype meta = elType->getMetatype();
|
||||
if (meta == TYPE_UNKNOWN) return 0;
|
||||
bool addForm = checkAndComparison(outVn);
|
||||
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||
int4 lsbCut = 0;
|
||||
if (addForm) {
|
||||
if (spc->isBigEndian()) {
|
||||
lsbCut = elType->getSize() - outSize - offset;
|
||||
}
|
||||
else
|
||||
lsbCut = offset;
|
||||
}
|
||||
else {
|
||||
// Check for natural integer truncation
|
||||
if (meta != TYPE_INT && meta != TYPE_UINT) return 0;
|
||||
type_metatype outMeta = outVn->getTypeDefFacing()->getMetatype();
|
||||
if (outMeta != TYPE_INT && outMeta != TYPE_UINT && outMeta != TYPE_UNKNOWN && outMeta != TYPE_BOOL)
|
||||
return false;
|
||||
// Check that LOAD is grabbing least significant bytes
|
||||
if (spc->isBigEndian()) {
|
||||
if (outSize + offset != elType->getSize()) return 0;
|
||||
}
|
||||
else {
|
||||
if (offset != 0) return 0;
|
||||
}
|
||||
}
|
||||
// Modify the LOAD
|
||||
Varnode *newOut = data.newUnique(elType->getSize(), elType);
|
||||
data.opSetOutput(op, newOut);
|
||||
if (addOp != (PcodeOp *)0) {
|
||||
data.opSetInput(op, rootPtr, 1);
|
||||
data.opDestroy(addOp);
|
||||
}
|
||||
if (addForm) {
|
||||
if (meta != TYPE_INT && meta != TYPE_UINT)
|
||||
elType = data.getArch()->types->getBase(elType->getSize(), TYPE_UINT);
|
||||
modifyAndComparison(data, outVn, newOut, elType, lsbCut);
|
||||
}
|
||||
else {
|
||||
PcodeOp *subOp = data.newOp(2,op->getAddr());
|
||||
data.opSetOpcode(subOp, CPUI_SUBPIECE);
|
||||
data.opSetInput(subOp,newOut,0); // Truncate new bigger LOAD output
|
||||
data.opSetInput(subOp, data.newConstant(4, 0), 1);
|
||||
data.opSetOutput(subOp, outVn); // Original LOAD output is now defined by SUBPIECE
|
||||
data.opInsertAfter(subOp, op);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
@ -1181,39 +1181,6 @@ public:
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSplitCopy : public Rule {
|
||||
public:
|
||||
RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitCopy(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSplitLoad : public Rule {
|
||||
public:
|
||||
RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitLoad(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSplitStore : public Rule {
|
||||
public:
|
||||
RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitStore(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubNormal : public Rule {
|
||||
public:
|
||||
RuleSubNormal(const string &g) : Rule( g, 0, "subnormal") {} ///< Constructor
|
||||
@ -1398,39 +1365,6 @@ public:
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarAnd : public Rule {
|
||||
public:
|
||||
RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarAnd(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarSubpiece : public Rule {
|
||||
public:
|
||||
RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarSubpiece(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSplitFlow : public Rule {
|
||||
public:
|
||||
RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitFlow(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RulePtrFlow : public Rule {
|
||||
Architecture *glb; ///< The address space manager
|
||||
bool hasTruncations; ///< \b true if this architecture needs truncated pointers
|
||||
@ -1448,63 +1382,6 @@ public:
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarCompZero : public Rule {
|
||||
public:
|
||||
RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarCompZero(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarShift : public Rule {
|
||||
public:
|
||||
RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarShift(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarZext : public Rule {
|
||||
public:
|
||||
RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarZext(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubvarSext : public Rule {
|
||||
int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed
|
||||
public:
|
||||
RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarSext(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
virtual void reset(Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleSubfloatConvert : public Rule {
|
||||
public:
|
||||
RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubfloatConvert(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleNegateNegate : public Rule {
|
||||
public:
|
||||
RuleNegateNegate(const string &g) : Rule( g, 0, "negatenegate") {} ///< Constructor
|
||||
@ -1695,5 +1572,18 @@ public:
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
class RuleExpandLoad : public Rule {
|
||||
static bool checkAndComparison(Varnode *vn);
|
||||
static void modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset);
|
||||
public:
|
||||
RuleExpandLoad(const string &g) : Rule( g, 0, "expandload") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleExpandLoad(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
} // End namespace ghidra
|
||||
#endif
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "subflow.hh"
|
||||
#include "funcdata.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
@ -1506,6 +1507,204 @@ void SubvariableFlow::doReplacement(void)
|
||||
}
|
||||
}
|
||||
|
||||
void RuleSubvarAnd::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_AND);
|
||||
}
|
||||
|
||||
int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
// if (vn->getSize() != 1) return 0; // Only for bitsize variables
|
||||
if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0;
|
||||
if ((outvn->getConsume() & 1)==0) return 0;
|
||||
uintb cmask;
|
||||
if (outvn->getConsume() == (uintb)1)
|
||||
cmask = (uintb)1;
|
||||
else {
|
||||
cmask = calc_mask(vn->getSize());
|
||||
cmask >>=8;
|
||||
while(cmask != 0) {
|
||||
if (cmask == outvn->getConsume()) break;
|
||||
cmask >>=8;
|
||||
}
|
||||
}
|
||||
if (cmask == 0) return 0;
|
||||
// if (vn->getConsume() == 0) return 0;
|
||||
// if ((vn->getConsume() & 0xff)==0xff) return 0;
|
||||
// if (op->getIn(1)->getOffset() != (uintb)1) return 0;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
SubvariableFlow subflow(&data,vn,cmask,false,false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarSubpiece::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_SUBPIECE);
|
||||
}
|
||||
|
||||
int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 flowsize = outvn->getSize();
|
||||
uintb mask = calc_mask( flowsize );
|
||||
mask <<= 8*((int4)op->getIn(1)->getOffset());
|
||||
bool aggressive = outvn->isPtrFlow();
|
||||
if (!aggressive) {
|
||||
if ((vn->getConsume() & mask) != vn->getConsume()) return 0;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
}
|
||||
bool big = false;
|
||||
if (flowsize >= 8 && vn->isInput()) {
|
||||
// Vector register inputs getting truncated to what actually gets used
|
||||
// happens occasionally. We let SubvariableFlow deal with this special case
|
||||
// to avoid overlapping inputs
|
||||
// TODO: ActionLaneDivide should be handling this
|
||||
if (vn->loneDescend() == op)
|
||||
big = true;
|
||||
}
|
||||
SubvariableFlow subflow(&data,vn,mask,aggressive,false,big);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarCompZero::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_NOTEQUAL);
|
||||
oplist.push_back(CPUI_INT_EQUAL);
|
||||
}
|
||||
|
||||
int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
uintb mask = vn->getNZMask();
|
||||
int4 bitnum = leastsigbit_set(mask);
|
||||
if (bitnum == -1) return 0;
|
||||
if ((mask >> bitnum) != 1) return 0; // Check if only one bit active
|
||||
|
||||
// Check if the active bit is getting tested
|
||||
if ((op->getIn(1)->getOffset()!=mask)&&
|
||||
(op->getIn(1)->getOffset()!=0))
|
||||
return 0;
|
||||
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
// We do a basic check that the stream from which it looks like
|
||||
// the bit is getting pulled is not fully consumed
|
||||
if (vn->isWritten()) {
|
||||
PcodeOp *andop = vn->getDef();
|
||||
if (andop->numInput()==0) return 0;
|
||||
Varnode *vn0 = andop->getIn(0);
|
||||
switch(andop->code()) {
|
||||
case CPUI_INT_AND:
|
||||
case CPUI_INT_OR:
|
||||
case CPUI_INT_RIGHT:
|
||||
{
|
||||
if (vn0->isConstant()) return 0;
|
||||
uintb mask0 = vn0->getConsume() & vn0->getNZMask();
|
||||
uintb wholemask = calc_mask(vn0->getSize()) & mask0;
|
||||
// We really need a popcnt here
|
||||
// We want: if the number of bits that are both consumed
|
||||
// and not known to be zero are "big" then don't continue
|
||||
// because it doesn't look like a few bits getting manipulated
|
||||
// within a status register
|
||||
if ((wholemask & 0xff)==0xff) return 0;
|
||||
if ((wholemask & 0xff00)==0xff00) return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||
if (!subflow.doTrace()) {
|
||||
return 0;
|
||||
}
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarShift::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_RIGHT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getIn(0);
|
||||
if (vn->getSize() != 1) return 0;
|
||||
if (!op->getIn(1)->isConstant()) return 0;
|
||||
int4 sa = (int4)op->getIn(1)->getOffset();
|
||||
uintb mask = vn->getNZMask();
|
||||
if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit
|
||||
mask = (mask >> sa) << sa;
|
||||
if (op->getOut()->hasNoDescend()) return 0;
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,false,false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarZext::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_ZEXT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getOut();
|
||||
Varnode *invn = op->getIn(0);
|
||||
uintb mask = calc_mask(invn->getSize());
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarSext::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_INT_SEXT);
|
||||
}
|
||||
|
||||
int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *vn = op->getOut();
|
||||
Varnode *invn = op->getIn(0);
|
||||
uintb mask = calc_mask(invn->getSize());
|
||||
|
||||
SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.doReplacement();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void RuleSubvarSext::reset(Funcdata &data)
|
||||
|
||||
{
|
||||
isaggressive = data.getArch()->aggressive_ext_trim;
|
||||
}
|
||||
|
||||
/// \brief Find or build the placeholder objects for a Varnode that needs to be split
|
||||
///
|
||||
/// Mark the Varnode so it doesn't get revisited.
|
||||
@ -1797,6 +1996,57 @@ bool SplitFlow::doTrace(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuleSplitFlow::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_SUBPIECE);
|
||||
}
|
||||
|
||||
int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
int4 loSize = (int4)op->getIn(1)->getOffset();
|
||||
if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part
|
||||
return 0;
|
||||
Varnode *vn = op->getIn(0);
|
||||
if (!vn->isWritten())
|
||||
return 0;
|
||||
if (vn->isPrecisLo() || vn->isPrecisHi())
|
||||
return 0;
|
||||
if (op->getOut()->getSize() + loSize != vn->getSize())
|
||||
return 0; // Make sure SUBPIECE is taking most significant part
|
||||
PcodeOp *concatOp = (PcodeOp *)0;
|
||||
PcodeOp *multiOp = vn->getDef();
|
||||
while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT
|
||||
Varnode *tmpvn = multiOp->getIn(0);
|
||||
if (!tmpvn->isWritten()) return 0;
|
||||
multiOp = tmpvn->getDef();
|
||||
}
|
||||
if (multiOp->code() == CPUI_PIECE) {
|
||||
if (vn->getDef() != multiOp)
|
||||
concatOp = multiOp;
|
||||
}
|
||||
else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL
|
||||
for(int4 i=0;i<multiOp->numInput();++i) {
|
||||
Varnode *invn = multiOp->getIn(i);
|
||||
if (!invn->isWritten()) continue;
|
||||
PcodeOp *tmpOp = invn->getDef();
|
||||
if (tmpOp->code() == CPUI_PIECE) {
|
||||
concatOp = tmpOp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (concatOp == (PcodeOp *)0) // Didn't find the concatenate
|
||||
return 0;
|
||||
if (concatOp->getIn(1)->getSize() != loSize)
|
||||
return 0;
|
||||
SplitFlow splitFlow(&data,vn,loSize);
|
||||
if (!splitFlow.doTrace()) return 0;
|
||||
splitFlow.apply();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// If \b pointer Varnode is written by a COPY, INT_ADD, PTRSUB, or PTRADD from another pointer to a
|
||||
/// - structure
|
||||
/// - array OR
|
||||
@ -2628,8 +2878,7 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto
|
||||
if (ptrType->isPointerRel()) {
|
||||
TypePointerRel *ptrRel = (TypePointerRel *)ptrType;
|
||||
resType = ptrRel->getParent();
|
||||
baseOffset = ptrRel->getPointerOffset();
|
||||
baseOffset = AddrSpace::addressToByteInt(baseOffset, ptrRel->getWordSize());
|
||||
baseOffset = ptrRel->getByteOffset();
|
||||
}
|
||||
else {
|
||||
resType = ((TypePointer *)ptrType)->getPtrTo();
|
||||
@ -2649,6 +2898,71 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto
|
||||
return (Datatype *)0;
|
||||
}
|
||||
|
||||
void RuleSplitCopy::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_COPY);
|
||||
}
|
||||
|
||||
int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *inType = op->getIn(0)->getTypeReadFacing(op);
|
||||
Datatype *outType = op->getOut()->getTypeDefFacing();
|
||||
type_metatype metain = inType->getMetatype();
|
||||
type_metatype metaout = outType->getMetatype();
|
||||
if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT &&
|
||||
metain != TYPE_ARRAY && metaout != TYPE_ARRAY &&
|
||||
metain != TYPE_STRUCT && metaout != TYPE_STRUCT)
|
||||
return false;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitCopy(op, inType, outType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RuleSplitLoad::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_LOAD);
|
||||
}
|
||||
|
||||
int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types);
|
||||
if (inType == (Datatype *)0)
|
||||
return 0;
|
||||
type_metatype metain = inType->getMetatype();
|
||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||
return 0;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitLoad(op, inType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RuleSplitStore::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_STORE);
|
||||
}
|
||||
|
||||
int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types);
|
||||
if (outType == (Datatype *)0)
|
||||
return 0;
|
||||
type_metatype metain = outType->getMetatype();
|
||||
if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT)
|
||||
return 0;
|
||||
SplitDatatype splitter(data);
|
||||
if (splitter.splitStore(op, outType))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// This method distinguishes between a floating-point variable with \e full precision, where all the
|
||||
/// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with
|
||||
/// smaller storage. Within the data-flow above the given Varnode, we search for the maximum
|
||||
@ -3062,6 +3376,32 @@ bool SubfloatFlow::doTrace(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuleSubfloatConvert::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT);
|
||||
}
|
||||
|
||||
int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
Varnode *invn = op->getIn(0);
|
||||
Varnode *outvn = op->getOut();
|
||||
int4 insize = invn->getSize();
|
||||
int4 outsize = outvn->getSize();
|
||||
if (outsize > insize) {
|
||||
SubfloatFlow subflow(&data,outvn,insize);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.apply();
|
||||
}
|
||||
else {
|
||||
SubfloatFlow subflow(&data,invn,outsize);
|
||||
if (!subflow.doTrace()) return 0;
|
||||
subflow.apply();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// \brief Find or build the placeholder objects for a Varnode that needs to be split into lanes
|
||||
///
|
||||
/// The Varnode is split based on the given subset of the lane description.
|
||||
|
@ -18,7 +18,8 @@
|
||||
#ifndef __SUBFLOW_HH__
|
||||
#define __SUBFLOW_HH__
|
||||
|
||||
#include "funcdata.hh"
|
||||
#include "ruleaction.hh"
|
||||
#include "transform.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
@ -128,6 +129,89 @@ public:
|
||||
void doReplacement(void); ///< Perform the discovered transform, making logical values explicit
|
||||
};
|
||||
|
||||
/// \brief Perform SubVariableFlow analysis triggered by INT_AND
|
||||
class RuleSubvarAnd : public Rule {
|
||||
public:
|
||||
RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarAnd(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE
|
||||
class RuleSubvarSubpiece : public Rule {
|
||||
public:
|
||||
RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarSubpiece(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit
|
||||
///
|
||||
/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant,
|
||||
/// check that input has only 1 bit that can possibly be non-zero
|
||||
/// and that the constant is testing this. This then triggers
|
||||
/// the full SubvariableFlow analysis.
|
||||
class RuleSubvarCompZero : public Rule {
|
||||
public:
|
||||
RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarCompZero(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT
|
||||
///
|
||||
/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero
|
||||
/// and it is getting shifted into the least significant bit position,
|
||||
/// trigger the full SubvariableFlow analysis.
|
||||
class RuleSubvarShift : public Rule {
|
||||
public:
|
||||
RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarShift(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT
|
||||
class RuleSubvarZext : public Rule {
|
||||
public:
|
||||
RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarZext(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT
|
||||
class RuleSubvarSext : public Rule {
|
||||
int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed
|
||||
public:
|
||||
RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubvarSext(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
virtual void reset(Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Class for splitting up Varnodes that hold 2 logical variables
|
||||
///
|
||||
/// Starting from a \e root Varnode provided to the constructor, \b this class looks for data-flow
|
||||
@ -147,6 +231,22 @@ public:
|
||||
bool doTrace(void); ///< Trace split through data-flow, constructing transform
|
||||
};
|
||||
|
||||
/// \brief Try to detect and split artificially joined Varnodes
|
||||
///
|
||||
/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL
|
||||
/// Then: check if the input to SUBPIECE can be viewed as two independent pieces
|
||||
/// If so: split the pieces into independent data-flows
|
||||
class RuleSplitFlow : public Rule {
|
||||
public:
|
||||
RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitFlow(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Split a p-code COPY, LOAD, or STORE op based on underlying composite data-type
|
||||
///
|
||||
/// During the cleanup phase, if a COPY, LOAD, or STORE occurs on a partial structure or array
|
||||
@ -208,6 +308,51 @@ public:
|
||||
static Datatype *getValueDatatype(PcodeOp *loadStore,int4 size,TypeFactory *tlst);
|
||||
};
|
||||
|
||||
/// \brief Split COPY ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is copied at once,
|
||||
/// rewrite the COPY operator as multiple COPYs.
|
||||
class RuleSplitCopy : public Rule {
|
||||
public:
|
||||
RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitCopy(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Split LOAD ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is loaded at once,
|
||||
/// rewrite the LOAD operator as multiple LOADs.
|
||||
class RuleSplitLoad : public Rule {
|
||||
public:
|
||||
RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitLoad(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Split STORE ops based on TypePartialStruct
|
||||
///
|
||||
/// If more than one logical component of a structure or array is stored at once,
|
||||
/// rewrite the STORE operator as multiple STOREs.
|
||||
class RuleSplitStore : public Rule {
|
||||
public:
|
||||
RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSplitStore(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Class for tracing changes of precision in floating point variables
|
||||
///
|
||||
/// It follows the flow of a logical lower precision value stored in higher precision locations
|
||||
@ -242,6 +387,18 @@ public:
|
||||
bool doTrace(void); ///< Trace logical value as far as possible
|
||||
};
|
||||
|
||||
/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT
|
||||
class RuleSubfloatConvert : public Rule {
|
||||
public:
|
||||
RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleSubfloatConvert(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Class for splitting data-flow on \e laned registers
|
||||
///
|
||||
/// From a root Varnode and a description of its \e lanes, trace data-flow as far as
|
||||
|
@ -113,6 +113,24 @@ void FunctionTestCollection::clear(void)
|
||||
console->reset();
|
||||
}
|
||||
|
||||
/// Remove any carriage return character as well.
|
||||
/// \param ref is the string to strip
|
||||
/// \return the stripped string
|
||||
string FunctionTestCollection::stripNewlines(const string &ref)
|
||||
|
||||
{
|
||||
string res;
|
||||
|
||||
for(int4 i=0;i<ref.size();++i) {
|
||||
char c = ref[i];
|
||||
if (c == '\r') continue; // Remove carriage return
|
||||
if (c == '\n')
|
||||
c = ' '; // Convert newline to space
|
||||
res.push_back(c);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// \param el is the root \<script> tag
|
||||
void FunctionTestCollection::restoreXmlCommands(const Element *el)
|
||||
|
||||
@ -122,7 +140,7 @@ void FunctionTestCollection::restoreXmlCommands(const Element *el)
|
||||
|
||||
for(iter=list.begin();iter!=list.end();++iter) {
|
||||
const Element *subel = *iter;
|
||||
commands.push_back(subel->getContent());
|
||||
commands.push_back(stripNewlines(subel->getContent()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -77,6 +77,7 @@ class FunctionTestCollection {
|
||||
mutable int4 numTestsApplied; ///< Count of tests that were executed
|
||||
mutable int4 numTestsSucceeded; ///< Count of tests that passed
|
||||
void clear(void); ///< Clear any previous architecture and function
|
||||
static string stripNewlines(const string &ref); ///< Convert any \e newline character to a \e space
|
||||
void restoreXmlCommands(const Element *el); ///< Reconstruct commands from an XML tag
|
||||
void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \<binaryimage> tag
|
||||
void startTests(void) const; ///< Initialize each FunctionTestProperty
|
||||
|
@ -20,16 +20,17 @@ namespace ghidra {
|
||||
|
||||
/// The base propagation ordering associated with each meta-type.
|
||||
/// The array elements correspond to the ordering of #type_metatype.
|
||||
sub_metatype Datatype::base2sub[15] = {
|
||||
SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UNION, SUB_STRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE,
|
||||
SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID
|
||||
sub_metatype Datatype::base2sub[18] = {
|
||||
SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UINT_ENUM, SUB_UNION, SUB_STRUCT, SUB_INT_ENUM, SUB_UINT_ENUM,
|
||||
SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN,
|
||||
SUB_SPACEBASE, SUB_VOID
|
||||
};
|
||||
|
||||
AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47);
|
||||
AttributeId ATTRIB_ARRAYSIZE = AttributeId("arraysize",48);
|
||||
AttributeId ATTRIB_CHAR = AttributeId("char",49);
|
||||
AttributeId ATTRIB_CORE = AttributeId("core",50);
|
||||
AttributeId ATTRIB_ENUM = AttributeId("enum",51);
|
||||
//AttributeId ATTRIB_ENUM = AttributeId("enum",51); // deprecated
|
||||
AttributeId ATTRIB_INCOMPLETE = AttributeId("incomplete",52);
|
||||
//AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53); // deprecated
|
||||
//AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54); // deprecated
|
||||
@ -250,12 +251,21 @@ void metatype2string(type_metatype metatype,string &res)
|
||||
case TYPE_ARRAY:
|
||||
res = "array";
|
||||
break;
|
||||
case TYPE_PARTIALENUM:
|
||||
res = "partenum";
|
||||
break;
|
||||
case TYPE_PARTIALSTRUCT:
|
||||
res = "partstruct";
|
||||
break;
|
||||
case TYPE_PARTIALUNION:
|
||||
res = "partunion";
|
||||
break;
|
||||
case TYPE_ENUM_INT:
|
||||
res = "enum_int";
|
||||
break;
|
||||
case TYPE_ENUM_UINT:
|
||||
res = "enum_uint";
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
res = "struct";
|
||||
break;
|
||||
@ -309,6 +319,12 @@ type_metatype string2metatype(const string &metastring)
|
||||
if (metastring=="array")
|
||||
return TYPE_ARRAY;
|
||||
break;
|
||||
case 'e':
|
||||
if (metastring=="enum_int")
|
||||
return TYPE_ENUM_INT;
|
||||
else if (metastring == "enum_uint")
|
||||
return TYPE_ENUM_UINT;
|
||||
break;
|
||||
case 's':
|
||||
if (metastring=="struct")
|
||||
return TYPE_STRUCT;
|
||||
@ -1083,6 +1099,12 @@ TypePointer *TypePointer::downChain(int8 &off,TypePointer *&par,int8 &parOff,boo
|
||||
}
|
||||
}
|
||||
|
||||
if (ptrto->isEnumType()) {
|
||||
// Go "into" the enumeration
|
||||
Datatype *tmp = typegrp.getBase(1, TYPE_UINT);
|
||||
off = 0;
|
||||
return typegrp.getTypePointer(size,tmp,wordsize);
|
||||
}
|
||||
type_metatype meta = ptrto->getMetatype();
|
||||
bool isArray = (meta == TYPE_ARRAY);
|
||||
if (isArray || meta == TYPE_STRUCT) {
|
||||
@ -1325,81 +1347,22 @@ TypeEnum::TypeEnum(const TypeEnum &op) : TypeBase(op)
|
||||
|
||||
{
|
||||
namemap = op.namemap;
|
||||
masklist = op.masklist;
|
||||
flags |= (op.flags&poweroftwo)|enumtype;
|
||||
}
|
||||
|
||||
/// Set the map. Calculate the independent bit-fields within the named values of the enumeration
|
||||
/// Two bits are in the same bit-field if there is a name in the map whose value
|
||||
/// has those two bits set. Bit-fields must be a contiguous range of bits.
|
||||
void TypeEnum::setNameMap(const map<uintb,string> &nmap)
|
||||
/// \param val is the given value to test
|
||||
/// \return \b true if \b this enumeration has a name with the value
|
||||
bool TypeEnum::hasNamedValue(uintb val) const
|
||||
|
||||
{
|
||||
map<uintb,string>::const_iterator iter;
|
||||
uintb curmask,lastmask;
|
||||
int4 maxbit;
|
||||
int4 curmaxbit;
|
||||
bool fieldisempty;
|
||||
|
||||
namemap = nmap;
|
||||
masklist.clear();
|
||||
|
||||
flags &= ~((uint4)poweroftwo);
|
||||
|
||||
maxbit = 8 * size - 1;
|
||||
|
||||
curmaxbit = 0;
|
||||
while(curmaxbit <= maxbit) {
|
||||
curmask = 1;
|
||||
curmask <<= curmaxbit;
|
||||
lastmask = 0;
|
||||
fieldisempty = true;
|
||||
while(curmask != lastmask) { // Repeat until there is no change in the current mask
|
||||
lastmask = curmask; // Note changes from last time through
|
||||
|
||||
for(iter=namemap.begin();iter!=namemap.end();++iter) { // For every named enumeration value
|
||||
uintb val = (*iter).first;
|
||||
if ((val & curmask) != 0) { // If the value shares ANY bits in common with the current mask
|
||||
curmask |= val; // Absorb ALL defined bits of the value into the current mask
|
||||
fieldisempty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in any holes in the mask (bit field must consist of contiguous bits
|
||||
int4 lsb = leastsigbit_set(curmask);
|
||||
int4 msb = mostsigbit_set(curmask);
|
||||
if (msb > curmaxbit)
|
||||
curmaxbit = msb;
|
||||
|
||||
uintb mask1 = 1;
|
||||
mask1 = (mask1 << lsb) - 1; // every bit below lsb is set to 1
|
||||
uintb mask2 = 1;
|
||||
mask2 <<= msb;
|
||||
mask2 <<= 1;
|
||||
mask2 -= 1; // every bit below or equal to msb is set to 1
|
||||
curmask = mask1 ^ mask2;
|
||||
}
|
||||
if (fieldisempty) { // If no value hits this bit
|
||||
if (!masklist.empty())
|
||||
masklist.back() |= curmask; // Include the bit with the previous mask
|
||||
else
|
||||
masklist.push_back(curmask);
|
||||
}
|
||||
else
|
||||
masklist.push_back(curmask);
|
||||
curmaxbit += 1;
|
||||
}
|
||||
if (masklist.size() > 1)
|
||||
flags |= poweroftwo;
|
||||
return (namemap.find(val) != namemap.end());
|
||||
}
|
||||
|
||||
/// Given a specific value of the enumeration, calculate the named representation of that value.
|
||||
/// The representation is returned as a list of names that must logically ORed and possibly complemented.
|
||||
/// If no representation is possible, no names will be returned.
|
||||
/// \param val is the value to find the representation for
|
||||
/// \param valnames will hold the returned list of names
|
||||
/// \return true if the representation needs to be complemented
|
||||
bool TypeEnum::getMatches(uintb val,vector<string> &valnames) const
|
||||
/// \param rep will contain the individual names in the representation and other transforms
|
||||
void TypeEnum::getMatches(uintb val,Representation &rep) const
|
||||
|
||||
{
|
||||
map<uintb,string>::const_iterator iter;
|
||||
@ -1407,33 +1370,47 @@ bool TypeEnum::getMatches(uintb val,vector<string> &valnames) const
|
||||
|
||||
for(count=0;count<2;++count) {
|
||||
bool allmatch = true;
|
||||
if (val == 0) { // Zero handled specially, it crosses all masks
|
||||
if (val == 0) { // Zero handled specially
|
||||
iter = namemap.find(val);
|
||||
if (iter != namemap.end())
|
||||
valnames.push_back( (*iter).second );
|
||||
rep.matchname.push_back( (*iter).second );
|
||||
else
|
||||
allmatch = false;
|
||||
}
|
||||
else {
|
||||
for(int4 i=0;i<masklist.size();++i) {
|
||||
uintb maskedval = val & masklist[i];
|
||||
if (maskedval == 0) // No component of -val- in this mask
|
||||
continue; // print nothing
|
||||
iter = namemap.find(maskedval);
|
||||
if (iter != namemap.end())
|
||||
valnames.push_back( (*iter).second ); // Found name for this component
|
||||
else { // If no name for this component
|
||||
allmatch = false; // Give up on representation
|
||||
break; // Stop searching for other components
|
||||
uintb bitsleft = val;
|
||||
uintb target = val;
|
||||
while(target != 0) {
|
||||
// Find named value that matches the largest number of most significant bits in bitsleft
|
||||
iter = namemap.upper_bound(target);
|
||||
if (iter == namemap.begin()) break; // All named values are greater than target
|
||||
--iter; // Biggest named value less than or equal to target
|
||||
uintb curval = (*iter).first;
|
||||
uintb diff = coveringmask(bitsleft ^ curval);
|
||||
if (diff >= bitsleft) break; // Could not match most significant bit of bitsleft
|
||||
if ((curval & diff) == 0) {
|
||||
// Found a named value that matches at least most significant bit of bitsleft
|
||||
rep.matchname.push_back( (*iter).second ); // Accept the name
|
||||
bitsleft ^= curval; // Remove the bits from bitsleft
|
||||
target = bitsleft; // Continue searching for named value that match the new bitsleft
|
||||
}
|
||||
else {
|
||||
// Not all the (one) bits of curval match into bitsleft, but we can restrict a further search.
|
||||
// Bits above diff in curval are the maximum we can hope to match with one named value.
|
||||
// Zero out bits below this and prepare to search at or below this value
|
||||
target = curval & ~diff;
|
||||
}
|
||||
}
|
||||
allmatch = (bitsleft == 0);
|
||||
}
|
||||
if (allmatch) { // If we have a complete representation
|
||||
rep.complement = (count==1); // Set whether we represented original value or complement
|
||||
return;
|
||||
}
|
||||
if (allmatch) // If we have a complete representation
|
||||
return (count==1); // Return whether we represented original value or complement
|
||||
val = val ^ calc_mask(size); // Switch value we are trying to represent (to complement)
|
||||
valnames.clear(); // Clear out old attempt
|
||||
rep.matchname.clear(); // Clear out old attempt
|
||||
}
|
||||
return false; // If we reach here, no representation was possible, -valnames- is empty
|
||||
// If we reach here, no representation was possible, -matchname- is empty
|
||||
}
|
||||
|
||||
int4 TypeEnum::compare(const Datatype &op,int4 level) const
|
||||
@ -1475,8 +1452,7 @@ void TypeEnum::encode(Encoder &encoder) const
|
||||
return;
|
||||
}
|
||||
encoder.openElement(ELEM_TYPE);
|
||||
encodeBasic(metatype,-1,encoder);
|
||||
encoder.writeString(ATTRIB_ENUM, "true");
|
||||
encodeBasic((metatype == TYPE_INT) ? TYPE_ENUM_INT : TYPE_ENUM_UINT,-1,encoder);
|
||||
map<uintb,string>::const_iterator iter;
|
||||
for(iter=namemap.begin();iter!=namemap.end();++iter) {
|
||||
encoder.openElement(ELEM_VAL);
|
||||
@ -1496,7 +1472,7 @@ string TypeEnum::decode(Decoder &decoder,TypeFactory &typegrp)
|
||||
{
|
||||
// uint4 elemId = decoder.openElement();
|
||||
decodeBasic(decoder);
|
||||
submeta = (metatype == TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM;
|
||||
metatype = (metatype == TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; // Use TYPE_INT or TYPE_UINT internally
|
||||
map<uintb,string> nmap;
|
||||
string warning;
|
||||
|
||||
@ -2265,6 +2241,81 @@ void TypeUnion::assignFieldOffsets(vector<TypeField> &list,int4 &newSize,int4 &n
|
||||
}
|
||||
}
|
||||
|
||||
TypePartialEnum::TypePartialEnum(const TypePartialEnum &op)
|
||||
: TypeEnum(op)
|
||||
{
|
||||
stripped = op.stripped;
|
||||
parent = op.parent;
|
||||
offset = op.offset;
|
||||
}
|
||||
|
||||
TypePartialEnum::TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip)
|
||||
: TypeEnum(sz, TYPE_PARTIALENUM)
|
||||
{
|
||||
flags |= has_stripped;
|
||||
stripped = strip;
|
||||
parent = par;
|
||||
offset = off;
|
||||
}
|
||||
|
||||
void TypePartialEnum::printRaw(ostream &s) const
|
||||
|
||||
{
|
||||
parent->printRaw(s);
|
||||
s << "[off=" << dec << offset << ",sz=" << size << ']';
|
||||
}
|
||||
|
||||
bool TypePartialEnum::hasNamedValue(uintb val) const
|
||||
|
||||
{
|
||||
val <<= 8*offset;
|
||||
return parent->hasNamedValue(val);
|
||||
}
|
||||
|
||||
void TypePartialEnum::getMatches(uintb val,Representation &rep) const
|
||||
|
||||
{
|
||||
val <<= 8*offset;
|
||||
rep.shiftAmount = offset * 8;
|
||||
parent->getMatches(val,rep);
|
||||
}
|
||||
|
||||
int4 TypePartialEnum::compare(const Datatype &op,int4 level) const
|
||||
|
||||
{
|
||||
int4 res = Datatype::compare(op,level);
|
||||
if (res != 0) return res;
|
||||
// Both must be partial
|
||||
TypePartialEnum *tp = (TypePartialEnum *) &op;
|
||||
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
|
||||
level -= 1;
|
||||
if (level < 0) {
|
||||
if (id == op.getId()) return 0;
|
||||
return (id < op.getId()) ? -1 : 1;
|
||||
}
|
||||
return parent->compare(*tp->parent,level); // Compare the underlying union
|
||||
}
|
||||
|
||||
int4 TypePartialEnum::compareDependency(const Datatype &op) const
|
||||
|
||||
{
|
||||
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
|
||||
TypePartialEnum *tp = (TypePartialEnum *) &op; // Both must be partial
|
||||
if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; // Compare absolute pointers
|
||||
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
|
||||
return (op.getSize()-size);
|
||||
}
|
||||
|
||||
void TypePartialEnum::encode(Encoder &encoder) const
|
||||
|
||||
{
|
||||
encoder.openElement(ELEM_TYPE);
|
||||
encodeBasic(TYPE_PARTIALENUM,-1,encoder);
|
||||
encoder.writeSignedInteger(ATTRIB_OFFSET, offset);
|
||||
parent->encodeRef(encoder);
|
||||
encoder.closeElement(ELEM_TYPE);
|
||||
}
|
||||
|
||||
TypePartialStruct::TypePartialStruct(const TypePartialStruct &op)
|
||||
: Datatype(op)
|
||||
{
|
||||
@ -3111,7 +3162,7 @@ void TypeFactory::setupSizes(void)
|
||||
setDefaultAlignmentMap();
|
||||
if (enumsize == 0) {
|
||||
enumsize = glb->getDefaultSize();
|
||||
enumtype = TYPE_UINT;
|
||||
enumtype = TYPE_ENUM_UINT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3866,6 +3917,11 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n)
|
||||
return (TypeStruct *) findAdd(tmp);
|
||||
}
|
||||
|
||||
/// Create a data-type representing storage of part of an \e array or \e structure.
|
||||
/// \param contain is the parent \e array or \e structure data-type that we are taking a part of.
|
||||
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||
/// \param sz is the number of bytes in the partial data-type
|
||||
/// \return the TypePartialStruct object
|
||||
TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz)
|
||||
|
||||
{
|
||||
@ -3887,6 +3943,11 @@ TypeUnion *TypeFactory::getTypeUnion(const string &n)
|
||||
return (TypeUnion *) findAdd(tmp);
|
||||
}
|
||||
|
||||
/// Create a data-type representing storage of part of a \e union data-type.
|
||||
/// \param contain is the parent \e union data-type that we are taking a part of.
|
||||
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||
/// \param sz is the number of bytes in the partial data-type
|
||||
/// \return the TypePartialUnion object
|
||||
TypePartialUnion *TypeFactory::getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz)
|
||||
|
||||
{
|
||||
@ -3907,6 +3968,19 @@ TypeEnum *TypeFactory::getTypeEnum(const string &n)
|
||||
return (TypeEnum *) findAdd(tmp);
|
||||
}
|
||||
|
||||
/// Create a data-type representing storage of part of an \e enumeration.
|
||||
/// \param contain is the parent \e enumeration data-type that we are taking a part of.
|
||||
/// \param off is the offset (in bytes) within the parent that the partial data-type starts at
|
||||
/// \param sz is the number of bytes in the partial data-type
|
||||
/// \return the TypePartialEnum object
|
||||
TypePartialEnum *TypeFactory::getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz)
|
||||
|
||||
{
|
||||
Datatype *strip = getBase(sz, TYPE_UNKNOWN);
|
||||
TypePartialEnum tpe(contain,off,sz,strip);
|
||||
return (TypePartialEnum *) findAdd(tpe);
|
||||
}
|
||||
|
||||
/// Creates the special TypeSpacebase with an associated address space and scope
|
||||
/// \param id is the address space
|
||||
/// \param addr specifies the function scope, or isInvalid() for global scope
|
||||
@ -4032,6 +4106,8 @@ Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size)
|
||||
// If we reach here, lastType is bigger than size
|
||||
if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY)
|
||||
return getTypePartialStruct(lastType, lastOff, size);
|
||||
else if (lastType->isEnumType() && !lastType->hasStripped())
|
||||
return getTypePartialEnum((TypeEnum *)lastType, lastOff, size);
|
||||
}
|
||||
return (Datatype *)0;
|
||||
}
|
||||
@ -4238,7 +4314,7 @@ Datatype *TypeFactory::decodeTypedef(Decoder &decoder)
|
||||
Datatype *TypeFactory::decodeEnum(Decoder &decoder,bool forcecore)
|
||||
|
||||
{
|
||||
TypeEnum te(1,TYPE_INT); // size and metatype are replaced
|
||||
TypeEnum te(1,TYPE_ENUM_INT); // metatype and size are replaced
|
||||
string warning = te.decode(decoder,*this);
|
||||
if (forcecore)
|
||||
te.flags |= Datatype::coretype;
|
||||
@ -4399,6 +4475,10 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore)
|
||||
ct = findAdd(ta);
|
||||
}
|
||||
break;
|
||||
case TYPE_ENUM_INT:
|
||||
case TYPE_ENUM_UINT:
|
||||
ct = decodeEnum(decoder,forcecore);
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
ct = decodeStruct(decoder,forcecore);
|
||||
break;
|
||||
@ -4438,12 +4518,6 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore)
|
||||
decoder.closeElement(elemId);
|
||||
return ct;
|
||||
}
|
||||
else if (attribId == ATTRIB_ENUM && decoder.readBool()) {
|
||||
decoder.rewindAttributes();
|
||||
ct = decodeEnum(decoder, forcecore);
|
||||
decoder.closeElement(elemId);
|
||||
return ct;
|
||||
}
|
||||
else if (attribId == ATTRIB_UTF && decoder.readBool()) {
|
||||
TypeUnicode tu;
|
||||
decoder.rewindAttributes();
|
||||
@ -4587,9 +4661,9 @@ void TypeFactory::parseEnumConfig(Decoder &decoder)
|
||||
uint4 elemId = decoder.openElement(ELEM_ENUM);
|
||||
enumsize = decoder.readSignedInteger(ATTRIB_SIZE);
|
||||
if (decoder.readBool(ATTRIB_SIGNED))
|
||||
enumtype = TYPE_INT;
|
||||
enumtype = TYPE_ENUM_INT;
|
||||
else
|
||||
enumtype = TYPE_UINT;
|
||||
enumtype = TYPE_ENUM_UINT;
|
||||
decoder.closeElement(elemId);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ extern AttributeId ATTRIB_ALIGNMENT; ///< Marshaling attribute "alignment"
|
||||
extern AttributeId ATTRIB_ARRAYSIZE; ///< Marshaling attribute "arraysize"
|
||||
extern AttributeId ATTRIB_CHAR; ///< Marshaling attribute "char"
|
||||
extern AttributeId ATTRIB_CORE; ///< Marshaling attribute "core"
|
||||
extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum"
|
||||
//extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum" deprecated
|
||||
extern AttributeId ATTRIB_INCOMPLETE; ///< Marshaling attribute "incomplete"
|
||||
//extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize" deprecated
|
||||
//extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize" deprecated
|
||||
@ -77,20 +77,23 @@ extern void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseadd
|
||||
/// The core meta-types supported by the decompiler. These are sizeless templates
|
||||
/// for the elements making up the type algebra. Index is important for Datatype::base2sub array.
|
||||
enum type_metatype {
|
||||
TYPE_VOID = 14, ///< Standard "void" type, absence of type
|
||||
TYPE_SPACEBASE = 13, ///< Placeholder for symbol/type look-up calculations
|
||||
TYPE_UNKNOWN = 12, ///< An unknown low-level type. Treated as an unsigned integer.
|
||||
TYPE_INT = 11, ///< Signed integer. Signed is considered less specific than unsigned in C
|
||||
TYPE_UINT = 10, ///< Unsigned integer
|
||||
TYPE_BOOL = 9, ///< Boolean
|
||||
TYPE_CODE = 8, ///< Data is actual executable code
|
||||
TYPE_FLOAT = 7, ///< Floating-point
|
||||
TYPE_VOID = 17, ///< Standard "void" type, absence of type
|
||||
TYPE_SPACEBASE = 16, ///< Placeholder for symbol/type look-up calculations
|
||||
TYPE_UNKNOWN = 15, ///< An unknown low-level type. Treated as an unsigned integer.
|
||||
TYPE_INT = 14, ///< Signed integer. Signed is considered less specific than unsigned in C
|
||||
TYPE_UINT = 13, ///< Unsigned integer
|
||||
TYPE_BOOL = 12, ///< Boolean
|
||||
TYPE_CODE = 11, ///< Data is actual executable code
|
||||
TYPE_FLOAT = 10, ///< Floating-point
|
||||
|
||||
TYPE_PTR = 6, ///< Pointer data-type
|
||||
TYPE_PTRREL = 5, ///< Pointer relative to another data-type (specialization of TYPE_PTR)
|
||||
TYPE_ARRAY = 4, ///< Array data-type, made up of a sequence of "element" datatype
|
||||
TYPE_STRUCT = 3, ///< Structure data-type, made up of component datatypes
|
||||
TYPE_UNION = 2, ///< An overlapping union of multiple datatypes
|
||||
TYPE_PTR = 9, ///< Pointer data-type
|
||||
TYPE_PTRREL = 8, ///< Pointer relative to another data-type (specialization of TYPE_PTR)
|
||||
TYPE_ARRAY = 7, ///< Array data-type, made up of a sequence of "element" datatype
|
||||
TYPE_ENUM_UINT = 6, ///< Unsigned enumeration data-type (specialization of TYPE_UINT)
|
||||
TYPE_ENUM_INT = 5, ///< Signed enumeration data-type (specialization of TYPE_INT)
|
||||
TYPE_STRUCT = 4, ///< Structure data-type, made up of component datatypes
|
||||
TYPE_UNION = 3, ///< An overlapping union of multiple datatypes
|
||||
TYPE_PARTIALENUM = 2, ///< Part of an enumerated value (specialization of TYPE_UINT)
|
||||
TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole
|
||||
TYPE_PARTIALUNION = 0 ///< Part of a union
|
||||
};
|
||||
@ -160,7 +163,7 @@ struct DatatypeCompare;
|
||||
/// Used for symbols, function prototypes, type propagation etc.
|
||||
class Datatype {
|
||||
protected:
|
||||
static sub_metatype base2sub[15];
|
||||
static sub_metatype base2sub[18];
|
||||
/// Boolean properties of datatypes
|
||||
enum {
|
||||
coretype = 1, ///< This is a basic type which will never be redefined
|
||||
@ -213,7 +216,6 @@ public:
|
||||
bool isCoreType(void) const { return ((flags&coretype)!=0); } ///< Is this a core data-type
|
||||
bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32|opaque_string))!=0); } ///< Does this print as a 'char'
|
||||
bool isEnumType(void) const { return ((flags&enumtype)!=0); } ///< Is this an enumerated type
|
||||
bool isPowerOfTwo(void) const { return ((flags&poweroftwo)!=0); } ///< Is this a flag-based enumeration
|
||||
bool isASCII(void) const { return ((flags&chartype)!=0); } ///< Does this print as an ASCII 'char'
|
||||
bool isUTF16(void) const { return ((flags&utf16)!=0); } ///< Does this print as UTF16 'wchar'
|
||||
bool isUTF32(void) const { return ((flags&utf32)!=0); } ///< Does this print as UTF32 'wchar'
|
||||
@ -466,24 +468,33 @@ public:
|
||||
/// This supports combinations of the enumeration values (using logical OR and bit-wise complement)
|
||||
/// by defining independent \b bit-fields.
|
||||
class TypeEnum : public TypeBase {
|
||||
public:
|
||||
/// \brief Class describing how a particular enumeration value is constructed using tokens
|
||||
class Representation {
|
||||
public:
|
||||
vector<string> matchname; ///< Name tokens that are ORed together
|
||||
bool complement; ///< If \b true, bitwise complement value after ORing
|
||||
int4 shiftAmount; ///< Number of bits to left-shift final value
|
||||
Representation(void) { complement = false; shiftAmount = 0; } ///< Constructor
|
||||
};
|
||||
protected:
|
||||
friend class TypeFactory;
|
||||
map<uintb,string> namemap; ///< Map from integer to name
|
||||
vector<uintb> masklist; ///< Masks for each bitfield within the enum
|
||||
void setNameMap(const map<uintb,string> &nmap); ///< Establish the value -> name map
|
||||
void setNameMap(const map<uintb,string> &nmap) { namemap = nmap; } ///< Establish the value -> name map
|
||||
string decode(Decoder &decoder,TypeFactory &typegrp); ///< Restore \b this enum data-type from a stream
|
||||
public:
|
||||
/// Construct from another TypeEnum
|
||||
TypeEnum(const TypeEnum &op);
|
||||
/// Construct from a size and meta-type (TYPE_INT or TYPE_UINT)
|
||||
TypeEnum(int4 s,type_metatype m) : TypeBase(s,m) {
|
||||
flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; }
|
||||
flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; }
|
||||
/// Construct from a size, meta-type, and name
|
||||
TypeEnum(int4 s,type_metatype m,const string &nm) : TypeBase(s,m,nm) {
|
||||
flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; }
|
||||
flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; }
|
||||
map<uintb,string>::const_iterator beginEnum(void) const { return namemap.begin(); } ///< Beginning of name map
|
||||
map<uintb,string>::const_iterator endEnum(void) const { return namemap.end(); } ///< End of name map
|
||||
bool getMatches(uintb val,vector<string> &matchname) const; ///< Recover the named representation
|
||||
virtual bool hasNamedValue(uintb val) const; ///< Does \b this have a (single) name for the given value
|
||||
virtual void getMatches(uintb val,Representation &rep) const; ///< Recover the named representation
|
||||
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||
virtual int4 compareDependency(const Datatype &op) const;
|
||||
virtual Datatype *clone(void) const { return new TypeEnum(*this); }
|
||||
@ -553,6 +564,27 @@ public:
|
||||
static void assignFieldOffsets(vector<TypeField> &list,int4 &newSize,int4 &newAlign,TypeUnion *tu); ///< Assign field offsets
|
||||
};
|
||||
|
||||
/// \brief A data-type thats holds part of a TypeEnum and possible additional padding
|
||||
class TypePartialEnum : public TypeEnum {
|
||||
friend class TypeFactory;
|
||||
Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required.
|
||||
TypeEnum *parent; ///< The enumeration data-type \b this is based on
|
||||
int4 offset; ///< Byte offset with the parent enum where \b this starts
|
||||
public:
|
||||
TypePartialEnum(const TypePartialEnum &op); ///< Construct from another TypePartialEnum
|
||||
TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip); ///< Constructor
|
||||
int4 getOffset(void) const { return offset; } ///< Get the byte offset into the containing data-type
|
||||
Datatype *getParent(void) const { return parent; } ///< Get the enumeration containing \b this piece
|
||||
virtual void printRaw(ostream &s) const;
|
||||
virtual bool hasNamedValue(uintb val) const;
|
||||
virtual void getMatches(uintb val,Representation &rep) const;
|
||||
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||
virtual int4 compareDependency(const Datatype &op) const;
|
||||
virtual Datatype *clone(void) const { return new TypePartialEnum(*this); }
|
||||
virtual void encode(Encoder &encoder) const;
|
||||
virtual Datatype *getStripped(void) const { return stripped; }
|
||||
};
|
||||
|
||||
/// \brief A data-type that holds \e part of a TypeStruct or TypeArray
|
||||
class TypePartialStruct : public Datatype {
|
||||
friend class TypeFactory;
|
||||
@ -634,7 +666,12 @@ public:
|
||||
/// \brief Get offset of \b this pointer relative to start of the containing data-type
|
||||
///
|
||||
/// \return the offset value in \e address \e units
|
||||
int4 getPointerOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); }
|
||||
int4 getAddressOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); }
|
||||
|
||||
/// \brief Get offset of \b this pointer relative to start of the containing data-type
|
||||
///
|
||||
/// \return the offset value in \e byte units
|
||||
int4 getByteOffset(void) const { return offset; }
|
||||
virtual void printRaw(ostream &s) const;
|
||||
virtual int4 compare(const Datatype &op,int4 level) const;
|
||||
virtual int4 compareDependency(const Datatype &op) const;
|
||||
@ -803,6 +840,7 @@ public:
|
||||
TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union
|
||||
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
|
||||
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration
|
||||
TypePartialEnum *getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz); ///< Create a partial enumeration
|
||||
TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type
|
||||
TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype
|
||||
Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type
|
||||
|
@ -175,10 +175,11 @@ OpCode TypeOp::floatSignManipulation(PcodeOp *op)
|
||||
return CPUI_MAX;
|
||||
}
|
||||
|
||||
/// \brief Propagate a dereferenced data-type up to its pointer data-type
|
||||
/// \brief Propagate a dereferenced data-type up to its pointer data-type through a LOAD or STORE
|
||||
///
|
||||
/// Don't create more than a depth of 1, i.e. ptr->ptr
|
||||
/// \param pt is the pointed-to data-type
|
||||
/// \param t is the TypeFactory containing the data-types
|
||||
/// \param dt is the pointed-to data-type
|
||||
/// \param sz is the size of the pointer
|
||||
/// \param wordsz is the wordsize associated with the pointer
|
||||
/// \return the TypePointer object
|
||||
@ -196,6 +197,36 @@ Datatype *TypeOp::propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wo
|
||||
return t->getTypePointer(sz,dt,wordsz);
|
||||
}
|
||||
|
||||
/// \brief Propagate a pointer data-type down to its element data-type through a LOAD or STORE
|
||||
///
|
||||
/// \param t is the TypeFactory containing the data-types
|
||||
/// \param dt is the pointer data-type
|
||||
/// \param sz is the size of the dereferenced pointer
|
||||
/// \return the dereferenced data-type
|
||||
Datatype *TypeOp::propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz)
|
||||
|
||||
{
|
||||
if (dt->getMetatype() != TYPE_PTR)
|
||||
return (Datatype *)0;
|
||||
Datatype *ptrto = ((TypePointer *)dt)->getPtrTo();
|
||||
if (ptrto->isVariableLength())
|
||||
return (Datatype *)0;
|
||||
if (ptrto->getSize() == sz)
|
||||
return ptrto;
|
||||
// If we reach here, there is a size mismatch between the pointer data-type and the dereferenced value.
|
||||
// We only propagate (partial) enumerations in this case.
|
||||
if (dt->isPointerRel()) {
|
||||
TypePointerRel *ptrrel = (TypePointerRel *)dt;
|
||||
Datatype *res = t->getExactPiece(ptrrel->getParent(), ptrrel->getByteOffset(), sz);
|
||||
if (res != (Datatype *)0 && res->isEnumType())
|
||||
return res;
|
||||
}
|
||||
else if (ptrto->isEnumType() && !ptrto->hasStripped()) {
|
||||
return t->getTypePartialEnum((TypeEnum *)ptrto, 0, sz);
|
||||
}
|
||||
return (Datatype *)0;
|
||||
}
|
||||
|
||||
/// \param t is the TypeFactory used to construct data-types
|
||||
/// \param opc is the op-code value the new object will represent
|
||||
/// \param n is the display name that will represent the op-code
|
||||
@ -463,13 +494,8 @@ Datatype *TypeOpLoad::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,
|
||||
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
||||
}
|
||||
else if (alttype->getMetatype()==TYPE_PTR) {
|
||||
newtype = ((TypePointer *)alttype)->getPtrTo();
|
||||
if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) // Size must be appropriate
|
||||
newtype = (Datatype *)0;
|
||||
}
|
||||
else
|
||||
newtype = (Datatype *)0; // Don't propagate anything
|
||||
newtype = propagateFromPointer(tlst, alttype, outvn->getSize());
|
||||
return newtype;
|
||||
}
|
||||
|
||||
@ -538,13 +564,8 @@ Datatype *TypeOpStore::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn
|
||||
AddrSpace *spc = op->getIn(0)->getSpaceFromConst();
|
||||
newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize());
|
||||
}
|
||||
else if (alttype->getMetatype()==TYPE_PTR) {
|
||||
newtype = ((TypePointer *)alttype)->getPtrTo();
|
||||
if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength())
|
||||
newtype = (Datatype *)0;
|
||||
}
|
||||
else
|
||||
newtype = (Datatype *)0; // Don't propagate anything
|
||||
newtype = propagateFromPointer(tlst, alttype, outvn->getSize());
|
||||
return newtype;
|
||||
}
|
||||
|
||||
@ -933,7 +954,7 @@ Datatype *TypeOpEqual::propagateAcrossCompare(Datatype *alttype,TypeFactory *typ
|
||||
}
|
||||
else if (alttype->isPointerRel() && !outvn->isConstant()) {
|
||||
TypePointerRel *relPtr = (TypePointerRel *)alttype;
|
||||
if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getPointerOffset() >= 0) {
|
||||
if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getByteOffset() >= 0) {
|
||||
// If we know the pointer is in the middle of a structure, don't propagate across comparison operators
|
||||
// as the two sides of the operator are likely to be different types , and the other side can also
|
||||
// get data-type information from the structure pointer
|
||||
@ -1383,7 +1404,7 @@ Datatype *TypeOpIntXor::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
||||
Datatype *TypeOpIntXor::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||
int4 inslot,int4 outslot)
|
||||
{
|
||||
if (!alttype->isPowerOfTwo()) {
|
||||
if (!alttype->isEnumType()) {
|
||||
if (alttype->getMetatype() != TYPE_FLOAT)
|
||||
return (Datatype *)0;
|
||||
if (floatSignManipulation(op) == CPUI_MAX)
|
||||
@ -1416,7 +1437,7 @@ Datatype *TypeOpIntAnd::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
|
||||
Datatype *TypeOpIntAnd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||
int4 inslot,int4 outslot)
|
||||
{
|
||||
if (!alttype->isPowerOfTwo()) {
|
||||
if (!alttype->isEnumType()) {
|
||||
if (alttype->getMetatype() != TYPE_FLOAT)
|
||||
return (Datatype *)0;
|
||||
if (floatSignManipulation(op) == CPUI_MAX)
|
||||
@ -1449,7 +1470,7 @@ Datatype *TypeOpIntOr::getOutputToken(const PcodeOp *op,CastStrategy *castStrate
|
||||
Datatype *TypeOpIntOr::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
|
||||
int4 inslot,int4 outslot)
|
||||
{
|
||||
if (!alttype->isPowerOfTwo()) return (Datatype *)0; // Only propagate flag enums
|
||||
if (!alttype->isEnumType()) return (Datatype *)0; // Only propagate enums
|
||||
Datatype *newtype;
|
||||
if (invn->isSpacebase()) {
|
||||
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();
|
||||
|
@ -181,6 +181,7 @@ public:
|
||||
/// \brief Return the floating-point operation associated with the \e sign bit manipulation by the given PcodeOp
|
||||
static OpCode floatSignManipulation(PcodeOp *op);
|
||||
static Datatype *propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wordsz);
|
||||
static Datatype *propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz);
|
||||
};
|
||||
|
||||
// Major classes of operations
|
||||
|
@ -297,6 +297,27 @@ void HighVariable::transferPiece(HighVariable *tv2)
|
||||
tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty);
|
||||
}
|
||||
|
||||
/// Except in specific circumstances, convert \b type into its stripped form.
|
||||
void HighVariable::stripType(void) const
|
||||
|
||||
{
|
||||
if (!type->hasStripped())
|
||||
return;
|
||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
||||
type_metatype meta = symbol->getType()->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
||||
type = type->getStripped(); // strip the partial union
|
||||
}
|
||||
}
|
||||
else if (type->isEnumType()) {
|
||||
if (inst.size() != 1 || !inst[0]->isConstant()) // Only preserve partial enum on a constant
|
||||
type = type->getStripped();
|
||||
}
|
||||
else
|
||||
type = type->getStripped();
|
||||
}
|
||||
|
||||
/// Only update if the cover is marked as \e dirty.
|
||||
/// Merge the covers of all Varnode instances.
|
||||
void HighVariable::updateInternalCover(void) const
|
||||
@ -386,17 +407,7 @@ void HighVariable::updateType(void) const
|
||||
vn = getTypeRepresentative();
|
||||
|
||||
type = vn->getType();
|
||||
if (type->hasStripped()) {
|
||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
||||
type_metatype meta = symbol->getType()->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
||||
type = type->getStripped(); // strip the partial union
|
||||
}
|
||||
}
|
||||
else
|
||||
type = type->getStripped();
|
||||
}
|
||||
stripType();
|
||||
// Update lock flags
|
||||
flags &= ~Varnode::typelock;
|
||||
if (vn->isTypeLock())
|
||||
@ -549,17 +560,7 @@ void HighVariable::finalizeDatatype(TypeFactory *typeFactory)
|
||||
if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN)
|
||||
return;
|
||||
type = tp;
|
||||
if (type->hasStripped()) {
|
||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||
if (symboloffset != -1) {
|
||||
type_metatype meta = symbol->getType()->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
|
||||
type = type->getStripped(); // strip the partial union
|
||||
}
|
||||
}
|
||||
else
|
||||
type = type->getStripped();
|
||||
}
|
||||
stripType();
|
||||
highflags |= type_finalized;
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -167,6 +167,7 @@ private:
|
||||
void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty
|
||||
void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems
|
||||
bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date
|
||||
void stripType(void) const; ///< Take the stripped form of the current data-type.
|
||||
public:
|
||||
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
|
||||
~HighVariable(void); ///< Destructor
|
||||
|
108
Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml
Normal file
108
Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml
Normal file
@ -0,0 +1,108 @@
|
||||
<decompilertest>
|
||||
<binaryimage arch="x86:LE:64:default:gcc">
|
||||
<!--
|
||||
Functions that read enum values and compare with constant values that should be printed by name.
|
||||
-->
|
||||
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||
4883ec184889e7e8f40f0000f6442408
|
||||
4175054883c418c3bf00000000e8e60f
|
||||
0000ebef4883ec184889e7e8d00f0000
|
||||
f744240c0003000075054883c418c3bf
|
||||
00000000e8bf0f0000ebef
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100065" readonly="true">
|
||||
f647082c7501c34883ec08
|
||||
bf02000000e88e0f00004883c408c3f7
|
||||
470c571300007501c34883ec08bf0300
|
||||
0000e8710f00004883c408c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x1000d3" readonly="true">
|
||||
48817f08000810007401c34883
|
||||
ec08bf06000000e81c0f00004883c408
|
||||
c3
|
||||
</bytechunk>
|
||||
<symbol space="ram" offset="0x100000" name="stackenumlow"/>
|
||||
<symbol space="ram" offset="0x100024" name="stackenumhigh"/>
|
||||
<symbol space="ram" offset="0x100065" name="ptrenumlow"/>
|
||||
<symbol space="ram" offset="0x10007f" name="ptrenumhigh"/>
|
||||
<symbol space="ram" offset="0x1000d3" name="ptrenumequal"/>
|
||||
<symbol space="ram" offset="0x101000" name="setStruct"/>
|
||||
</binaryimage>
|
||||
<script>
|
||||
<com>parse line enum flags {
|
||||
FLAG_1=1,
|
||||
FLAG_2 = 2,
|
||||
FLAG_4 = 4,
|
||||
FLAG_8 = 8,
|
||||
FLAG_10 = 0x10,
|
||||
FLAG_20 = 0x20,
|
||||
FLAG_40 = 0x40,
|
||||
FLAG_80 = 0x80,
|
||||
FLAG_100 = 0x100,
|
||||
FLAG_200 = 0x200,
|
||||
FLAG_400 = 0x400,
|
||||
FLAG_800 = 0x800,
|
||||
FLAG_1000 = 0x1000,
|
||||
FLAG_2000 = 0x2000,
|
||||
FLAG_4000 = 0x4000,
|
||||
FLAG_8000 = 0x8000,
|
||||
FLAG_10000 = 0x10000,
|
||||
FLAG_20000 = 0x20000,
|
||||
FLAG_40000 = 0x40000,
|
||||
FLAG_80000 = 0x80000,
|
||||
FLAG_100000 = 0x100000,
|
||||
FLAG_200000 = 0x200000,
|
||||
FLAG_400000 = 0x400000,
|
||||
FLAG_800000 = 0x800000,
|
||||
HIGH_1 = 0x100000000,
|
||||
HIGH_2 = 0x200000000,
|
||||
HIGH_4 = 0x400000000,
|
||||
HIGH_8 = 0x800000000,
|
||||
HIGH_10 = 0x1000000000,
|
||||
HIGH_20 = 0x2000000000,
|
||||
HIGH_40 = 0x4000000000,
|
||||
HIGH_80 = 0x8000000000,
|
||||
HIGH_100 = 0x10000000000,
|
||||
HIGH_200 = 0x20000000000,
|
||||
HIGH_400 = 0x40000000000,
|
||||
HIGH_800 = 0x80000000000,
|
||||
HIGH_1000 = 0x100000000000,
|
||||
HIGH_2000 = 0x200000000000,
|
||||
HIGH_4000 = 0x400000000000,
|
||||
HIGH_8000 = 0x800000000000,
|
||||
HIGH_10000 = 0x1000000000000,
|
||||
HIGH_20000 = 0x2000000000000,
|
||||
HIGH_40000 = 0x4000000000000,
|
||||
HIGH_80000 = 0x8000000000000,
|
||||
HIGH_100000 = 0x10000000000000,
|
||||
HIGH_200000 = 0x20000000000000,
|
||||
HIGH_400000 = 0x40000000000000,
|
||||
HIGH_800000 = 0x80000000000000 };</com>
|
||||
<com>parse line struct enumstruct { int4 a; int4 b; flags flagfield; };</com>
|
||||
<com>parse line extern void setStruct(enumstruct *ptr);</com>
|
||||
<com>parse line extern void ptrenumlow(enumstruct *ptr);</com>
|
||||
<com>parse line extern void ptrenumhigh(enumstruct *ptrhigh);</com>
|
||||
<com>parse line extern void ptrenumequal(enumstruct *ptrequal);</com>
|
||||
<com>lo fu stackenumlow</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu stackenumhigh</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu ptrenumlow</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu ptrenumhigh</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu ptrenumequal</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>quit</com>
|
||||
</script>
|
||||
<stringmatch name="Enum Reading #1" min="1" max="1">Stack_18\.flagfield & \(FLAG_40\|FLAG_1\)\) .= 0\)</stringmatch>
|
||||
<stringmatch name="Enum Reading #2" min="1" max="1">Stack_18\.flagfield\._4_4_ & \(HIGH_200\|HIGH_100\) >> 0x20\) .= 0\)</stringmatch>
|
||||
<stringmatch name="Enum Reading #3" min="1" max="1">ptr->flagfield & \(FLAG_20\|FLAG_8\|FLAG_4\)\) .= 0\)</stringmatch>
|
||||
<stringmatch name="Enum Reading #4" min="1" max="1">if \(\(ptrhigh->flagfield & \(HIGH_1000\|HIGH_200\|HIGH_100\|HIGH_40\|HIGH_10\|HIGH_4\|HIGH_2\|HIGH_1\)\) .= 0</stringmatch>
|
||||
<stringmatch name="Enum Reading #5" min="1" max="1">if \(ptrequal->flagfield .= \(FLAG_100000\|FLAG_800\)\)</stringmatch>
|
||||
</decompilertest>
|
@ -22,7 +22,8 @@ f30f1efa488b0748ba454c4556454e54
|
||||
<bytechunk space="ram" offset="0x1000c5" readonly="true">
|
||||
488b0748ba770061007200
|
||||
6e0048891048ba69006e006700210048
|
||||
895008c3
|
||||
895008c348b84e454741544956454889
|
||||
47f9c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100140" readonly="true">
|
||||
546865206e756d626572206973205858
|
||||
@ -31,6 +32,7 @@ f30f1efa488b0748ba454c4556454e54
|
||||
<symbol space="ram" offset="0x100040" name="fillin_edit"/>
|
||||
<symbol space="ram" offset="0x1000a0" name="overlap"/>
|
||||
<symbol space="ram" offset="0x1000c5" name="fillin_wide"/>
|
||||
<symbol space="ram" offset="0x1000e4" name="negative_ptr"/>
|
||||
</binaryimage>
|
||||
<script>
|
||||
<com>option readonly on</com>
|
||||
@ -40,6 +42,7 @@ f30f1efa488b0748ba454c4556454e54
|
||||
<com>parse line extern void fillin_edit(mystring *ptr,int4 extra,int8 pos);</com>
|
||||
<com>parse line extern void overlap(mystring *ptr);</com>
|
||||
<com>parse line extern void fillin_wide(mywide *ptr);</com>
|
||||
<com>parse line extern void negative_ptr(char *negptr);</com>
|
||||
<com>lo fu fillin</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
@ -52,6 +55,9 @@ f30f1efa488b0748ba454c4556454e54
|
||||
<com>lo fu fillin_wide</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu negative_ptr</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>quit</com>
|
||||
</script>
|
||||
<stringmatch name="Heap string #1" min="1" max="1">builtin_strncpy\(ptr-\>val,"Message: ",9\);</stringmatch>
|
||||
@ -60,4 +66,5 @@ f30f1efa488b0748ba454c4556454e54
|
||||
<stringmatch name="Heap string #4" min="1" max="1">builtin_strncpy\(ptr-\>val \+ pos \+ 0xf,"FOUR",4\);</stringmatch>
|
||||
<stringmatch name="Heap string #5" min="1" max="1">builtin_strncpy\(ptr-\>val,"ELEVENTWELVE",0xc\);</stringmatch>
|
||||
<stringmatch name="Heap string #6" min="1" max="1">builtin_memcpy\(ptr-\>wval,L"warning!",0x10\);</stringmatch>
|
||||
<stringmatch name="Heap string #7" min="1" max="1">builtin_strncpy\(negptr \+ -7,"NEGATIVE",8\);</stringmatch>
|
||||
</decompilertest>
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -212,4 +212,52 @@ TEST(cast_integertoken) {
|
||||
ASSERT(!longPrinted(CPUI_INT_RIGHT,parse("uint8"),0x100000000));
|
||||
}
|
||||
|
||||
TEST(enum_matching) {
|
||||
TypeTestEnvironment::build();
|
||||
TypeEnum *enum3 = (TypeEnum *)parse("enum enum3 { ZERO=0, ONE=1, TWO=2, FOUR=4, EIGHT=8 }");
|
||||
TypeEnum::Representation rep;
|
||||
enum3->getMatches(5, rep);
|
||||
ASSERT(rep.matchname.size() == 2);
|
||||
ASSERT(rep.matchname[0] == "FOUR");
|
||||
ASSERT(rep.matchname[1] == "ONE");
|
||||
ASSERT(!rep.complement);
|
||||
rep.matchname.clear();
|
||||
enum3->getMatches(0xfffffffffffffff7,rep);
|
||||
ASSERT(rep.matchname.size() == 1);
|
||||
ASSERT(rep.matchname[0] == "EIGHT");
|
||||
ASSERT(rep.complement);
|
||||
rep.matchname.clear();
|
||||
rep.complement = false;
|
||||
enum3->getMatches(0,rep);
|
||||
ASSERT(rep.matchname.size() == 1);
|
||||
ASSERT(rep.matchname[0] == "ZERO");
|
||||
ASSERT(!rep.complement);
|
||||
rep.matchname.clear();
|
||||
enum3->getMatches(0x10, rep);
|
||||
ASSERT(rep.matchname.size() == 0);
|
||||
ASSERT(!rep.complement);
|
||||
}
|
||||
|
||||
TEST(enum_matching2) {
|
||||
TypeTestEnvironment::build();
|
||||
TypeEnum *enum4 = (TypeEnum *)parse("enum enum4 { ZERO=0, ONE=1, TWO=2, FOUR=4, SIX=6, EIGHT=8, ELEVEN=11 }");
|
||||
TypeEnum::Representation rep;
|
||||
enum4->getMatches(12,rep);
|
||||
ASSERT(rep.matchname.size()==2);
|
||||
ASSERT(rep.matchname[0] == "EIGHT");
|
||||
ASSERT(rep.matchname[1] == "FOUR");
|
||||
ASSERT(!rep.complement);
|
||||
rep.matchname.clear();
|
||||
enum4->getMatches(7,rep);
|
||||
ASSERT(rep.matchname.size()==2);
|
||||
ASSERT(rep.matchname[0] == "SIX");
|
||||
ASSERT(rep.matchname[1] == "ONE");
|
||||
ASSERT(!rep.complement);
|
||||
rep.matchname.clear();
|
||||
enum4->getMatches(11,rep);
|
||||
ASSERT(rep.matchname.size()==1);
|
||||
ASSERT(rep.matchname[0] == "ELEVEN");
|
||||
ASSERT(!rep.complement);
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
@ -412,6 +412,7 @@ public class GnuDemanglerParser {
|
||||
|
||||
demangled = cleanupRustLegacySymbol(demangled);
|
||||
|
||||
String rawDemangled = demangled;
|
||||
if (simplify) {
|
||||
demangled = replaceStdLibraryTypes(demangled);
|
||||
}
|
||||
@ -421,10 +422,16 @@ public class GnuDemanglerParser {
|
||||
|
||||
DemangledObjectBuilder builder = getSpecializedBuilder(demangled);
|
||||
if (builder != null) {
|
||||
return builder.build();
|
||||
DemangledObject dobj = builder.build();
|
||||
// note: the raw demangled is before any simplifications
|
||||
dobj.setRawDemangledString(rawDemangled);
|
||||
return dobj;
|
||||
}
|
||||
|
||||
return parseFunctionOrVariable(demangled);
|
||||
DemangledObject dobj = parseFunctionOrVariable(demangled);
|
||||
// note: the raw demangled is before any simplifications
|
||||
dobj.setRawDemangledString(rawDemangled);
|
||||
return dobj;
|
||||
}
|
||||
|
||||
private DemangledObjectBuilder getSpecializedBuilder(String demangled) {
|
||||
|
@ -115,6 +115,10 @@ public class GnuDemanglerTest extends AbstractGenericTest {
|
||||
"virtual thunk to undefined __thiscall std::ostringstream::~ostringstream(void)",
|
||||
signature);
|
||||
|
||||
assertEquals(
|
||||
"virtual thunk to std::basic_ostringstream<char, std::char_traits<char>, pool_allocator<char> >::~basic_ostringstream()",
|
||||
dobj.getRawDemangled());
|
||||
|
||||
//
|
||||
// Now disable demangled string replacement
|
||||
//
|
||||
|
@ -35,8 +35,6 @@ import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.tab.GTabPanel;
|
||||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.events.ProgramLocationPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.core.progmgr.MultiTabPlugin;
|
||||
import ghidra.app.util.viewer.field.OpenCloseField;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingModel;
|
||||
@ -208,8 +206,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
goTo(tool, program, "1004c61");
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
@ -230,8 +227,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("100f3ff")), program));
|
||||
goTo(tool, program, "100f3ff");
|
||||
assertEquals(addr("100f3ff"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
@ -252,8 +248,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertNotNull(nextDiff);
|
||||
assertTrue(nextDiff.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
goTo(tool, program, "1004c61");
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
@ -263,8 +258,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertEquals(cb.getCurrentSelection(),
|
||||
new ProgramSelection(addr("100415a"), addr("100415a")));
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1002055")), program));
|
||||
goTo(tool, program, "1002055");
|
||||
assertEquals(addr("1002055"), getDiffAddress());
|
||||
|
||||
invokeLater(prevDiff);
|
||||
@ -284,8 +278,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertNotNull(diffDetails);
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1004c61")), program));
|
||||
goTo(tool, program, "1004c61");
|
||||
assertEquals(addr("1004c61"), getDiffAddress());
|
||||
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
|
||||
|
||||
@ -335,8 +328,7 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertNotNull(diffDetails);
|
||||
assertTrue(diffDetails.isEnabled());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("100")), program));
|
||||
goTo(tool, program, "100");
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
invokeLater(diffDetails);
|
||||
|
||||
@ -353,11 +345,8 @@ public class DiffTest extends DiffTestAdapter {
|
||||
assertTrue(info.indexOf("Bookmark Diffs") == -1);
|
||||
assertEquals(addr("100"), getDiffAddress());
|
||||
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1001014")), program));
|
||||
goTo(tool, program, "1001014");
|
||||
assertEquals(addr("1001014"), getDiffAddress());
|
||||
invokeLater(diffDetails);
|
||||
waitForSwing();
|
||||
assertEquals(true, isDiffDetailsDisplayed());
|
||||
|
||||
// Check where there are no differences
|
||||
@ -406,10 +395,8 @@ public class DiffTest extends DiffTestAdapter {
|
||||
diffAs.addRange(addr("1002304"), addr("1002304"));
|
||||
diffAs.addRange(addr("1002306"), addr("1002306"));
|
||||
|
||||
tool.firePluginEvent(
|
||||
new ProgramSelectionPluginEvent("test", new ProgramSelection(as), program));
|
||||
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
|
||||
new ProgramLocation(program, addr("1001000")), program));
|
||||
makeSelection(tool, program, as);
|
||||
goTo(tool, program, "1001000");
|
||||
assertTrue(setPgm2Selection.isEnabled());
|
||||
|
||||
invokeLater(setPgm2Selection);
|
||||
|
@ -216,14 +216,19 @@ class PyGhidraLauncher:
|
||||
raise Exception("org.eclipse.jdt.launching.VM_ARGUMENTS not found")
|
||||
|
||||
def _jvm_args(self) -> List[str]:
|
||||
|
||||
properties = [
|
||||
f"-Dpyghidra.sys.prefix={sys.prefix}",
|
||||
f"-Dpyghidra.sys.executable={sys.executable}"
|
||||
]
|
||||
|
||||
if self._dev_mode and self._java_home:
|
||||
return self._parse_dev_args()
|
||||
return properties + self._parse_dev_args()
|
||||
|
||||
suffix = "_" + platform.system().upper()
|
||||
if suffix == "_DARWIN":
|
||||
suffix = "_MACOS"
|
||||
option_pattern: re.Pattern = re.compile(fr"VMARGS(?:{suffix})?=(.+)")
|
||||
properties = []
|
||||
|
||||
root = self._install_dir
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -15,29 +15,12 @@
|
||||
*/
|
||||
package sarif;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.Array;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.ByteDataType;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.FunctionDefinition;
|
||||
import ghidra.program.model.data.FunctionDefinitionDataType;
|
||||
import ghidra.program.model.data.Pointer;
|
||||
import ghidra.program.model.data.Pointer32DataType;
|
||||
import ghidra.program.model.data.PointerDataType;
|
||||
import ghidra.program.model.data.ProgramBasedDataTypeManager;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.model.data.TypeDef;
|
||||
import ghidra.program.model.data.TypedefDataType;
|
||||
import ghidra.program.model.data.Union;
|
||||
import ghidra.program.model.data.UnionDataType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.util.ProgramDiff;
|
||||
|
||||
public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
@ -63,11 +46,11 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
cp = new CategoryPath("/A/B/C/D/E/F/G/H");
|
||||
dataMgr.createCategory(cp);
|
||||
assertTrue(dataMgr.containsCategory(cp));
|
||||
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -78,9 +61,9 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
assertNotNull(array);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -93,9 +76,9 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
assertNotNull(td);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -110,9 +93,9 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
assertNotNull(ptr);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -130,24 +113,24 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
p = new Pointer32DataType(null);
|
||||
ptr = (Pointer) dataMgr.resolve(p, null);
|
||||
assertNotNull(ptr);
|
||||
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateStructure() throws Exception {
|
||||
ProgramBasedDataTypeManager dataMgr = program.getDataTypeManager();
|
||||
StructureDataType sdt = new StructureDataType("test", 0);
|
||||
Structure sdt = createComplexStructureDataType(dataMgr);
|
||||
Structure struct = (Structure) dataMgr.addDataType(sdt, null);
|
||||
assertNotNull(struct);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -158,9 +141,9 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
assertNotNull(union);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -172,9 +155,75 @@ public class DataTypesSarifTest extends AbstractSarifTest {
|
||||
assertNotNull(funcDef);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
static Structure createComplexStructureDataType(DataTypeManager dtm) throws Exception {
|
||||
|
||||
/**
|
||||
* /structA
|
||||
* aligned(8) pack(disabled)
|
||||
* Structure structA {
|
||||
* 0 char 1 a0 ""
|
||||
* 4 int 4 a1 ""
|
||||
* 16 int[4] 16 a3 ""
|
||||
* 32 char[0] 0 az ""
|
||||
* 40 short 2 a4 ""
|
||||
* 48 int[0] 0 aflex ""
|
||||
* }
|
||||
* Length: 48 Alignment: 8
|
||||
*/
|
||||
Structure structA = new StructureDataType("structA", 0);
|
||||
structA.insertAtOffset(0, CharDataType.dataType, -1, "a0", null);
|
||||
structA.insertAtOffset(4, IntegerDataType.dataType, -1, "a1", null);
|
||||
structA.insertAtOffset(0x10, new ArrayDataType(CharDataType.dataType, 0, -1), -1, "az",
|
||||
null);
|
||||
structA.insertAtOffset(0x10, new ArrayDataType(IntegerDataType.dataType, 4, -1), -1, "a3",
|
||||
null);
|
||||
structA.insertAtOffset(0x28, ShortDataType.dataType, -1, "a4", null);
|
||||
structA.insertAtOffset(0x30, new ArrayDataType(IntegerDataType.dataType, 0, -1), -1,
|
||||
"aflex", null);
|
||||
structA.setExplicitMinimumAlignment(8);
|
||||
|
||||
/**
|
||||
* /structB
|
||||
* pack()
|
||||
* Structure structB {
|
||||
* 0 short 2 b0 ""
|
||||
* 2 byte[0] 0 bfs ""
|
||||
* 2 int:4(0) 1 bf1 ""
|
||||
* 2 int:6(4) 2 bf2 ""
|
||||
* 3 int:2(2) 1 bf3 ""
|
||||
* 8 structA 48 b1 ""
|
||||
* }
|
||||
* Length: 56 Alignment: 8
|
||||
*/
|
||||
Structure structB = new StructureDataType("structB", 0);
|
||||
structB.setPackingEnabled(true);
|
||||
structB.add(ShortDataType.dataType, "b0", null);
|
||||
structB.add(new ArrayDataType(ByteDataType.dataType, 0, -1), "bfs", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 4, "bf1", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 6, "bf2", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 2, "bf3", null);
|
||||
structB.add(structA, "b1", null);
|
||||
|
||||
/**
|
||||
* /structC
|
||||
* pack()
|
||||
* Structure structC {
|
||||
* 0 char 1 c0 ""
|
||||
* 8 structB 56 c1 ""
|
||||
* }
|
||||
* Length: 64 Alignment: 8
|
||||
*/
|
||||
Structure structC = new StructureDataType("structC", 0);
|
||||
structC.setPackingEnabled(true);
|
||||
structC.add(CharDataType.dataType, "c0", null);
|
||||
structC.add(structB, "c1", null);
|
||||
|
||||
return structC;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -18,15 +18,8 @@ package sarif;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.data.ArrayDataType;
|
||||
import ghidra.program.model.data.ByteDataType;
|
||||
import ghidra.program.model.data.CharDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.IntegerDataType;
|
||||
import ghidra.program.model.data.ProgramBasedDataTypeManager;
|
||||
import ghidra.program.model.data.ShortDataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.util.ProgramDiff;
|
||||
|
||||
public class DefinedDataSarifTest extends AbstractSarifTest {
|
||||
@ -37,58 +30,16 @@ public class DefinedDataSarifTest extends AbstractSarifTest {
|
||||
|
||||
@Test
|
||||
public void testDefinedData() throws Exception {
|
||||
Structure structA = new StructureDataType("structA", 0);
|
||||
structA.add(IntegerDataType.dataType, "a0", null);
|
||||
structA.add(new ArrayDataType(CharDataType.dataType, 0, -1), "az", null);
|
||||
structA.add(DataType.DEFAULT);
|
||||
structA.add(ByteDataType.dataType, "a1", null);
|
||||
structA.add(new ArrayDataType(IntegerDataType.dataType, 0, -1), "aflex", null);
|
||||
|
||||
/**
|
||||
* /structB
|
||||
* pack()
|
||||
* Structure structB {
|
||||
* 0 short 2 b0 ""
|
||||
* 2 byte[0] 0 bfs ""
|
||||
* 4 int:4(0) 1 bf1 ""
|
||||
* 4 int:6(4) 2 bf2 ""
|
||||
* 5 int:2(2) 1 bf3 ""
|
||||
* 6 structA 6 b1 ""
|
||||
* }
|
||||
* Size = 12 Actual Alignment = 4
|
||||
*/
|
||||
Structure structB = new StructureDataType("structB", 0);
|
||||
structB.setPackingEnabled(true);
|
||||
structB.add(ShortDataType.dataType, "b0", null);
|
||||
structB.add(new ArrayDataType(ByteDataType.dataType, 0, -1), "bfs", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 4, "bf1", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 6, "bf2", null);
|
||||
structB.addBitField(IntegerDataType.dataType, 2, "bf3", null);
|
||||
structB.add(structA, "b1", null);
|
||||
|
||||
/**
|
||||
* /structC
|
||||
* pack()
|
||||
* Structure structC {
|
||||
* 0 char 1 c0 ""
|
||||
* 4 structB 12 c1 ""
|
||||
* }
|
||||
* Size = 16 Actual Alignment = 4
|
||||
*/
|
||||
Structure structC = new StructureDataType("structC", 0);
|
||||
structC.setPackingEnabled(true);
|
||||
structC.add(CharDataType.dataType, "c0", null);
|
||||
structC.add(structB, "c1", null);
|
||||
|
||||
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||
structC = (Structure) dtm.resolve(structC, null);
|
||||
Structure sdt = DataTypesSarifTest.createComplexStructureDataType(dtm);
|
||||
Structure struct = (Structure) dtm.resolve(sdt, null);
|
||||
|
||||
program.getListing().createData(addr(0x100), structC);
|
||||
program.getListing().createData(addr(0x100), struct);
|
||||
|
||||
ProgramDiff programDiff = readWriteCompare();
|
||||
|
||||
|
||||
AddressSetView differences = programDiff.getDifferences(monitor);
|
||||
assert(differences.isEmpty());
|
||||
assert (differences.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -227,8 +227,12 @@ class ComponentNode extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getComponentCount() {
|
||||
return windowPlaceholders.size();
|
||||
// we may be a single component or in a tabbed pane of components
|
||||
List<ComponentPlaceholder> activeComponents = new ArrayList<>();
|
||||
populateActiveComponents(activeComponents);
|
||||
return activeComponents.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -101,6 +101,11 @@ class DetachedWindowNode extends WindowNode {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getComponentCount() {
|
||||
return child.getComponentCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
String getTitle() {
|
||||
if (window instanceof JDialog) {
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -27,13 +27,14 @@ import org.jdesktop.animation.timing.Animator;
|
||||
import org.jdesktop.animation.timing.TimingTargetAdapter;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.event.mouse.GMouseListenerAdapter;
|
||||
import docking.menu.DialogToolbarButton;
|
||||
import docking.util.AnimationUtils;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.*;
|
||||
@ -81,6 +82,7 @@ public class DialogComponentProvider
|
||||
private TaskMonitorComponent taskMonitorComponent;
|
||||
|
||||
private static final KeyStroke ESC_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||
private DockingAction closeAction;
|
||||
|
||||
private CardLayout progressCardLayout;
|
||||
private JButton defaultButton;
|
||||
@ -89,11 +91,14 @@ public class DialogComponentProvider
|
||||
private Component focusComponent;
|
||||
private JPanel toolbar;
|
||||
|
||||
private final Map<DockingActionIf, DialogToolbarButton> actionMap = new HashMap<>();
|
||||
private final DialogComponentProviderPopupActionManager popupManager =
|
||||
private Map<DockingActionIf, DialogToolbarButton> toolbarButtonsByAction = new HashMap<>();
|
||||
private DialogComponentProviderPopupActionManager popupManager =
|
||||
new DialogComponentProviderPopupActionManager(this);
|
||||
private final PopupHandler popupHandler = new PopupHandler();
|
||||
private final Set<DockingActionIf> dialogActions = new HashSet<>();
|
||||
private PopupHandler popupHandler = new PopupHandler();
|
||||
private Set<DockingActionIf> dialogActions = new HashSet<>();
|
||||
|
||||
// we track these separately so that we can remove them on dispose
|
||||
private Set<DialogActionProxy> keyBindingProxyActions = new HashSet<>();
|
||||
|
||||
private Point initialLocation;
|
||||
private boolean resizeable = true;
|
||||
@ -104,6 +109,7 @@ public class DialogComponentProvider
|
||||
|
||||
private Dimension defaultSize;
|
||||
private String accessibleDescription;
|
||||
private Tool tool;
|
||||
|
||||
/**
|
||||
* Constructor for a DialogComponentProvider that will be modal and will include a status line and
|
||||
@ -175,21 +181,37 @@ public class DialogComponentProvider
|
||||
panel.add(buttonPanel);
|
||||
rootPanel.add(panel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
installEscapeAction();
|
||||
|
||||
doInitialize();
|
||||
}
|
||||
|
||||
private void installEscapeAction() {
|
||||
Action escAction = new AbstractAction("ESCAPE") {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
escapeCallback();
|
||||
}
|
||||
};
|
||||
closeAction = new ActionBuilder("Close Dialog", title)
|
||||
.sharedKeyBinding()
|
||||
.keyBinding(ESC_KEYSTROKE)
|
||||
.enabledWhen(this::isMyDialog)
|
||||
.onAction(c -> escapeCallback())
|
||||
.build();
|
||||
|
||||
KeyBindingUtils.registerAction(rootPanel, ESC_KEYSTROKE, escAction,
|
||||
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||
addAction(closeAction);
|
||||
}
|
||||
|
||||
private boolean isMyDialog(ActionContext c) {
|
||||
//
|
||||
// Each dialog registers a shared action bound to Escape. If all dialog actions are
|
||||
// enabled, then the user will get prompted to pick which dialog to close when pressing
|
||||
// Escape. Thus, we limit the enablement of each action to be the dialog that contains the
|
||||
// focused component. We use the action context to find out if this dialog is the active
|
||||
// dialog.
|
||||
//
|
||||
Window window = WindowUtilities.windowForComponent(c.getSourceComponent());
|
||||
if (!(window instanceof DockingDialog dockingDialog)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dockingDialog.containsProvider(DialogComponentProvider.this);
|
||||
}
|
||||
|
||||
/** a callback mechanism for children to do work */
|
||||
@ -197,6 +219,16 @@ public class DialogComponentProvider
|
||||
// may be overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given keystroke is the trigger for this dialog's close action.
|
||||
* @param ks the keystroke
|
||||
* @return true if the given keystroke is the trigger for this dialog's close action
|
||||
*/
|
||||
public boolean isCloseKeyStroke(KeyStroke ks) {
|
||||
KeyStroke currentCloseKs = closeAction.getKeyBinding();
|
||||
return Objects.equals(ks, currentCloseKs);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
@ -905,11 +937,16 @@ public class DialogComponentProvider
|
||||
|
||||
closeDialog();
|
||||
|
||||
if (tool != null) {
|
||||
keyBindingProxyActions.forEach(a -> tool.removeAction(a));
|
||||
}
|
||||
keyBindingProxyActions.clear();
|
||||
|
||||
popupManager.dispose();
|
||||
|
||||
dialogActions.forEach(DockingActionIf::dispose);
|
||||
|
||||
actionMap.clear();
|
||||
toolbarButtonsByAction.clear();
|
||||
dialogActions.clear();
|
||||
}
|
||||
|
||||
@ -1125,6 +1162,38 @@ public class DialogComponentProvider
|
||||
return ((dialog != null) && dialog.isShowing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time the dialog is show. The given tool is the parent tool of the dialog.
|
||||
* @param t the tool
|
||||
*/
|
||||
void dialogShown(Tool t) {
|
||||
setTool(t);
|
||||
dialogShown();
|
||||
}
|
||||
|
||||
private void setTool(Tool tool) {
|
||||
|
||||
if (this.tool != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize the first time we are shown
|
||||
this.tool = tool;
|
||||
|
||||
if (tool == null) {
|
||||
// The tool can be null for dialogs shown before the framework is initialized, like
|
||||
// dialogs shown over the splash screen. Without a tool, we cannot add key binding
|
||||
// actions.
|
||||
return;
|
||||
}
|
||||
|
||||
// Any actions in this list already were added before we had a tool. Add them now. Any
|
||||
// future calls to addKeyBindingAction() will get added to the tool at that time.
|
||||
for (DockingActionIf proxy : keyBindingProxyActions) {
|
||||
tool.addAction(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you want to do something when the dialog is made visible
|
||||
*/
|
||||
@ -1204,7 +1273,7 @@ public class DialogComponentProvider
|
||||
if (context == null) {
|
||||
context = new DefaultActionContext();
|
||||
}
|
||||
Set<DockingActionIf> keySet = actionMap.keySet();
|
||||
Set<DockingActionIf> keySet = toolbarButtonsByAction.keySet();
|
||||
for (DockingActionIf action : keySet) {
|
||||
action.setEnabled(action.isEnabledForContext(context));
|
||||
}
|
||||
@ -1227,7 +1296,7 @@ public class DialogComponentProvider
|
||||
|
||||
DialogToolbarButton button = new DialogToolbarButton(action, this);
|
||||
toolbar.add(button);
|
||||
actionMap.put(action, button);
|
||||
toolbarButtonsByAction.put(action, button);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1236,7 +1305,7 @@ public class DialogComponentProvider
|
||||
* the tool, as this dialog will do that for you.
|
||||
* @param action the action
|
||||
*/
|
||||
public void addAction(final DockingActionIf action) {
|
||||
public void addAction(DockingActionIf action) {
|
||||
dialogActions.add(action);
|
||||
addToolbarAction(action);
|
||||
popupManager.addAction(action);
|
||||
@ -1245,25 +1314,49 @@ public class DialogComponentProvider
|
||||
|
||||
private void addKeyBindingAction(DockingActionIf action) {
|
||||
|
||||
// add the action to the tool in order get key event management (key bindings
|
||||
// options and key event processing)
|
||||
DockingWindowManager dwm = DockingWindowManager.getActiveInstance();
|
||||
if (dwm == null) {
|
||||
// This implies the client dialog has been shown outside of the plugin framework. In
|
||||
// that case, the client will not get key event processing for dialog actions.
|
||||
return;
|
||||
}
|
||||
DialogActionProxy proxy = new DialogActionProxy(action);
|
||||
keyBindingProxyActions.add(proxy);
|
||||
|
||||
Tool tool = dwm.getTool();
|
||||
tool.addAction(new DialogActionProxy(action));
|
||||
// The tool will be null when clients add actions to this dialog before it has been shown.
|
||||
// This is different than ComponentProviders, which require a Tool at construction. The
|
||||
// DialogComponentProvider did not originally require a Tool at construction. Rather than
|
||||
// refactor the dialog to require a Tool, it was easier to simply delay adding actions until
|
||||
// the Tool is set when the dialog is first shown.
|
||||
if (tool != null) {
|
||||
tool.addAction(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAction(DockingActionIf action) {
|
||||
dialogActions.remove(action);
|
||||
JButton button = actionMap.remove(action);
|
||||
JButton button = toolbarButtonsByAction.remove(action);
|
||||
if (button != null && toolbar != null) {
|
||||
toolbar.remove(button);
|
||||
}
|
||||
|
||||
popupManager.removeAction(action);
|
||||
|
||||
removeKeyBindingAction(action);
|
||||
}
|
||||
|
||||
private void removeKeyBindingAction(DockingActionIf action) {
|
||||
|
||||
DialogActionProxy proxy = null;
|
||||
for (DialogActionProxy actionProxy : keyBindingProxyActions) {
|
||||
if (action == actionProxy.getAction()) {
|
||||
proxy = actionProxy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyBindingProxyActions.remove(proxy);
|
||||
if (tool != null) {
|
||||
tool.removeAction(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -49,6 +49,10 @@ public class DialogComponentProviderPopupActionManager {
|
||||
popupActions.add(action);
|
||||
}
|
||||
|
||||
void removeAction(DockingActionIf action) {
|
||||
popupActions.remove(action);
|
||||
}
|
||||
|
||||
void popupMenu(ActionContext actionContext, MouseEvent e) {
|
||||
if (e.isConsumed()) {
|
||||
return;
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -75,12 +75,15 @@ class DockableToolBarManager {
|
||||
ToolBarCloseAction closeAction = new ToolBarCloseAction(owner);
|
||||
closeButtonManager = new ToolBarItemManager(closeAction, winMgr);
|
||||
|
||||
CloseLastProviderAction closeLastProviderAction = new CloseLastProviderAction(owner);
|
||||
|
||||
ToolBarMenuAction dropDownAction = new ToolBarMenuAction(owner);
|
||||
menuButtonManager = new ToolBarItemManager(dropDownAction, winMgr);
|
||||
|
||||
// we need to add this action to the tool in order to use key bindings
|
||||
Tool tool = winMgr.getTool();
|
||||
tool.addLocalAction(provider, closeAction);
|
||||
tool.addLocalAction(provider, closeLastProviderAction);
|
||||
tool.addLocalAction(provider, dropDownAction);
|
||||
}
|
||||
|
||||
@ -216,6 +219,42 @@ class DockableToolBarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to close the provider on Escape if the provider is the last in the window. This
|
||||
* allows users to close transient providers (like search results) easily.
|
||||
*/
|
||||
private class CloseLastProviderAction extends DockingAction {
|
||||
|
||||
CloseLastProviderAction(String owner) {
|
||||
super("Close Window for Last Provider", owner, KeyBindingType.SHARED);
|
||||
setKeyBindingData(new KeyBindingData("ESCAPE"));
|
||||
setDescription("Close the window if this provider is the last provider in the window");
|
||||
markHelpUnnecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
ComponentPlaceholder placeholder = dockableComponent.getComponentWindowingPlaceholder();
|
||||
if (placeholder != null) {
|
||||
placeholder.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
DockingWindowManager dwm = DockingWindowManager.getActiveInstance();
|
||||
ComponentProvider provider = context.getComponentProvider();
|
||||
if (provider == null) {
|
||||
// Some context providers do not specify the provider when creating a contexts
|
||||
provider = dwm.getActiveComponentProvider();
|
||||
}
|
||||
if (provider != dockableComponent.getComponentProvider()) {
|
||||
return false; // not my provider
|
||||
}
|
||||
return dwm.isLastProviderInDetachedWindow(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions added to toolbar for displaying the drop-down menu.
|
||||
*/
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -62,10 +62,9 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
||||
* only happens during tests and one-off main methods that are not part of a
|
||||
* running tool.
|
||||
*
|
||||
* @param componentProvider the dialog content for this dialog
|
||||
* @return the hidden frame
|
||||
*/
|
||||
private static JFrame createHiddenParentFrame(DialogComponentProvider componentProvider) {
|
||||
private static JFrame createHiddenParentFrame() {
|
||||
|
||||
//
|
||||
// Note: we expect to only get here when there is no parent window found. This usually
|
||||
@ -118,7 +117,7 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
||||
}
|
||||
|
||||
private DockingDialog(DialogComponentProvider comp, Component centeredOnComponent) {
|
||||
super(createHiddenParentFrame(comp), comp.getTitle(), comp.isModal());
|
||||
super(createHiddenParentFrame(), comp.getTitle(), comp.isModal());
|
||||
init(comp);
|
||||
initializeLocationAndSize(centeredOnComponent);
|
||||
}
|
||||
@ -211,7 +210,11 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
||||
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
component.dialogShown();
|
||||
Tool tool = null;
|
||||
if (owningWindowManager != null) {
|
||||
tool = owningWindowManager.getTool();
|
||||
}
|
||||
component.dialogShown(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -299,6 +302,15 @@ public class DockingDialog extends JDialog implements HelpDescriptor {
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given provider is the provider owned by this dialog.
|
||||
* @param dcp the provider to check
|
||||
* @return true if the given provider is the provider owned by this dialog
|
||||
*/
|
||||
public boolean containsProvider(DialogComponentProvider dcp) {
|
||||
return component == dcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers the dialog on the given component.
|
||||
* @param c the component to center over.
|
||||
|
@ -461,6 +461,23 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
return isActiveWindowManager && isFocusedProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given provider is in a non-main window (a {@link DetachedWindowNode})
|
||||
* and is the last component provider in that window.
|
||||
* @param provider the provider
|
||||
* @return true if the last provider in a non-main window
|
||||
*/
|
||||
public boolean isLastProviderInDetachedWindow(ComponentProvider provider) {
|
||||
|
||||
Window providerWindow = getProviderWindow(provider);
|
||||
WindowNode providerNode = root.getNodeForWindow(providerWindow);
|
||||
if (!(providerNode instanceof DetachedWindowNode windowNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return windowNode.getComponentCount() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visible state of the set of docking windows.
|
||||
*
|
||||
@ -944,7 +961,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
if (visibleState) {
|
||||
movePlaceholderToFront(placeholder, false);
|
||||
if (placeholder.getNode() == null) {
|
||||
root.add(placeholder);
|
||||
root.addToNewWindow(placeholder);
|
||||
}
|
||||
if (requestFocus) {
|
||||
setNextFocusPlaceholder(placeholder);
|
||||
@ -1142,7 +1159,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
void movePlaceholder(ComponentPlaceholder source, Point p) {
|
||||
ComponentNode sourceNode = source.getNode();
|
||||
sourceNode.remove(source);
|
||||
root.add(source, p);
|
||||
root.addToNewWindow(source, p);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
@ -1368,6 +1385,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
|
||||
private synchronized ComponentPlaceholder maybeGetPlaceholderToFocus() {
|
||||
|
||||
if (nextFocusedPlaceholder != null) {
|
||||
ComponentPlaceholder temp = nextFocusedPlaceholder;
|
||||
setNextFocusPlaceholder(null);
|
||||
@ -1375,8 +1393,12 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Component permanentFocusOwner = kfm.getPermanentFocusOwner();
|
||||
Component focusOwner = kfm.getFocusOwner();
|
||||
if (focusOwner == null) {
|
||||
|
||||
// A null focus owner and a null permanent focus owner imply that Java did not know who
|
||||
// should get focus. Make sure one of our widgets gets focus.
|
||||
if (focusOwner == null && permanentFocusOwner == null) {
|
||||
return findNextFocusedComponent();
|
||||
}
|
||||
return null;
|
||||
@ -1446,6 +1468,7 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
|
||||
private ComponentPlaceholder findNextFocusedComponent() {
|
||||
|
||||
Iterator<ComponentPlaceholder> iterator = lastFocusedPlaceholders.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ComponentPlaceholder placeholder = iterator.next();
|
||||
@ -1454,7 +1477,6 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
return getActivePlaceholder(defaultProvider);
|
||||
}
|
||||
|
||||
@ -1797,7 +1819,8 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
|
||||
bestCenter = centeredOnComponent;
|
||||
}
|
||||
|
||||
DockingDialog dialog = DockingDialog.createDialog(bestParent, provider, bestCenter);
|
||||
DockingDialog dialog =
|
||||
DockingDialog.createDialog(bestParent, provider, bestCenter);
|
||||
dialog.setVisible(true);
|
||||
};
|
||||
|
||||
|
@ -216,8 +216,9 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(event);
|
||||
|
||||
// note: this call has no effect if 'action' is null
|
||||
SwingUtilities.notifyAction(action, keyStroke, event, event.getSource(),
|
||||
event.getModifiersEx());
|
||||
Object source = event.getSource();
|
||||
int modifiersEx = event.getModifiersEx();
|
||||
SwingUtilities.notifyAction(action, keyStroke, event, source, modifiersEx);
|
||||
|
||||
}
|
||||
return wasInProgress;
|
||||
@ -240,7 +241,16 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
||||
// processed with modal dialogs open. For now, do not let key bindings get processed
|
||||
// for modal dialogs. This can be changed in the future if needed.
|
||||
DockingDialog dialog = (DockingDialog) activeWindow;
|
||||
return !dialog.isModal();
|
||||
if (!dialog.isModal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow modal dialogs to process close keystrokes (e.g., ESCAPE) so they can be closed
|
||||
DialogComponentProvider provider = dialog.getComponent();
|
||||
if (provider.isCloseKeyStroke(keyStroke)) {
|
||||
return true;
|
||||
}
|
||||
return false; // modal dialog; non-escape key
|
||||
}
|
||||
return true; // default case; allow it through
|
||||
}
|
||||
@ -275,6 +285,15 @@ public class KeyBindingOverrideKeyEventDispatcher implements KeyEventDispatcher
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// Special Case: We allow Escape to go through. This doesn't seem useful to text widgets
|
||||
// but does allow for closing of windows. If we find text widgets that need Escape, then
|
||||
// we will have to update how we make this decision, such as by having the concerned text
|
||||
// widgets register actions for Escape and then check for that action.
|
||||
int code = event.getKeyCode();
|
||||
if (code == KeyEvent.VK_ESCAPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've made the executive decision to allow all keys to go through to the text component
|
||||
// unless they are modified with the 'Alt'/'Ctrl'/etc keys, unless they directly used
|
||||
// by the text component
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -52,6 +52,12 @@ abstract class Node {
|
||||
*/
|
||||
abstract List<Node> getChildren();
|
||||
|
||||
/**
|
||||
* Returns the number of visible components in this node.
|
||||
* @return the number of visible components in this node.
|
||||
*/
|
||||
abstract int getComponentCount();
|
||||
|
||||
/**
|
||||
* Recursively closes all nodes.
|
||||
*/
|
||||
|
@ -213,19 +213,19 @@ class RootNode extends WindowNode {
|
||||
}
|
||||
}
|
||||
|
||||
void add(ComponentPlaceholder info) {
|
||||
add(info, (Point) null);
|
||||
void addToNewWindow(ComponentPlaceholder placeholder) {
|
||||
addToNewWindow(placeholder, (Point) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new sub-window for the given component a positions it at the given location.
|
||||
*
|
||||
* @param info the component to be put in its own window.
|
||||
* @param placeholder the component to be put in its own window.
|
||||
* @param loc the location for the new window.
|
||||
*/
|
||||
void add(ComponentPlaceholder info, Point loc) {
|
||||
void addToNewWindow(ComponentPlaceholder placeholder, Point loc) {
|
||||
ComponentNode node = new ComponentNode(winMgr);
|
||||
info.setNode(node);
|
||||
placeholder.setNode(node);
|
||||
node.parent = this;
|
||||
DetachedWindowNode windowNode =
|
||||
new DetachedWindowNode(winMgr, this, node, dropTargetFactory);
|
||||
@ -233,18 +233,18 @@ class RootNode extends WindowNode {
|
||||
windowNode.setInitialLocation(loc.x, loc.y);
|
||||
}
|
||||
detachedWindows.add(windowNode);
|
||||
info.getNode().add(info);
|
||||
info.requestFocusWhenReady();
|
||||
placeholder.getNode().add(placeholder);
|
||||
placeholder.requestFocusWhenReady();
|
||||
notifyWindowAdded(windowNode);
|
||||
}
|
||||
|
||||
void add(ComponentPlaceholder info, WindowPosition initialPosition) {
|
||||
void add(ComponentPlaceholder placeholder, WindowPosition initialPosition) {
|
||||
if (initialPosition == WindowPosition.WINDOW) {
|
||||
add(info);
|
||||
addToNewWindow(placeholder);
|
||||
return;
|
||||
}
|
||||
ComponentNode node = new ComponentNode(winMgr);
|
||||
info.setNode(node);
|
||||
placeholder.setNode(node);
|
||||
if (child == null) {
|
||||
node.parent = this;
|
||||
child = node;
|
||||
@ -266,7 +266,7 @@ class RootNode extends WindowNode {
|
||||
}
|
||||
child.parent = this;
|
||||
}
|
||||
info.getNode().add(info);
|
||||
placeholder.getNode().add(placeholder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -635,6 +635,11 @@ class RootNode extends WindowNode {
|
||||
return windowWrapper.getWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getComponentCount() {
|
||||
return child.getComponentCount();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -205,6 +205,18 @@ class SplitNode extends Node {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getComponentCount() {
|
||||
int n = 0;
|
||||
if (child1 != null) {
|
||||
n += child1.getComponentCount();
|
||||
}
|
||||
if (child2 != null) {
|
||||
n += child2.getComponentCount();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return printTree();
|
||||
|
@ -350,7 +350,6 @@ public abstract class DockingAction implements DockingActionIf {
|
||||
|
||||
@Override
|
||||
public void setKeyBindingData(KeyBindingData newKeyBindingData) {
|
||||
|
||||
if (!supportsKeyBinding(newKeyBindingData)) {
|
||||
return;
|
||||
}
|
||||
@ -365,6 +364,11 @@ public abstract class DockingAction implements DockingActionIf {
|
||||
firePropertyChanged(KEYBINDING_DATA_PROPERTY, oldData, keyBindingData);
|
||||
}
|
||||
|
||||
// this allows framework classes to directly set the default value
|
||||
protected void setDefaultKeyBindingData(KeyBindingData kbd) {
|
||||
defaultKeyBindingData = kbd;
|
||||
}
|
||||
|
||||
private boolean supportsKeyBinding(KeyBindingData kbData) {
|
||||
|
||||
KeyBindingType type = getKeyBindingType();
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -363,9 +363,14 @@ public class KeyBindings {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) update the options with the new value
|
||||
ActionTrigger newTrigger = keyBinding.getActionTrigger();
|
||||
String fullName = getFullName();
|
||||
keyStrokeOptions.setActionTrigger(fullName, newTrigger);
|
||||
|
||||
// 2) update our state so the UI shows the new value
|
||||
currentKeyStroke = newTrigger.getKeyStroke();
|
||||
currentMouseBinding = newTrigger.getMouseBinding();
|
||||
}
|
||||
|
||||
private boolean hasChanged() {
|
||||
@ -409,11 +414,11 @@ public class KeyBindings {
|
||||
DockingActionIf action = getRepresentativeAction();
|
||||
KeyBindingData defaultBinding = action.getDefaultKeyBindingData();
|
||||
|
||||
cancelChanges();
|
||||
|
||||
if (!matches(defaultBinding)) {
|
||||
apply(options, defaultBinding);
|
||||
}
|
||||
|
||||
cancelChanges();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -120,14 +120,21 @@ public class SharedStubKeyBindingAction extends DockingAction implements Options
|
||||
void addClientAction(DockingActionIf action) {
|
||||
|
||||
// 1) Validate new action keystroke against existing actions
|
||||
ActionTrigger defaultKs = validateActionsHaveTheSameDefaultKeyStroke(action);
|
||||
ActionTrigger defaultTrigger = validateActionsHaveTheSameDefaultKeyStroke(action);
|
||||
|
||||
// 2) Add the action and the validated keystroke, as this is the default keystroke
|
||||
clientActions.put(action, defaultKs);
|
||||
clientActions.put(action, defaultTrigger);
|
||||
|
||||
// 3) Update the given action with the current option value. This allows clients to
|
||||
// add and remove actions after the tool has been initialized.
|
||||
updateActionKeyStrokeFromOptions(action, defaultKs);
|
||||
updateActionKeyStrokeFromOptions(action, defaultTrigger);
|
||||
|
||||
// 4) Store the default value for this stub so later requests to Restore Defaults will have
|
||||
// the correct value.
|
||||
if (defaultTrigger != null) {
|
||||
KeyBindingData defaultKbd = new KeyBindingData(defaultTrigger);
|
||||
setDefaultKeyBindingData(defaultKbd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -48,7 +48,7 @@ public class OptionsDialog extends ReusableDialogComponentProvider {
|
||||
|
||||
public OptionsDialog(String title, String rootNodeName, Options[] options,
|
||||
OptionsEditorListener listener, boolean showRestoreDefaultsButton) {
|
||||
super("OptionsDialog.Foofoo", true, false, true, false);
|
||||
super("Options Dialog", true, false, true, false);
|
||||
this.listener = listener;
|
||||
panel = new OptionsPanel(rootNodeName, options, showRestoreDefaultsButton,
|
||||
new OptionsPropertyChangeListener());
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -177,79 +177,6 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
||||
return previewLabel;
|
||||
}
|
||||
|
||||
// overridden to grab the Escape and enter key events before our parent window gets them
|
||||
@Override
|
||||
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
|
||||
|
||||
if (handleEscapeKey(ks, e, condition, pressed)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handleEnterKey(ks, e, condition, pressed)) {
|
||||
return true;
|
||||
}
|
||||
return super.processKeyBinding(ks, e, condition, pressed);
|
||||
}
|
||||
|
||||
private boolean handleEscapeKey(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
|
||||
if ((condition == JComponent.WHEN_FOCUSED) && (ks.getKeyCode() == KeyEvent.VK_ESCAPE)) {
|
||||
|
||||
if (getMatchingWindow().isShowing()) {
|
||||
hideMatchingWindow();
|
||||
e.consume();
|
||||
return true;
|
||||
}
|
||||
else if (pressed) {
|
||||
// do not return after this call so that the event will continue to be processed
|
||||
fireEditingCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleEnterKey(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
|
||||
if ((condition != JComponent.WHEN_FOCUSED) || (ks.getKeyCode() != KeyEvent.VK_ENTER)) {
|
||||
return false; // enter key not pressed!
|
||||
}
|
||||
|
||||
if (ignoreEnterKeyPress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// O.K., if we are consuming key presses, then we only want to do so when the selection
|
||||
// window is showing. This will close the selection window and not send the Enter event up
|
||||
// to our parent component.
|
||||
boolean listShowing = isMatchingListShowing();
|
||||
if (consumeEnterKeyPress) {
|
||||
if (listShowing) {
|
||||
setTextFromListOnEnterPress();
|
||||
validateChosenItemAgainstText(true);
|
||||
e.consume();
|
||||
return true; // don't let our parent see the event
|
||||
}
|
||||
else if (pressed) {
|
||||
validateChosenItemAgainstText(false);
|
||||
fireEditingStopped();
|
||||
}
|
||||
|
||||
// Return false, even though 'consumeEnterKeyPress' is set, so that our
|
||||
// parent can process the event.
|
||||
return false;
|
||||
}
|
||||
|
||||
// When we aren't consuming Enter key presses, then we just take the user's selection
|
||||
// and signal that editing is finished, while letting our parent component handle the event
|
||||
if (pressed) {
|
||||
setTextFromListOnEnterPress();
|
||||
validateChosenItemAgainstText(listShowing);
|
||||
fireEditingStopped();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void validateChosenItemAgainstText(boolean isListShowing) {
|
||||
//
|
||||
// If the text differs from that of the chosen item, then the implication is the user has
|
||||
@ -884,62 +811,117 @@ public class DropDownTextField<T> extends JTextField implements GComponent {
|
||||
KeyEvent.VK_KP_DOWN)) {
|
||||
//@formatter:on
|
||||
|
||||
if (!getMatchingWindow().isShowing()) {
|
||||
updateDisplayContents(getText());
|
||||
event.consume();
|
||||
}
|
||||
else { // update the window if it is showing
|
||||
if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_KP_UP) {
|
||||
decrementListSelection();
|
||||
}
|
||||
else {
|
||||
incrementListSelection();
|
||||
}
|
||||
event.consume();
|
||||
setTextFromSelectedListItemAndKeepMatchingWindowOpen();
|
||||
}
|
||||
handleArrowKey(event);
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_ENTER) {
|
||||
handleEnterKey(event);
|
||||
}
|
||||
else if (keyCode == KeyEvent.VK_ESCAPE) {
|
||||
handleEscapeKey(event);
|
||||
}
|
||||
|
||||
setToolTipText(getToolTipText());
|
||||
}
|
||||
|
||||
private void incrementListSelection() {
|
||||
int index = list.getSelectedIndex();
|
||||
int listSize = list.getModel().getSize();
|
||||
|
||||
if (index < 0) { // no selection
|
||||
index = 0;
|
||||
private void handleEscapeKey(KeyEvent event) {
|
||||
if (getMatchingWindow().isShowing()) {
|
||||
hideMatchingWindow();
|
||||
}
|
||||
else if (index == listSize - 1) { // last element selected - wrap
|
||||
index = 0;
|
||||
else {
|
||||
fireEditingCancelled();
|
||||
}
|
||||
else { // just increment
|
||||
index++;
|
||||
}
|
||||
|
||||
list.setSelectedIndex(index);
|
||||
list.ensureIndexIsVisible(index);
|
||||
event.consume();
|
||||
}
|
||||
|
||||
private void decrementListSelection() {
|
||||
int index = list.getSelectedIndex();
|
||||
int listSize = list.getModel().getSize();
|
||||
private void handleEnterKey(KeyEvent event) {
|
||||
|
||||
if (index < 0) { // no selection
|
||||
index = 0;
|
||||
}
|
||||
else if (index == 0) { // first element - wrap
|
||||
index = listSize - 1;
|
||||
}
|
||||
else { // just decrement
|
||||
index--;
|
||||
if (ignoreEnterKeyPress) {
|
||||
return;
|
||||
}
|
||||
|
||||
list.setSelectedIndex(index);
|
||||
list.ensureIndexIsVisible(index);
|
||||
// O.K., if we are consuming key presses, then we only want to do so when the selection
|
||||
// window is showing. This will close the selection window and not send the Enter event up
|
||||
// to our parent component.
|
||||
boolean listShowing = isMatchingListShowing();
|
||||
if (consumeEnterKeyPress) {
|
||||
if (listShowing) {
|
||||
setTextFromListOnEnterPress();
|
||||
validateChosenItemAgainstText(true);
|
||||
event.consume();
|
||||
return; // don't let our parent see the event
|
||||
}
|
||||
|
||||
validateChosenItemAgainstText(false);
|
||||
fireEditingStopped();
|
||||
|
||||
// Even though 'consumeEnterKeyPress' is set, do not consume the event so that our
|
||||
// parent can process the event.
|
||||
return;
|
||||
}
|
||||
|
||||
// When we aren't consuming Enter key presses, then just take the user's selection and
|
||||
// signal that editing is finished, while letting our parent component handle the event
|
||||
setTextFromListOnEnterPress();
|
||||
validateChosenItemAgainstText(listShowing);
|
||||
fireEditingStopped();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleArrowKey(KeyEvent event) {
|
||||
|
||||
int keyCode = event.getKeyCode();
|
||||
if (!getMatchingWindow().isShowing()) {
|
||||
updateDisplayContents(getText());
|
||||
event.consume();
|
||||
}
|
||||
else { // update the window if it is showing
|
||||
if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_KP_UP) {
|
||||
decrementListSelection();
|
||||
}
|
||||
else {
|
||||
incrementListSelection();
|
||||
}
|
||||
event.consume();
|
||||
setTextFromSelectedListItemAndKeepMatchingWindowOpen();
|
||||
}
|
||||
}
|
||||
|
||||
private void incrementListSelection() {
|
||||
int index = list.getSelectedIndex();
|
||||
int listSize = list.getModel().getSize();
|
||||
|
||||
if (index < 0) { // no selection
|
||||
index = 0;
|
||||
}
|
||||
else if (index == listSize - 1) { // last element selected - wrap
|
||||
index = 0;
|
||||
}
|
||||
else { // just increment
|
||||
index++;
|
||||
}
|
||||
|
||||
list.setSelectedIndex(index);
|
||||
list.ensureIndexIsVisible(index);
|
||||
}
|
||||
|
||||
private void decrementListSelection() {
|
||||
int index = list.getSelectedIndex();
|
||||
int listSize = list.getModel().getSize();
|
||||
|
||||
if (index < 0) { // no selection
|
||||
index = 0;
|
||||
}
|
||||
else if (index == 0) { // first element - wrap
|
||||
index = listSize - 1;
|
||||
}
|
||||
else { // just decrement
|
||||
index--;
|
||||
}
|
||||
|
||||
list.setSelectedIndex(index);
|
||||
list.ensureIndexIsVisible(index);
|
||||
}
|
||||
|
||||
// we know the cast is safe because we put the items in the list
|
||||
protected void setTextFromSelectedListItemAndKeepMatchingWindowOpen() {
|
||||
T selectedItem = list.getSelectedValue();
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -245,10 +245,12 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
protected TableSortingContext<T> createSortingContext(TableSortState newSortState) {
|
||||
|
||||
if (!isValidSortState(newSortState)) {
|
||||
Msg.error(this, """
|
||||
"Table '%s' sort is invalid. Assuming columns have been removed. \
|
||||
Setting unsorted.""".formatted(getName()));
|
||||
newSortState = TableSortState.createUnsortedSortState();
|
||||
if (!isDisposed) {
|
||||
Msg.error(this, """
|
||||
"Table '%s' sort is invalid. Assuming columns have been removed. \
|
||||
Setting unsorted.""".formatted(getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return new TableSortingContext<>(newSortState, getComparatorChain(newSortState));
|
||||
|
@ -396,6 +396,9 @@
|
||||
<optional>
|
||||
<attribute name="storage"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="stackspill"/>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="hidden_return">
|
||||
<optional>
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -150,6 +150,7 @@ public class MultiSlotAssign extends AssignAction {
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
int sizeLeft = dt.getLength();
|
||||
int align = dt.getAlignment();
|
||||
int iter = firstIter;
|
||||
int endIter = resource.getNumParamEntry();
|
||||
if (enforceAlignment) {
|
||||
@ -161,7 +162,6 @@ public class MultiSlotAssign extends AssignAction {
|
||||
} // Reached end of resource list
|
||||
if (entry.getType() == resourceType && entry.getAllGroups().length == 1) { // Single register
|
||||
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
|
||||
int align = dt.getAlignment();
|
||||
int regSize = entry.getSize();
|
||||
if (align <= regSize || (resourcesConsumed % align) == 0) {
|
||||
break;
|
||||
@ -186,11 +186,12 @@ public class MultiSlotAssign extends AssignAction {
|
||||
continue;
|
||||
} // Already consumed
|
||||
int trialSize = entry.getSize();
|
||||
entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize, 1, param);
|
||||
entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize, align, param);
|
||||
tmpStatus[entry.getGroup()] = -1; // Consume the register
|
||||
Varnode vn = new Varnode(param.address, trialSize);
|
||||
pieces.add(vn);
|
||||
sizeLeft -= trialSize;
|
||||
align = 1; // Treat remaining partial pieces as having no alignment requirement
|
||||
}
|
||||
boolean onePieceJoin = false;
|
||||
if (sizeLeft > 0) { // Have to use stack to get enough bytes
|
||||
@ -198,7 +199,7 @@ public class MultiSlotAssign extends AssignAction {
|
||||
return FAIL;
|
||||
}
|
||||
int grp = stackEntry.getGroup();
|
||||
tmpStatus[grp] = stackEntry.getAddrBySlot(tmpStatus[grp], sizeLeft, 1, param); // Consume all the space we need
|
||||
tmpStatus[grp] = stackEntry.getAddrBySlot(tmpStatus[grp], sizeLeft, align, param); // Consume all the space we need
|
||||
if (param.address == null) {
|
||||
return FAIL;
|
||||
}
|
||||
@ -257,6 +258,7 @@ public class MultiSlotAssign extends AssignAction {
|
||||
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
|
||||
}
|
||||
encoder.writeBool(ATTRIB_ALIGN, enforceAlignment);
|
||||
encoder.writeBool(ATTRIB_STACKSPILL, consumeFromStack);
|
||||
encoder.closeElement(ELEM_JOIN);
|
||||
}
|
||||
|
||||
@ -276,6 +278,9 @@ public class MultiSlotAssign extends AssignAction {
|
||||
else if (name.equals(ATTRIB_ALIGN.name())) {
|
||||
enforceAlignment = SpecXmlUtils.decodeBoolean(attrib.getValue());
|
||||
}
|
||||
else if (name.equals(ATTRIB_STACKSPILL.name())) {
|
||||
consumeFromStack = SpecXmlUtils.decodeBoolean(attrib.getValue());
|
||||
}
|
||||
}
|
||||
parser.end(elem);
|
||||
try {
|
||||
|
@ -114,7 +114,7 @@ public record AttributeId(String name, int id) {
|
||||
public static final AttributeId ATTRIB_ARRAYSIZE = new AttributeId("arraysize", 48);
|
||||
public static final AttributeId ATTRIB_CHAR = new AttributeId("char", 49);
|
||||
public static final AttributeId ATTRIB_CORE = new AttributeId("core", 50);
|
||||
public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51);
|
||||
// public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51); // deprecated
|
||||
public static final AttributeId ATTRIB_INCOMPLETE = new AttributeId("incomplete", 52);
|
||||
// public static final AttributeId ATTRIB_ENUMSIZE = new AttributeId("enumsize", 53); // deprecated
|
||||
// public static final AttributeId ATTRIB_INTSIZE = new AttributeId("intsize", 54); // deprecated
|
||||
@ -242,6 +242,7 @@ public record AttributeId(String name, int id) {
|
||||
|
||||
// public static final AttributeId ATTRIB_ADDRESS = new AttributeId("address", 148);
|
||||
public static final AttributeId ATTRIB_STORAGE = new AttributeId("storage", 149);
|
||||
public static final AttributeId ATTRIB_STACKSPILL = new AttributeId("stackspill", 150);
|
||||
|
||||
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 150);
|
||||
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 151);
|
||||
}
|
||||
|
@ -278,6 +278,13 @@ public class PcodeDataTypeManager {
|
||||
decoder.closeElement(el);
|
||||
return new PartialUnion(progDataTypes, dt, offset, size);
|
||||
}
|
||||
else if (meta.equals("partenum")) {
|
||||
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
|
||||
// int offset = (int) decoder.readSignedInteger(ATTRIB_OFFSET);
|
||||
// DataType dt = decodeDataType(decoder);
|
||||
decoder.closeElementSkipping(el);
|
||||
return AbstractIntegerDataType.getUnsignedDataType(size, progDataTypes);
|
||||
}
|
||||
else { // We typically reach here if the decompiler invents a new type
|
||||
// probably an unknown with a non-standard size
|
||||
int size = (int) decoder.readSignedInteger(ATTRIB_SIZE);
|
||||
@ -541,11 +548,10 @@ public class PcodeDataTypeManager {
|
||||
private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException {
|
||||
encoder.openElement(ELEM_TYPE);
|
||||
encodeNameIdAttributes(encoder, type);
|
||||
String metatype = type.isSigned() ? "int" : "uint";
|
||||
String metatype = type.isSigned() ? "enum_int" : "enum_uint";
|
||||
String[] names = type.getNames();
|
||||
encoder.writeString(ATTRIB_METATYPE, metatype);
|
||||
encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength());
|
||||
encoder.writeBool(ATTRIB_ENUM, true);
|
||||
for (String name : names) {
|
||||
encoder.openElement(ELEM_VAL);
|
||||
encoder.writeString(ATTRIB_NAME, name);
|
||||
|
@ -1,13 +1,26 @@
|
||||
# RV32D Standard Extension
|
||||
|
||||
macro invalidOpForMultD(fr1, fr2) {
|
||||
if ((fr1[0,63] != 0 || fr2[0,63] != 0x7FF0000000000000)
|
||||
&& (fr1[0,63] != 0x7FF0000000000000 || fr2[0,63] != 0)) goto <MULTVALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<MULTVALID>
|
||||
}
|
||||
|
||||
macro invalidOpForAddD(fr1, fr2) {
|
||||
if ((fr1 != 0xFFF0000000000000 || fr2 != 0x7FF0000000000000)
|
||||
&& (fr1 != 0x7FF0000000000000 || fr2 != 0xFFF0000000000000)) goto <ADDVALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<ADDVALID>
|
||||
}
|
||||
|
||||
# fadd.d D,S,T,m 02000053 fe00007f SIMPLE (0, 0)
|
||||
:fadd.d frd,frs1D,frs2D,FRM is frs1D & frd & frs2D & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x1
|
||||
{
|
||||
invalidOpForAddD(frs1D, frs2D);
|
||||
frd = frs1D f+ frs2D;
|
||||
}
|
||||
|
||||
|
||||
# fclass.d d,S e2001053 fff0707f SIMPLE (0, 0)
|
||||
:fclass.d rd,frs1D is frs1D & rd & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct3=0x1 & funct7=0x71 & op2024=0x0
|
||||
{
|
||||
@ -63,21 +76,40 @@
|
||||
# fcvt.w.d d,S,m c2000053 fff0007f SIMPLE (0, 0)
|
||||
:fcvt.w.d rdW,frs1D,FRM is frs1D & FRM & rdW & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x61 & op2024=0x0
|
||||
{
|
||||
rdW = trunc(frs1D);
|
||||
local low:8 = 0xC1E0000000000000;
|
||||
local high:8 = 0x41E0000000000000;
|
||||
if (frs1D f>= low && frs1D f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(WXLEN) = trunc(frs1D);
|
||||
rdW = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fcvt.wu.d d,S,m c2100053 fff0007f SIMPLE (0, 0)
|
||||
:fcvt.wu.d rdW,frs1D,FRM is frs1D & FRM & rdW & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x61 & op2024=0x1
|
||||
{
|
||||
local low:8 = 0x0;
|
||||
local high:8 = 0x41F0000000000000;
|
||||
if (frs1D f>= low && frs1D f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(WXLEN) = trunc(frs1D);
|
||||
#TODO unsigned
|
||||
rdW = trunc(frs1D);
|
||||
rdW = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fdiv.d D,S,T,m 1a000053 fe00007f SIMPLE (0, 0)
|
||||
:fdiv.d frd,frs1D,frs2D,FRM is frs1D & frd & frs2D & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0xd
|
||||
{
|
||||
if ((frs1D[0,63] != 0 || frs2D[0,63] != 0)
|
||||
&& (frs1D[0,63] != 0x7FF0000000000000 || frs2D[0,63] != 0x7FF0000000000000)) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
if (frs1D[52,11] == 0x7ff || frs1D[0,63] == 0 || frs2D[0,63] != 0) goto <NOTDIVZERO>;
|
||||
setFflags($(FFLAGDZ));
|
||||
<NOTDIVZERO>
|
||||
frd = frs1D f/ frs2D;
|
||||
}
|
||||
|
||||
@ -115,7 +147,10 @@
|
||||
# fmadd.d D,S,T,R,m 02000043 0600007f SIMPLE (0, 0)
|
||||
:fmadd.d frd,frs1D,frs2D,frs3D,FRM is frs1D & frd & frs2D & FRM & frs3D & op0001=0x3 & op0204=0x0 & op0506=0x2 & op2526=0x1
|
||||
{
|
||||
frd = (frs1D f* frs2D) f+ frs3D;
|
||||
invalidOpForMultD(frs1D, frs2D);
|
||||
local tmp:$(DFLEN) = frs1D f* frs2D;
|
||||
invalidOpForAddD(tmp, frs3D);
|
||||
frd = tmp f+ frs3D;
|
||||
}
|
||||
|
||||
|
||||
@ -154,13 +189,17 @@
|
||||
# fmsub.d D,S,T,R,m 02000047 0600007f SIMPLE (0, 0)
|
||||
:fmsub.d frd,frs1D,frs2D,frs3D,FRM is frs1D & frd & frs2D & FRM & frs3D & op0001=0x3 & op0204=0x1 & op0506=0x2 & op2526=0x1
|
||||
{
|
||||
frd = (frs1D f* frs2D) f- frs3D;
|
||||
invalidOpForMultD(frs1D, frs2D);
|
||||
local tmp:$(DFLEN) = frs1D f* frs2D;
|
||||
invalidOpForAddD(tmp, f- frs3D);
|
||||
frd = tmp f- frs3D;
|
||||
}
|
||||
|
||||
|
||||
# fmul.d D,S,T,m 12000053 fe00007f SIMPLE (0, 0)
|
||||
:fmul.d frd,frs1D,frs2D,FRM is frs1D & frd & frs2D & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x9
|
||||
{
|
||||
invalidOpForMultD(frs1D, frs2D);
|
||||
frd = frs1D f* frs2D;
|
||||
}
|
||||
|
||||
@ -168,14 +207,20 @@
|
||||
# fnmadd.d D,S,T,R,m 0200004f 0600007f SIMPLE (0, 0)
|
||||
:fnmadd.d frd,frs1D,frs2D,frs3D,FRM is frs1D & frd & frs2D & FRM & frs3D & op0001=0x3 & op0204=0x3 & op0506=0x2 & op2526=0x1
|
||||
{
|
||||
frd = (f- (frs1D f* frs2D)) f- frs3D;
|
||||
invalidOpForMultD(frs1D, frs2D);
|
||||
local tmp:$(DFLEN) = f- (frs1D f* frs2D);
|
||||
invalidOpForAddD(tmp, f- frs3D);
|
||||
frd = tmp f- frs3D;
|
||||
}
|
||||
|
||||
|
||||
# fnmsub.d D,S,T,R,m 0200004b 0600007f SIMPLE (0, 0)
|
||||
:fnmsub.d frd,frs1D,frs2D,frs3D,FRM is frs1D & frd & frs2D & FRM & frs3D & op0001=0x3 & op0204=0x2 & op0506=0x2 & op2526=0x1
|
||||
{
|
||||
frd = (f- (frs1D f* frs2D)) f+ frs3D;
|
||||
invalidOpForMultD(frs1D, frs2D);
|
||||
local tmp:$(DFLEN) = f- (frs1D f* frs2D);
|
||||
invalidOpForAddD(tmp, frs3D);
|
||||
frd = tmp f+ frs3D;
|
||||
}
|
||||
|
||||
|
||||
@ -235,6 +280,9 @@
|
||||
# fsqrt.d D,S,m 5a000053 fff0007f SIMPLE (0, 0)
|
||||
:fsqrt.d frd,frs1D,FRM is frs1D & frd & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x2d & op2024=0x0
|
||||
{
|
||||
if (nan(frs1D) | frs1D f>= 0) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
frd = sqrt(frs1D);
|
||||
}
|
||||
|
||||
@ -242,5 +290,6 @@
|
||||
# fsub.d D,S,T,m 0a000053 fe00007f SIMPLE (0, 0)
|
||||
:fsub.d frd,frs1D,frs2D,FRM is frs1D & frd & frs2D & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x5
|
||||
{
|
||||
invalidOpForAddD(frs1D, f- frs2D);
|
||||
frd = frs1D f- frs2D;
|
||||
}
|
||||
|
@ -1,9 +1,24 @@
|
||||
# RV32F Standard Extension
|
||||
|
||||
macro invalidOpForMultS(fr1, fr2) {
|
||||
if ((fr1[0,31] != 0 || fr2[0,31] != 0x7F800000)
|
||||
&& (fr1[0,31] != 0x7F800000 || fr2[0,31] != 0)) goto <MULTVALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<MULTVALID>
|
||||
}
|
||||
|
||||
macro invalidOpForAddS(fr1, fr2) {
|
||||
if ((fr1 != 0xFF800000 || fr2 != 0x7F800000)
|
||||
&& (fr1 != 0x7F800000 || fr2 != 0xFF800000)) goto <ADDVALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<ADDVALID>
|
||||
}
|
||||
|
||||
# fadd.s D,S,T,m 00000053 fe00007f SIMPLE (0, 0)
|
||||
:fadd.s frd,frs1S,frs2S,FRM is frs1S & frd & frs2S & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x0
|
||||
{
|
||||
local tmp:4 = frs1S f+ frs2S;
|
||||
invalidOpForAddS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = frs1S f+ frs2S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -29,7 +44,7 @@
|
||||
# fcvt.s.w D,s,m d0000053 fff0007f SIMPLE (0, 0)
|
||||
:fcvt.s.w frd,rs1W,FRM is frd & FRM & rs1W & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x68 & op2024=0x0
|
||||
{
|
||||
local tmp:4 = int2float(rs1W);
|
||||
local tmp:$(SFLEN) = int2float(rs1W);
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -39,7 +54,7 @@
|
||||
{
|
||||
#ATTN unsigned can be an issue here
|
||||
local u32:$(XLEN2) = zext(rs1W);
|
||||
local tmp:4 = int2float(u32);
|
||||
local tmp:$(SFLEN) = int2float(u32);
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -47,22 +62,42 @@
|
||||
# fcvt.w.s d,S,m c0000053 fff0007f SIMPLE (0, 0)
|
||||
:fcvt.w.s rdW,frs1S,FRM is frs1S & FRM & rdW & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x60 & op2024=0x0
|
||||
{
|
||||
rdW = trunc(frs1S);
|
||||
local low:4 = 0xCF000000;
|
||||
local high:4 = 0x4F000000;
|
||||
if (frs1S f>= low && frs1S f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(WXLEN) = trunc(frs1S);
|
||||
rdW = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fcvt.wu.s d,S,m c0100053 fff0007f SIMPLE (0, 0)
|
||||
:fcvt.wu.s rdW,frs1S,FRM is frs1S & FRM & rdW & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x60 & op2024=0x1
|
||||
{
|
||||
local low:4 = 0x0;
|
||||
local high:4 = 0x4F800000;
|
||||
if (frs1S f>= low && frs1S f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(WXLEN) = trunc(frs1S);
|
||||
#TODO unsigned
|
||||
rdW = trunc(frs1S);
|
||||
rdW = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fdiv.s D,S,T,m 18000053 fe00007f SIMPLE (0, 0)
|
||||
:fdiv.s frd,frs1S,frs2S,FRM is frs1S & frd & frs2S & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0xc
|
||||
{
|
||||
local tmp:4 = frs1S f/ frs2S;
|
||||
if ((frs1S[0,31] != 0 || frs2S[0,31] != 0)
|
||||
&& (frs1S[0,31] != 0x7F800000 || frs2S[0,31] != 0x7F800000)) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
if (frs1S[23,8] == 0xff || frs1S[0,31] == 0 || frs2S[0,31] != 0) goto <NOTDIVZERO>;
|
||||
setFflags($(FFLAGDZ));
|
||||
<NOTDIVZERO>
|
||||
|
||||
local tmp:$(SFLEN) = frs1S f/ frs2S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -99,7 +134,10 @@
|
||||
# fmadd.s D,S,T,R,m 00000043 0600007f SIMPLE (0, 0)
|
||||
:fmadd.s frd,frs1S,frs2S,frs3S,FRM is frs1S & frd & frs2S & FRM & frs3S & op0001=0x3 & op0204=0x0 & op0506=0x2 & op2526=0x0
|
||||
{
|
||||
local tmp:4 = (frs1S f* frs2S) f+ frs3S;
|
||||
invalidOpForMultS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = (frs1S f* frs2S);
|
||||
invalidOpForAddS(tmp, frs3S);
|
||||
tmp = tmp f+ frs3S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -139,7 +177,10 @@
|
||||
# fmsub.s D,S,T,R,m 00000047 0600007f SIMPLE (0, 0)
|
||||
:fmsub.s frd,frs1S,frs2S,frs3S,FRM is frs1S & frd & frs2S & FRM & frs3S & op0001=0x3 & op0204=0x1 & op0506=0x2 & op2526=0x0
|
||||
{
|
||||
local tmp:4 = (frs1S f* frs2S) f- frs3S;
|
||||
invalidOpForMultS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = (frs1S f* frs2S);
|
||||
invalidOpForAddS(tmp, f- frs3S);
|
||||
tmp = tmp f- frs3S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -147,7 +188,8 @@
|
||||
# fmul.s D,S,T,m 10000053 fe00007f SIMPLE (0, 0)
|
||||
:fmul.s frd,frs1S,frs2S,FRM is frs1S & frd & frs2S & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x8
|
||||
{
|
||||
local tmp:4 = frs1S f* frs2S;
|
||||
invalidOpForMultS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = frs1S f* frs2S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -170,7 +212,10 @@
|
||||
# fnmadd.s D,S,T,R,m 0000004f 0600007f SIMPLE (0, 0)
|
||||
:fnmadd.s frd,frs1S,frs2S,frs3S,FRM is frs1S & frd & frs2S & FRM & frs3S & op0001=0x3 & op0204=0x3 & op0506=0x2 & op2526=0x0
|
||||
{
|
||||
local tmp:4 = (f- (frs1S f* frs2S)) f- frs3S;
|
||||
invalidOpForMultS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = (f- (frs1S f* frs2S));
|
||||
invalidOpForAddS(tmp, f- frs3S);
|
||||
tmp = tmp f- frs3S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -178,7 +223,10 @@
|
||||
# fnmsub.s D,S,T,R,m 0000004b 0600007f SIMPLE (0, 0)
|
||||
:fnmsub.s frd,frs1S,frs2S,frs3S,FRM is frs1S & frd & frs2S & FRM & frs3S & op0001=0x3 & op0204=0x2 & op0506=0x2 & op2526=0x0
|
||||
{
|
||||
local tmp:4 = (f- (frs1S f* frs2S)) f+ frs3S;
|
||||
invalidOpForMultS(frs1S, frs2S);
|
||||
local tmp:$(SFLEN) = (f- (frs1S f* frs2S));
|
||||
invalidOpForAddS(tmp, frs3S);
|
||||
tmp = tmp f+ frs3S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -209,7 +257,7 @@
|
||||
# fneg.s D,U 20001053 fe00707f ALIAS (0, 0)
|
||||
:fneg.s frd,frs1S is frs1S & frd & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct3=0x1 & funct7=0x10 & op1519=op2024
|
||||
{
|
||||
local tmp:4 = f- frs1S;
|
||||
local tmp:$(SFLEN) = f- frs1S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -225,7 +273,7 @@
|
||||
# fabs.s D,U 20002053 fe00707f ALIAS (0, 0)
|
||||
:fabs.s frd,frs1S is frd & frs1S & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct3=0x2 & funct7=0x10 & op1519=op2024
|
||||
{
|
||||
local tmp:4 = abs(frs1S);
|
||||
local tmp:$(SFLEN) = abs(frs1S);
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -233,7 +281,10 @@
|
||||
# fsqrt.s D,S,m 58000053 fff0007f SIMPLE (0, 0)
|
||||
:fsqrt.s frd,frs1S,FRM is frs1S & frd & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x2c & op2024=0x0
|
||||
{
|
||||
local tmp:4 = sqrt(frs1S);
|
||||
if (nan(frs1S) | frs1S f>= 0) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(SFLEN) = sqrt(frs1S);
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
@ -241,7 +292,8 @@
|
||||
# fsub.s D,S,T,m 08000053 fe00007f SIMPLE (0, 0)
|
||||
:fsub.s frd,frs1S,frs2S,FRM is frs1S & frd & frs2S & FRM & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x4
|
||||
{
|
||||
local tmp:4 = frs1S f- frs2S;
|
||||
invalidOpForAddS(frs1S, f- frs2S);
|
||||
local tmp:$(SFLEN) = frs1S f- frs2S;
|
||||
fassignS(frd, tmp);
|
||||
}
|
||||
|
||||
|
@ -21,15 +21,27 @@
|
||||
# fcvt.l.d d,S,m c2200053 fff0007f SIMPLE (64, 0)
|
||||
:fcvt.l.d rdL,frs1D,FRM is frs1D & FRM & rdL & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x61 & op2024=0x2
|
||||
{
|
||||
rdL = trunc(frs1D);
|
||||
local low:8 = 0xC3E0000000000000;
|
||||
local high:8 = 0x43E0000000000000;
|
||||
if (frs1D f>= low && frs1D f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(DXLEN) = trunc(frs1D);
|
||||
rdL = zext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fcvt.lu.d d,S,m c2300053 fff0007f SIMPLE (64, 0)
|
||||
:fcvt.lu.d rdL,frs1D,FRM is frs1D & FRM & rdL & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x61 & op2024=0x3
|
||||
{
|
||||
local low:8 = 0x0;
|
||||
local high:8 = 0x43F0000000000000;
|
||||
if (frs1D f>= low && frs1D f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
#TODO unsigned
|
||||
rdL = trunc(frs1D);
|
||||
local tmp:$(DXLEN) = trunc(frs1D);
|
||||
rdL = zext(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,15 +3,27 @@
|
||||
# fcvt.l.s d,S,m c0200053 fff0007f SIMPLE (64, 0)
|
||||
:fcvt.l.s rdL,frs1S,FRM is frs1S & FRM & rdL & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x60 & op2024=0x2
|
||||
{
|
||||
rdL = trunc(frs1S);
|
||||
local low:4 = 0xDF000000;
|
||||
local high:4 = 0x5F000000;
|
||||
if (frs1S f>= low && frs1S f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(DXLEN) = trunc(frs1S);
|
||||
rdL = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
# fcvt.lu.s d,S,m c0300053 fff0007f SIMPLE (64, 0)
|
||||
:fcvt.lu.s rdL,frs1S,FRM is frs1S & FRM & rdL & op0001=0x3 & op0204=0x4 & op0506=0x2 & funct7=0x60 & op2024=0x3
|
||||
{
|
||||
local low:4 = 0x0;
|
||||
local high:4 = 0x5F800000;
|
||||
if (frs1S f>= low && frs1S f<= high) goto <VALID>;
|
||||
setFflags($(FFLAGNV));
|
||||
<VALID>
|
||||
local tmp:$(DXLEN) = trunc(frs1S);
|
||||
#TODO unsigned
|
||||
rdL = trunc(frs1S);
|
||||
rdL = sext(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,11 @@
|
||||
@define DXLEN 8
|
||||
@define QXLEN 16
|
||||
|
||||
@define FFLAGNX 0x1
|
||||
@define FFLAGUF 0x2
|
||||
@define FFLAGOF 0x4
|
||||
@define FFLAGDZ 0x8
|
||||
@define FFLAGNV 0x10
|
||||
|
||||
define pcodeop unimp;
|
||||
define pcodeop trap;
|
||||
@ -75,6 +80,10 @@ rdL: zero is r0711 & zero & op0711=0 { export 0:8; }
|
||||
# # 128-bit quad-precision $(QFLEN)
|
||||
# fmt: ".q" is op2526=3 { export $(QFLEN):1; }
|
||||
|
||||
macro setFflags(val) {
|
||||
fflags = fflags | val;
|
||||
fcsr = fcsr | val;
|
||||
}
|
||||
|
||||
frd: fr0711 is fr0711 { export fr0711; }
|
||||
frs1: fr1519 is fr1519 { export fr1519; }
|
||||
@ -83,16 +92,46 @@ frs3: fr2731 is fr2731 { export fr2731; }
|
||||
|
||||
#TODO dest may be bad, might need an assign macro
|
||||
#frdS: fr0711 is fr0711 { local tmp = fr0711:$(SFLEN); export tmp; }
|
||||
frs1S: fr1519 is fr1519 { local tmp = fr1519:$(SFLEN); export tmp; }
|
||||
frs2S: fr2024 is fr2024 { local tmp = fr2024:$(SFLEN); export tmp; }
|
||||
frs3S: fr2731 is fr2731 { local tmp = fr2731:$(SFLEN); export tmp; }
|
||||
frs1S: fr1519 is fr1519 {
|
||||
local tmp = fr1519:$(SFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[22,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
frs2S: fr2024 is fr2024 {
|
||||
local tmp = fr2024:$(SFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[22,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
frs3S: fr2731 is fr2731 {
|
||||
local tmp = fr2731:$(SFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[22,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
|
||||
@if ((FPSIZE == "64") || (FPSIZE == "128"))
|
||||
#TODO dest may be bad, might need an assign macro
|
||||
#frdD: fr0711 is fr0711 { local tmp = fr0711:$(DFLEN); export tmp; }
|
||||
frs1D: fr1519 is fr1519 { local tmp = fr1519:$(DFLEN); export tmp; }
|
||||
frs2D: fr2024 is fr2024 { local tmp = fr2024:$(DFLEN); export tmp; }
|
||||
frs3D: fr2731 is fr2731 { local tmp = fr2731:$(DFLEN); export tmp; }
|
||||
frs1D: fr1519 is fr1519 {
|
||||
local tmp = fr1519:$(DFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[51,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
frs2D: fr2024 is fr2024 {
|
||||
local tmp = fr2024:$(DFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[51,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
frs3D: fr2731 is fr2731 {
|
||||
local tmp = fr2731:$(DFLEN);
|
||||
local invalid:$(XLEN) = zext(nan(tmp) && tmp[51,1] == 0);
|
||||
setFflags($(FFLAGNV) * invalid);
|
||||
export tmp;
|
||||
}
|
||||
@endif
|
||||
|
||||
macro fassignS(dest, src) {
|
||||
@ -103,7 +142,6 @@ macro fassignS(dest, src) {
|
||||
@endif
|
||||
}
|
||||
|
||||
|
||||
macro assignW(dest, src) {
|
||||
@if ADDRSIZE == "32"
|
||||
dest = src;
|
||||
@ -136,7 +174,6 @@ macro assignD(dest, src) {
|
||||
@endif
|
||||
}
|
||||
|
||||
|
||||
immI: sop2031 is sop2031 { local tmp:$(XLEN) = sop2031; export tmp; }
|
||||
|
||||
immS: imm is op0711 & sop2531 [ imm = (sop2531 << 5) | op0711; ] { local tmp:$(XLEN) = imm; export tmp; }
|
||||
|
@ -2,23 +2,23 @@
|
||||
<!--Information from "MSP430 Embedded Application Binary Interface Rev A -->
|
||||
<compiler_spec>
|
||||
<data_organization>
|
||||
<machine_alignment value="2" />
|
||||
<default_alignment value="1" />
|
||||
<default_pointer_alignment value="2" />
|
||||
<pointer_size value="2" />
|
||||
<short_size value="2" />
|
||||
<integer_size value="2" />
|
||||
<wchar_size value="2" />
|
||||
<long_size value="4" />
|
||||
<long_long_size value="8" />
|
||||
<float_size value="4" />
|
||||
<double_size value="8" />
|
||||
<long_double_size value="8" />
|
||||
<machine_alignment value="2"/>
|
||||
<default_alignment value="1"/>
|
||||
<default_pointer_alignment value="2"/>
|
||||
<pointer_size value="2"/>
|
||||
<short_size value="2"/>
|
||||
<integer_size value="2"/>
|
||||
<wchar_size value="2"/>
|
||||
<long_size value="4"/>
|
||||
<long_long_size value="8"/>
|
||||
<float_size value="4"/>
|
||||
<double_size value="8"/>
|
||||
<long_double_size value="8"/>
|
||||
<size_alignment_map>
|
||||
<entry size="1" alignment="1" />
|
||||
<entry size="2" alignment="2" />
|
||||
<entry size="4" alignment="2" />
|
||||
<entry size="8" alignment="2" />
|
||||
<entry size="1" alignment="1"/>
|
||||
<entry size="2" alignment="2"/>
|
||||
<entry size="4" alignment="2"/>
|
||||
<entry size="8" alignment="2"/>
|
||||
</size_alignment_map>
|
||||
</data_organization>
|
||||
<global>
|
||||
@ -40,23 +40,43 @@
|
||||
<pentry maxsize="2" minsize="1">
|
||||
<register name="R15"/>
|
||||
</pentry>
|
||||
<pentry maxsize="500" minsize="1" align="2">
|
||||
<pentry maxsize="500" minsize="1" align="1">
|
||||
<addr space="stack" offset="2"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="struct" minsize="1"/>
|
||||
<convert_to_ptr/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any" maxsize="4"/>
|
||||
<join stackspill="true"/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any" minsize="5" maxsize="8"/>
|
||||
<join stackspill="false"/>
|
||||
</rule>
|
||||
</input>
|
||||
<output>
|
||||
<pentry minsize="1" maxsize="2">
|
||||
<register name="R12"/>
|
||||
</pentry>
|
||||
<pentry minsize="3" maxsize="4">
|
||||
<addr space="join" piece1="R13" piece2="R12"/>
|
||||
<pentry minsize="1" maxsize="2">
|
||||
<register name="R13"/>
|
||||
</pentry>
|
||||
<pentry minsize="5" maxsize="6">
|
||||
<addr space="join" piece1="R14" piece2="R13" piece3="R12"/>
|
||||
<pentry minsize="1" maxsize="2">
|
||||
<register name="R14"/>
|
||||
</pentry>
|
||||
<pentry minsize="7" maxsize="8">
|
||||
<addr space="join" piece1="R15" piece2="R14" piece3="R13" piece4="R12"/>
|
||||
<pentry minsize="1" maxsize="2">
|
||||
<register name="R15"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="struct"/>
|
||||
<hidden_return/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="any" maxsize="8"/>
|
||||
<join/>
|
||||
</rule>
|
||||
</output>
|
||||
<unaffected>
|
||||
<register name="SP"/>
|
||||
|
@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -52,14 +52,14 @@ public class BookmarkPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
public void testBookmarks() {
|
||||
removeFlowArrows();
|
||||
createBookmarks();
|
||||
performAction("Show Bookmarks", "BookmarkPlugin", true);
|
||||
performAction("Bookmarks", "BookmarkPlugin", true);
|
||||
captureIsolatedProvider(BookmarkProvider.class, 900, 300);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBookmarksFilter() {
|
||||
performAction("Show Bookmarks", "BookmarkPlugin", true);
|
||||
performAction("Bookmarks", "BookmarkPlugin", true);
|
||||
performAction("Filter Bookmarks", "BookmarkPlugin", false);
|
||||
waitForSwing();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user