Compare commits

...

49 Commits

Author SHA1 Message Date
Sleigh-InSPECtor
4b9ce97754
Merge a11bb2d30d into ac58d20693 2024-11-21 11:23:50 +10:30
ghidra1
ac58d20693 GP-0 Corrected test failures and fixed cspec encoding bug 2024-11-20 10:04:56 -05:00
Ryan Kurtz
22ddbc3f4b Merge remote-tracking branch 'origin/GP-5137_dev747368_delete_duplicate_processor_list_plugin' 2024-11-20 06:15:14 -05:00
Ryan Kurtz
55b2016b16 Merge remote-tracking branch 'origin/GP-5136_ghidra1_SarifStructureComponents' 2024-11-20 06:13:59 -05:00
Ryan Kurtz
4d96514f1b Merge remote-tracking branch 'origin/GP-1-dragonmacher-go-to-bug-fix' 2024-11-20 06:12:50 -05:00
ghidra1
79b008d7e4 Merge remote-tracking branch 'origin/GP-0-dragonmacher-test-fixes-11-19-24' 2024-11-19 18:53:52 -05:00
dragonmacher
ac22ed69f2 Test fixes 2024-11-19 18:07:54 -05:00
dragonmacher
68bb1e182a Fixed Go To for functions with default names so that a table of choices
is not shown
2024-11-19 15:09:12 -05:00
Ryan Kurtz
9421ec0ab0 Merge remote-tracking branch 'origin/GP-4885-dragonmacher-gnu-demangler-keep-raw-demangled--SQUASHED' 2024-11-19 13:11:55 -05:00
Ryan Kurtz
650c5d8756 Merge remote-tracking branch 'origin/GP-1-dragonmacher-focus-fix' 2024-11-19 12:47:54 -05:00
Ryan Kurtz
58e48456de Merge remote-tracking branch 'origin/GP-1-dragonmacher-dialog-escape-bug' 2024-11-19 12:47:36 -05:00
Ryan Kurtz
a7351baba4 Merge remote-tracking branch 'origin/GP-4202_James_msp430_cspec_improvements--SQUASHED' 2024-11-19 12:43:45 -05:00
Ryan Kurtz
329f8b4a19 Merge remote-tracking branch 'origin/patch' 2024-11-19 12:41:27 -05:00
Ryan Kurtz
fc7b0ef327 Merge remote-tracking branch 'origin/GP-0_d-millar_accidental_reversion_in_GP-4686' into patch 2024-11-19 12:37:29 -05:00
d-millar
454d57d754 GP-0: reversion for GP-4686 2024-11-19 12:21:39 -05:00
James
8abe4d76f0 GP-4202 msp430 cspec improvements 2024-11-19 09:57:12 -05:00
dragonmacher
8f08f2730d GP-4885 - Gnu Demangler - Save the raw demangled string 2024-11-19 09:21:42 -05:00
dragonmacher
b67fab8b51 Fix for Escape action not working for some dialogs 2024-11-18 18:45:17 -05:00
ghidra1
058ecb5eef GP-0 Revert GP-4886 change pending further review and rework 2024-11-18 11:43:17 -05:00
Ryan Kurtz
81432f75c7 GP-5132: PyGhidra launchers for VSCode (message for previous 'init' commit) 2024-11-18 11:05:12 -05:00
Ryan Kurtz
69d6495b60 init 2024-11-18 11:00:47 -05:00
dev747368
dc17dcd639 GP-5137 delete duplicate / unneeded processorlist plugin 2024-11-18 15:23:42 +00:00
Ryan Kurtz
4cbc94d960 Merge remote-tracking branch 'origin/GP-2470_EnumVariants' (#2581) 2024-11-18 06:26:59 -05:00
Ryan Kurtz
b12d39ee3f Merge remote-tracking branch 'origin/GP-5127_MultiSlotAlignment' 2024-11-18 06:22:59 -05:00
Ryan Kurtz
294f81dfc2 Merge remote-tracking branch 'origin/patch' 2024-11-18 06:20:49 -05:00
Ryan Kurtz
0411db7e88 Merge remote-tracking branch 'origin/GP-5130_HeapSequenceIndex' into patch 2024-11-18 06:18:32 -05:00
Ryan Kurtz
600c02703a Merge remote-tracking branch 'origin/GP-0_d-millar_fix_for_5120' (#7176) 2024-11-18 06:11:58 -05:00
dragonmacher
7712224ff1 Fixed focus issue that caused new dialogs to sometimes have focus taken
away
2024-11-15 20:52:34 -05:00
ghidra1
2390546961 GP-5136 Changed SARIF export to only include defined-components when
outputing a structure definition
2024-11-15 16:18:46 -05:00
caheckman
55a026b3ba GP-2470 Support for partial enums 2024-11-15 20:54:17 +00:00
caheckman
ab7684a230 GP-5127 Alignment fix for MultiSlotAssign 2024-11-15 20:31:06 +00:00
d-millar
eced291c6e GP-0: broken in 5120 2024-11-15 20:27:17 +00:00
caheckman
306e15a33d GP-5130 Negative offsets in HeapSequence 2024-11-15 19:09:46 +00:00
Ryan Kurtz
c9a51029bb Merge remote-tracking branch
'origin/GP-5120_d-millar_inferior_thread--SQUASHED' (Closes #7176)
2024-11-15 13:34:13 -05:00
Ryan Kurtz
a7c192c58e Merge remote-tracking branch
'origin/GP-5119_d-millar_possible_putreg_errors' (Closes #7174)
2024-11-15 13:32:52 -05:00
Ryan Kurtz
b536b99e60 Merge remote-tracking branch
'origin/GP-5105_d-millar_better_labels--SQUASHED' (Closes #7167)
2024-11-15 13:30:35 -05:00
Ryan Kurtz
83d38b8368 Merge remote-tracking branch 'origin/GP-4886_ghidra1_UnassignedStorageForDefaultDatatype' 2024-11-15 13:24:39 -05:00
Ryan Kurtz
a9c2c6efc7 Merge remote-tracking branch
'origin/GP-5135_dragonmacher_PR-7193_gemesa_listing-highlight-color'
(Closes #7193)
2024-11-15 13:23:07 -05:00
Ryan Kurtz
f7b21f5ea3 Merge remote-tracking branch 'origin/GP-0-dragonmacher-test-fixes-11-15-24' 2024-11-15 13:19:28 -05:00
dragonmacher
2ac3fe7b1b test fixes 2024-11-15 13:09:38 -05:00
Andras Gemes
bd9959730f Gui: Rework listing highlighter colors (dark theme) 2024-11-15 12:50:38 -05:00
d-millar
6be0884f18 GP-5120: single pattern for regions
GP-5120: single pattern for regions
GP-5120: mismatched errors
GP-5120: fix for inferior_thread is None
2024-11-15 11:54:05 -05:00
d-millar
8e6d269b8e GP-5105: same for ttd
GP-5105: better labeling
2024-11-15 11:53:45 -05:00
Ryan Kurtz
3fb7d914f5 Merge remote-tracking branch
'origin/GP-5114-dragonmacher-escape-action--SQUASHED' (#7136)
2024-11-15 10:00:45 -05:00
dragonmacher
aaf23cf096 GP-5114 - Updated component providers to close on Escape when they are the only provider in a window 2024-11-14 17:32:01 -05:00
d-millar
54cd023fc9 GP-5119: get_register_by_index can fail 2024-11-11 21:37:43 +00:00
caheckman
33646bbcdc Move UNASSIGNED check into LocalSymbolMap 2024-10-09 23:06:43 +00:00
ghidra1
38445e387b GP-4886 Do not assign storage for DEFAULT/unassigned datatype and create
undefined-typedef instead of DWORD-typedef as a default named type when
demangling.
2024-08-30 15:01:43 -04:00
Sleigh-InSPECtor
a11bb2d30d RISCV: NV and DZ float flags 2024-05-22 10:33:14 +09:30
82 changed files with 2261 additions and 1665 deletions

View File

@ -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)."

View File

@ -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}")

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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()) {

View File

@ -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|

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -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()));

View File

@ -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());

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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;

View File

@ -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|

View File

@ -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);
}

View File

@ -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

View File

@ -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") );

View File

@ -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());

View File

@ -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());

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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()));
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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;
}

View File

@ -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

View 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 &amp; \(FLAG_40\|FLAG_1\)\) .= 0\)</stringmatch>
<stringmatch name="Enum Reading #2" min="1" max="1">Stack_18\.flagfield\._4_4_ &amp; \(HIGH_200\|HIGH_100\) &gt;&gt; 0x20\) .= 0\)</stringmatch>
<stringmatch name="Enum Reading #3" min="1" max="1">ptr-&gt;flagfield &amp; \(FLAG_20\|FLAG_8\|FLAG_4\)\) .= 0\)</stringmatch>
<stringmatch name="Enum Reading #4" min="1" max="1">if \(\(ptrhigh-&gt;flagfield &amp; \(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-&gt;flagfield .= \(FLAG_100000\|FLAG_800\)\)</stringmatch>
</decompilertest>

View File

@ -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>

View File

@ -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

View File

@ -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) {

View File

@ -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
//

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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);
}
}
/**

View File

@ -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;

View File

@ -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.
*/

View File

@ -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.

View File

@ -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);
};

View File

@ -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

View File

@ -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.
*/

View File

@ -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
//==================================================================================================

View File

@ -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();

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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

View File

@ -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());

View File

@ -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();

View File

@ -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));

View File

@ -396,6 +396,9 @@
<optional>
<attribute name="storage"/>
</optional>
<optional>
<attribute name="stackspill"/>
</optional>
</element>
<element name="hidden_return">
<optional>

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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; }

View File

@ -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"/>

View File

@ -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();