mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-06-30 23:04:52 +00:00
Compare commits
56 Commits
ca9fadc5ac
...
2079e296e6
Author | SHA1 | Date | |
---|---|---|---|
|
2079e296e6 | ||
|
28846ef279 | ||
|
e7595341c4 | ||
|
bf71142709 | ||
|
36a707471e | ||
|
4b30e484b0 | ||
|
ae3f6feb70 | ||
|
2b73a6157f | ||
|
34272fd3ff | ||
|
3b6d5e43ce | ||
|
b86ad84c04 | ||
|
72d4a342a6 | ||
|
b68fa6c745 | ||
|
62f41a7179 | ||
|
a977a35f5f | ||
|
d5cbda1e21 | ||
|
4d8ec78908 | ||
|
02aba11104 | ||
|
184c657cfd | ||
|
13821930da | ||
|
e9e4ee48ce | ||
|
21a3896018 | ||
|
eb5e6a323a | ||
|
ea785546cf | ||
|
008a4ef948 | ||
|
5ab72bf4f2 | ||
|
627e3f14fa | ||
|
b4ef357e53 | ||
|
6b94d4b69b | ||
|
2fc70183e5 | ||
|
625df03c15 | ||
|
8336bdde74 | ||
|
0e33958c76 | ||
|
20702592dd | ||
|
1314f21613 | ||
|
42710d014d | ||
|
87747c20b3 | ||
|
7198cad876 | ||
|
9f8b03a90f | ||
|
38eec6d58b | ||
|
ad56d6b0e7 | ||
|
b43c58f07b | ||
|
fd95d254dd | ||
|
4b39eaaebe | ||
|
fcf4376222 | ||
|
dd72290f8d | ||
|
0229b93b0d | ||
|
82677d9bb7 | ||
|
999004245a | ||
|
740e5d94c5 | ||
|
043f66b9af | ||
|
1087a923b1 | ||
|
22bd5e2d3c | ||
|
2630edd76e | ||
|
5604178194 | ||
|
94bae1a917 |
|
@ -1,6 +1,7 @@
|
|||
##VERSION: 2.0
|
||||
##MODULE IP: JSch License
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/debugger-launchers/local-gdb.bat||GHIDRA||||END|
|
||||
data/scripts/fallback_info_proc_mappings.gdb||GHIDRA||||END|
|
||||
data/scripts/fallback_maintenance_info_sections.gdb||GHIDRA||||END|
|
||||
data/scripts/getpid-linux-i386.gdb||GHIDRA||||END|
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
::@title gdb
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Launch with <tt>gdb</tt></h3>
|
||||
::@desc <p>
|
||||
::@desc This will launch the target on the local machine using <tt>gdb</tt>.
|
||||
::@desc For setup instructions, press <b>F1</b>.
|
||||
::@desc </p>
|
||||
::@desc </body></html>
|
||||
::@menu-group local
|
||||
::@icon icon.debugger
|
||||
::@help TraceRmiLauncherServicePlugin#gdb
|
||||
::@enum StartCmd:str run start starti
|
||||
::@arg :file "Image" "The target binary executable image"
|
||||
::@args "Arguments" "Command-line arguments to pass to the target"
|
||||
::@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||
::@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."
|
||||
::@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
||||
::@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||
|
||||
@echo off
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||
IF EXIST %GHIDRA_HOME%\.git (
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||
)
|
||||
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||
)
|
||||
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||
|
||||
set target_image=%1
|
||||
shift
|
||||
set target_args=%*
|
||||
|
||||
"%OPT_GDB_PATH%" ^
|
||||
-q ^
|
||||
-ex "set pagination off" ^
|
||||
-ex "set confirm off" ^
|
||||
-ex "show version" ^
|
||||
-ex "python import ghidragdb" ^
|
||||
-ex "target exec %target_image%" ^
|
||||
-ex "set args %target_args%" ^
|
||||
-ex "set inferior-tty %TTY_TARGET%" ^
|
||||
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||
-ex "ghidra trace start" ^
|
||||
-ex "ghidra trace sync-enable" ^
|
||||
-ex "%OPT_START_CMD%" ^
|
||||
-ex "set confirm on" ^
|
||||
-ex "set pagination on" ^
|
||||
|
||||
|
|
@ -85,9 +85,9 @@ data64_compiler_map = {
|
|||
|
||||
x86_compiler_map = {
|
||||
'GNU/Linux': 'gcc',
|
||||
'Windows': 'Visual Studio',
|
||||
'Windows': 'windows',
|
||||
# This may seem wrong, but Ghidra cspecs really describe the ABI
|
||||
'Cygwin': 'Visual Studio',
|
||||
'Cygwin': 'windows',
|
||||
}
|
||||
|
||||
compiler_map = {
|
||||
|
@ -104,7 +104,7 @@ def get_arch():
|
|||
|
||||
def get_endian():
|
||||
parm = gdb.parameter('endian')
|
||||
if parm != 'auto':
|
||||
if not parm in ['', 'auto', 'default']:
|
||||
return parm
|
||||
# Once again, we have to hack using the human-readable 'show'
|
||||
show = gdb.execute('show endian', to_string=True)
|
||||
|
@ -132,7 +132,7 @@ def get_osabi():
|
|||
def compute_ghidra_language():
|
||||
# First, check if the parameter is set
|
||||
lang = gdb.parameter('ghidra-language')
|
||||
if lang != 'auto':
|
||||
if not lang in ['', 'auto', 'default']:
|
||||
return lang
|
||||
|
||||
# Get the list of possible languages for the arch. We'll need to sift
|
||||
|
@ -157,7 +157,7 @@ def compute_ghidra_language():
|
|||
def compute_ghidra_compiler(lang):
|
||||
# First, check if the parameter is set
|
||||
comp = gdb.parameter('ghidra-compiler')
|
||||
if comp != 'auto':
|
||||
if not comp in ['', 'auto', 'default']:
|
||||
return comp
|
||||
|
||||
# Check if the selected lang has specific compiler recommendations
|
||||
|
|
|
@ -19,9 +19,13 @@ import os.path
|
|||
import socket
|
||||
import time
|
||||
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
print(f"Unable to import 'psutil' - check that it has been installed")
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import Client, Address, AddressRange, TraceObject
|
||||
import psutil
|
||||
|
||||
import gdb
|
||||
|
||||
|
|
|
@ -93,8 +93,14 @@ class InferiorState(object):
|
|||
if first or hashable_frame not in self.visited:
|
||||
commands.putreg(
|
||||
frame, util.get_register_descs(frame.architecture(), 'general'))
|
||||
commands.putmem("$pc", "1", from_tty=False)
|
||||
commands.putmem("$sp", "1", from_tty=False)
|
||||
try:
|
||||
commands.putmem("$pc", "1", from_tty=False)
|
||||
except MemoryError as e:
|
||||
print(f"Couldn't record page with PC: {e}")
|
||||
try:
|
||||
commands.putmem("$sp", "1", from_tty=False)
|
||||
except MemoryError as e:
|
||||
print(f"Couldn't record page with SP: {e}")
|
||||
self.visited.add(hashable_frame)
|
||||
if first or self.regions or self.threads or self.modules:
|
||||
# Sections, memory syscalls, or stack allocations
|
||||
|
|
|
@ -28,6 +28,8 @@ def _compute_gdb_ver():
|
|||
top = blurb.split('\n')[0]
|
||||
full = top.split(' ')[-1]
|
||||
major, minor = full.split('.')[:2]
|
||||
if '-' in minor:
|
||||
minor = minor[:minor.find('-')]
|
||||
return GdbVersion(full, int(major), int(minor))
|
||||
|
||||
|
||||
|
@ -379,7 +381,10 @@ class RegisterDesc(namedtuple('BaseRegisterDesc', ['name'])):
|
|||
|
||||
def get_register_descs(arch, group='all'):
|
||||
if hasattr(arch, "registers"):
|
||||
return arch.registers(group)
|
||||
try:
|
||||
return arch.registers(group)
|
||||
except ValueError: # No such group, or version too old
|
||||
return arch.registers()
|
||||
else:
|
||||
descs = []
|
||||
regset = gdb.execute(
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
##MODULE IP: Apache License 2.0 with LLVM Exceptions
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/debugger-launchers/local-lldb.bat||GHIDRA||||END|
|
||||
src/llvm-project/lldb/bindings/java/java-typemaps.swig||Apache License 2.0 with LLVM Exceptions||||END|
|
||||
src/llvm-project/lldb/bindings/java/java.swig||Apache License 2.0 with LLVM Exceptions||||END|
|
||||
src/main/py/LICENSE||GHIDRA||||END|
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
::@title lldb
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Launch with <tt>lldb</tt></h3>
|
||||
::@desc <p>
|
||||
::@desc This will launch the target on the local machine using <tt>lldb</tt>.
|
||||
::@desc For setup instructions, press <b>F1</b>.
|
||||
::@desc </p>
|
||||
::@desc </body></html>
|
||||
::@menu-group local
|
||||
::@icon icon.debugger
|
||||
::@help TraceRmiLauncherServicePlugin#lldb
|
||||
::@enum StartCmd:str "process launch" "process launch --stop-at-entry"
|
||||
::@arg :file "Image" "The target binary executable image"
|
||||
::@args "Arguments" "Command-line arguments to pass to the target"
|
||||
::@env OPT_LLDB_PATH:file="lldb" "lldb command" "The path to lldb. Omit the full path to resolve using the system PATH."
|
||||
::@env OPT_START_CMD:StartCmd="process launch" "Run command" "The lldb command to actually run the target."
|
||||
::@env OPT_EXTRA_TTY:bool=false "Target TTY" "Provide a separate terminal emulator for the target."
|
||||
::@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||
|
||||
@echo off
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||
IF EXIST %GHIDRA_HOME%\.git (
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||
)
|
||||
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||
)
|
||||
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||
|
||||
set target_image=%1
|
||||
shift
|
||||
set target_args=%*
|
||||
|
||||
IF DEFINED target_args (
|
||||
argspart=-o "settings set target.run-args %target_args%"
|
||||
)
|
||||
|
||||
IF DEFINED TARGET_TTY (
|
||||
ttypart=-o "settings set target.output-path %TTY_TARGET%" -o "settings set target.input-path $TTY_TARGET"
|
||||
)
|
||||
|
||||
"%OPT_LLDB_PATH%" ^
|
||||
-o "version" ^
|
||||
-o "script import ghidralldb" ^
|
||||
-o "target create %target_image%" ^
|
||||
%argspart% ^
|
||||
%ttypart% ^
|
||||
-o "ghidra trace connect %GHIDRA_TRACE_RMI_ADDR%" ^
|
||||
-o "ghidra trace start" ^
|
||||
-o "ghidra trace sync-enable" ^
|
||||
-o "%OPT_START_CMD%"
|
|
@ -23,7 +23,10 @@ import socket
|
|||
import sys
|
||||
import time
|
||||
|
||||
import psutil
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
print(f"Unable to import 'psutil' - check that it has been installed")
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import Client, Address, AddressRange, TraceObject
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -29,10 +31,13 @@ public class BatchScriptTraceRmiLaunchOpinion extends AbstractTraceRmiLaunchOpin
|
|||
@Override
|
||||
public Collection<TraceRmiLaunchOffer> getOffers(TraceRmiLauncherServicePlugin plugin,
|
||||
Program program) {
|
||||
return getScriptPaths(plugin.getTool())
|
||||
.flatMap(rf -> Stream.of(rf.listFiles(crf -> crf.getName().endsWith(".bat"))))
|
||||
.flatMap(sf -> createOffer(plugin, program, sf))
|
||||
.collect(Collectors.toList());
|
||||
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
|
||||
return getScriptPaths(plugin.getTool())
|
||||
.flatMap(rf -> Stream.of(rf.listFiles(crf -> crf.getName().endsWith(".bat"))))
|
||||
.flatMap(sf -> createOffer(plugin, program, sf))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
protected Stream<TraceRmiLaunchOffer> createOffer(TraceRmiLauncherServicePlugin plugin,
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
|
@ -29,10 +31,13 @@ public class UnixShellScriptTraceRmiLaunchOpinion extends AbstractTraceRmiLaunch
|
|||
@Override
|
||||
public Collection<TraceRmiLaunchOffer> getOffers(TraceRmiLauncherServicePlugin plugin,
|
||||
Program program) {
|
||||
return getScriptPaths(plugin.getTool())
|
||||
.flatMap(rf -> Stream.of(rf.listFiles(crf -> crf.getName().endsWith(".sh"))))
|
||||
.flatMap(sf -> createOffer(plugin, program, sf))
|
||||
.collect(Collectors.toList());
|
||||
if (OperatingSystem.CURRENT_OPERATING_SYSTEM != OperatingSystem.WINDOWS) {
|
||||
return getScriptPaths(plugin.getTool())
|
||||
.flatMap(rf -> Stream.of(rf.listFiles(crf -> crf.getName().endsWith(".sh"))))
|
||||
.flatMap(sf -> createOffer(plugin, program, sf))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
protected Stream<TraceRmiLaunchOffer> createOffer(TraceRmiLauncherServicePlugin plugin,
|
||||
|
|
|
@ -33,15 +33,17 @@ public class PrivatelyQueuedListener<P> {
|
|||
DataStructureErrorHandlerFactory.createListenerErrorHandler();
|
||||
|
||||
protected class ListenerHandler implements InvocationHandler {
|
||||
private static final Method OBJECT_HASHCODE;
|
||||
static {
|
||||
private static final Method OBJECT_HASHCODE = initObjectHashCode();
|
||||
|
||||
private static Method initObjectHashCode() {
|
||||
try {
|
||||
OBJECT_HASHCODE = Object.class.getMethod("hashCode");
|
||||
return Object.class.getMethod("hashCode");
|
||||
}
|
||||
catch (NoSuchMethodException | SecurityException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected final Class<P> iface;
|
||||
|
||||
public ListenerHandler(Class<P> iface) {
|
||||
|
|
|
@ -732,3 +732,11 @@ PCodeTest({
|
|||
'language_id': 'Xtensa:BE:32:default',
|
||||
'ccflags': '-L %(toolchain_dir)s/lib/gcc/xtensa-elf/%(gcc_version)s',
|
||||
})
|
||||
|
||||
PCodeTest({
|
||||
'name': 'ARC700',
|
||||
'build_all': 1,
|
||||
'toolchain': 'ARC/arc700-elf',
|
||||
'language_id': 'ARCompact:LE:32:default',
|
||||
'ccflags': '-mcpu=arc700 -lgcc',
|
||||
})
|
||||
|
|
|
@ -609,6 +609,13 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-
|
|||
<P><A name="h"></A><FONT size="7"><B>H</B></FONT></P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H2><A name="HexShort"></A>Hex Short</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>A display format in the <A href="#ByteViewer">Byte Viewer</A> used to display short
|
||||
values in hex.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="HexInteger"></A>Hex Integer</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -616,6 +623,20 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-
|
|||
values in hex.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="HexLong"></A>Hex Long</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>A display format in the <A href="#ByteViewer">Byte Viewer</A> used to display long
|
||||
values in hex.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="HexLongLong"></A>Hex Long Long</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>A display format in the <A href="#ByteViewer">Byte Viewer</A> used to display longlong
|
||||
values in hex.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="HijackedFile"></A>Hijacked File</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -42,11 +42,10 @@ public class SetRegisterCmd implements Command<Program> {
|
|||
* A null value indicates that no value should be associated over the range.
|
||||
*/
|
||||
public SetRegisterCmd(Register register, Address start, Address end, BigInteger value) {
|
||||
if (start.getAddressSpace() != end.getAddressSpace()) {
|
||||
if (!start.getAddressSpace().equals(end.getAddressSpace())) {
|
||||
throw new IllegalArgumentException(
|
||||
"start and end address must be in the same address space");
|
||||
"start and end address must be within the same address space");
|
||||
}
|
||||
|
||||
this.register = register;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
*/
|
||||
package ghidra.app.merge.listing;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import ghidra.app.merge.MergeConstants;
|
||||
import ghidra.app.merge.ProgramMultiUserMergeManager;
|
||||
import ghidra.app.merge.tool.ListingMergePanel;
|
||||
|
@ -24,17 +32,10 @@ import ghidra.program.model.lang.Register;
|
|||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
/**
|
||||
* <code>RegisterMergeManager</code> handles the merge for a single named register.
|
||||
*/
|
||||
|
@ -98,6 +99,17 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
* @param latestChanges the address set of changes between original and latest versioned program.
|
||||
* @param myChanges the address set of changes between original and my modified program.
|
||||
*/
|
||||
/**
|
||||
* Creates a RegisterMergeManager.
|
||||
* @param registerName
|
||||
* @param mergeManager
|
||||
* @param resultPgm the program to be updated with the result of the merge.
|
||||
* @param originalPgm the program that was checked out.
|
||||
* @param latestPgm the latest checked-in version of the program.
|
||||
* @param myPgm the program requesting to be checked in.
|
||||
* @param latestChanges the address set of changes between original and latest versioned program.
|
||||
* @param myChanges the address set of changes between original and my modified program.
|
||||
*/
|
||||
RegisterMergeManager(String registerName, ProgramMultiUserMergeManager mergeManager,
|
||||
Program resultPgm, Program originalPgm, Program latestPgm, Program myPgm,
|
||||
ProgramChangeSet latestChanges, ProgramChangeSet myChanges) {
|
||||
|
@ -123,10 +135,7 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.merge.MergeResolver#apply()
|
||||
*/
|
||||
public void apply() {
|
||||
void apply() {
|
||||
conflictOption = conflictPanel.getSelectedOptions();
|
||||
|
||||
// If the "Use For All" check box is selected
|
||||
|
@ -138,27 +147,10 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
merge(min, max, resultReg);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.merge.MergeResolver#cancel()
|
||||
*/
|
||||
public void cancel() {
|
||||
void cancel() {
|
||||
conflictOption = CANCELED;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.merge.MergeResolver#getDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return "Merge Register";
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.merge.MergeResolver#getName()
|
||||
*/
|
||||
public String getName() {
|
||||
return "Register Merger";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param monitor
|
||||
|
@ -167,9 +159,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
if (conflictSet != null) {
|
||||
return; //This method only needs to be called once.
|
||||
}
|
||||
RegisterConflicts rc =
|
||||
new RegisterConflicts(registerName, originalContext, latestContext, myContext,
|
||||
resultContext);
|
||||
RegisterConflicts rc = new RegisterConflicts(registerName, originalContext, latestContext,
|
||||
myContext, resultContext);
|
||||
Memory resultMem = resultPgm.getMemory();
|
||||
AddressSetView myDiffs =
|
||||
rc.getRegisterDifferences(registerName, originalContext, myContext, mySet, monitor);
|
||||
|
@ -177,8 +168,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
conflictSet = new AddressSet();
|
||||
rvrs = rc.getConflicts(setToCheck, monitor);
|
||||
if (rvrs.length > 0) {
|
||||
for (int j = 0; j < rvrs.length; j++) {
|
||||
conflictSet.add(rvrs[j]);
|
||||
for (AddressRange rvr : rvrs) {
|
||||
conflictSet.add(rvr);
|
||||
}
|
||||
}
|
||||
autoSet = setToCheck.subtract(conflictSet);
|
||||
|
@ -187,20 +178,19 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
/**
|
||||
* Merges all the register values for the named register being managed by this merge manager.
|
||||
* @param monitor the monitor that provides feedback to the user.
|
||||
* @throws ProgramConflictException
|
||||
* @throws CancelledException if the user cancels
|
||||
*/
|
||||
public void merge(TaskMonitor monitor) throws CancelledException {
|
||||
monitor.setMessage("Auto-merging " + registerName +
|
||||
" Register Values and determining conflicts.");
|
||||
monitor.setMessage(
|
||||
"Auto-merging " + registerName + " Register Values and determining conflicts.");
|
||||
|
||||
determineConflicts(monitor);
|
||||
|
||||
// Auto merge any program context changes from my program where the
|
||||
// resulting program has the mem addresses but the latest doesn't.
|
||||
AddressRangeIterator arIter = autoSet.getAddressRanges();
|
||||
try {
|
||||
while (arIter.hasNext() && !monitor.isCancelled()) {
|
||||
while (arIter.hasNext() && !monitor.isCancelled()) {
|
||||
try {
|
||||
AddressRange range = arIter.next();
|
||||
Address rangeMin = range.getMinAddress();
|
||||
Address rangeMax = range.getMaxAddress();
|
||||
|
@ -215,9 +205,10 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
valueRange.getMaxAddress(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// ignore since processor context-register is not handled by this merge manager
|
||||
catch (ContextChangeException e) {
|
||||
// processor context-register is not handled here and should not occur
|
||||
throw new AssertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
int totalConflicts = rvrs.length;
|
||||
|
@ -298,7 +289,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
resultContext.setValue(resultRegister, minAddress, maxAddress, myValue);
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// ignore since this merge manager does not handle the processor context register
|
||||
// processor context-register is not handled here and should not occur
|
||||
throw new AssertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,7 +308,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
}
|
||||
|
||||
private void showMergePanel(final Address minAddress, final Address maxAddress,
|
||||
final BigInteger latestValue, final BigInteger myValue, final BigInteger originalValue) {
|
||||
final BigInteger latestValue, final BigInteger myValue,
|
||||
final BigInteger originalValue) {
|
||||
this.min = minAddress;
|
||||
this.max = maxAddress;
|
||||
try {
|
||||
|
@ -339,9 +332,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VerticalChoicesPanel panel =
|
||||
getConflictsPanel(minAddress, maxAddress, latestValue, myValue,
|
||||
originalValue, changeListener);
|
||||
VerticalChoicesPanel panel = getConflictsPanel(minAddress, maxAddress,
|
||||
latestValue, myValue, originalValue, changeListener);
|
||||
listingMergePanel.setBottomComponent(panel);
|
||||
}
|
||||
});
|
||||
|
@ -377,12 +369,11 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
conflictPanel.clear();
|
||||
}
|
||||
conflictPanel.setTitle("\"" + registerName + "\" Register Value");
|
||||
String text =
|
||||
"Register: " + ConflictUtility.getEmphasizeString(registerName) +
|
||||
ConflictUtility.spaces(4) + "Address Range: " +
|
||||
ConflictUtility.getAddressString(minAddress) + " - " +
|
||||
ConflictUtility.getAddressString(maxAddress) +
|
||||
"<br>Select the desired register value for the address range.";
|
||||
String text = "Register: " + ConflictUtility.getEmphasizeString(registerName) +
|
||||
ConflictUtility.spaces(4) + "Address Range: " +
|
||||
ConflictUtility.getAddressString(minAddress) + " - " +
|
||||
ConflictUtility.getAddressString(maxAddress) +
|
||||
"<br>Select the desired register value for the address range.";
|
||||
conflictPanel.setHeader(text);
|
||||
conflictPanel.setRowHeader(getRegisterInfo(-1, null));
|
||||
conflictPanel.addRadioButtonRow(getRegisterInfo(MergeConstants.LATEST, latestValue),
|
||||
|
@ -431,7 +422,8 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
Register conflictResultReg;
|
||||
|
||||
RegisterConflicts(String registerName, ProgramContext originalContext,
|
||||
ProgramContext latestContext, ProgramContext myContext, ProgramContext resultContext) {
|
||||
ProgramContext latestContext, ProgramContext myContext,
|
||||
ProgramContext resultContext) {
|
||||
this.conflictRegisterName = registerName;
|
||||
this.conflictOriginalContext = originalContext;
|
||||
this.conflictLatestContext = latestContext;
|
||||
|
@ -496,13 +488,11 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
|
||||
ArrayList<AddressRange> conflicts = new ArrayList<AddressRange>();
|
||||
|
||||
AddressSet tempLatestChanges =
|
||||
getRegisterDifferences(conflictRegisterName, conflictOriginalContext,
|
||||
conflictLatestContext, addressSet, monitor);
|
||||
AddressSet tempLatestChanges = getRegisterDifferences(conflictRegisterName,
|
||||
conflictOriginalContext, conflictLatestContext, addressSet, monitor);
|
||||
|
||||
AddressSet tempMyChanges =
|
||||
getRegisterDifferences(conflictRegisterName, conflictOriginalContext,
|
||||
conflictMyContext, addressSet, monitor);
|
||||
AddressSet tempMyChanges = getRegisterDifferences(conflictRegisterName,
|
||||
conflictOriginalContext, conflictMyContext, addressSet, monitor);
|
||||
|
||||
AddressSet bothChanged = tempMyChanges.intersect(tempLatestChanges);
|
||||
|
||||
|
@ -513,19 +503,16 @@ class RegisterMergeManager implements ListingMergeConstants {
|
|||
Address rangeMin = range.getMinAddress();
|
||||
Address rangeMax = range.getMaxAddress();
|
||||
|
||||
AddressRangeIterator it1 =
|
||||
conflictLatestContext.getRegisterValueAddressRanges(conflictLatestReg,
|
||||
rangeMin, rangeMax);
|
||||
AddressRangeIterator it2 =
|
||||
conflictMyContext.getRegisterValueAddressRanges(conflictMyReg, rangeMin,
|
||||
rangeMax);
|
||||
AddressRangeIterator it1 = conflictLatestContext
|
||||
.getRegisterValueAddressRanges(conflictLatestReg, rangeMin, rangeMax);
|
||||
AddressRangeIterator it2 = conflictMyContext
|
||||
.getRegisterValueAddressRanges(conflictMyReg, rangeMin, rangeMax);
|
||||
AddressRangeIterator it = new CombinedAddressRangeIterator(it1, it2);
|
||||
|
||||
while (it.hasNext()) {
|
||||
AddressRange addrRange = it.next();
|
||||
BigInteger lastestValue =
|
||||
conflictLatestContext.getValue(conflictLatestReg,
|
||||
addrRange.getMinAddress(), false);
|
||||
BigInteger lastestValue = conflictLatestContext.getValue(conflictLatestReg,
|
||||
addrRange.getMinAddress(), false);
|
||||
BigInteger myValue =
|
||||
conflictMyContext.getValue(conflictMyReg, addrRange.getMinAddress(), false);
|
||||
boolean sameValue =
|
||||
|
|
|
@ -67,8 +67,7 @@ public class FindPossibleReferencesPlugin extends Plugin {
|
|||
final static String RESTORE_SELECTION_ACTION_NAME = "Restore Direct Refs Search Selection";
|
||||
final static String SEARCH_DIRECT_REFS_ACTION_NAME = "Search for Direct References";
|
||||
final static String SEARCH_DIRECT_REFS_ACTION_HELP = "Direct_Refs_Search_Alignment";
|
||||
private DockingAction action;
|
||||
private ArrayList<TableComponentProvider<ReferenceAddressPair>> providerList;
|
||||
private List<TableComponentProvider<ReferenceAddressPair>> providerList;
|
||||
|
||||
public FindPossibleReferencesPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
@ -98,22 +97,21 @@ public class FindPossibleReferencesPlugin extends Plugin {
|
|||
}
|
||||
|
||||
private void createActions() {
|
||||
action = new ActionBuilder(SEARCH_DIRECT_REFS_ACTION_NAME, getName())
|
||||
.menuPath(ToolConstants.MENU_SEARCH, "For Direct References")
|
||||
.menuGroup("search for", "DirectReferences")
|
||||
.helpLocation(new HelpLocation(HelpTopics.SEARCH, SEARCH_DIRECT_REFS_ACTION_NAME))
|
||||
.description(getPluginDescription().getDescription())
|
||||
.withContext(ListingActionContext.class, true)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.onAction(this::findReferences)
|
||||
.enabledWhen(this::hasCorrectAddressSize)
|
||||
.buildAndInstall(tool);
|
||||
new ActionBuilder(SEARCH_DIRECT_REFS_ACTION_NAME, getName())
|
||||
.menuPath(ToolConstants.MENU_SEARCH, "For Direct References")
|
||||
.menuGroup("search for", "DirectReferences")
|
||||
.helpLocation(new HelpLocation(HelpTopics.SEARCH, SEARCH_DIRECT_REFS_ACTION_NAME))
|
||||
.description(getPluginDescription().getDescription())
|
||||
.withContext(ListingActionContext.class, true)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.onAction(this::findReferences)
|
||||
.enabledWhen(this::hasCorrectAddressSize)
|
||||
.buildAndInstall(tool);
|
||||
|
||||
}
|
||||
|
||||
private boolean hasCorrectAddressSize(NavigatableActionContext context) {
|
||||
int size =
|
||||
context.getProgram().getAddressFactory().getDefaultAddressSpace().getSize();
|
||||
int size = context.getProgram().getAddressFactory().getDefaultAddressSpace().getSize();
|
||||
if ((size == 64) || (size == 32) || (size == 24) || (size == 16) || (size == 20) ||
|
||||
(size == 21)) {
|
||||
return true;
|
||||
|
@ -142,10 +140,10 @@ public class FindPossibleReferencesPlugin extends Plugin {
|
|||
localAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.SEARCH, RESTORE_SELECTION_ACTION_NAME));
|
||||
String group = "selection";
|
||||
localAction.setMenuBarData(
|
||||
new MenuData(new String[] { "Restore Search Selection" }, group));
|
||||
localAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Restore Search Selection" }, group));
|
||||
localAction
|
||||
.setMenuBarData(new MenuData(new String[] { "Restore Search Selection" }, group));
|
||||
localAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Restore Search Selection" }, group));
|
||||
|
||||
localAction.setDescription(
|
||||
"Sets the program selection back to the selection this search was based upon.");
|
||||
|
@ -194,9 +192,8 @@ public class FindPossibleReferencesPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
if (currentProgram.getMemory()
|
||||
.getBlock(
|
||||
fromAddr)
|
||||
.getType() == MemoryBlockType.BIT_MAPPED) {
|
||||
.getBlock(fromAddr)
|
||||
.getType() == MemoryBlockType.BIT_MAPPED) {
|
||||
Msg.showWarn(getClass(), null, "Search For Direct References",
|
||||
"Cannot search for direct references on bit memory!");
|
||||
return;
|
||||
|
|
|
@ -45,6 +45,8 @@ import ghidra.util.HelpLocation;
|
|||
import ghidra.util.table.*;
|
||||
import ghidra.util.table.actions.DeleteTableRowAction;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
import utility.function.Callback;
|
||||
import utility.function.Dummy;
|
||||
|
||||
public class TableComponentProvider<T> extends ComponentProviderAdapter
|
||||
implements TableModelListener, NavigatableRemovalListener {
|
||||
|
@ -60,11 +62,13 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
private String programName;
|
||||
private String windowSubMenu;
|
||||
private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>();
|
||||
private Callback closedCallback = Dummy.callback();
|
||||
|
||||
private Navigatable navigatable;
|
||||
private SelectionNavigationAction selectionNavigationAction;
|
||||
private DockingAction selectAction;
|
||||
private DockingAction removeItemsAction;
|
||||
private DockingAction externalGotoAction;
|
||||
|
||||
private Function<MouseEvent, ActionContext> contextProvider = null;
|
||||
|
||||
|
@ -170,7 +174,7 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
selectionNavigationAction
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Selection_Navigation"));
|
||||
|
||||
DockingAction externalGotoAction = new DockingAction("Go to External Location", getName()) {
|
||||
externalGotoAction = new DockingAction("Go to External Location", getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
gotoExternalAddress(getSelectedExternalAddress());
|
||||
|
@ -203,9 +207,21 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
new MenuData(new String[] { "GoTo External Location" }, icon, null));
|
||||
externalGotoAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Navigation"));
|
||||
|
||||
plugin.getTool().addLocalAction(this, selectAction);
|
||||
plugin.getTool().addLocalAction(this, selectionNavigationAction);
|
||||
plugin.getTool().addLocalAction(this, externalGotoAction);
|
||||
tool.addLocalAction(this, selectAction);
|
||||
tool.addLocalAction(this, selectionNavigationAction);
|
||||
tool.addLocalAction(this, externalGotoAction);
|
||||
}
|
||||
|
||||
public void removeAllActions() {
|
||||
tool.removeLocalAction(this, externalGotoAction);
|
||||
tool.removeLocalAction(this, selectAction);
|
||||
tool.removeLocalAction(this, selectionNavigationAction);
|
||||
|
||||
// this action is conditionally added
|
||||
if (removeItemsAction != null) {
|
||||
tool.removeAction(removeItemsAction);
|
||||
removeItemsAction = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void installRemoveItemsAction() {
|
||||
|
@ -295,6 +311,8 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
this.closedCallback.call();
|
||||
|
||||
if (navigatable != null) {
|
||||
navigatable.removeNavigatableListener(this);
|
||||
}
|
||||
|
@ -419,4 +437,12 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
public void setActionContextProvider(Function<MouseEvent, ActionContext> contextProvider) {
|
||||
this.contextProvider = contextProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to know when this provider is closed.
|
||||
* @param c the callback
|
||||
*/
|
||||
public void setClosedCallback(Callback c) {
|
||||
this.closedCallback = Dummy.ifNull(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.util.bin.format.omf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class Omf2or4 implements StructConverter {
|
||||
|
||||
private int length;
|
||||
private long value;
|
||||
|
||||
public Omf2or4(int length, long value) {
|
||||
this.length = length;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public long value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
return length == 2 ? WORD : DWORD;
|
||||
}
|
||||
}
|
|
@ -32,9 +32,9 @@ public class OmfComdatExternalSymbol extends OmfExternalSymbol {
|
|||
|
||||
long max = reader.getPointerIndex() + getRecordLength() - 1;
|
||||
while (reader.getPointerIndex() < max) {
|
||||
int nameIndex = OmfRecord.readIndex(reader);
|
||||
int type = OmfRecord.readIndex(reader);
|
||||
externalLookups.add(new ExternalLookup(nameIndex, type));
|
||||
OmfIndex nameIndex = OmfRecord.readIndex(reader);
|
||||
OmfIndex type = OmfRecord.readIndex(reader);
|
||||
externalLookups.add(new ExternalLookup(nameIndex.value(), type.value()));
|
||||
}
|
||||
|
||||
readCheckSumByte(reader);
|
||||
|
|
|
@ -27,8 +27,8 @@ public class OmfComdefRecord extends OmfExternalSymbol {
|
|||
|
||||
long max = reader.getPointerIndex() + getRecordLength() - 1;
|
||||
while (reader.getPointerIndex() < max) {
|
||||
String name = OmfRecord.readString(reader);
|
||||
int typeIndex = OmfRecord.readIndex(reader);
|
||||
OmfString name = OmfRecord.readString(reader);
|
||||
OmfIndex typeIndex = OmfRecord.readIndex(reader);
|
||||
byte dataType = reader.readNextByte();
|
||||
int byteLength = 0;
|
||||
if (dataType == 0x61) { // FAR data, reads numElements and elSize
|
||||
|
@ -40,7 +40,7 @@ public class OmfComdefRecord extends OmfExternalSymbol {
|
|||
// Values 1 thru 5f plus 61, read the byte length
|
||||
byteLength = readCommunalLength(reader);
|
||||
}
|
||||
symbols.add(new OmfSymbol(name, typeIndex, 0, dataType, byteLength));
|
||||
symbols.add(new OmfSymbol(name.str(), typeIndex.value(), 0, dataType, byteLength));
|
||||
}
|
||||
readCheckSumByte(reader);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.io.IOException;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfCommentRecord extends OmfRecord {
|
||||
// Language translator comment
|
||||
|
@ -34,7 +36,7 @@ public class OmfCommentRecord extends OmfRecord {
|
|||
|
||||
private byte commentType;
|
||||
private byte commentClass;
|
||||
private String value;
|
||||
private OmfString value;
|
||||
|
||||
public OmfCommentRecord(BinaryReader reader) throws IOException {
|
||||
readRecordHeader(reader);
|
||||
|
@ -46,7 +48,7 @@ public class OmfCommentRecord extends OmfRecord {
|
|||
case COMMENT_CLASS_DEFAULT_LIBRARY:
|
||||
byte[] bytes = reader.readNextByteArray(getRecordLength() -
|
||||
3 /* 3 = sizeof(commentType+commentClass+trailing_crcbyte*/);
|
||||
value = new String(bytes, StandardCharsets.US_ASCII); // assuming ASCII
|
||||
value = new OmfString(bytes.length, new String(bytes, StandardCharsets.US_ASCII)); // assuming ASCII
|
||||
break;
|
||||
case COMMENT_CLASS_LIBMOD:
|
||||
value = readString(reader);
|
||||
|
@ -67,6 +69,24 @@ public class OmfCommentRecord extends OmfRecord {
|
|||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
return value.str();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
int strlen = getRecordLength() - 3;
|
||||
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", "");
|
||||
struct.add(BYTE, "comment_type", null);
|
||||
struct.add(BYTE, "comment_class", null);
|
||||
if (strlen > 0) {
|
||||
struct.add(new StringDataType(), strlen, "str", null);
|
||||
}
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,21 +23,21 @@ import ghidra.app.util.bin.BinaryReader;
|
|||
* Object representing data loaded directly into the final image.
|
||||
*/
|
||||
public abstract class OmfData extends OmfRecord implements Comparable<OmfData> {
|
||||
protected int segmentIndex;
|
||||
protected long dataOffset;
|
||||
protected OmfIndex segmentIndex;
|
||||
protected Omf2or4 dataOffset;
|
||||
|
||||
/**
|
||||
* @return get the segments index for this datablock
|
||||
*/
|
||||
public int getSegmentIndex() {
|
||||
return segmentIndex;
|
||||
return segmentIndex.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the starting offset, within the loaded image, of this data
|
||||
*/
|
||||
public long getDataOffset() {
|
||||
return dataOffset;
|
||||
return dataOffset.value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,7 +47,7 @@ public abstract class OmfData extends OmfRecord implements Comparable<OmfData> {
|
|||
*/
|
||||
@Override
|
||||
public int compareTo(OmfData o) {
|
||||
return Long.compare(dataOffset, o.dataOffset);
|
||||
return Long.compare(dataOffset.value(), o.dataOffset.value());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,7 +27,7 @@ public class OmfEnumeratedData extends OmfData {
|
|||
readRecordHeader(reader);
|
||||
long start = reader.getPointerIndex();
|
||||
segmentIndex = OmfRecord.readIndex(reader);
|
||||
dataOffset = OmfRecord.readInt2Or4(reader, hasBigFields()) & 0xffffffffL;
|
||||
dataOffset = OmfRecord.readInt2Or4(reader, hasBigFields());
|
||||
streamOffset = reader.getPointerIndex();
|
||||
streamLength = getRecordLength() - 1 - (int) (streamOffset - start);
|
||||
reader.setPointerIndex(streamOffset + streamLength); // Skip over the data when reading header
|
||||
|
|
|
@ -20,11 +20,17 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfExternalSymbol extends OmfRecord {
|
||||
|
||||
private boolean isStatic;
|
||||
protected List<OmfSymbol> symbols = new ArrayList<>();
|
||||
|
||||
private record Reference(OmfString name, OmfIndex type) {}
|
||||
|
||||
private List<Reference> refs = new ArrayList<>();
|
||||
|
||||
protected OmfExternalSymbol(boolean isStatic) {
|
||||
this.isStatic = isStatic;
|
||||
|
@ -36,9 +42,10 @@ public class OmfExternalSymbol extends OmfRecord {
|
|||
|
||||
long max = reader.getPointerIndex() + getRecordLength() - 1;
|
||||
while (reader.getPointerIndex() < max) {
|
||||
String name = OmfRecord.readString(reader);
|
||||
int type = OmfRecord.readIndex(reader);
|
||||
symbols.add(new OmfSymbol(name, type, 0, 0, 0));
|
||||
OmfString name = OmfRecord.readString(reader);
|
||||
OmfIndex type = OmfRecord.readIndex(reader);
|
||||
refs.add(new Reference(name, type));
|
||||
symbols.add(new OmfSymbol(name.str(), type.value(), 0, 0, 0));
|
||||
}
|
||||
|
||||
readCheckSumByte(reader);
|
||||
|
@ -51,4 +58,19 @@ public class OmfExternalSymbol extends OmfRecord {
|
|||
public boolean isStatic() {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
for (Reference ref : refs) {
|
||||
struct.add(ref.name.toDataType(), "name", null);
|
||||
struct.add(ref.type.toDataType(), "type", null);
|
||||
}
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,41 +17,52 @@ package ghidra.app.util.bin.format.omf;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class OmfFileHeader extends OmfRecord {
|
||||
|
||||
private String objectName; // Name of the object module
|
||||
private String libModuleName = null; // Name of the module (within a library)
|
||||
private String translator = null; // Usually the compiler/linker used to produce this object
|
||||
private OmfString objectName; // Name of the object module
|
||||
private String libModuleName = null; // Name of the module (within a library)
|
||||
private String translator = null; // Usually the compiler/linker used to produce this object
|
||||
private boolean isLittleEndian;
|
||||
private ArrayList<String> nameList = new ArrayList<String>(); // Indexable List of segment, group, ... names
|
||||
private ArrayList<OmfSegmentHeader> segments = new ArrayList<OmfSegmentHeader>();
|
||||
private ArrayList<OmfGroupRecord> groups = new ArrayList<OmfGroupRecord>();
|
||||
private ArrayList<OmfExternalSymbol> externsymbols = new ArrayList<OmfExternalSymbol>();
|
||||
private ArrayList<OmfSymbolRecord> symbols = new ArrayList<OmfSymbolRecord>();
|
||||
private ArrayList<OmfFixupRecord> fixup = new ArrayList<OmfFixupRecord>();
|
||||
private ArrayList<OmfSegmentHeader> extraSeg = null; // Holds implied segments that don't have official header record
|
||||
private List<OmfRecord> records = new ArrayList<>();
|
||||
private List<String> nameList = new ArrayList<>(); // Indexable List of segment, group, ... names
|
||||
private List<OmfSegmentHeader> segments = new ArrayList<>();
|
||||
private List<OmfGroupRecord> groups = new ArrayList<>();
|
||||
private List<OmfExternalSymbol> externsymbols = new ArrayList<>();
|
||||
private List<OmfSymbolRecord> symbols = new ArrayList<>();
|
||||
private List<OmfFixupRecord> fixup = new ArrayList<>();
|
||||
private List<OmfSegmentHeader> extraSeg = null; // Holds implied segments that don't have official header record
|
||||
// private OmfModuleEnd endModule = null;
|
||||
private boolean format16bit;
|
||||
|
||||
public OmfFileHeader(BinaryReader reader) throws IOException {
|
||||
readRecordHeader(reader);
|
||||
objectName = readString(reader); // This is usually the source code filename
|
||||
objectName = readString(reader); // This is usually the source code filename
|
||||
readCheckSumByte(reader);
|
||||
isLittleEndian = reader.isLittleEndian();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the list of records}
|
||||
*/
|
||||
public List<OmfRecord> getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is usually the original source filename
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return objectName;
|
||||
return objectName.str();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,42 +99,42 @@ public class OmfFileHeader extends OmfRecord {
|
|||
/**
|
||||
* @return the list of segments in this file
|
||||
*/
|
||||
public ArrayList<OmfSegmentHeader> getSegments() {
|
||||
public List<OmfSegmentHeader> getSegments() {
|
||||
return segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of segments which are Borland extensions
|
||||
*/
|
||||
public ArrayList<OmfSegmentHeader> getExtraSegments() {
|
||||
public List<OmfSegmentHeader> getExtraSegments() {
|
||||
return extraSeg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of group records for this file
|
||||
*/
|
||||
public ArrayList<OmfGroupRecord> getGroups() {
|
||||
public List<OmfGroupRecord> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of symbols that are external to this file
|
||||
*/
|
||||
public ArrayList<OmfExternalSymbol> getExternalSymbols() {
|
||||
public List<OmfExternalSymbol> getExternalSymbols() {
|
||||
return externsymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of symbols exported by this file
|
||||
*/
|
||||
public ArrayList<OmfSymbolRecord> getPublicSymbols() {
|
||||
public List<OmfSymbolRecord> getPublicSymbols() {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of relocation records for this file
|
||||
*/
|
||||
public ArrayList<OmfFixupRecord> getFixups() {
|
||||
public List<OmfFixupRecord> getFixups() {
|
||||
return fixup;
|
||||
}
|
||||
|
||||
|
@ -313,14 +324,15 @@ public class OmfFileHeader extends OmfRecord {
|
|||
public static OmfFileHeader parse(BinaryReader reader, TaskMonitor monitor, MessageLog log)
|
||||
throws IOException, OmfException {
|
||||
OmfRecord record = OmfRecord.readRecord(reader);
|
||||
if (!(record instanceof OmfFileHeader)) {
|
||||
if (!(record instanceof OmfFileHeader header)) {
|
||||
throw new OmfException("Object file does not start with proper header");
|
||||
}
|
||||
OmfFileHeader header = (OmfFileHeader) record;
|
||||
header.records.add(header);
|
||||
OmfData lastDataBlock = null;
|
||||
|
||||
while (true) {
|
||||
record = OmfRecord.readRecord(reader);
|
||||
header.records.add(record);
|
||||
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
|
@ -403,8 +415,8 @@ public class OmfFileHeader extends OmfRecord {
|
|||
* @param groups is the list of specific segments that are grouped together in memory
|
||||
* @throws OmfException for malformed index/alignment/combining fields
|
||||
*/
|
||||
public static void doLinking(long startAddress, ArrayList<OmfSegmentHeader> segments,
|
||||
ArrayList<OmfGroupRecord> groups) throws OmfException {
|
||||
public static void doLinking(long startAddress, List<OmfSegmentHeader> segments,
|
||||
List<OmfGroupRecord> groups) throws OmfException {
|
||||
// Link anything in groups first
|
||||
for (int i = 0; i < groups.size(); ++i) {
|
||||
OmfGroupRecord group = groups.get(i);
|
||||
|
@ -489,4 +501,16 @@ public class OmfFileHeader extends OmfRecord {
|
|||
private static void logRecord(String description, OmfRecord record, MessageLog log) {
|
||||
log.appendMsg(description + " (" + record + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
struct.add(objectName.toDataType(), "name", null);
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,10 @@ public class OmfFixupRecord extends OmfRecord {
|
|||
private byte first;
|
||||
private byte hiFixup;
|
||||
private byte fixData;
|
||||
private int index;
|
||||
private int frameDatum;
|
||||
private int targetDatum;
|
||||
private int targetDisplacement;
|
||||
private OmfIndex index;
|
||||
private OmfIndex frameDatum;
|
||||
private OmfIndex targetDatum;
|
||||
private Omf2or4 targetDisplacement;
|
||||
|
||||
/**
|
||||
* Read the next subrecord from the input reader
|
||||
|
@ -85,7 +85,7 @@ public class OmfFixupRecord extends OmfRecord {
|
|||
int method;
|
||||
final var rec = new Subrecord();
|
||||
rec.first = reader.readNextByte();
|
||||
rec.index = -1;
|
||||
rec.index = new OmfIndex(1, -1);
|
||||
if (rec.isThreadSubrecord()) {
|
||||
method = rec.getThreadMethod();
|
||||
if (method < 4) {
|
||||
|
@ -93,8 +93,8 @@ public class OmfFixupRecord extends OmfRecord {
|
|||
}
|
||||
return rec;
|
||||
}
|
||||
rec.targetDisplacement = 0;
|
||||
rec.targetDatum = 0;
|
||||
rec.targetDisplacement = new Omf2or4(2, 0);
|
||||
rec.targetDatum = new OmfIndex(1, 0);
|
||||
rec.hiFixup = reader.readNextByte();
|
||||
rec.fixData = reader.readNextByte();
|
||||
method = rec.getFrameMethod();
|
||||
|
@ -135,7 +135,7 @@ public class OmfFixupRecord extends OmfRecord {
|
|||
* @return Get the index for explicit thread or frame
|
||||
*/
|
||||
public int getIndex() {
|
||||
return index;
|
||||
return index.value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,11 +170,11 @@ public class OmfFixupRecord extends OmfRecord {
|
|||
}
|
||||
|
||||
public int getTargetDatum() {
|
||||
return targetDatum;
|
||||
return targetDatum.value();
|
||||
}
|
||||
|
||||
public int getTargetDisplacement() {
|
||||
return targetDisplacement;
|
||||
return (int) targetDisplacement.value();
|
||||
}
|
||||
|
||||
public int getLocationType() {
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.omf;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -24,7 +25,7 @@ import ghidra.program.model.address.AddressSpace;
|
|||
import ghidra.program.model.lang.Language;
|
||||
|
||||
public class OmfGroupRecord extends OmfRecord {
|
||||
private int groupNameIndex;
|
||||
private OmfIndex groupNameIndex;
|
||||
private String groupName;
|
||||
private long vma = -1; // Assigned (by linker) starting address of the whole group
|
||||
private GroupSubrecord[] group;
|
||||
|
@ -72,7 +73,7 @@ public class OmfGroupRecord extends OmfRecord {
|
|||
}
|
||||
|
||||
public int getSegmentIndex(int i) {
|
||||
return group[i].segmentIndex;
|
||||
return group[i].segmentIndex.value();
|
||||
}
|
||||
|
||||
public Address getAddress(Language language) {
|
||||
|
@ -80,19 +81,19 @@ public class OmfGroupRecord extends OmfRecord {
|
|||
return addrSpace.getAddress(vma);
|
||||
}
|
||||
|
||||
public void resolveNames(ArrayList<String> nameList) throws OmfException {
|
||||
if (groupNameIndex <= 0) {
|
||||
public void resolveNames(List<String> nameList) throws OmfException {
|
||||
if (groupNameIndex.value() <= 0) {
|
||||
throw new OmfException("Cannot have unused group name");
|
||||
}
|
||||
if (groupNameIndex > nameList.size()) {
|
||||
if (groupNameIndex.value() > nameList.size()) {
|
||||
throw new OmfException("Group name index out of bounds");
|
||||
}
|
||||
groupName = nameList.get(groupNameIndex - 1);
|
||||
groupName = nameList.get(groupNameIndex.value() - 1);
|
||||
}
|
||||
|
||||
public static class GroupSubrecord {
|
||||
private byte componentType;
|
||||
private int segmentIndex;
|
||||
private OmfIndex segmentIndex;
|
||||
|
||||
public static GroupSubrecord read(BinaryReader reader) throws IOException {
|
||||
GroupSubrecord subrec = new GroupSubrecord();
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.util.bin.format.omf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfIndex implements StructConverter {
|
||||
|
||||
private int length;
|
||||
private int value;
|
||||
|
||||
public OmfIndex(int length, int value) {
|
||||
this.length = length;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
return length == 2 ? WORD : BYTE;
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@ public class OmfIteratedData extends OmfData {
|
|||
* Contain the definition of one part of a datablock with possible recursion
|
||||
*/
|
||||
public static class DataBlock {
|
||||
private int repeatCount;
|
||||
private Omf2or4 repeatCount;
|
||||
private int blockCount;
|
||||
private byte[] simpleBlock = null;
|
||||
private DataBlock[] nestedBlock = null;
|
||||
|
@ -86,7 +86,7 @@ public class OmfIteratedData extends OmfData {
|
|||
public static DataBlock read(BinaryReader reader, boolean hasBigFields) throws IOException {
|
||||
DataBlock subblock = new DataBlock();
|
||||
subblock.repeatCount = OmfRecord.readInt2Or4(reader, hasBigFields);
|
||||
subblock.blockCount = reader.readNextShort() & 0xffff;
|
||||
subblock.blockCount = reader.readNextUnsignedShort();
|
||||
if (subblock.blockCount == 0) {
|
||||
int size = reader.readNextByte() & 0xff;
|
||||
subblock.simpleBlock = new byte[size];
|
||||
|
@ -110,7 +110,7 @@ public class OmfIteratedData extends OmfData {
|
|||
* @return The position after the block
|
||||
*/
|
||||
public int fillBuffer(byte[] buffer, int pos) {
|
||||
for (int i = 0; i < repeatCount; ++i) {
|
||||
for (int i = 0; i < (int) repeatCount.value(); ++i) {
|
||||
if (simpleBlock != null) {
|
||||
for (byte element : simpleBlock) {
|
||||
buffer[pos] = element;
|
||||
|
@ -139,7 +139,7 @@ public class OmfIteratedData extends OmfData {
|
|||
length += block.getLength();
|
||||
}
|
||||
}
|
||||
return length * repeatCount;
|
||||
return length * (int) repeatCount.value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,24 +17,39 @@ package ghidra.app.util.bin.format.omf;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfNamesRecord extends OmfRecord {
|
||||
private ArrayList<String> name;
|
||||
private List<OmfString> names = new ArrayList<>();
|
||||
|
||||
public OmfNamesRecord(BinaryReader reader) throws IOException {
|
||||
readRecordHeader(reader);
|
||||
long max = reader.getPointerIndex() + getRecordLength() - 1;
|
||||
name = new ArrayList<String>();
|
||||
while (reader.getPointerIndex() < max) {
|
||||
String nm = OmfRecord.readString(reader);
|
||||
name.add(nm);
|
||||
names.add(OmfRecord.readString(reader));
|
||||
}
|
||||
readCheckSumByte(reader);
|
||||
}
|
||||
|
||||
public void appendNames(ArrayList<String> namelist) {
|
||||
namelist.addAll(name);
|
||||
public void appendNames(List<String> namelist) {
|
||||
namelist.addAll(names.stream().map(name -> name.str()).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
for (OmfString name : names) {
|
||||
struct.add(name.toDataType(), -1, "name", null);
|
||||
}
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@ import java.lang.reflect.Field;
|
|||
import java.lang.reflect.Modifier;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public abstract class OmfRecord {
|
||||
public abstract class OmfRecord implements StructConverter {
|
||||
public final static byte RHEADR = (byte) 0x6E; // Obsolete
|
||||
public final static byte REGINT = (byte) 0x70; // Obsolete
|
||||
public final static byte REDATA = (byte) 0x72; // Obsolete
|
||||
|
@ -68,6 +71,8 @@ public abstract class OmfRecord {
|
|||
public final static byte START = (byte) 0xF0;
|
||||
public final static byte END = (byte) 0xF1;
|
||||
|
||||
protected static final String CATEGORY_PATH = "/OMF";
|
||||
|
||||
protected byte recordType;
|
||||
protected int recordLength;
|
||||
protected long recordOffset;
|
||||
|
@ -120,22 +125,26 @@ public abstract class OmfRecord {
|
|||
return (reader.readNextByte() & 0xff);
|
||||
}
|
||||
|
||||
public static int readInt2Or4(BinaryReader reader, boolean isBig) throws IOException {
|
||||
if (isBig)
|
||||
return reader.readNextInt();
|
||||
return (reader.readNextShort() & 0xffff);
|
||||
public static Omf2or4 readInt2Or4(BinaryReader reader, boolean isBig) throws IOException {
|
||||
if (isBig) {
|
||||
return new Omf2or4(4, reader.readNextInt());
|
||||
}
|
||||
return new Omf2or4(2, reader.readNextUnsignedShort());
|
||||
}
|
||||
|
||||
public static int readIndex(BinaryReader reader) throws IOException {
|
||||
public static OmfIndex readIndex(BinaryReader reader) throws IOException {
|
||||
int length;
|
||||
int indexWord;
|
||||
byte firstByte = reader.readNextByte();
|
||||
if ((firstByte & 0x80) != 0) {
|
||||
indexWord = (firstByte & 0x7f) * 0x100 + (reader.readNextByte() & 0xff);
|
||||
length = 2;
|
||||
}
|
||||
else {
|
||||
indexWord = firstByte;
|
||||
length = 1;
|
||||
}
|
||||
return indexWord;
|
||||
return new OmfIndex(length, indexWord);
|
||||
}
|
||||
|
||||
public static OmfRecord readRecord(BinaryReader reader) throws IOException, OmfException {
|
||||
|
@ -215,9 +224,9 @@ public abstract class OmfRecord {
|
|||
* @return the read OMF string
|
||||
* @throws IOException if an IO-related error occurred
|
||||
*/
|
||||
public static String readString(BinaryReader reader) throws IOException {
|
||||
public static OmfString readString(BinaryReader reader) throws IOException {
|
||||
int count = reader.readNextByte() & 0xff;
|
||||
return reader.readNextAsciiString(count);
|
||||
return new OmfString(count, reader.readNextAsciiString(count));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,4 +258,16 @@ public abstract class OmfRecord {
|
|||
return String.format("name: %s, type: 0x%x, offset: 0x%x, length: 0x%x",
|
||||
getRecordName(recordType & (byte) 0xfe), recordType, recordOffset, recordLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
struct.add(new ArrayDataType(BYTE, getRecordLength() - 1, 1), "contents", null);
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,24 +17,25 @@ package ghidra.app.util.bin.format.omf;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.OmfLoader;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfSegmentHeader extends OmfRecord {
|
||||
private byte segAttr; // first byte of Segment Attributes
|
||||
private int frameNumber;
|
||||
private int offset;
|
||||
private long segmentLength;
|
||||
private int segmentNameIndex;
|
||||
private int classNameIndex;
|
||||
private int overlayNameIndex;
|
||||
private Omf2or4 segmentLength;
|
||||
private OmfIndex segmentNameIndex;
|
||||
private OmfIndex classNameIndex;
|
||||
private OmfIndex overlayNameIndex;
|
||||
private String segmentName;
|
||||
private String className;
|
||||
private String overlayName;
|
||||
|
@ -48,10 +49,10 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
OmfSegmentHeader(int num, int datatype) {
|
||||
// generate a special Borland header
|
||||
segAttr = (byte) 0xa9;
|
||||
segmentLength = 0;
|
||||
segmentNameIndex = 0;
|
||||
classNameIndex = 0;
|
||||
overlayNameIndex = 0;
|
||||
segmentLength = new Omf2or4(2,0);
|
||||
segmentNameIndex = new OmfIndex(1, 0);
|
||||
classNameIndex = new OmfIndex(1, 0);
|
||||
overlayNameIndex = new OmfIndex(1, 0);
|
||||
overlayName = "";
|
||||
if (datatype == 1) {
|
||||
segmentName = "EXTRATEXT_";
|
||||
|
@ -90,7 +91,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
offset = reader.readNextByte() & 0xff;
|
||||
vma = (long) frameNumber + offset;
|
||||
}
|
||||
segmentLength = OmfRecord.readInt2Or4(reader, hasBigFields) & 0xffffffffL;
|
||||
segmentLength = OmfRecord.readInt2Or4(reader, hasBigFields);
|
||||
segmentNameIndex = OmfRecord.readIndex(reader);
|
||||
classNameIndex = OmfRecord.readIndex(reader);
|
||||
overlayNameIndex = OmfRecord.readIndex(reader);
|
||||
|
@ -98,10 +99,10 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
int B = (segAttr >> 1) & 1;
|
||||
if (B == 1) { // Ignore the segmentLength field
|
||||
if (getRecordType() == OmfRecord.SEGDEF) {
|
||||
segmentLength = 0x10000L; // Exactly 64K segment
|
||||
segmentLength = new Omf2or4(segmentLength.length(), 0x10000L); // Exactly 64K segment
|
||||
}
|
||||
else {
|
||||
segmentLength = 0x100000000L; // Exactly 4G segment
|
||||
segmentLength = new Omf2or4(segmentLength.length(), 0x100000000L); // Exactly 4G segment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +190,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
* @return the length of the segment in bytes
|
||||
*/
|
||||
public long getSegmentLength() {
|
||||
return segmentLength;
|
||||
return segmentLength.value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -277,7 +278,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
throw new OmfException("Unsupported alignment type");
|
||||
}
|
||||
vma = firstValidAddress;
|
||||
firstValidAddress = vma + segmentLength;
|
||||
firstValidAddress = vma + segmentLength.value();
|
||||
return firstValidAddress;
|
||||
}
|
||||
|
||||
|
@ -288,33 +289,33 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
* @param nameList is the array of names associated with the file
|
||||
* @throws OmfException for improper name indices
|
||||
*/
|
||||
protected void resolveNames(ArrayList<String> nameList) throws OmfException {
|
||||
if (segmentNameIndex == 0) {
|
||||
protected void resolveNames(List<String> nameList) throws OmfException {
|
||||
if (segmentNameIndex.value() == 0) {
|
||||
segmentName = ""; // Name is unused
|
||||
}
|
||||
else {
|
||||
if (segmentNameIndex > nameList.size()) {
|
||||
if (segmentNameIndex.value() > nameList.size()) {
|
||||
throw new OmfException("Segment name index out of bounds");
|
||||
}
|
||||
segmentName = nameList.get(segmentNameIndex - 1);
|
||||
segmentName = nameList.get(segmentNameIndex.value() - 1);
|
||||
}
|
||||
if (classNameIndex == 0) {
|
||||
if (classNameIndex.value() == 0) {
|
||||
className = "";
|
||||
}
|
||||
else {
|
||||
if (classNameIndex > nameList.size()) {
|
||||
if (classNameIndex.value() > nameList.size()) {
|
||||
throw new OmfException("Class name index out of bounds");
|
||||
}
|
||||
className = nameList.get(classNameIndex - 1);
|
||||
className = nameList.get(classNameIndex.value() - 1);
|
||||
}
|
||||
if (overlayNameIndex == 0) {
|
||||
if (overlayNameIndex.value() == 0) {
|
||||
overlayName = "";
|
||||
}
|
||||
else {
|
||||
if (overlayNameIndex > nameList.size()) {
|
||||
if (overlayNameIndex.value() > nameList.size()) {
|
||||
throw new OmfException("Overlay name index out of bounds");
|
||||
}
|
||||
overlayName = nameList.get(overlayNameIndex - 1);
|
||||
overlayName = nameList.get(overlayNameIndex.value() - 1);
|
||||
}
|
||||
|
||||
// Once we know the class name, we can make some educated guesses about read/write/exec permissions
|
||||
|
@ -347,8 +348,8 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
*/
|
||||
protected void appendEnumeratedData(OmfEnumeratedData rec) {
|
||||
long blockend = rec.getDataOffset() + rec.getLength();
|
||||
if (blockend > segmentLength) {
|
||||
segmentLength = blockend;
|
||||
if (blockend > segmentLength.value()) {
|
||||
segmentLength = new Omf2or4(segmentLength.length(), blockend);
|
||||
}
|
||||
dataBlocks.add(rec);
|
||||
}
|
||||
|
@ -380,7 +381,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
this.log = log;
|
||||
pointer = 0;
|
||||
dataUpNext = 0;
|
||||
if (pointer < segmentLength) {
|
||||
if (pointer < segmentLength.value()) {
|
||||
establishNextBuffer();
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +424,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
}
|
||||
}
|
||||
// We may have filler after the last block
|
||||
long size = segmentLength - pointer;
|
||||
long size = segmentLength.value() - pointer;
|
||||
if (size > OmfLoader.MAX_UNINITIALIZED_FILL) {
|
||||
throw new IOException("Large hole at the end of OMF segment: " + segmentName);
|
||||
}
|
||||
|
@ -436,7 +437,7 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (pointer < segmentLength) {
|
||||
if (pointer < segmentLength.value()) {
|
||||
if (bufferpointer < buffer.length) {
|
||||
pointer++;
|
||||
return buffer[bufferpointer++] & 0xff;
|
||||
|
@ -454,4 +455,25 @@ public class OmfSegmentHeader extends OmfRecord {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
struct.add(BYTE, "segment_attr", null);
|
||||
int A = (segAttr >> 5) & 7;
|
||||
if (A == 0) {
|
||||
struct.add(WORD, "frame_number", null);
|
||||
struct.add(BYTE, "offset", null);
|
||||
}
|
||||
struct.add(segmentLength.toDataType(), "segment_length", null);
|
||||
struct.add(segmentNameIndex.toDataType(), "segment_name_index", null);
|
||||
struct.add(classNameIndex.toDataType(), "class_name_index", null);
|
||||
struct.add(overlayNameIndex.toDataType(), "overlay_name_index", null);
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* ###
|
||||
* 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.util.bin.format.omf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfString implements StructConverter {
|
||||
|
||||
private int length;
|
||||
private String str;
|
||||
|
||||
public OmfString(int length, String str) {
|
||||
this.length = length;
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public String str() {
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
if (length == 0) {
|
||||
return BYTE;
|
||||
}
|
||||
|
||||
StructureDataType struct = new StructureDataType("OmfString", 0);
|
||||
struct.add(BYTE, "length", "");
|
||||
struct.add(new StringDataType(), length, "str", null);
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
}
|
|
@ -20,13 +20,18 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class OmfSymbolRecord extends OmfRecord {
|
||||
private int baseGroupIndex;
|
||||
private int baseSegmentIndex;
|
||||
private OmfIndex baseGroupIndex;
|
||||
private OmfIndex baseSegmentIndex;
|
||||
private int baseFrame;
|
||||
private boolean isStatic;
|
||||
private OmfSymbol[] symbol;
|
||||
private List<Reference> refs = new ArrayList<>();
|
||||
|
||||
private record Reference(OmfString name, Omf2or4 offset, OmfIndex type) {}
|
||||
|
||||
public OmfSymbolRecord(BinaryReader reader, boolean isStatic) throws IOException {
|
||||
this.isStatic = isStatic;
|
||||
|
@ -35,17 +40,18 @@ public class OmfSymbolRecord extends OmfRecord {
|
|||
boolean hasBigFields = hasBigFields();
|
||||
baseGroupIndex = OmfRecord.readIndex(reader);
|
||||
baseSegmentIndex = OmfRecord.readIndex(reader);
|
||||
if (baseSegmentIndex == 0) {
|
||||
baseFrame = reader.readNextShort() & 0xffff;
|
||||
if (baseSegmentIndex.value() == 0) {
|
||||
baseFrame = reader.readNextUnsignedShort();
|
||||
}
|
||||
|
||||
ArrayList<OmfSymbol> symbollist = new ArrayList<OmfSymbol>();
|
||||
while (reader.getPointerIndex() < max) {
|
||||
String name = OmfRecord.readString(reader);
|
||||
long offset = OmfRecord.readInt2Or4(reader, hasBigFields) & 0xffffffffL;
|
||||
int type = OmfRecord.readIndex(reader);
|
||||
OmfSymbol subrec = new OmfSymbol(name, type, offset, 0, 0);
|
||||
OmfString name = OmfRecord.readString(reader);
|
||||
Omf2or4 offset = OmfRecord.readInt2Or4(reader, hasBigFields);
|
||||
OmfIndex type = OmfRecord.readIndex(reader);
|
||||
OmfSymbol subrec = new OmfSymbol(name.str(), type.value(), offset.value(), 0, 0);
|
||||
symbollist.add(subrec);
|
||||
refs.add(new Reference(name, offset, type));
|
||||
}
|
||||
readCheckSumByte(reader);
|
||||
symbol = new OmfSymbol[symbollist.size()];
|
||||
|
@ -57,11 +63,11 @@ public class OmfSymbolRecord extends OmfRecord {
|
|||
}
|
||||
|
||||
public int getGroupIndex() {
|
||||
return baseGroupIndex;
|
||||
return baseGroupIndex.value();
|
||||
}
|
||||
|
||||
public int getSegmentIndex() {
|
||||
return baseSegmentIndex;
|
||||
return baseSegmentIndex.value();
|
||||
}
|
||||
|
||||
public int numSymbols() {
|
||||
|
@ -76,4 +82,29 @@ public class OmfSymbolRecord extends OmfRecord {
|
|||
return List.of(symbol);
|
||||
}
|
||||
|
||||
public int getBaseFrame() {
|
||||
return baseFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(getRecordName(getRecordType()), 0);
|
||||
struct.add(BYTE, "type", null);
|
||||
struct.add(WORD, "length", null);
|
||||
struct.add(baseGroupIndex.toDataType(), "base_group_index", null);
|
||||
struct.add(baseSegmentIndex.toDataType(), "base_segment_index", null);
|
||||
if (baseSegmentIndex.value() == 0) {
|
||||
struct.add(WORD, "base_frame", null);
|
||||
}
|
||||
for (Reference ref : refs) {
|
||||
struct.add(ref.name.toDataType(), "name", null);
|
||||
struct.add(ref.offset.toDataType(), "offset", null);
|
||||
struct.add(ref.type.toDataType(), "type", null);
|
||||
}
|
||||
struct.add(BYTE, "checksum", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath(OmfRecord.CATEGORY_PATH));
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ package ghidra.app.util.headless;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import generic.stl.Pair;
|
||||
import ghidra.*;
|
||||
|
@ -37,7 +37,76 @@ import ghidra.util.exception.InvalidInputException;
|
|||
*/
|
||||
public class AnalyzeHeadless implements GhidraLaunchable {
|
||||
|
||||
/**
|
||||
* Headless command line arguments.
|
||||
* <p>
|
||||
* NOTE: Please update 'analyzeHeadlessREADME.html' if changing command line parameters
|
||||
*/
|
||||
private enum Arg {
|
||||
//@formatter:off
|
||||
IMPORT("-import", true, "[<directory>|<file>]+"),
|
||||
PROCESS("-process", true, "[<project_file>]"),
|
||||
PRE_SCRIPT("-prescript", true, "<ScriptName>"),
|
||||
POST_SCRIPT("-postscript", true, "<ScriptName>"),
|
||||
SCRIPT_PATH("-scriptPath", true, "\"<path1>[;<path2>...]\""),
|
||||
PROPERTIES_PATH("-propertiesPath", true, "\"<path1>[;<path2>...]\""),
|
||||
SCRIPT_LOG("-scriptlog", true, "<path to script log file>"),
|
||||
LOG("-log", true, "<path to log file>"),
|
||||
OVERWRITE("-overwrite", false),
|
||||
RECURSIVE("-recursive", false),
|
||||
READ_ONLY("-readOnly", false),
|
||||
DELETE_PROJECT("-deleteproject", false),
|
||||
NO_ANALYSIS("-noanalysis", false),
|
||||
PROCESSOR("-processor", true, "<languageID>"),
|
||||
CSPEC("-cspec", true, "<compilerSpecID>"),
|
||||
ANALYSIS_TIMEOUT_PER_FILE("-analysisTimeoutPerFile", true, "<timeout in seconds>"),
|
||||
KEYSTORE("-keystore", true, "<KeystorePath>"),
|
||||
CONNECT("-connect", false, "[<userID>]"),
|
||||
PASSWORD("-p", false),
|
||||
COMMIT("-commit", false, "[\"<comment>\"]]"),
|
||||
OK_TO_DELETE("-okToDelete", false),
|
||||
MAX_CPU("-max-cpu", true, "<max cpu cores to use>"),
|
||||
LIBRARY_SEARCH_PATHS("-librarySearchPaths", true, "<path1>[;<path2>...]"),
|
||||
LOADER("-loader", true, "<desired loader name>"),
|
||||
LOADER_ARGS(Loader.COMMAND_LINE_ARG_PREFIX + "-", true, "<loader argument value>") {
|
||||
@Override
|
||||
public boolean matches(String arg) {
|
||||
return arg.startsWith(Loader.COMMAND_LINE_ARG_PREFIX + "-");
|
||||
}
|
||||
};
|
||||
//@formatter:on
|
||||
|
||||
private String name;
|
||||
private boolean requiresSubArgs;
|
||||
private String subArgFormat;
|
||||
|
||||
private Arg(String name, boolean requiresSubArgs, String subArgFormat) {
|
||||
this.name = name;
|
||||
this.requiresSubArgs = requiresSubArgs;
|
||||
this.subArgFormat = subArgFormat;
|
||||
}
|
||||
|
||||
private Arg(String name, boolean requiresSubArgs) {
|
||||
this(name, requiresSubArgs, "");
|
||||
}
|
||||
|
||||
public String usage() {
|
||||
return "%s%s%s".formatted(name, subArgFormat.isEmpty() ? "" : " ", subArgFormat);
|
||||
}
|
||||
|
||||
public boolean matches(String arg) {
|
||||
return arg.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int EXIT_CODE_ERROR = 1;
|
||||
private static final Set<String> ALL_ARG_NAMES =
|
||||
Arrays.stream(Arg.values()).map(a -> a.name).collect(Collectors.toSet());
|
||||
|
||||
/**
|
||||
* The entry point of 'analyzeHeadless.bat'. Parses the command line arguments to the script
|
||||
|
@ -64,7 +133,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
if (args[0].startsWith("ghidra:")) {
|
||||
optionStartIndex = 1;
|
||||
try {
|
||||
ghidraURL = new URL(args[0]);
|
||||
ghidraURL = new URI(args[0]).toURL();
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
System.err.println("Invalid Ghidra URL: " + args[0]);
|
||||
|
@ -98,10 +167,10 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
File logFile = null;
|
||||
File scriptLogFile = null;
|
||||
for (int argi = optionStartIndex; argi < args.length; argi++) {
|
||||
if (checkArgument("-log", args, argi)) {
|
||||
if (checkArgument(Arg.LOG, args, argi)) {
|
||||
logFile = new File(args[++argi]);
|
||||
}
|
||||
else if (checkArgument("-scriptlog", args, argi)) {
|
||||
else if (checkArgument(Arg.SCRIPT_LOG, args, argi)) {
|
||||
scriptLogFile = new File(args[++argi]);
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +227,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
String languageId = null;
|
||||
String compilerSpecId = null;
|
||||
String keystorePath = null;
|
||||
String serverUID = null;
|
||||
String userId = null;
|
||||
boolean allowPasswordPrompt = false;
|
||||
List<Pair<String, String[]>> preScripts = new LinkedList<>();
|
||||
List<Pair<String, String[]>> postScripts = new LinkedList<>();
|
||||
|
@ -166,57 +235,57 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
for (int argi = startIndex; argi < args.length; argi++) {
|
||||
|
||||
String arg = args[argi];
|
||||
if (checkArgument("-log", args, argi)) {
|
||||
if (checkArgument(Arg.LOG, args, argi)) {
|
||||
// Already processed
|
||||
argi++;
|
||||
}
|
||||
else if (checkArgument("-scriptlog", args, argi)) {
|
||||
else if (checkArgument(Arg.SCRIPT_LOG, args, argi)) {
|
||||
// Already processed
|
||||
argi++;
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("-overwrite")) {
|
||||
else if (checkArgument(Arg.OVERWRITE, args, argi)) {
|
||||
options.enableOverwriteOnConflict(true);
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("-noanalysis")) {
|
||||
else if (checkArgument(Arg.NO_ANALYSIS, args, argi)) {
|
||||
options.enableAnalysis(false);
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("-deleteproject")) {
|
||||
else if (checkArgument(Arg.DELETE_PROJECT, args, argi)) {
|
||||
options.setDeleteCreatedProjectOnClose(true);
|
||||
}
|
||||
else if (checkArgument("-loader", args, argi)) {
|
||||
else if (checkArgument(Arg.LOADER, args, argi)) {
|
||||
loaderName = args[++argi];
|
||||
}
|
||||
else if (arg.startsWith(Loader.COMMAND_LINE_ARG_PREFIX)) {
|
||||
if (args[argi + 1].startsWith("-")) {
|
||||
else if (checkArgument(Arg.LOADER_ARGS, args, argi)) {
|
||||
if (ALL_ARG_NAMES.contains(args[argi + 1])) {
|
||||
throw new InvalidInputException(args[argi] + " expects value to follow.");
|
||||
}
|
||||
loaderArgs.add(new Pair<>(arg, args[++argi]));
|
||||
}
|
||||
else if (checkArgument("-processor", args, argi)) {
|
||||
else if (checkArgument(Arg.PROCESSOR, args, argi)) {
|
||||
languageId = args[++argi];
|
||||
}
|
||||
else if (checkArgument("-cspec", args, argi)) {
|
||||
else if (checkArgument(Arg.CSPEC, args, argi)) {
|
||||
compilerSpecId = args[++argi];
|
||||
}
|
||||
else if (checkArgument("-prescript", args, argi)) {
|
||||
else if (checkArgument(Arg.PRE_SCRIPT, args, argi)) {
|
||||
String scriptName = args[++argi];
|
||||
String[] scriptArgs = getSubArguments(args, argi);
|
||||
String[] scriptArgs = getSubArguments(args, argi, ALL_ARG_NAMES);
|
||||
argi += scriptArgs.length;
|
||||
preScripts.add(new Pair<>(scriptName, scriptArgs));
|
||||
}
|
||||
else if (checkArgument("-postscript", args, argi)) {
|
||||
else if (checkArgument(Arg.POST_SCRIPT, args, argi)) {
|
||||
String scriptName = args[++argi];
|
||||
String[] scriptArgs = getSubArguments(args, argi);
|
||||
String[] scriptArgs = getSubArguments(args, argi, ALL_ARG_NAMES);
|
||||
argi += scriptArgs.length;
|
||||
postScripts.add(new Pair<>(scriptName, scriptArgs));
|
||||
}
|
||||
else if (checkArgument("-scriptPath", args, argi)) {
|
||||
else if (checkArgument(Arg.SCRIPT_PATH, args, argi)) {
|
||||
options.setScriptDirectories(args[++argi]);
|
||||
}
|
||||
else if (checkArgument("-propertiesPath", args, argi)) {
|
||||
else if (checkArgument(Arg.PROPERTIES_PATH, args, argi)) {
|
||||
options.setPropertiesFileDirectories(args[++argi]);
|
||||
}
|
||||
else if (checkArgument("-import", args, argi)) {
|
||||
else if (checkArgument(Arg.IMPORT, args, argi)) {
|
||||
File inputFile = null;
|
||||
try {
|
||||
inputFile = new File(args[++argi]);
|
||||
|
@ -242,7 +311,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
nextArg = args[++argi];
|
||||
|
||||
// Check if next argument is a parameter
|
||||
if (nextArg.charAt(0) == '-') {
|
||||
if (ALL_ARG_NAMES.contains(nextArg)) {
|
||||
argi--;
|
||||
break;
|
||||
}
|
||||
|
@ -258,29 +327,29 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
filesToImport.add(otherFile);
|
||||
}
|
||||
}
|
||||
else if ("-connect".equals(args[argi])) {
|
||||
else if (checkArgument(Arg.CONNECT, args, argi)) {
|
||||
if ((argi + 1) < args.length) {
|
||||
arg = args[argi + 1];
|
||||
if (!arg.startsWith("-")) {
|
||||
if (!ALL_ARG_NAMES.contains(arg)) {
|
||||
// serverUID is optional argument after -connect
|
||||
serverUID = arg;
|
||||
userId = arg;
|
||||
++argi;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("-commit".equals(args[argi])) {
|
||||
else if (checkArgument(Arg.COMMIT, args, argi)) {
|
||||
String comment = null;
|
||||
if ((argi + 1) < args.length) {
|
||||
arg = args[argi + 1];
|
||||
if (!arg.startsWith("-")) {
|
||||
// comment is optional argument after -commit
|
||||
if (!ALL_ARG_NAMES.contains(arg)) {
|
||||
// commit is optional argument after -commit
|
||||
comment = arg;
|
||||
++argi;
|
||||
}
|
||||
}
|
||||
options.setCommitFiles(true, comment);
|
||||
}
|
||||
else if (checkArgument("-keystore", args, argi)) {
|
||||
else if (checkArgument(Arg.KEYSTORE, args, argi)) {
|
||||
keystorePath = args[++argi];
|
||||
File keystore = new File(keystorePath);
|
||||
if (!keystore.isFile()) {
|
||||
|
@ -288,13 +357,13 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
keystore.getAbsolutePath() + " is not a valid keystore file.");
|
||||
}
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("-p")) {
|
||||
else if (checkArgument(Arg.PASSWORD, args, argi)) {
|
||||
allowPasswordPrompt = true;
|
||||
}
|
||||
else if ("-analysisTimeoutPerFile".equalsIgnoreCase(args[argi])) {
|
||||
else if (checkArgument(Arg.ANALYSIS_TIMEOUT_PER_FILE, args, argi)) {
|
||||
options.setPerFileAnalysisTimeout(args[++argi]);
|
||||
}
|
||||
else if ("-process".equals(args[argi])) {
|
||||
else if (checkArgument(Arg.PROCESS, args, argi)) {
|
||||
if (options.runScriptsNoImport) {
|
||||
throw new InvalidInputException(
|
||||
"The -process option may only be specified once.");
|
||||
|
@ -302,7 +371,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
String processBinary = null;
|
||||
if ((argi + 1) < args.length) {
|
||||
arg = args[argi + 1];
|
||||
if (!arg.startsWith("-")) {
|
||||
if (!ALL_ARG_NAMES.contains(arg)) {
|
||||
// processBinary is optional argument after -process
|
||||
processBinary = arg;
|
||||
++argi;
|
||||
|
@ -310,11 +379,11 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
}
|
||||
options.setRunScriptsNoImport(true, processBinary);
|
||||
}
|
||||
else if ("-recursive".equals(args[argi])) {
|
||||
else if (checkArgument(Arg.RECURSIVE, args, argi)) {
|
||||
Integer depth = null;
|
||||
if ((argi + 1) < args.length) {
|
||||
arg = args[argi + 1];
|
||||
if (!arg.startsWith("-")) {
|
||||
if (!ALL_ARG_NAMES.contains(arg)) {
|
||||
// depth is optional argument after -recursive
|
||||
try {
|
||||
depth = Integer.parseInt(arg);
|
||||
|
@ -327,10 +396,10 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
}
|
||||
options.enableRecursiveProcessing(true, depth);
|
||||
}
|
||||
else if ("-readOnly".equalsIgnoreCase(args[argi])) {
|
||||
else if (checkArgument(Arg.READ_ONLY, args, argi)) {
|
||||
options.enableReadOnlyProcessing(true);
|
||||
}
|
||||
else if (checkArgument("-max-cpu", args, argi)) {
|
||||
else if (checkArgument(Arg.MAX_CPU, args, argi)) {
|
||||
String cpuVal = args[++argi];
|
||||
try {
|
||||
options.setMaxCpu(Integer.parseInt(cpuVal));
|
||||
|
@ -339,12 +408,15 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
throw new InvalidInputException("Invalid value for max-cpu: " + cpuVal);
|
||||
}
|
||||
}
|
||||
else if ("-okToDelete".equalsIgnoreCase(args[argi])) {
|
||||
else if (checkArgument(Arg.OK_TO_DELETE, args, argi)) {
|
||||
options.setOkToDelete(true);
|
||||
}
|
||||
else if (checkArgument("-librarySearchPaths", args, argi)) {
|
||||
else if (checkArgument(Arg.LIBRARY_SEARCH_PATHS, args, argi)) {
|
||||
LibrarySearchPathManager.setLibraryPaths(args[++argi].split(";"));
|
||||
}
|
||||
else if (ALL_ARG_NAMES.contains(args[argi])) {
|
||||
throw new AssertionError("Valid option was not processed: " + args[argi]);
|
||||
}
|
||||
else {
|
||||
throw new InvalidInputException("Bad argument: " + arg);
|
||||
}
|
||||
|
@ -362,7 +434,7 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
|
||||
// Set up optional Ghidra Server authenticator
|
||||
try {
|
||||
options.setClientCredentials(serverUID, keystorePath, allowPasswordPrompt);
|
||||
options.setClientCredentials(userId, keystorePath, allowPasswordPrompt);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new InvalidInputException(
|
||||
|
@ -438,47 +510,48 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
* @param execCmd the command used to run the headless analyzer from the calling method.
|
||||
*/
|
||||
public static void usage(String execCmd) {
|
||||
System.out.println("Headless Analyzer Usage: " + execCmd);
|
||||
System.out.println(" <project_location> <project_name>[/<folder_path>]");
|
||||
System.out.println(
|
||||
" | ghidra://<server>[:<port>]/<repository_name>[/<folder_path>]");
|
||||
System.out.println(
|
||||
" [[-import [<directory>|<file>]+] | [-process [<project_file>]]]");
|
||||
System.out.println(" [-preScript <ScriptName>]");
|
||||
System.out.println(" [-postScript <ScriptName>]");
|
||||
System.out.println(" [-scriptPath \"<path1>[;<path2>...]\"]");
|
||||
System.out.println(" [-propertiesPath \"<path1>[;<path2>...]\"]");
|
||||
System.out.println(" [-scriptlog <path to script log file>]");
|
||||
System.out.println(" [-log <path to log file>]");
|
||||
System.out.println(" [-overwrite]");
|
||||
System.out.println(" [-recursive]");
|
||||
System.out.println(" [-readOnly]");
|
||||
System.out.println(" [-deleteProject]");
|
||||
System.out.println(" [-noanalysis]");
|
||||
System.out.println(" [-processor <languageID>]");
|
||||
System.out.println(" [-cspec <compilerSpecID>]");
|
||||
System.out.println(" [-analysisTimeoutPerFile <timeout in seconds>]");
|
||||
System.out.println(" [-keystore <KeystorePath>]");
|
||||
System.out.println(" [-connect <userID>]");
|
||||
System.out.println(" [-p]");
|
||||
System.out.println(" [-commit [\"<comment>\"]]");
|
||||
System.out.println(" [-okToDelete]");
|
||||
System.out.println(" [-max-cpu <max cpu cores to use>]");
|
||||
System.out.println(" [-loader <desired loader name>]");
|
||||
// ** NOTE: please update 'analyzeHeadlessREADME.html' if changing command line parameters **
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final String INDENT = " ";
|
||||
|
||||
sb.append("Headless Analyzer Usage: %s\n".formatted(execCmd));
|
||||
sb.append(INDENT + "<project_location> <project_name>[/<folder_path>]\n");
|
||||
sb.append(INDENT + " | ghidra://<server>[:<port>]/<repository_name>[/<folder_path>]\n");
|
||||
for (Arg arg : Arg.values()) {
|
||||
switch (arg) {
|
||||
case IMPORT -> {
|
||||
// Can't use both IMPORT and PROCESS, so must handle the usage a little
|
||||
// differently
|
||||
sb.append(
|
||||
INDENT + "[[%s] | [%s]]\n".formatted(arg.usage(), Arg.PROCESS.usage()));
|
||||
}
|
||||
case PROCESS -> {
|
||||
// Handled above by IMPORT
|
||||
}
|
||||
case LOADER_ARGS -> {
|
||||
// Loader args are a little different because we don't know the full
|
||||
// argument name ahead of time...just what it starts with
|
||||
sb.append(INDENT + "[%s<loader argument name> %s]\n"
|
||||
.formatted(Arg.LOADER_ARGS.name, Arg.LOADER_ARGS.subArgFormat));
|
||||
}
|
||||
default -> {
|
||||
sb.append(INDENT + "[%s]\n".formatted(arg.usage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.CURRENT_PLATFORM.getOperatingSystem() != OperatingSystem.WINDOWS) {
|
||||
System.out.println();
|
||||
System.out.println(
|
||||
sb.append("\n");
|
||||
sb.append(
|
||||
" - All uses of $GHIDRA_HOME or $USER_HOME in script path must be" +
|
||||
" preceded by '\\'");
|
||||
" preceded by '\\'\n");
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println(
|
||||
sb.append("\n");
|
||||
sb.append(
|
||||
"Please refer to 'analyzeHeadlessREADME.html' for detailed usage examples " +
|
||||
"and notes.");
|
||||
"and notes.\n");
|
||||
|
||||
System.out.println();
|
||||
sb.append("\n");
|
||||
System.out.println(sb);
|
||||
System.exit(EXIT_CODE_ERROR);
|
||||
}
|
||||
|
||||
|
@ -486,23 +559,22 @@ public class AnalyzeHeadless implements GhidraLaunchable {
|
|||
usage("analyzeHeadless");
|
||||
}
|
||||
|
||||
private String[] getSubArguments(String[] args, int argi) {
|
||||
List<String> subArgs = new LinkedList<>();
|
||||
private String[] getSubArguments(String[] args, int argi, Set<String> argNames) {
|
||||
List<String> subArgs = new ArrayList<>();
|
||||
int i = argi + 1;
|
||||
while (i < args.length && !args[i].startsWith("-")) {
|
||||
while (i < args.length && !argNames.contains(args[i])) {
|
||||
subArgs.add(args[i++]);
|
||||
}
|
||||
return subArgs.toArray(new String[0]);
|
||||
return subArgs.toArray(new String[subArgs.size()]);
|
||||
}
|
||||
|
||||
private boolean checkArgument(String optionName, String[] args, int argi)
|
||||
private boolean checkArgument(Arg arg, String[] args, int argi)
|
||||
throws InvalidInputException {
|
||||
// everything after this requires an argument
|
||||
if (!optionName.equalsIgnoreCase(args[argi])) {
|
||||
if (!arg.matches(args[argi])) {
|
||||
return false;
|
||||
}
|
||||
if (argi + 1 == args.length) {
|
||||
throw new InvalidInputException(optionName + " requires an argument");
|
||||
if (arg.requiresSubArgs && argi + 1 == args.length) {
|
||||
throw new InvalidInputException(args[argi] + " requires an argument");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -879,7 +879,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||
}
|
||||
monitor.initialize(totalCount);
|
||||
|
||||
ElfRelocationContext context = ElfRelocationContext.getRelocationContext(this, symbolMap);
|
||||
ElfRelocationContext<?> context = ElfRelocationContext.getRelocationContext(this, symbolMap);
|
||||
try {
|
||||
for (ElfRelocationTable relocationTable : relocationTables) {
|
||||
monitor.checkCancelled();
|
||||
|
@ -894,7 +894,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||
}
|
||||
|
||||
private void processRelocationTable(ElfRelocationTable relocationTable,
|
||||
ElfRelocationContext context, TaskMonitor monitor) throws CancelledException {
|
||||
ElfRelocationContext<?> context, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
Address defaultBase = getDefaultAddress(elf.adjustAddressForPrelink(0));
|
||||
AddressSpace defaultSpace = defaultBase.getAddressSpace();
|
||||
|
@ -953,7 +953,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
|||
}
|
||||
|
||||
private void processRelocationTableEntries(ElfRelocationTable relocationTable,
|
||||
ElfRelocationContext context, AddressSpace relocationSpace, long baseWordOffset,
|
||||
ElfRelocationContext<?> context, AddressSpace relocationSpace, long baseWordOffset,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (context != null) {
|
||||
|
|
|
@ -244,22 +244,25 @@ public class MachoPrelinkUtils {
|
|||
* @param offset The offset within the provider to check.
|
||||
* @return True A valid {@link LoadSpec} for the Mach-O at the given provider's offset, or null
|
||||
* if it is not a Mach-O or a valid {@link LoadSpec} could not be found.
|
||||
* @throws IOException if there was an IO-related problem.
|
||||
*/
|
||||
private static LoadSpec getMachoLoadSpec(ByteProvider provider, long offset)
|
||||
throws IOException {
|
||||
Collection<LoadSpec> loadSpecs = new MachoLoader().findSupportedLoadSpecs(
|
||||
new ByteProviderWrapper(provider, offset, provider.length() - offset));
|
||||
private static LoadSpec getMachoLoadSpec(ByteProvider provider, long offset) {
|
||||
try {
|
||||
Collection<LoadSpec> loadSpecs = new MachoLoader().findSupportedLoadSpecs(
|
||||
new ByteProviderWrapper(provider, offset, provider.length() - offset));
|
||||
|
||||
// Getting a LoadSpec back means it's a Mach-O we can load. We also need to make sure
|
||||
// the LoadSpec has a language/compiler spec defined to know we support the processor the
|
||||
// loader detected.
|
||||
if (!loadSpecs.isEmpty()) {
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
if (loadSpec.getLanguageCompilerSpec() != null) {
|
||||
return loadSpec;
|
||||
// Getting a LoadSpec back means it's a Mach-O we can load. We also need to make sure
|
||||
// the LoadSpec has a language/compiler spec defined to know we support the processor the
|
||||
// loader detected.
|
||||
if (!loadSpecs.isEmpty()) {
|
||||
LoadSpec loadSpec = loadSpecs.iterator().next();
|
||||
if (loadSpec.getLanguageCompilerSpec() != null) {
|
||||
return loadSpec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -412,7 +412,9 @@ public class MachoProgramBuilder {
|
|||
}
|
||||
}
|
||||
if (segmentFragment == null) {
|
||||
log.appendMsg("Could not find/fixup segment in Program Tree: " + segmentName);
|
||||
if (segment.getVMsize() != 0 || segment.getFileSize() != 0) {
|
||||
log.appendMsg("Could not find/fixup segment in Program Tree: " + segmentName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ProgramModule segmentModule = rootModule.createModule(segmentName + suffix);
|
||||
|
@ -830,8 +832,10 @@ public class MachoProgramBuilder {
|
|||
List<DyldChainedFixupsCommand> loadCommands =
|
||||
machoHeader.getLoadCommands(DyldChainedFixupsCommand.class);
|
||||
if (!loadCommands.isEmpty()) {
|
||||
BinaryReader memReader = new BinaryReader(new MemoryByteProvider(memory, imagebase),
|
||||
!memory.isBigEndian());
|
||||
for (DyldChainedFixupsCommand loadCommand : loadCommands) {
|
||||
fixups.addAll(loadCommand.getChainedFixups(reader, imagebase.getOffset(),
|
||||
fixups.addAll(loadCommand.getChainedFixups(memReader, imagebase.getOffset(),
|
||||
symbolTable, log, monitor));
|
||||
}
|
||||
}
|
||||
|
@ -842,7 +846,8 @@ public class MachoProgramBuilder {
|
|||
Section threadStartsSection =
|
||||
machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.THREAD_STARTS);
|
||||
|
||||
if (chainStartsSection != null) {
|
||||
if (chainStartsSection != null && chainStartsSection.getAddress() != 0 &&
|
||||
chainStartsSection.getSize() != 0) {
|
||||
reader.setPointerIndex(chainStartsSection.getOffset());
|
||||
DyldChainedStartsOffsets chainedStartsOffsets =
|
||||
new DyldChainedStartsOffsets(reader);
|
||||
|
@ -852,7 +857,8 @@ public class MachoProgramBuilder {
|
|||
imagebase.getOffset(), symbolTable, log, monitor));
|
||||
}
|
||||
}
|
||||
else if (threadStartsSection != null) {
|
||||
else if (threadStartsSection != null && threadStartsSection.getAddress() != 0 &&
|
||||
threadStartsSection.getSize() != 0) {
|
||||
Address threadSectionStart = space.getAddress(threadStartsSection.getAddress());
|
||||
Address threadSectionEnd =
|
||||
threadSectionStart.add(threadStartsSection.getSize() - 1);
|
||||
|
|
|
@ -27,9 +27,9 @@ import ghidra.app.util.bin.format.omf.*;
|
|||
import ghidra.app.util.bin.format.omf.OmfFixupRecord.Subrecord;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Undefined;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Language;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
|
@ -47,7 +47,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
public final static long IMAGE_BASE = 0x2000; // Base offset to start loading segments
|
||||
public final static long MAX_UNINITIALIZED_FILL = 0x2000; // Maximum zero bytes added to pad initialized segments
|
||||
|
||||
private ArrayList<OmfSymbol> externsyms = new ArrayList<>();
|
||||
private List<OmfSymbol> externsyms = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* OMF usually stores a string describing the compiler that produced it in a
|
||||
|
@ -137,19 +137,42 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
// We don't use the file bytes to create block because the bytes are manipulated before
|
||||
// forming the block. Creating the FileBytes anyway in case later we want access to all
|
||||
// the original bytes.
|
||||
MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
FileBytes fileBytes = MemoryBlockUtils.createFileBytes(program, provider, monitor);
|
||||
|
||||
try {
|
||||
processSegmentHeaders(reader, header, program, monitor, log);
|
||||
processPublicSymbols(header, program, monitor, log);
|
||||
processExternalSymbols(header, program, monitor, log);
|
||||
processRelocations(header, program, monitor, log);
|
||||
markupRecords(program, fileBytes, header, log, monitor);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void markupRecords(Program program, FileBytes fileBytes, OmfFileHeader fileHeader,
|
||||
MessageLog log, TaskMonitor monitor) {
|
||||
monitor.setMessage("Marking up records...");
|
||||
int size =
|
||||
fileHeader.getRecords().stream().mapToInt(r -> r.getRecordLength() + 3).sum();
|
||||
try {
|
||||
Address recordSpaceAddr = AddressSpace.OTHER_SPACE.getAddress(0);
|
||||
MemoryBlock headerBlock = MemoryBlockUtils.createInitializedBlock(program, true,
|
||||
"RECORDS", recordSpaceAddr, fileBytes, 0, size, "", "", false,
|
||||
false, false, log);
|
||||
Address start = headerBlock.getStart();
|
||||
|
||||
for (OmfRecord record : fileHeader.getRecords()) {
|
||||
DataUtilities.createData(program, start.add(record.getRecordOffset()),
|
||||
record.toDataType(), -1, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Failed to markup records");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a (hopefully) descriptive error, if we can't process a specific relocation
|
||||
* @param program is the Program
|
||||
|
@ -181,7 +204,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
MessageLog log) {
|
||||
Language language = program.getLanguage();
|
||||
OmfFixupRecord.Subrecord[] targetThreads = new Subrecord[4];
|
||||
ArrayList<OmfGroupRecord> groups = header.getGroups();
|
||||
List<OmfGroupRecord> groups = header.getGroups();
|
||||
long targetAddr; // Address of item being referred to
|
||||
Address locAddress; // Location of data to be patched
|
||||
DataConverter converter = DataConverter.getInstance(!header.isLittleEndian());
|
||||
|
@ -364,7 +387,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
|
||||
final Language language = program.getLanguage();
|
||||
|
||||
ArrayList<OmfSegmentHeader> segments = header.getSegments();
|
||||
List<OmfSegmentHeader> segments = header.getSegments();
|
||||
for (OmfSegmentHeader segment : segments) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
|
@ -432,9 +455,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
MessageLog log) {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
ArrayList<OmfSymbolRecord> symbols = header.getPublicSymbols();
|
||||
ArrayList<OmfSegmentHeader> segments = header.getSegments();
|
||||
ArrayList<OmfGroupRecord> groups = header.getGroups();
|
||||
List<OmfSymbolRecord> symbols = header.getPublicSymbols();
|
||||
List<OmfSegmentHeader> segments = header.getSegments();
|
||||
List<OmfGroupRecord> groups = header.getGroups();
|
||||
Language language = program.getLanguage();
|
||||
|
||||
monitor.setMessage("Creating Public Symbols");
|
||||
|
@ -536,7 +559,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader {
|
|||
private void processExternalSymbols(OmfFileHeader header, Program program, TaskMonitor monitor,
|
||||
MessageLog log) {
|
||||
|
||||
ArrayList<OmfExternalSymbol> symbolrecs = header.getExternalSymbols();
|
||||
List<OmfExternalSymbol> symbolrecs = header.getExternalSymbols();
|
||||
if (symbolrecs.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -93,34 +93,28 @@ public class ProgramOpener {
|
|||
*/
|
||||
public Program openProgram(ProgramLocator locator, TaskMonitor monitor) {
|
||||
if (locator.isURL()) {
|
||||
try {
|
||||
return openURL(locator, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Program Open Failed",
|
||||
"Failed to open Ghidra URL: " + locator.getURL());
|
||||
}
|
||||
return null;
|
||||
return openURL(locator, monitor);
|
||||
}
|
||||
return openProgram(locator, locator.getDomainFile(), monitor);
|
||||
}
|
||||
|
||||
private Program openURL(ProgramLocator locator, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
private Program openURL(ProgramLocator locator, TaskMonitor monitor) {
|
||||
URL ghidraUrl = locator.getURL();
|
||||
|
||||
AtomicReference<Program> openedProgram = new AtomicReference<>();
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, new GhidraURLResultHandlerAdapter() {
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m) {
|
||||
Program p = openProgram(locator, domainFile, m); // may return null
|
||||
openedProgram.set(p);
|
||||
}
|
||||
}, monitor);
|
||||
|
||||
try {
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, new GhidraURLResultHandlerAdapter() {
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m) {
|
||||
Program p = openProgram(locator, domainFile, m); // may return null
|
||||
openedProgram.set(p);
|
||||
}
|
||||
}, monitor);
|
||||
}
|
||||
catch (IOException | CancelledException e) {
|
||||
// IOException reported to user by GhidraURLResultHandlerAdapter
|
||||
return null;
|
||||
}
|
||||
return openedProgram.get();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,10 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
ProgramContext pc = program.getProgramContext();
|
||||
Register regDR0 = pc.getRegister(regNameDR0);
|
||||
|
||||
// Initially Direction was 0x1e240
|
||||
setRegValue(pc, addr("1002085"), addr("1002100"), regDR0, 0x5L);
|
||||
|
||||
setRegValue(pc, addr("TextOverlay:1001700"), addr("TextOverlay:1001780"), regDR0,
|
||||
0x5L);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,6 +65,9 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
Register regDR0 = pc.getRegister(regNameDR0);
|
||||
|
||||
setRegValue(pc, addr("1002000"), addr("1002074"), regDR0, 0x22L);
|
||||
|
||||
setRegValue(pc, addr("TextOverlay:1001630"), addr("TextOverlay:1001680"), regDR0,
|
||||
0x22L);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -89,6 +94,34 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
for (Address a = addr("1002101"); a.compareTo(addr("1002150")) <= 0; a = a.add(0x1L)) {
|
||||
assertUndefinedRegValue("DR0", a);
|
||||
}
|
||||
|
||||
// Check Overlay
|
||||
|
||||
// Neither set it
|
||||
for (Address a = addr("TextOverlay:1001629"); a
|
||||
.compareTo(addr("TextOverlay:1001629")) <= 0; a = a.add(0x1L)) {
|
||||
assertUndefinedRegValue("DR0", a);
|
||||
}
|
||||
// From MY
|
||||
for (Address a = addr("TextOverlay:1001630"); a
|
||||
.compareTo(addr("TextOverlay:1001680")) <= 0; a = a.add(0x1L)) {
|
||||
assertRegValue("DR0", a, 0x22L);
|
||||
}
|
||||
// Neither set it
|
||||
for (Address a = addr("TextOverlay:1001681"); a
|
||||
.compareTo(addr("TextOverlay:10016ff")) <= 0; a = a.add(0x1L)) {
|
||||
assertUndefinedRegValue("DR0", a);
|
||||
}
|
||||
// From LATEST
|
||||
for (Address a = addr("TextOverlay:1001700"); a
|
||||
.compareTo(addr("TextOverlay:1001780")) <= 0; a = a.add(0x1L)) {
|
||||
assertRegValue("DR0", a, 0x5L);
|
||||
}
|
||||
// Neither set it
|
||||
for (Address a = addr("TextOverlay:1001781"); a
|
||||
.compareTo(addr("TextOverlay:100182f")) <= 0; a = a.add(0x1L)) {
|
||||
assertUndefinedRegValue("DR0", a);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -100,7 +133,6 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
ProgramContext pc = program.getProgramContext();
|
||||
Register regDR0 = pc.getRegister(regNameDR0);
|
||||
|
||||
// Initially Direction was 0x1e240
|
||||
setRegValue(pc, addr("10022d4"), addr("10022d9"), regDR0, 0x66L);
|
||||
}
|
||||
|
||||
|
@ -807,6 +839,9 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
|
||||
setRegValue(pc, addr("10022d4"), addr("10022e5"), reg1, 0x66L);
|
||||
setRegValue(pc, addr("10022ee"), addr("10022fc"), reg1, 0x44L);
|
||||
|
||||
setRegValue(pc, addr("TextOverlay:1001700"), addr("TextOverlay:1001700"), reg1,
|
||||
0x44L);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -816,6 +851,9 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
|
||||
setRegValue(pc, addr("10022d4"), addr("10022e5"), reg1, 0x7L);
|
||||
setRegValue(pc, addr("10022ee"), addr("10022fc"), reg1, 0x5L);
|
||||
|
||||
setRegValue(pc, addr("TextOverlay:1001700"), addr("TextOverlay:1001700"), reg1,
|
||||
0x5L);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -824,6 +862,8 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
chooseRadioButton(CHECKED_OUT_BUTTON_NAME);
|
||||
checkDisplayValues(Long.valueOf(0x44L), Long.valueOf(0x5L), (Long) null);
|
||||
chooseRadioButton(CHECKED_OUT_BUTTON_NAME);
|
||||
checkDisplayValues(Long.valueOf(0x44L), Long.valueOf(0x5L), (Long) null);
|
||||
chooseRadioButton(CHECKED_OUT_BUTTON_NAME);
|
||||
waitForMergeCompletion();
|
||||
|
||||
for (Address a = addr("10022d4"); a.compareTo(addr("10022e5")) <= 0; a = a.add(0x1L)) {
|
||||
|
@ -832,6 +872,7 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
|
|||
for (Address a = addr("10022ee"); a.compareTo(addr("10022fc")) <= 0; a = a.add(0x1L)) {
|
||||
assertRegValue("DR0", a, 0x5L);
|
||||
}
|
||||
assertRegValue("DR0", addr("TextOverlay:1001700"), 0x5L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -372,6 +372,11 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration
|
|||
return (dtc != null) ? dtc.getLength() : -1;
|
||||
}
|
||||
|
||||
protected DataType getDataType(Composite c, int index) {
|
||||
DataTypeComponent dtc = c.getComponent(index);
|
||||
return (dtc != null) ? dtc.getDataType() : null;
|
||||
}
|
||||
|
||||
protected DataType getDataType(int index) {
|
||||
DataTypeComponent dtc = getComponent(index);
|
||||
return (dtc != null) ? dtc.getDataType() : null;
|
||||
|
|
|
@ -89,7 +89,7 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
|||
|
||||
programDTM.remove(complexStructure, TaskMonitor.DUMMY);
|
||||
programDTM.getCategory(pgmRootCat.getCategoryPath())
|
||||
.removeCategory("Temp", TaskMonitor.DUMMY);
|
||||
.removeCategory("Temp", TaskMonitor.DUMMY);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
|
@ -486,15 +486,17 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
|||
|
||||
@Test
|
||||
public void testComponentDataTypeRemoved() {
|
||||
|
||||
// Get the data types we want to hold onto for comparison later
|
||||
DataType dt3 = getDataType(complexStructure, 3);
|
||||
DataType dt5 = getDataType(complexStructure, 5);
|
||||
DataType dt8 = getDataType(complexStructure, 8);
|
||||
DataType dt10 = getDataType(complexStructure, 10);
|
||||
|
||||
init(complexStructure, pgmTestCat);
|
||||
DataType undef = DataType.DEFAULT;
|
||||
|
||||
assertEquals(23, model.getNumComponents());
|
||||
// Clone the data types we want to hold onto for comparison later, since reload can close the viewDTM.
|
||||
DataType dt3 = getDataType(3).clone(programDTM);
|
||||
DataType dt5 = getDataType(5).clone(programDTM);
|
||||
DataType dt8 = getDataType(8).clone(programDTM);
|
||||
DataType dt10 = getDataType(10).clone(programDTM);
|
||||
|
||||
runSwing(
|
||||
() -> complexStructure.getDataTypeManager().remove(simpleUnion, TaskMonitor.DUMMY));
|
||||
|
@ -521,9 +523,8 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
|||
waitForSwing();
|
||||
assertTrue(simpleStructure.isEquivalent(getDataType(0)));
|
||||
|
||||
runSwing(() -> simpleStructure.getDataTypeManager()
|
||||
.remove(
|
||||
simpleStructure, TaskMonitor.DUMMY));
|
||||
runSwing(
|
||||
() -> simpleStructure.getDataTypeManager().remove(simpleStructure, TaskMonitor.DUMMY));
|
||||
waitForSwing();
|
||||
assertEquals(29, model.getNumComponents());// becomes undefined bytes
|
||||
}
|
||||
|
@ -581,18 +582,21 @@ public class StructureEditorNotifiedTest extends AbstractStructureEditorTest {
|
|||
|
||||
@Test
|
||||
public void testComponentDataTypeReplaced() throws Exception {
|
||||
|
||||
// Get the data types we want to hold onto for comparison later
|
||||
DataType dt15 = getDataType(complexStructure, 15);
|
||||
DataType dt16 = getDataType(complexStructure, 16);
|
||||
DataType dt18 = getDataType(complexStructure, 18);
|
||||
DataType dt19 = getDataType(complexStructure, 19);
|
||||
DataType dt20 = getDataType(complexStructure, 20);
|
||||
String dt21Name = getDataType(complexStructure, 21).getName();
|
||||
DataType dt22 = getDataType(complexStructure, 22);
|
||||
|
||||
init(complexStructure, pgmTestCat);
|
||||
|
||||
int numComps = model.getNumComponents();
|
||||
int len = model.getLength();
|
||||
// Clone the data types we want to hold onto for comparison later, since reload can close the viewDTM.
|
||||
DataType dt15 = getDataType(15).clone(programDTM);
|
||||
DataType dt16 = getDataType(16).clone(programDTM);
|
||||
DataType dt18 = getDataType(18).clone(programDTM);
|
||||
DataType dt19 = getDataType(19).clone(programDTM);
|
||||
DataType dt20 = getDataType(20).clone(programDTM);
|
||||
String dt21Name = getDataType(21).getName();
|
||||
DataType dt22 = getDataType(22).clone(programDTM);
|
||||
|
||||
assertEquals(87, complexStructure.getComponent(16).getDataType().getLength());
|
||||
assertEquals(29, complexStructure.getComponent(19).getDataType().getLength());
|
||||
assertEquals(24, complexStructure.getComponent(20).getDataType().getLength());
|
||||
|
|
|
@ -502,8 +502,9 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
@Test
|
||||
public void testCloseEditorProviderAndSave() throws Exception {
|
||||
Window dialog;
|
||||
DataType oldDt = complexStructure.clone(null);
|
||||
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
DataType oldDt = model.viewComposite.clone(null);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
|
@ -538,8 +539,9 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
@Test
|
||||
public void testCloseEditorAndNoSave() throws Exception {
|
||||
|
||||
DataType oldDt = complexStructure.clone(null);
|
||||
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
DataType oldDt = model.viewComposite.clone(null);
|
||||
|
||||
// Change the structure
|
||||
runSwing(() -> {
|
||||
|
|
|
@ -779,15 +779,18 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest {
|
|||
|
||||
@Test
|
||||
public void testApplyNameChange() throws Exception {
|
||||
|
||||
DataType viewCopy = complexUnion.clone(null);
|
||||
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
|
||||
model.setName("FooBarUnion");
|
||||
DataType viewCopy = model.viewComposite.clone(null);
|
||||
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
assertTrue(viewCopy.isEquivalent(complexUnion));
|
||||
|
||||
assertEquals("FooBarUnion", model.getCompositeName());
|
||||
assertEquals("complexUnion", complexUnion.getName());
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
|
||||
invoke(applyAction);
|
||||
assertTrue(viewCopy.isEquivalent(complexUnion));
|
||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||
|
|
|
@ -200,8 +200,9 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
|||
@Test
|
||||
public void testCloseEditorProviderAndSave() throws Exception {
|
||||
Window dialog;
|
||||
DataType oldDt = complexUnion.clone(null);
|
||||
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
DataType oldDt = model.viewComposite.clone(null);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
|
@ -236,8 +237,9 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
|||
@Test
|
||||
public void testCloseEditorAndNoSave() throws Exception {
|
||||
Window dialog;
|
||||
DataType oldDt = complexUnion.clone(null);
|
||||
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
DataType oldDt = model.viewComposite.clone(null);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
|
|
|
@ -465,12 +465,6 @@ public class FakeSharedProject {
|
|||
|
||||
void refresh() {
|
||||
DefaultProjectData projectData = getProjectData();
|
||||
try {
|
||||
projectData.refresh(true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// shouldn't happen
|
||||
throw new AssertionFailedError("Unable to refresh project " + this);
|
||||
}
|
||||
projectData.refresh(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,6 +202,7 @@ class MergeProgramGenerator_DiffTestPrograms implements MergeProgramGenerator {
|
|||
builder.createMemory(".data", "0x1008000", 0x600);
|
||||
builder.createMemory(".datau", "0x1008600", 0x1344);
|
||||
builder.createMemory(".rsrc", "0x100a000", 0x5400);
|
||||
builder.createOverlayMemory("TextOverlay", "0x01001630", 0x200);
|
||||
|
||||
// for FunctionMergeManager2Test
|
||||
//
|
||||
|
|
|
@ -87,17 +87,41 @@
|
|||
<p>This view does not support <a href="#EditBytes">editing</a>.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3><a name="Add_Byteviewer_HexInteger_Panel"></a><a name="HexInteger"></a>HexInteger </h3>
|
||||
<h3><a name="Add_Byteviewer_HexShortPanel"></a><a name="HexShort"></a>Hex Short</h3>
|
||||
<blockquote>
|
||||
<p>This format shows four byte numbers represented as an eight digit hex number. </p>
|
||||
<p>This format shows two-byte numbers represented as an four-digit hex number. </p>
|
||||
<p> This view supports <a href="#EditBytes">editing</a>. When a byte
|
||||
is changed, both bytes associated with this address are rendered in
|
||||
<font color="#ff0000"> red</font> to denote the change.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3><a name="Add_Byteviewer_HexInteger_Panel"></a><a name="HexInteger"></a>Hex Integer</h3>
|
||||
<blockquote>
|
||||
<p>This format shows four-byte numbers represented as an eight-digit hex number. </p>
|
||||
<p> This view supports <a href="#EditBytes">editing</a>. When a byte
|
||||
is changed, all four bytes associated with this address are rendered in
|
||||
<font color="#ff0000"> red</font> to denote the change.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3><a name="Add_Byteviewer_HexLong_Panel"></a><a name="HexLongr"></a>Hex Long</h3>
|
||||
<blockquote>
|
||||
<p>This format shows eight-byte numbers represented as an 16-digit hex number. </p>
|
||||
<p> This view supports <a href="#EditBytes">editing</a>. When a byte
|
||||
is changed, all eight bytes associated with this address are rendered in
|
||||
<font color="#ff0000"> red</font> to denote the change.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3><a name="Add_Byteviewer_HexLongLong_Panel"></a><a name="HexLongLong"></a>Hex Long Long</h3>
|
||||
<blockquote>
|
||||
<p>This format shows 16-byte numbers represented as an 32-digit hex number. </p>
|
||||
<p> This view supports <a href="#EditBytes">editing</a>. When a byte
|
||||
is changed, all 16 bytes associated with this address are rendered in
|
||||
<font color="#ff0000"> red</font> to denote the change.</p>
|
||||
</blockquote>
|
||||
|
||||
<h3><a name="Add_Byteviewer_Integer_Panel"></a><a name="Integer"></a>Integer </h3>
|
||||
<blockquote>
|
||||
<p>This view shows four byte numbers represented in decimal format. </p>
|
||||
<p>This view shows four-byte numbers represented in decimal format. </p>
|
||||
<p> This view does not support <a href="#EditBytes">editing</a>.</p>
|
||||
</blockquote>
|
||||
|
||||
|
|
|
@ -912,9 +912,8 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
|||
|
||||
ByteField getField(BigInteger index, int fieldNum) {
|
||||
if (indexMap != null) {
|
||||
int fieldOffset = indexMap.getFieldOffset(index, fieldNum, fieldFactories);
|
||||
if (fieldNum < fieldFactories.length) {
|
||||
return (ByteField) fieldFactories[fieldOffset].getField(index);
|
||||
return (ByteField) fieldFactories[fieldNum].getField(index);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -78,6 +78,19 @@ class FileByteBlock implements ByteBlock {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.core.format.ByteBlock#getShort(int)
|
||||
*/
|
||||
public short getShort(BigInteger bigIndex) throws ByteBlockAccessException {
|
||||
int index = bigIndex.intValue();
|
||||
if (index < buf.length) {
|
||||
byte[] b = new byte[2];
|
||||
System.arraycopy(buf, index, b, 0, b.length);
|
||||
return converter.getShort(b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.core.format.ByteBlock#getInt(int)
|
||||
*/
|
||||
|
@ -114,6 +127,18 @@ class FileByteBlock implements ByteBlock {
|
|||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.core.format.ByteBlock#setShort(int, short)
|
||||
*/
|
||||
public void setShort(BigInteger bigIndex, short value) throws ByteBlockAccessException {
|
||||
int index = bigIndex.intValue();
|
||||
if (index < buf.length) {
|
||||
byte[] b = new byte[2];
|
||||
converter.putShort(b, 0, value);
|
||||
System.arraycopy(b, 0, buf, index, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.core.format.ByteBlock#setInt(int, int)
|
||||
*/
|
||||
|
|
|
@ -225,6 +225,44 @@ public class MemoryByteBlock implements ByteBlock {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short value at the given index.
|
||||
*
|
||||
* @param index byte index
|
||||
* @throws ByteBlockAccessException if the block cannot be read
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public short getShort(BigInteger index) throws ByteBlockAccessException {
|
||||
Address addr = getAddress(index);
|
||||
try {
|
||||
return memory.getShort(addr, bigEndian);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new ByteBlockAccessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the short at the given index.
|
||||
*
|
||||
* @param index byte index
|
||||
* @param value value to set
|
||||
* @throws ByteBlockAccessException if the block cannot be updated
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this block.
|
||||
*/
|
||||
@Override
|
||||
public void setShort(BigInteger index, short value) throws ByteBlockAccessException {
|
||||
Address addr = getAddress(index);
|
||||
checkEditsAllowed(addr, 2);
|
||||
try {
|
||||
memory.setShort(addr, value, bigEndian);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new ByteBlockAccessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this block can be modified.
|
||||
*/
|
||||
|
|
|
@ -61,6 +61,15 @@ public interface ByteBlock {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short value at the given index.
|
||||
* @param index byte index
|
||||
* @throws ByteBlockAccessException if the block cannot be read
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this
|
||||
* block.
|
||||
*/
|
||||
public short getShort(BigInteger index) throws ByteBlockAccessException;
|
||||
|
||||
/**
|
||||
* Get the int value at the given index.
|
||||
* @param index byte index
|
||||
|
@ -89,6 +98,16 @@ public interface ByteBlock {
|
|||
*/
|
||||
public void setByte(BigInteger index, byte value) throws ByteBlockAccessException;
|
||||
|
||||
/**
|
||||
* Set the short at the given index.
|
||||
* @param index byte index
|
||||
* @param value value to set
|
||||
* @throws ByteBlockAccessException if the block cannot be updated
|
||||
* @throws IndexOutOfBoundsException if the given index is not in this
|
||||
* block.
|
||||
*/
|
||||
public void setShort(BigInteger index, short value) throws ByteBlockAccessException;
|
||||
|
||||
/**
|
||||
* Set the int at the given index.
|
||||
* @param index byte index
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,236 +15,22 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.format;
|
||||
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Converts byte values to Integer represented as an 8 digit hex number.
|
||||
* Converts byte values to Integer represented as an 4-byte/8-digit hex number.
|
||||
*/
|
||||
public class HexIntegerFormatModel implements UniversalDataFormatModel {
|
||||
|
||||
private int symbolSize;
|
||||
public class HexIntegerFormatModel extends HexValueFormatModel {
|
||||
|
||||
public HexIntegerFormatModel() {
|
||||
|
||||
symbolSize = 8;
|
||||
super("Hex Integer", 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this formatter.
|
||||
*/
|
||||
public String getName() {
|
||||
return "HexInteger";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes to make a unit; in this case,
|
||||
* returns 4.
|
||||
*/
|
||||
public int getUnitByteSize() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte used to generate the character at a given
|
||||
* position.
|
||||
* @param position number in the range 0 to 7
|
||||
*/
|
||||
public int getByteOffset(ByteBlock block, int position) {
|
||||
|
||||
int o = position / 2;
|
||||
|
||||
if (block.isBigEndian()) {
|
||||
return o;
|
||||
}
|
||||
return 3 - o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the byte offset into a unit, get the column position.
|
||||
*/
|
||||
public int getColumnPosition(ByteBlock block, int byteOffset) {
|
||||
if (byteOffset > 3) {
|
||||
throw new IllegalArgumentException("invalid byteOffset: " + byteOffset);
|
||||
}
|
||||
if (block.isBigEndian()) {
|
||||
return byteOffset * 2;
|
||||
}
|
||||
return (3 - byteOffset) * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of characters required to display a
|
||||
* unit.
|
||||
* @return 4 for number of characters in the integer representation.
|
||||
*/
|
||||
public int getDataUnitSymbolSize() {
|
||||
return symbolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation at the given index in the block.
|
||||
* @param block block to change
|
||||
* @param index byte index into the block
|
||||
* @throws ByteBlockAccessException if the block cannot be read
|
||||
* @throws IndexOutOfBoundsException if index is not valid for the
|
||||
* block
|
||||
*/
|
||||
@Override
|
||||
public String getDataRepresentation(ByteBlock block, BigInteger index)
|
||||
throws ByteBlockAccessException {
|
||||
|
||||
int i = block.getInt(index);
|
||||
|
||||
String str = Integer.toHexString(i);
|
||||
|
||||
return pad(str);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true to allow values to be changed.
|
||||
*/
|
||||
public boolean isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite a value in a ByteBlock.
|
||||
* @param block block to change
|
||||
* @param index byte index into the block
|
||||
* @param pos The position within the unit where c will be the
|
||||
* new character.
|
||||
* @param c new character to put at pos param
|
||||
* @return true if the replacement is legal, false if the
|
||||
* replacement value would not make sense for this format, e.g.
|
||||
* attempt to put a 'z' in a hex unit.
|
||||
* @throws ByteBlockAccessException if the block cannot be updated
|
||||
* @throws IndexOutOfBoundsException if index is not valid for the
|
||||
* block
|
||||
*/
|
||||
public boolean replaceValue(ByteBlock block, BigInteger index, int charPosition, char c)
|
||||
throws ByteBlockAccessException {
|
||||
|
||||
if (charPosition < 0 || charPosition > symbolSize - 1) {
|
||||
return false;
|
||||
}
|
||||
char[] charArray = { c };
|
||||
String s = new String(charArray);
|
||||
try {
|
||||
Integer.parseInt(s, 16);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte cb = Byte.parseByte(new String(charArray), 16);
|
||||
// get the correct byte offset based on position
|
||||
int byteOffset = getByteOffset(block, charPosition);
|
||||
BigInteger saveIndex = index;
|
||||
index = index.add(BigInteger.valueOf(byteOffset));
|
||||
byte b = block.getByte(index);
|
||||
b = adjustByte(b, cb, charPosition);
|
||||
int intValue = getInt(block, saveIndex, b, byteOffset);
|
||||
block.setInt(saveIndex, intValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of units in a group. A group may represent
|
||||
* multiple units shown as one entity. This format does not
|
||||
* support groups.
|
||||
*/
|
||||
public int getGroupSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of units in a group. This format does not
|
||||
* support groups.
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public void setGroupSize(int groupSize) {
|
||||
throw new UnsupportedOperationException("groups are not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of characters separating units.
|
||||
*/
|
||||
public int getUnitDelimiterSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.plugin.core.format.DataFormatModel#validateBytesPerLine(int)
|
||||
*/
|
||||
public boolean validateBytesPerLine(int bytesPerLine) {
|
||||
return bytesPerLine % 4 == 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// *** private methods ***
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Returns value with leading zeros.
|
||||
*/
|
||||
private String pad(String value) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int len = symbolSize - value.length();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
sb.append("0");
|
||||
}
|
||||
sb.append(value);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust byte b to use either the upper 4 bits or
|
||||
* the lower 4 bits of newb according to charPosition.
|
||||
*/
|
||||
private byte adjustByte(byte b, byte newb, int charPosition) {
|
||||
if (charPosition % 2 == 0) {
|
||||
// its the high order byte
|
||||
b &= 0x0f;
|
||||
newb <<= 4;
|
||||
}
|
||||
else {
|
||||
b &= 0xf0;
|
||||
}
|
||||
b += newb;
|
||||
return b;
|
||||
}
|
||||
|
||||
private int getInt(ByteBlock block, BigInteger offset, byte newb, int byteOffset) {
|
||||
byte[] b = new byte[4];
|
||||
try {
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
b[i] = block.getByte(offset.add(BigInteger.valueOf(i)));
|
||||
}
|
||||
b[byteOffset] = newb;
|
||||
|
||||
if (block.isBigEndian()) {
|
||||
return (b[0] << 24) | ((b[1] << 16) & 0x00FF0000) | ((b[2] << 8) & 0x0000FF00) |
|
||||
(b[3] & 0x000000FF);
|
||||
}
|
||||
return (b[3] << 24) | ((b[2] << 16) & 0x00FF0000) | ((b[1] << 8) & 0x0000FF00) |
|
||||
(b[0] & 0x000000FF);
|
||||
|
||||
}
|
||||
catch (ByteBlockAccessException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see ghidra.app.plugin.format.DataFormatModel#getHelpLocation()
|
||||
*/
|
||||
public HelpLocation getHelpLocation() {
|
||||
return new HelpLocation("ByteViewerPlugin", "HexInteger");
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* ###
|
||||
* 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.format;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Converts byte values to Long represented as an 8-byte/16--digit hex number.
|
||||
*/
|
||||
public class HexLongFormatModel extends HexValueFormatModel {
|
||||
|
||||
public HexLongFormatModel() {
|
||||
super("Hex Long", 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataRepresentation(ByteBlock block, BigInteger index)
|
||||
throws ByteBlockAccessException {
|
||||
long l = block.getLong(index);
|
||||
String str = Long.toHexString(l);
|
||||
return pad(str);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* 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.format;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Converts byte values to LongLong represented as an 16-byte/32-digit hex number.
|
||||
*/
|
||||
public class HexLongLongFormatModel extends HexValueFormatModel {
|
||||
|
||||
public HexLongLongFormatModel() {
|
||||
super("Hex Long Long", 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataRepresentation(ByteBlock block, BigInteger index)
|
||||
throws ByteBlockAccessException {
|
||||
long l0 = block.getLong(index);
|
||||
String str0 = pad(Long.toHexString(l0));
|
||||
long l1 = block.getLong(index.add(BigInteger.valueOf(8)));
|
||||
String str1 = pad(Long.toHexString(l1));
|
||||
String str = str1.substring(16) + str0.substring(16);
|
||||
return pad(str);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* ###
|
||||
* 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.format;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Converts byte values to Short represented as an 2-byte/4-digit hex number.
|
||||
*/
|
||||
public class HexShortFormatModel extends HexValueFormatModel {
|
||||
|
||||
public HexShortFormatModel() {
|
||||
super("Hex Short", 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataRepresentation(ByteBlock block, BigInteger index)
|
||||
throws ByteBlockAccessException {
|
||||
short s = block.getShort(index);
|
||||
String str = Integer.toHexString(s & 0xFFFF);
|
||||
return pad(str);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/* ###
|
||||
* 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.format;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Converts byte values to value represented as a 2, 4, 8, or 16-byte hex number.
|
||||
*/
|
||||
public abstract class HexValueFormatModel implements UniversalDataFormatModel {
|
||||
|
||||
protected String name;
|
||||
|
||||
private int symbolSize;
|
||||
protected int nbytes;
|
||||
|
||||
public HexValueFormatModel(String name, int nbytes) {
|
||||
this.name = name;
|
||||
this.nbytes = nbytes;
|
||||
symbolSize = nbytes * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnitByteSize() {
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getByteOffset(ByteBlock block, int position) {
|
||||
|
||||
int o = position / 2;
|
||||
|
||||
if (block.isBigEndian()) {
|
||||
return o;
|
||||
}
|
||||
return nbytes - 1 - o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPosition(ByteBlock block, int byteOffset) {
|
||||
if (byteOffset > nbytes - 1) {
|
||||
throw new IllegalArgumentException("invalid byteOffset: " + byteOffset);
|
||||
}
|
||||
if (block.isBigEndian()) {
|
||||
return byteOffset * 2;
|
||||
}
|
||||
return (nbytes - 1 - byteOffset) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataUnitSymbolSize() {
|
||||
return symbolSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract String getDataRepresentation(ByteBlock block, BigInteger index)
|
||||
throws ByteBlockAccessException;
|
||||
|
||||
@Override
|
||||
public boolean isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceValue(ByteBlock block, BigInteger index, int charPosition, char c)
|
||||
throws ByteBlockAccessException {
|
||||
|
||||
if (charPosition < 0 || charPosition > symbolSize - 1) {
|
||||
// Not sure how this is possible, but...
|
||||
return false;
|
||||
}
|
||||
char[] charArray = { c };
|
||||
byte cb = Byte.parseByte(new String(charArray), 16);
|
||||
// get the correct byte offset based on position
|
||||
int byteOffset = getByteOffset(block, charPosition);
|
||||
index = index.add(BigInteger.valueOf(byteOffset));
|
||||
byte b = block.getByte(index);
|
||||
b = adjustByte(b, cb, charPosition);
|
||||
block.setByte(index, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of units in a group. This format does not
|
||||
* support groups.
|
||||
*/
|
||||
@Override
|
||||
public void setGroupSize(int groupSize) {
|
||||
throw new UnsupportedOperationException("groups are not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnitDelimiterSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateBytesPerLine(int bytesPerLine) {
|
||||
return bytesPerLine % nbytes == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value with leading zeros.
|
||||
*/
|
||||
protected String pad(String value) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int len = symbolSize - value.length();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
sb.append("0");
|
||||
}
|
||||
sb.append(value);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust byte b to use either the upper 4 bits or
|
||||
* the lower 4 bits of newb according to charPosition.
|
||||
*/
|
||||
private byte adjustByte(byte b, byte newb, int charPosition) {
|
||||
if (charPosition % 2 == 0) {
|
||||
// its the high order byte
|
||||
b &= 0x0f;
|
||||
newb <<= 4;
|
||||
}
|
||||
else {
|
||||
b &= 0xf0;
|
||||
}
|
||||
b += newb;
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpLocation getHelpLocation() {
|
||||
return new HelpLocation("ByteViewerPlugin", "HexValue");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
}
|
|
@ -305,7 +305,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
field = asciiC.getField(loc.getIndex(), loc.getFieldNum());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground());
|
||||
|
||||
final ByteViewerComponent hexIntC = findComponent(panel, "HexInteger");
|
||||
final ByteViewerComponent hexIntC = findComponent(panel, "Hex Integer");
|
||||
SwingUtilities.invokeAndWait(() -> panel.setCurrentView(hexIntC));
|
||||
|
||||
loc = getFieldLocation(addr);
|
||||
|
@ -456,14 +456,14 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexInteger() throws Exception {
|
||||
public void testUndoRedoHexShort() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final Address addr = getAddr(0x01001003);
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
ByteViewerComponent c = findComponent(panel, "HexInteger");
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Short");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
|
@ -504,6 +504,228 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexInteger() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final Address addr = getAddr(0x01001003);
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Integer");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol());
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
byte value = program.getMemory().getByte(addr);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0xa0, memory.getByte(addr));
|
||||
|
||||
undo(program);
|
||||
|
||||
assertEquals(value, memory.getByte(addr));
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
|
||||
redo(program);
|
||||
|
||||
// field color should show edit color
|
||||
loc = getFieldLocation(addr);
|
||||
field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexLong() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final Address addr = getAddr(0x01001003);
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Long");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol());
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
byte value = program.getMemory().getByte(addr);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0xa0, memory.getByte(addr));
|
||||
|
||||
undo(program);
|
||||
|
||||
assertEquals(value, memory.getByte(addr));
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
|
||||
redo(program);
|
||||
|
||||
// field color should show edit color
|
||||
loc = getFieldLocation(addr);
|
||||
field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexLongLong() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final Address addr = getAddr(0x01001003);
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Long Long");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol());
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
byte value = program.getMemory().getByte(addr);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_A, 'a');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0xa0, memory.getByte(addr));
|
||||
|
||||
undo(program);
|
||||
|
||||
assertEquals(value, memory.getByte(addr));
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
|
||||
redo(program);
|
||||
|
||||
// field color should show edit color
|
||||
loc = getFieldLocation(addr);
|
||||
field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, field.getForeground());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexShort2() throws Exception {
|
||||
// remove code browser plugin so the cursor position does not
|
||||
// get changed because of location events that the code browser
|
||||
// generates.
|
||||
tool.removePlugins(new Plugin[] { cbPlugin });
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Short");
|
||||
panel.setCurrentView(c);
|
||||
// make 3 changes
|
||||
// verify that the Undo button is enabled and undo can be done 5 times
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
Address addr = getAddr(0x01001003);
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
char[] values = { 'a', '1', '2' };
|
||||
int[] keyCodes =
|
||||
{ KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3 };
|
||||
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
FieldLocation loc = currentComponent.getCursorLocation();
|
||||
KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0,
|
||||
keyCodes[i], values[i]);
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
}
|
||||
|
||||
});
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
assertTrue(program.canUndo());
|
||||
|
||||
undo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
if (i == 2) {
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
}
|
||||
else {
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
}
|
||||
assertTrue(!program.canUndo());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
redo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
|
||||
assertTrue(program.canUndo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexInteger2() throws Exception {
|
||||
// remove code browser plugin so the cursor position does not
|
||||
|
@ -514,7 +736,7 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
env.showTool();
|
||||
addViews();
|
||||
|
||||
ByteViewerComponent c = findComponent(panel, "HexInteger");
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Integer");
|
||||
panel.setCurrentView(c);
|
||||
// make 5 changes
|
||||
// verify that the Undo button is enabled and undo can be done 5 times
|
||||
|
@ -579,6 +801,160 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(program.canUndo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexLong2() throws Exception {
|
||||
// remove code browser plugin so the cursor position does not
|
||||
// get changed because of location events that the code browser
|
||||
// generates.
|
||||
tool.removePlugins(new Plugin[] { cbPlugin });
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Long");
|
||||
panel.setCurrentView(c);
|
||||
// make 9 changes
|
||||
// verify that the Undo button is enabled and undo can be done 5 times
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
Address addr = getAddr(0x01001003);
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
char[] values = { 'a', '1', '2', 'b', '3', 'c', '4', 'd', '5' };
|
||||
int[] keyCodes =
|
||||
{ KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3,
|
||||
KeyEvent.VK_C, KeyEvent.VK_4, KeyEvent.VK_D, KeyEvent.VK_5 };
|
||||
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
for (int i = 0; i < 9; i++) {
|
||||
FieldLocation loc = currentComponent.getCursorLocation();
|
||||
KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0,
|
||||
keyCodes[i], values[i]);
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
}
|
||||
|
||||
});
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
assertTrue(program.canUndo());
|
||||
|
||||
undo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
if (i == 8) {
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
}
|
||||
else {
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
}
|
||||
assertTrue(!program.canUndo());
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
|
||||
redo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
|
||||
assertTrue(program.canUndo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedoHexLongLong2() throws Exception {
|
||||
// remove code browser plugin so the cursor position does not
|
||||
// get changed because of location events that the code browser
|
||||
// generates.
|
||||
tool.removePlugins(new Plugin[] { cbPlugin });
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
ByteViewerComponent c = findComponent(panel, "Hex Long Long");
|
||||
panel.setCurrentView(c);
|
||||
// make 9 changes
|
||||
// verify that the Undo button is enabled and undo can be done 5 times
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
Address addr = getAddr(0x01001003);
|
||||
FieldLocation loc = getFieldLocation(addr);
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
char[] values = { 'a', '1', '2', 'b', '3', 'c', '4', 'd', '5', 'e', '6', 'f', '7', 'a',
|
||||
'8', 'b', '9' };
|
||||
int[] keyCodes =
|
||||
{ KeyEvent.VK_P, KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_B, KeyEvent.VK_3,
|
||||
KeyEvent.VK_C, KeyEvent.VK_4, KeyEvent.VK_D, KeyEvent.VK_5, KeyEvent.VK_E, KeyEvent.VK_6,
|
||||
KeyEvent.VK_F, KeyEvent.VK_7, KeyEvent.VK_G, KeyEvent.VK_8, KeyEvent.VK_H, KeyEvent.VK_9 };
|
||||
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
for (int i = 0; i < 17; i++) {
|
||||
FieldLocation loc = currentComponent.getCursorLocation();
|
||||
KeyEvent ev = new KeyEvent(currentComponent, 0, new Date().getTime(), 0,
|
||||
keyCodes[i], values[i]);
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
}
|
||||
|
||||
});
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
for (int i = 0; i < 17; i++) {
|
||||
assertTrue(program.canUndo());
|
||||
|
||||
undo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
if (i == 16) {
|
||||
assertTrue(fg == null ||
|
||||
ByteViewerComponentProvider.CURSOR_NON_ACTIVE_COLOR == field.getForeground());
|
||||
}
|
||||
else {
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
}
|
||||
assertTrue(!program.canUndo());
|
||||
|
||||
for (int i = 0; i < 17; i++) {
|
||||
|
||||
redo(program);
|
||||
|
||||
FieldLocation loc = c.getCursorLocation();
|
||||
ByteField field = c.getField(loc.getIndex(), loc.getFieldNum());
|
||||
Color fg = field.getForeground();
|
||||
assertEquals(fg, ByteViewerComponentProvider.CHANGED_VALUE_COLOR);
|
||||
}
|
||||
|
||||
assertTrue(program.canUndo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditInputHex() throws Exception {
|
||||
env.showTool();
|
||||
|
@ -717,12 +1093,129 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(value, program.getMemory().getByte(getAddr(0x01001000)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditModeHexShort() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Short");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001003));
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(),
|
||||
c.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003)));
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) c.getCurrentField()).getForeground());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
action.setSelected(false);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(!action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR,
|
||||
c.getFocusedCursorColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditModeHexInteger() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "HexInteger");
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Integer");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001003));
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(),
|
||||
c.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003)));
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) c.getCurrentField()).getForeground());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
action.setSelected(false);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(!action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR,
|
||||
c.getFocusedCursorColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditModeHexLong() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Long");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001003));
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR, c.getFocusedCursorColor());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
KeyEvent ev = new KeyEvent(c, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
c.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol(),
|
||||
c.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
assertEquals((byte) 0x10, program.getMemory().getByte(getAddr(0x01001003)));
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) c.getCurrentField()).getForeground());
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
action.setSelected(false);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
assertTrue(!action.isSelected());
|
||||
assertEquals(ByteViewerComponentProvider.CURSOR_ACTIVE_COLOR,
|
||||
c.getFocusedCursorColor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditModeHexLongLong() throws Exception {
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Long Long");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
|
@ -1563,7 +2056,10 @@ public class ByteViewerPlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
ByteViewerOptionsDialog dialog = launchByteViewerOptions();
|
||||
setViewSelected(dialog, "Ascii", true);
|
||||
setViewSelected(dialog, "Octal", true);
|
||||
setViewSelected(dialog, "HexInteger", true);
|
||||
setViewSelected(dialog, "Hex Short", true);
|
||||
setViewSelected(dialog, "Hex Integer", true);
|
||||
setViewSelected(dialog, "Hex Long", true);
|
||||
setViewSelected(dialog, "Hex Long Long", true);
|
||||
pressButtonByText(dialog.getComponent(), "OK");
|
||||
}
|
||||
|
||||
|
|
|
@ -237,13 +237,72 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration
|
|||
assertEquals(insertionStr, findLabelStr(panel, "Insertion"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexShortView() throws Exception {
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Short");
|
||||
panel.setCurrentView(c);
|
||||
assertEquals(8, c.getNumberOfFields());
|
||||
assertEquals(2, c.getDataModel().getUnitByteSize());
|
||||
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
});
|
||||
// verify that the 2 bytes are represented as an 4 digit hex number
|
||||
assertEquals(4, c.getCurrentField().getNumCols(loc.getRow()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherEditsHexShort() throws Exception {
|
||||
// verify that the 4 byte string is rendered in red when a byte
|
||||
// is changed from another view, e.g. Ascii or Hex
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Ascii");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
|
||||
final ByteViewerComponent hexComp = findComponent(panel, "Hex Short");
|
||||
|
||||
runSwing(() -> {
|
||||
ProgramByteBlockSet blockset =
|
||||
(ProgramByteBlockSet) plugin.getProvider().getByteBlockSet();
|
||||
ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000));
|
||||
FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset());
|
||||
hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0);
|
||||
});
|
||||
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) hexComp.getCurrentField()).getForeground());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexIntegerView() throws Exception {
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "HexInteger");
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Integer");
|
||||
panel.setCurrentView(c);
|
||||
assertEquals(4, c.getNumberOfFields());
|
||||
assertEquals(4, c.getDataModel().getUnitByteSize());
|
||||
|
@ -282,7 +341,125 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration
|
|||
});
|
||||
program.flushEvents();
|
||||
|
||||
final ByteViewerComponent hexComp = findComponent(panel, "HexInteger");
|
||||
final ByteViewerComponent hexComp = findComponent(panel, "Hex Integer");
|
||||
|
||||
runSwing(() -> {
|
||||
ProgramByteBlockSet blockset =
|
||||
(ProgramByteBlockSet) plugin.getProvider().getByteBlockSet();
|
||||
ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000));
|
||||
FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset());
|
||||
hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0);
|
||||
});
|
||||
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) hexComp.getCurrentField()).getForeground());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexLongView() throws Exception {
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Long");
|
||||
panel.setCurrentView(c);
|
||||
assertEquals(2, c.getNumberOfFields());
|
||||
assertEquals(8, c.getDataModel().getUnitByteSize());
|
||||
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
});
|
||||
// verify that the 8 bytes are represented as an 8 digit hex number
|
||||
assertEquals(16, c.getCurrentField().getNumCols(loc.getRow()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherEditsHexLong() throws Exception {
|
||||
// verify that the 4 byte string is rendered in red when a byte
|
||||
// is changed from another view, e.g. Ascii or Hex
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Ascii");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
|
||||
final ByteViewerComponent hexComp = findComponent(panel, "Hex Long");
|
||||
|
||||
runSwing(() -> {
|
||||
ProgramByteBlockSet blockset =
|
||||
(ProgramByteBlockSet) plugin.getProvider().getByteBlockSet();
|
||||
ByteBlockInfo bbInfo = blockset.getByteBlockInfo(getAddr(0x01001000));
|
||||
FieldLocation l = hexComp.getFieldLocation(bbInfo.getBlock(), bbInfo.getOffset());
|
||||
hexComp.setCursorPosition(l.getIndex(), l.getFieldNum(), 0, 0);
|
||||
});
|
||||
|
||||
assertEquals(ByteViewerComponentProvider.CHANGED_VALUE_COLOR,
|
||||
((ByteField) hexComp.getCurrentField()).getForeground());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexLongLongView() throws Exception {
|
||||
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Hex Long Long");
|
||||
panel.setCurrentView(c);
|
||||
assertEquals(1, c.getNumberOfFields());
|
||||
assertEquals(16, c.getDataModel().getUnitByteSize());
|
||||
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
});
|
||||
// verify that the 16 bytes are represented as an 32 digit hex number
|
||||
assertEquals(32, c.getCurrentField().getNumCols(loc.getRow()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherEditsHexLongLong() throws Exception {
|
||||
// verify that the 4 byte string is rendered in red when a byte
|
||||
// is changed from another view, e.g. Ascii or Hex
|
||||
env.showTool();
|
||||
addViews();
|
||||
|
||||
final ByteViewerComponent c = findComponent(panel, "Ascii");
|
||||
panel.setCurrentView(c);
|
||||
|
||||
final ToggleDockingAction action =
|
||||
(ToggleDockingAction) getAction(plugin, "Enable/Disable Byteviewer Editing");
|
||||
final FieldLocation loc = getFieldLocation(getAddr(0x01001000));
|
||||
runSwing(() -> {
|
||||
ByteViewerComponent currentComponent = panel.getCurrentComponent();
|
||||
currentComponent.setCursorPosition(loc.getIndex(), loc.getFieldNum(), 0, 0);
|
||||
action.setSelected(true);
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
KeyEvent ev =
|
||||
new KeyEvent(currentComponent, 0, new Date().getTime(), 0, KeyEvent.VK_1, '1');
|
||||
currentComponent.keyPressed(ev, loc.getIndex(), loc.getFieldNum(), loc.getRow(),
|
||||
loc.getCol(), currentComponent.getCurrentField());
|
||||
});
|
||||
program.flushEvents();
|
||||
|
||||
final ByteViewerComponent hexComp = findComponent(panel, "Hex Long Long");
|
||||
|
||||
runSwing(() -> {
|
||||
ProgramByteBlockSet blockset =
|
||||
|
@ -402,7 +579,7 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration
|
|||
ByteViewerOptionsDialog dialog = launchByteViewerOptions();
|
||||
setViewSelected(dialog, "Ascii", true);
|
||||
setViewSelected(dialog, "Octal", true);
|
||||
setViewSelected(dialog, "HexInteger", true);
|
||||
setViewSelected(dialog, "Hex Integer", true);
|
||||
setViewSelected(dialog, "Integer", true);
|
||||
pressButtonByText(dialog.getComponent(), "OK");
|
||||
waitForSwing();
|
||||
|
@ -805,7 +982,10 @@ public class ByteViewerPluginFormatsTest extends AbstractGhidraHeadedIntegration
|
|||
ByteViewerOptionsDialog dialog = launchByteViewerOptions();
|
||||
setViewSelected(dialog, "Ascii", true);
|
||||
setViewSelected(dialog, "Octal", true);
|
||||
setViewSelected(dialog, "HexInteger", true);
|
||||
setViewSelected(dialog, "Hex Short", true);
|
||||
setViewSelected(dialog, "Hex Integer", true);
|
||||
setViewSelected(dialog, "Hex Long", true);
|
||||
setViewSelected(dialog, "Hex Long Long", true);
|
||||
pressButtonByText(dialog.getComponent(), "OK");
|
||||
waitForSwing();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.stream.Collectors;
|
|||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
|
@ -120,7 +119,7 @@ public class DiffClangHighlightController extends LocationClangHighlightControll
|
|||
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
|
||||
(ClangSyntaxToken) tok, defaultParenColor);
|
||||
reHighlightDiffs(tokens);
|
||||
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
|
||||
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||
}
|
||||
|
||||
TokenBin tokenBin = null;
|
||||
|
|
|
@ -102,6 +102,7 @@ model {
|
|||
include "blockaction.cc"
|
||||
include "merge.cc"
|
||||
include "double.cc"
|
||||
include "constseq.cc"
|
||||
include "coreaction.cc"
|
||||
include "condexe.cc"
|
||||
include "override.cc"
|
||||
|
|
|
@ -64,6 +64,7 @@ src/decompile/datatests/retstruct.xml||GHIDRA||||END|
|
|||
src/decompile/datatests/sbyte.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/skipnext2.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/stackreturn.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/stackstring.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/switchhide.xml||GHIDRA||||END|
|
||||
src/decompile/datatests/switchind.xml||GHIDRA||||END|
|
||||
|
|
|
@ -18,8 +18,6 @@ package classrecovery;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.decompiler.DecompileOptions;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.app.decompiler.util.FillOutStructureHelper;
|
||||
import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair;
|
||||
import ghidra.app.util.opinion.PeLoader;
|
||||
|
@ -1480,10 +1478,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
}
|
||||
}
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
for (Function constructor : constructorList) {
|
||||
|
||||
|
@ -1568,7 +1563,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
|
||||
monitor.checkCancelled();
|
||||
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false, null);
|
||||
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
|
||||
stores = removePcodeOpsNotInFunction(function, stores);
|
||||
|
||||
|
@ -2484,7 +2479,7 @@ public class RTTIWindowsClassRecoverer extends RTTIClassRecoverer {
|
|||
baseClassDescriptorAddress.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Continue if the class has mult inh but base class is not on the parent list
|
||||
if (!recoveredClass.getParentList().contains(baseClass)) {
|
||||
continue;
|
||||
|
|
|
@ -498,16 +498,6 @@ public class RecoveredClass {
|
|||
String fieldName = newComponent.getFieldName();
|
||||
String comment = newComponent.getComment();
|
||||
|
||||
// if it is any empty placeholder structure - replace with
|
||||
// undefined1 dt
|
||||
if (newComponentDataType instanceof Structure &&
|
||||
newComponentDataType.isNotYetDefined()) {
|
||||
|
||||
computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1,
|
||||
fieldName, comment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if new component is an existing class data type pointer then replace current item
|
||||
// with a void pointer of same size if there is room
|
||||
if (newComponentDataType instanceof Pointer &&
|
||||
|
@ -529,9 +519,19 @@ public class RecoveredClass {
|
|||
// if the new component is a non-empty structure, check to see if the current
|
||||
// structure has undefined or equivalent components and replace with new struct if so
|
||||
if (newComponentDataType instanceof Structure) {
|
||||
|
||||
// if new component is any empty placeholder structure AND if the existing component
|
||||
// is undefined then replace with undefined1 dt
|
||||
if (newComponentDataType.isNotYetDefined()) {
|
||||
if (Undefined.isUndefined(currentComponentDataType)) {
|
||||
computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1,
|
||||
fieldName, comment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (EditStructureUtils.hasReplaceableComponentsAtOffset(computedClassStructure,
|
||||
offset, (Structure) newComponentDataType, monitor)) {
|
||||
|
||||
offset, (Structure)newComponentDataType, monitor)) {
|
||||
|
||||
boolean successfulClear =
|
||||
EditStructureUtils.clearLengthAtOffset(computedClassStructure, offset,
|
||||
length, monitor);
|
||||
|
|
|
@ -22,8 +22,6 @@ import java.util.stream.Collectors;
|
|||
import ghidra.app.cmd.function.ApplyFunctionSignatureCmd;
|
||||
import ghidra.app.cmd.label.AddLabelCmd;
|
||||
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
|
||||
import ghidra.app.decompiler.DecompileOptions;
|
||||
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||
import ghidra.app.decompiler.util.FillOutStructureHelper;
|
||||
import ghidra.app.decompiler.util.FillOutStructureHelper.OffsetPcodeOpPair;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
|
||||
|
@ -1135,10 +1133,7 @@ public class RecoveredClassHelper {
|
|||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
Address vftableAddress = null;
|
||||
for (HighVariable highVariable : highVariables) {
|
||||
|
@ -1146,7 +1141,8 @@ public class RecoveredClassHelper {
|
|||
monitor.checkCancelled();
|
||||
|
||||
Structure structure =
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false,
|
||||
decompilerUtils.getDecompilerInterface());
|
||||
|
||||
NoisyStructureBuilder componentMap = fillStructHelper.getComponentMap();
|
||||
|
||||
|
@ -1202,13 +1198,12 @@ public class RecoveredClassHelper {
|
|||
|
||||
Address address = getTargetAddressFromPcodeOp(pcodeOp);
|
||||
if (address.equals(vftableReference)) {
|
||||
|
||||
Varnode[] inputs = pcodeOp.getInputs();
|
||||
for (Varnode input : inputs) {
|
||||
monitor.checkCancelled();
|
||||
if (input.getHigh() != null) {
|
||||
highVars.add(input.getHigh());
|
||||
}
|
||||
Varnode input = pcodeOp.getInput(1);
|
||||
if (input.getDef() != null && input.getDef().getOpcode() == PcodeOp.CAST) {
|
||||
input = input.getDef().getInput(0);
|
||||
}
|
||||
if (input.getHigh() != null) {
|
||||
highVars.add(input.getHigh());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5855,16 +5850,13 @@ public class RecoveredClassHelper {
|
|||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
|
||||
DecompileOptions decompileOptions =
|
||||
DecompilerUtils.getDecompileOptions(serviceProvider, program);
|
||||
FillOutStructureHelper fillStructHelper =
|
||||
new FillOutStructureHelper(program, decompileOptions, monitor);
|
||||
FillOutStructureHelper fillStructHelper = new FillOutStructureHelper(program, monitor);
|
||||
|
||||
for (HighVariable highVariable : highVariables) {
|
||||
|
||||
monitor.checkCancelled();
|
||||
|
||||
fillStructHelper.processStructure(highVariable, function, true, false);
|
||||
fillStructHelper.processStructure(highVariable, function, true, false, null);
|
||||
List<OffsetPcodeOpPair> stores = fillStructHelper.getStorePcodeOps();
|
||||
stores = removePcodeOpsNotInFunction(function, stores);
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ DECCORE=capability architecture options graph cover block cast typeop database c
|
|||
type variable varmap jumptable emulate emulateutil flow userop multiprecision \
|
||||
funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
|
||||
heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
|
||||
transform coreaction condexe override dynamic crc32 prettyprint \
|
||||
transform constseq coreaction condexe override dynamic crc32 prettyprint \
|
||||
printlanguage printc printjava memstate opbehavior paramid signature $(COREEXT_NAMES)
|
||||
# Files used for any project that use the sleigh decoder
|
||||
SLEIGH= sleigh pcodeparse pcodecompile sleighbase slghsymbol \
|
||||
|
|
|
@ -1321,7 +1321,6 @@ void Architecture::parseCompilerConfig(DocumentStorage &store)
|
|||
if (miter == protoModels.end()) { // If __thiscall doesn't exist we clone it off of the default
|
||||
createModelAlias("__thiscall",defaultfp->getName());
|
||||
}
|
||||
userops.setDefaults(this);
|
||||
initializeSegments();
|
||||
PreferSplitManager::initialize(splitrecords);
|
||||
types->setupSizes(); // If no data_organization was registered, set up default values
|
||||
|
|
491
Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc
Normal file
491
Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc
Normal file
|
@ -0,0 +1,491 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
#include "constseq.hh"
|
||||
#include "funcdata.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
const int4 StringSequence::MINIMUM_SEQUENCE_LENGTH = 4;
|
||||
|
||||
/// \brief Set-up for recovering COPY ops into a memory range, given a Symbol and an Address being COPYed into
|
||||
///
|
||||
/// The SymbolEntry and Address are passed in, with an expected data-type. Check if there is an array
|
||||
/// of the data-type within the Symbol, and if so, initialize the memory range for the
|
||||
/// the sequence. Follow on with gathering PcodeOps and testing if the sequence is viable. If not, the
|
||||
/// the size the memory range will be set to zero.
|
||||
/// \param fdata is the function containing the root COPY
|
||||
/// \param ct is the specific data-type for which there should be an array
|
||||
/// \param ent is the given Symbol
|
||||
/// \param root is the COPY holding the constant
|
||||
/// \param addr is the Address being COPYed into
|
||||
StringSequence::StringSequence(Funcdata &fdata,Datatype *ct,SymbolEntry *ent,PcodeOp *root,const Address &addr)
|
||||
: data(fdata)
|
||||
{
|
||||
rootOp = root;
|
||||
rootAddr = addr;
|
||||
charType = ct;
|
||||
entry = ent;
|
||||
size = 0;
|
||||
if (entry->getAddr().getSpace() != addr.getSpace())
|
||||
return;
|
||||
int8 off = rootAddr.getOffset() - entry->getFirst();
|
||||
if (off >= entry->getSize())
|
||||
return;
|
||||
if (rootOp->getIn(0)->getOffset() == 0)
|
||||
return;
|
||||
Datatype *parentType = entry->getSymbol()->getType();
|
||||
Datatype *lastType = (Datatype *)0;
|
||||
int8 lastOff = 0;
|
||||
do {
|
||||
if (parentType == ct)
|
||||
break;
|
||||
lastType = parentType;
|
||||
lastOff = off;
|
||||
parentType = parentType->getSubType(off, &off);
|
||||
} while(parentType != (Datatype *)0);
|
||||
if (parentType != ct || lastType == (Datatype *)0 || lastType->getMetatype() != TYPE_ARRAY)
|
||||
return;
|
||||
startAddr = rootAddr - lastOff;
|
||||
size = ((TypeArray *)lastType)->numElements() * charType->getAlignSize();
|
||||
block = rootOp->getParent();
|
||||
if (collectCopyOps()) {
|
||||
if (checkCopyInterference()) {
|
||||
if (formByteArray()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
void StringSequence::clear(void)
|
||||
|
||||
{
|
||||
size = 0;
|
||||
moveOps.clear();
|
||||
}
|
||||
|
||||
/// The COPYs must be in the same basic block.
|
||||
/// If any COPY size does not match the \b copyType, return \b false.
|
||||
/// If there is a COPY to the array entry before rootVn, return \b false.
|
||||
/// Otherwise earlier COPYs are skipped. No COPYs are collected after the first gap (entry with no COPY to it).
|
||||
/// \return \b true to indicate legal COPY ops of constants were recovered.
|
||||
bool StringSequence::collectCopyOps(void)
|
||||
|
||||
{
|
||||
Address endAddr = startAddr + (size - 1); // startAddr - endAddr bounds the formal array
|
||||
Address beginAddr = startAddr; // Start search for COPYs at the start of the array
|
||||
if (startAddr != rootAddr) {
|
||||
beginAddr = rootAddr - charType->getAlignSize(); // or the first address before the root address (whichever is later)
|
||||
}
|
||||
VarnodeLocSet::const_iterator iter = data.beginLoc(beginAddr);
|
||||
VarnodeLocSet::const_iterator enditer = data.endLoc(endAddr);
|
||||
int4 diff = rootAddr.getOffset() - startAddr.getOffset();
|
||||
while(iter != enditer) {
|
||||
Varnode *vn = *iter;
|
||||
++iter;
|
||||
if (!vn->isWritten()) continue;
|
||||
PcodeOp *op = vn->getDef();
|
||||
if (op->code() != CPUI_COPY) continue;
|
||||
if (op->getParent() != block) continue;
|
||||
if (!op->getIn(0)->isConstant()) continue;
|
||||
if (vn->getSize() != charType->getSize())
|
||||
return false; // COPY is the wrong size (has yet to be split)
|
||||
int4 tmpDiff = vn->getOffset() - startAddr.getOffset();
|
||||
if (tmpDiff < diff) {
|
||||
if (tmpDiff + charType->getAlignSize() == diff)
|
||||
return false; // COPY to previous element, rootVn is not the first in sequence
|
||||
continue;
|
||||
}
|
||||
else if (tmpDiff > diff) {
|
||||
if (tmpDiff - diff < charType->getAlignSize())
|
||||
continue;
|
||||
if (tmpDiff - diff > charType->getAlignSize())
|
||||
break; // Gap in COPYs
|
||||
diff = tmpDiff; // Advanced by one character
|
||||
}
|
||||
moveOps.emplace_back(vn->getOffset(),op,-1);
|
||||
}
|
||||
return (moveOps.size() >= MINIMUM_SEQUENCE_LENGTH);
|
||||
}
|
||||
|
||||
/// The output Varnodes themselves should be verified to only be read outside of the basic block.
|
||||
/// So effectively only LOADs, STOREs, and CALLs can really interfere. Check for these between the given ops.
|
||||
/// \param startOp is the is the starting COPY
|
||||
/// \param endOp is the ending COPY
|
||||
/// \return \b true if there is no interference, \b false if there is possible interference
|
||||
bool StringSequence::checkBetweenCopy(PcodeOp *startOp,PcodeOp *endOp)
|
||||
|
||||
{
|
||||
startOp = startOp->nextOp();
|
||||
while(startOp != endOp) {
|
||||
if (startOp->getEvalType() == PcodeOp::special) {
|
||||
OpCode opc = startOp->code();
|
||||
if (opc != CPUI_INDIRECT && opc != CPUI_CALLOTHER &&
|
||||
opc != CPUI_SEGMENTOP && opc != CPUI_CPOOLREF && opc != CPUI_NEW)
|
||||
return false;
|
||||
}
|
||||
startOp = startOp->nextOp();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Sort the COPY ops based on block order. Starting with the root COPY, walk backward until an interfering
|
||||
/// gap is found or until the earliest COPY is reached. Similarly, walk forward until an interfering gap is found.
|
||||
/// Truncate the COPY op array to be this smaller set. If too many were truncated, return \b false.
|
||||
/// \return \b true if a maximal set of COPYs is found containing at the least the minimum number required
|
||||
bool StringSequence::checkCopyInterference(void)
|
||||
|
||||
{
|
||||
sort(moveOps.begin(),moveOps.end()); // Sort COPYs based on basic block order
|
||||
int4 pos;
|
||||
for(pos=0;pos<moveOps.size();++pos) {
|
||||
if (moveOps[pos].op == rootOp) break;
|
||||
}
|
||||
if (pos == moveOps.size()) return false;
|
||||
PcodeOp *curOp = moveOps[pos].op;
|
||||
int4 startingPos,endingPos;
|
||||
for(startingPos=pos-1;startingPos>=0;--startingPos) {
|
||||
PcodeOp *prevOp = moveOps[startingPos].op;
|
||||
if (!checkBetweenCopy(prevOp,curOp))
|
||||
break;
|
||||
curOp = prevOp;
|
||||
}
|
||||
startingPos += 1;
|
||||
curOp = moveOps[pos].op;
|
||||
for(endingPos=pos+1;endingPos < moveOps.size();++endingPos) {
|
||||
PcodeOp *nextOp = moveOps[endingPos].op;
|
||||
if (!checkBetweenCopy(curOp,nextOp))
|
||||
break;
|
||||
curOp = nextOp;
|
||||
}
|
||||
if (endingPos- startingPos < MINIMUM_SEQUENCE_LENGTH)
|
||||
return false;
|
||||
if (startingPos > 0) {
|
||||
for(int4 i=startingPos;i<endingPos;++i) {
|
||||
moveOps[i-startingPos] = moveOps[i];
|
||||
}
|
||||
}
|
||||
moveOps.resize(endingPos-startingPos,WriteNode(0,(PcodeOp *)0,-1));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Construct a Varnode, with data-type, that acts as a pointer (in)to the Symbol to the root Address
|
||||
///
|
||||
/// First, a PTRSUB is built from the base register to the Symbol. Then depending on its data-type, additional
|
||||
/// PTRSUBs and PTRADDs are buit to get from the start of the Symbol to the memory region holding the character data.
|
||||
/// All the new Varnodes have the appropriate pointer data-type set. The final Varnode holding the pointer to
|
||||
/// the memory region is returned.
|
||||
/// \param insertPoint is the point before which all new PTRSUBs and PTRADDs are inserted
|
||||
Varnode *StringSequence::constructTypedPointer(PcodeOp *insertPoint)
|
||||
|
||||
{
|
||||
Varnode *spacePtr;
|
||||
AddrSpace *spc = rootAddr.getSpace();
|
||||
TypeFactory *types = data.getArch()->types;
|
||||
if (spc->getType() == IPTR_SPACEBASE)
|
||||
spacePtr = data.constructSpacebaseInput(spc);
|
||||
else
|
||||
spacePtr = data.constructConstSpacebase(spc);
|
||||
Datatype *baseType = entry->getSymbol()->getType();
|
||||
PcodeOp *ptrsub = data.newOp(2, insertPoint->getAddr());
|
||||
data.opSetOpcode(ptrsub, CPUI_PTRSUB);
|
||||
data.opSetInput(ptrsub,spacePtr,0);
|
||||
uintb baseOff = AddrSpace::byteToAddress(entry->getFirst(),spc->getWordSize()); // Convert to address units
|
||||
data.opSetInput(ptrsub,data.newConstant(spacePtr->getSize(), baseOff),1);
|
||||
spacePtr = data.newUniqueOut(spacePtr->getSize(), ptrsub);
|
||||
data.opInsertBefore(ptrsub, insertPoint);
|
||||
TypePointer *curType = types->getTypePointerStripArray(spacePtr->getSize(), baseType, spc->getWordSize());
|
||||
spacePtr->updateType(curType, false, false);
|
||||
int8 curOff = rootAddr.getOffset() - entry->getFirst();
|
||||
while(baseType != charType) {
|
||||
int4 elSize = -1;
|
||||
if (baseType->getMetatype() == TYPE_ARRAY)
|
||||
elSize = ((TypeArray *)baseType)->getBase()->getAlignSize();
|
||||
int8 newOff;
|
||||
baseType = baseType->getSubType(curOff, &newOff );
|
||||
if (baseType == (Datatype *)0) break;
|
||||
curOff -= newOff;
|
||||
baseOff = AddrSpace::byteToAddress(curOff, spc->getWordSize());
|
||||
if (elSize >= 0) {
|
||||
if (curOff == 0) { // Don't create a PTRADD( #0, ...)
|
||||
// spacePtr already has data-type with ARRAY stripped
|
||||
// baseType is already updated
|
||||
continue;
|
||||
}
|
||||
ptrsub = data.newOp(3, insertPoint->getAddr());
|
||||
data.opSetOpcode(ptrsub, CPUI_PTRADD);
|
||||
int8 numEl = curOff / elSize;
|
||||
data.opSetInput(ptrsub,data.newConstant(4, numEl),1);
|
||||
data.opSetInput(ptrsub,data.newConstant(4,elSize),2);
|
||||
}
|
||||
else {
|
||||
ptrsub = data.newOp(2, insertPoint->getAddr());
|
||||
data.opSetOpcode(ptrsub, CPUI_PTRSUB);
|
||||
data.opSetInput(ptrsub,data.newConstant(spacePtr->getSize(), baseOff), 1);
|
||||
}
|
||||
data.opSetInput(ptrsub,spacePtr,0);
|
||||
spacePtr = data.newUniqueOut(spacePtr->getSize(), ptrsub);
|
||||
data.opInsertBefore(ptrsub, insertPoint);
|
||||
curType = types->getTypePointerStripArray(spacePtr->getSize(), baseType, spc->getWordSize());
|
||||
spacePtr->updateType(curType, false, false);
|
||||
curOff = newOff;
|
||||
}
|
||||
if (curOff != 0) {
|
||||
PcodeOp *addOp = data.newOp(2, insertPoint->getAddr());
|
||||
data.opSetOpcode(addOp, CPUI_INT_ADD);
|
||||
data.opSetInput(addOp, spacePtr, 0);
|
||||
baseOff = AddrSpace::byteToAddress(curOff, spc->getWordSize());
|
||||
data.opSetInput(addOp, data.newConstant(spacePtr->getSize(), baseOff), 1);
|
||||
spacePtr = data.newUniqueOut(spacePtr->getSize(), addOp);
|
||||
data.opInsertBefore(addOp, insertPoint);
|
||||
curType = types->getTypePointer(spacePtr->getSize(), charType, spc->getWordSize());
|
||||
spacePtr->updateType(curType, false, false);
|
||||
}
|
||||
return spacePtr;
|
||||
}
|
||||
|
||||
/// Create an array of bytes from the root Varnode to the extent of the memory region.
|
||||
/// Run through the COPYs and place their constant input into the array.
|
||||
/// If there are gaps in the byte array, remove any COPY that doesn't write to the contiguous
|
||||
/// region in front of the root Varnode. Return \b false if the contiguous region is too small.
|
||||
/// \return \b true if there exists enough COPYs that write into the region in front of the root Varnode without gaps
|
||||
bool StringSequence::formByteArray(void)
|
||||
|
||||
{
|
||||
int4 diff = rootAddr.getOffset() - startAddr.getOffset();
|
||||
byteArray.resize(size-diff,0);
|
||||
vector<uint1> used(size-diff,0);
|
||||
int4 elSize = charType->getSize();
|
||||
bool isBigEndian = rootAddr.isBigEndian();
|
||||
for(int4 i=0;i<moveOps.size();++i) {
|
||||
int4 bytePos = moveOps[i].offset - rootAddr.getOffset();
|
||||
if (used[bytePos] != 0)
|
||||
return false; // Multiple COPYs to same place
|
||||
uint8 val = moveOps[i].op->getIn(0)->getOffset();
|
||||
used[bytePos] = (val == 0) ? 2 : 1; // Mark byte as used, a 2 indicates a null terminator
|
||||
if (isBigEndian) {
|
||||
for(int4 j=0;j<elSize;++j) {
|
||||
uint1 b = (val >> (elSize-1-j)*8) & 0xff;
|
||||
byteArray[bytePos+j] = b;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int4 j=0;j<elSize;++j) {
|
||||
byteArray[bytePos+j] = (uint1)val;
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
int4 bigElSize = charType->getAlignSize();
|
||||
int4 count;
|
||||
for(count=0;count<moveOps.size();++count) {
|
||||
uint1 val = used[ count * bigElSize ];
|
||||
if (val != 1) { // Count number of characters not including null terminator
|
||||
if (val == 2)
|
||||
count += 1; // Allow a single null terminator
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count < MINIMUM_SEQUENCE_LENGTH)
|
||||
return false;
|
||||
if (count != moveOps.size()) {
|
||||
uintb maxOff = rootAddr.getOffset() + count * bigElSize;
|
||||
vector<WriteNode> finalOps;
|
||||
for(int4 i=0;i<moveOps.size();++i) {
|
||||
if (moveOps[i].offset < maxOff)
|
||||
finalOps.push_back(moveOps[i]);
|
||||
}
|
||||
moveOps.swap(finalOps);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Use the \b charType to select the appropriate string copying function. If a match to the \b charType
|
||||
/// doesn't exist, use a built-in \b memcpy function. The id of the selected built-in function is returned.
|
||||
/// The value indicating either the number of characters or number of bytes being copied is also passed back.
|
||||
/// \param index will hold the number of elements being copied
|
||||
uint4 StringSequence::selectStringCopyFunction(int4 &index)
|
||||
|
||||
{
|
||||
TypeFactory *types = data.getArch()->types;
|
||||
if (charType == types->getTypeChar(types->getSizeOfChar())) {
|
||||
index = moveOps.size();
|
||||
return UserPcodeOp::BUILTIN_STRNCPY;
|
||||
}
|
||||
else if (charType == types->getTypeChar(types->getSizeOfWChar())) {
|
||||
index = moveOps.size();
|
||||
return UserPcodeOp::BUILTIN_WCSNCPY;
|
||||
}
|
||||
index = moveOps.size() * charType->getSize();
|
||||
return UserPcodeOp::BUILTIN_MEMCPY;
|
||||
}
|
||||
|
||||
/// A built-in user-op that copies string data is created. Its first (destination) parameter is constructed
|
||||
/// as a pointer to the array holding the character data, which may be nested in other arrays or structures.
|
||||
/// The second (source) parameter is an \e internal \e string constructed from the \b byteArray. The
|
||||
/// third parameter is the constant indicating the length of the string. The \e user-op is inserted just before
|
||||
/// the last PcodeOp moving a character into the memory region.
|
||||
/// \return the constructed PcodeOp representing the \b memcpy
|
||||
PcodeOp *StringSequence::buildStringCopy(void)
|
||||
|
||||
{
|
||||
PcodeOp *insertPoint = moveOps[0].op; // Earliest COPY in the block
|
||||
int4 numBytes = moveOps.size() * charType->getSize();
|
||||
Architecture *glb = data.getArch();
|
||||
TypeFactory *types = glb->types;
|
||||
Datatype *charPtrType = types->getTypePointer(types->getSizeOfPointer(),charType,rootAddr.getSpace()->getWordSize());
|
||||
Varnode *srcPtr = data.getInternalString(byteArray.data(), numBytes, charPtrType, insertPoint);
|
||||
if (srcPtr == (Varnode *)0)
|
||||
return (PcodeOp *)0;
|
||||
int4 index;
|
||||
uint4 builtInId = selectStringCopyFunction(index);
|
||||
glb->userops.registerBuiltin(builtInId);
|
||||
PcodeOp *copyOp = data.newOp(4,insertPoint->getAddr());
|
||||
data.opSetOpcode(copyOp, CPUI_CALLOTHER);
|
||||
data.opSetInput(copyOp, data.newConstant(4, builtInId), 0);
|
||||
Varnode *destPtr = constructTypedPointer(insertPoint);
|
||||
data.opSetInput(copyOp, destPtr, 1);
|
||||
data.opSetInput(copyOp, srcPtr, 2);
|
||||
Varnode *lenVn = data.newConstant(4,index);
|
||||
lenVn->updateType(copyOp->inputTypeLocal(3), false, false);
|
||||
data.opSetInput(copyOp, lenVn, 3);
|
||||
data.opInsertBefore(copyOp, insertPoint);
|
||||
return copyOp;
|
||||
}
|
||||
|
||||
/// \brief Analyze output descendants of the given PcodeOp being removed
|
||||
///
|
||||
/// Record any \b points where the output is being read, for later replacement.
|
||||
/// Keep track of CPUI_PIECE ops whose input is from a PcodeOp being removed, and if both inputs are
|
||||
/// visited, remove the input \e points and add the CPUI_PIECE to the list of PcodeOps being removed.
|
||||
/// \param curNode is the given PcodeOp being removed
|
||||
/// \param xref are the set of CPUI_PIECE ops with one input visited
|
||||
/// \param points is the set of input points whose PcodeOp is being removed
|
||||
/// \param deadOps is the current collection of PcodeOps being removed
|
||||
void StringSequence::removeForward(const WriteNode &curNode,map<PcodeOp *,list<WriteNode>::iterator> &xref,
|
||||
list<WriteNode> &points,vector<WriteNode> &deadOps)
|
||||
{
|
||||
Varnode *vn = curNode.op->getOut();
|
||||
list<PcodeOp *>::const_iterator iter;
|
||||
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
||||
PcodeOp *op = *iter;
|
||||
map<PcodeOp *,list<WriteNode>::iterator>::iterator miter = xref.find(op);
|
||||
if (miter != xref.end()) {
|
||||
// We have seen the PIECE twice
|
||||
uintb off = (*(*miter).second).offset;
|
||||
if (curNode.offset < off)
|
||||
off = curNode.offset;
|
||||
points.erase((*miter).second);
|
||||
deadOps.emplace_back(off,op,-1);
|
||||
}
|
||||
else {
|
||||
int4 slot = op->getSlot(vn);
|
||||
points.emplace_back(curNode.offset,op,slot);
|
||||
if (op->code() == CPUI_PIECE) {
|
||||
list<WriteNode>::iterator xrefIter = points.end();
|
||||
--xrefIter;
|
||||
xref[op] = xrefIter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The COPY ops are removed. Any descendants of the COPY output are redefined with an INDIRECT around
|
||||
/// the a CALLOTHER op. If the COPYs feed into a PIECE op (as part of a CONCAT stack), the PIECE is removed
|
||||
/// as well, which may cascade into removal of other PIECE ops in the stack.
|
||||
/// \param replaceOp is the CALLOTHER op creating the INDIRECT effect
|
||||
void StringSequence::removeCopyOps(PcodeOp *replaceOp)
|
||||
|
||||
{
|
||||
map<PcodeOp *,list<WriteNode>::iterator> concatSet;
|
||||
list<WriteNode> points;
|
||||
vector<WriteNode> deadOps;
|
||||
for(int4 i=0;i<moveOps.size();++i) {
|
||||
removeForward(moveOps[i],concatSet,points,deadOps);
|
||||
}
|
||||
int4 pos = 0;
|
||||
while(pos < deadOps.size()) {
|
||||
removeForward(deadOps[pos],concatSet,points,deadOps);
|
||||
pos += 1;
|
||||
}
|
||||
for(list<WriteNode>::iterator iter=points.begin();iter!=points.end();++iter) {
|
||||
PcodeOp *op = (*iter).op;
|
||||
Varnode *vn = op->getIn((*iter).slot);
|
||||
if (vn->getDef()->code() != CPUI_INDIRECT) {
|
||||
Varnode *newIn = data.newConstant(vn->getSize(),0);
|
||||
PcodeOp *indOp = data.newOp(2, replaceOp->getAddr());
|
||||
data.opSetOpcode(indOp,CPUI_INDIRECT);
|
||||
data.opSetInput(indOp,newIn,0);
|
||||
data.opSetInput(indOp,data.newVarnodeIop(replaceOp),1);
|
||||
data.opSetOutput(indOp, vn);
|
||||
data.markIndirectCreation(indOp, false);
|
||||
data.opInsertBefore(indOp,replaceOp);
|
||||
}
|
||||
}
|
||||
for(int4 i=0;i<moveOps.size();++i)
|
||||
data.opDestroy(moveOps[i].op);
|
||||
for(int4 i=0;i<deadOps.size();++i)
|
||||
data.opDestroy(deadOps[i].op);
|
||||
}
|
||||
|
||||
/// The transform can only fail if the byte array does not encode a valid string, in which case \b false is returned.
|
||||
/// Otherwise, a CALLOTHER representing \b memcpy is constructed taking the string constant as its \e source pointer.
|
||||
/// The original COPY ops are removed.
|
||||
/// \return \b true if the transform succeeded and the CALLOTHER is created
|
||||
bool StringSequence::transform(void)
|
||||
|
||||
{
|
||||
PcodeOp *memCpyOp = buildStringCopy();
|
||||
if (memCpyOp == (PcodeOp *)0)
|
||||
return false;
|
||||
removeCopyOps(memCpyOp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuleStringSequence::getOpList(vector<uint4> &oplist) const
|
||||
|
||||
{
|
||||
oplist.push_back(CPUI_COPY);
|
||||
}
|
||||
|
||||
/// \class RuleStringSequence
|
||||
/// \brief Replace a sequence of COPY ops moving single characters with a \b memcpy CALLOTHER copying a whole string
|
||||
///
|
||||
/// Given a root COPY of a constant character, search for other COPYs in the same basic block that form a sequence
|
||||
/// of characters that can be interpreted as a single string. Replace the sequence of COPYs with a single
|
||||
/// \b memcpy CALLOTHER.
|
||||
int4 RuleStringSequence::applyOp(PcodeOp *op,Funcdata &data)
|
||||
|
||||
{
|
||||
if (!op->getIn(0)->isConstant()) return 0; // Constant
|
||||
Varnode *outvn = op->getOut();
|
||||
Datatype *ct = outvn->getType();
|
||||
if (!ct->isCharPrint()) return 0; // Copied to a "char" data-type Varnode
|
||||
if (ct->isOpaqueString()) return 0;
|
||||
if (!outvn->isAddrTied()) return 0;
|
||||
SymbolEntry *entry = data.getScopeLocal()->queryContainer(outvn->getAddr(), outvn->getSize(), op->getAddr());
|
||||
if (entry == (SymbolEntry *)0)
|
||||
return 0;
|
||||
StringSequence sequence(data,ct,entry,op,outvn->getAddr());
|
||||
if (!sequence.isValid())
|
||||
return 0;
|
||||
if (!sequence.transform())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
86
Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh
Normal file
86
Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/// \file constseq.hh
|
||||
/// \brief Classes for combining constants written to a contiguous region of memory
|
||||
#ifndef __CONSTSEQ_HH__
|
||||
#define __CONSTSEQ_HH__
|
||||
|
||||
#include "ruleaction.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
/// \brief A class for collecting sequences of COPY ops that hold string data
|
||||
///
|
||||
/// Given a starting Address and a Symbol with a character array as a component, a class instance collects
|
||||
/// a maximal set of COPY ops that can be treated as writing a single string into memory. Then, if the
|
||||
/// transform() method is called, an explicit string is constructed, and the COPYs are replaced with a
|
||||
/// \b memcpy CALLOTHER that takes the string as its source input.
|
||||
class StringSequence {
|
||||
public:
|
||||
static const int4 MINIMUM_SEQUENCE_LENGTH; ///< Minimum number of sequential characters to trigger replacement with CALLOTHER
|
||||
/// \brief Helper class holding a data-flow edge and optionally a memory offset being COPYed into or from
|
||||
class WriteNode {
|
||||
friend class StringSequence;
|
||||
uintb offset; ///< Offset into the memory region
|
||||
PcodeOp *op; ///< PcodeOp moving into/outof memory region
|
||||
int4 slot; ///< either input slot (>=0) or output (-1)
|
||||
public:
|
||||
WriteNode(uintb 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(); }
|
||||
};
|
||||
private:
|
||||
Funcdata &data; ///< Function being analyzed
|
||||
PcodeOp *rootOp; ///< The root PcodeOp
|
||||
Address rootAddr; ///< Address within the memory region associated with the root PcodeOp
|
||||
Address startAddr; ///< Starting address of the memory region
|
||||
SymbolEntry *entry; ///< Symbol at the root Address
|
||||
int4 size; ///< Size of the memory region in bytes
|
||||
Datatype *charType; ///< Element data-type
|
||||
BlockBasic *block; ///< Basic block containing all the COPY ops
|
||||
vector<WriteNode> moveOps; ///< COPYs into the array memory region
|
||||
vector<uint1> byteArray; ///< Constants collected in a single array
|
||||
bool collectCopyOps(void); ///< Collect ops COPYing constants into the memory region
|
||||
bool checkBetweenCopy(PcodeOp *startOp,PcodeOp *endOp); ///< Check for interfering ops between the two given COPYs
|
||||
bool checkCopyInterference(void); ///< Find maximal set of COPYs containing the root COPY with no interfering ops in between
|
||||
bool formByteArray(void); ///< Put constant values from COPYs into a single byte array
|
||||
uint4 selectStringCopyFunction(int4 &index); ///< Pick either strncpy, wcsncpy, or memcpy function used to copy string
|
||||
PcodeOp *buildStringCopy(void); ///< Build the strncpy,wcsncpy, or memcpy function with string as input
|
||||
static void removeForward(const WriteNode &curNode,map<PcodeOp *,list<WriteNode>::iterator> &xref,
|
||||
list<WriteNode> &points,vector<WriteNode> &deadOps);
|
||||
void removeCopyOps(PcodeOp *replaceOp); ///< Remove all the COPY ops from the basic block
|
||||
Varnode *constructTypedPointer(PcodeOp *insertPoint);
|
||||
public:
|
||||
StringSequence(Funcdata &fdata,Datatype *ct,SymbolEntry *ent,PcodeOp *root,const Address &addr);
|
||||
bool isValid(void) const { return size != 0; } ///< Return \b true if COPYs are found that look like a valid string
|
||||
void clear(void); ///< Clear any resources used and mark the sequence as invalid
|
||||
bool transform(void); ///< Transform COPYs into a single memcpy user-op
|
||||
};
|
||||
|
||||
class RuleStringSequence : public Rule {
|
||||
public:
|
||||
RuleStringSequence(const string &g) : Rule( g, 0, "stringsequence") {} ///< Constructor
|
||||
virtual Rule *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Rule *)0;
|
||||
return new RuleStringSequence(getGroup());
|
||||
}
|
||||
virtual void getOpList(vector<uint4> &oplist) const;
|
||||
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
|
||||
};
|
||||
|
||||
} // End namespace ghidra
|
||||
#endif
|
|
@ -17,6 +17,7 @@
|
|||
#include "condexe.hh"
|
||||
#include "double.hh"
|
||||
#include "subflow.hh"
|
||||
#include "constseq.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
|
@ -2277,28 +2278,17 @@ int4 ActionRestructureVarnode::apply(Funcdata &data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int4 ActionRestructureHigh::apply(Funcdata &data)
|
||||
int4 ActionMappedLocalSync::apply(Funcdata &data)
|
||||
|
||||
{
|
||||
if (!data.isHighOn()) return 0;
|
||||
ScopeLocal *l1 = data.getScopeLocal();
|
||||
|
||||
#ifdef OPACTION_DEBUG
|
||||
if ((flags&rule_debug)!=0)
|
||||
l1->turnOnDebug();
|
||||
#endif
|
||||
|
||||
l1->restructureHigh();
|
||||
if (data.syncVarnodesWithSymbols(l1,true,true))
|
||||
count += 1;
|
||||
|
||||
#ifdef OPACTION_DEBUG
|
||||
if ((flags&rule_debug)==0) return 0;
|
||||
l1->turnOffDebug();
|
||||
ostringstream s;
|
||||
data.getScopeLocal()->printEntries(s);
|
||||
data.getArch()->printDebug(s.str());
|
||||
#endif
|
||||
if (l1->hasOverlapProbems())
|
||||
data.warningHeader("Could not reconcile some variable overlaps");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2645,7 +2635,7 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
|
|||
|
||||
{
|
||||
Datatype *ct;
|
||||
Varnode *vn,*vnout;
|
||||
Varnode *vn,*vnout,*vnin;
|
||||
PcodeOp *newop;
|
||||
|
||||
ct = op->getOpcode()->getInputCast(op,slot,castStrategy); // Input type expected by this operation
|
||||
|
@ -2657,13 +2647,20 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
|
|||
return 0;
|
||||
}
|
||||
|
||||
vn = op->getIn(slot);
|
||||
vnin = vn = op->getIn(slot);
|
||||
// Check to make sure we don't have a double cast
|
||||
if (vn->isWritten() && (vn->getDef()->code() == CPUI_CAST)) {
|
||||
if (vn->isImplied() && (vn->loneDescend() == op)) {
|
||||
vn->updateType(ct,false,false);
|
||||
if (vn->getType()==ct)
|
||||
if (vn->isImplied()) {
|
||||
if (vn->loneDescend() == op) {
|
||||
vn->updateType(ct,false,false);
|
||||
if (vn->getType()==ct)
|
||||
return 1;
|
||||
}
|
||||
vnin = vn->getDef()->getIn(0); // Cast directly from input of previous cast
|
||||
if (ct == vnin->getType()) { // If the earlier data-type is what the input expects
|
||||
data.opSetInput(op, vnin, slot); // Just use the earlier Varnode
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vn->isConstant()) {
|
||||
|
@ -2682,14 +2679,14 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
|
|||
return 1;
|
||||
}
|
||||
newop = data.newOp(1,op->getAddr());
|
||||
vnout = data.newUniqueOut(vn->getSize(),newop);
|
||||
vnout = data.newUniqueOut(vnin->getSize(),newop);
|
||||
vnout->updateType(ct,false,false);
|
||||
vnout->setImplied();
|
||||
#ifdef CPUI_STATISTICS
|
||||
data.getArch()->stats->countCast();
|
||||
#endif
|
||||
data.opSetOpcode(newop,CPUI_CAST);
|
||||
data.opSetInput(newop,vn,0);
|
||||
data.opSetInput(newop,vnin,0);
|
||||
data.opSetInput(op,vnout,slot);
|
||||
data.opInsertBefore(newop,op); // Cast comes AFTER operation
|
||||
if (ct->needsResolution()) {
|
||||
|
@ -2924,6 +2921,7 @@ void ActionNameVars::linkSymbols(Funcdata &data,vector<Varnode *> &namerec)
|
|||
linkSpacebaseSymbol(curvn, data, namerec);
|
||||
}
|
||||
|
||||
TypeFactory *typeFactory = data.getArch()->types;
|
||||
for(int4 i=0;i<manage->numSpaces();++i) { // Build a list of nameable highs
|
||||
spc = manage->getSpace(i);
|
||||
if (spc == (AddrSpace *)0) continue;
|
||||
|
@ -2948,6 +2946,8 @@ void ActionNameVars::linkSymbols(Funcdata &data,vector<Varnode *> &namerec)
|
|||
if (vn->getSize() == sym->getType()->getSize())
|
||||
sym->getScope()->overrideSizeLockType(sym,high->getType());
|
||||
}
|
||||
if (vn->isAddrTied() && !sym->getScope()->isGlobal())
|
||||
high->finalizeDatatype(typeFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4562,10 +4562,7 @@ int4 ActionInputPrototype::apply(Funcdata &data)
|
|||
ParamActive active(false);
|
||||
Varnode *vn;
|
||||
|
||||
// Clear any unlocked local variables because these are
|
||||
// getting cleared anyway in the restructure and may be
|
||||
// using symbol names that we want
|
||||
data.getScopeLocal()->clearUnlockedCategory(-1);
|
||||
data.getScopeLocal()->clearCategory(Symbol::fake_input);
|
||||
data.getFuncProto().clearUnlockedInput();
|
||||
if (!data.getFuncProto().isInputLocked()) {
|
||||
VarnodeDefSet::const_iterator iter,enditer;
|
||||
|
@ -5329,7 +5326,7 @@ void ActionDatabase::buildDefaultGroups(void)
|
|||
"deadcode", "typerecovery", "stackptrflow",
|
||||
"blockrecovery", "stackvars", "deadcontrolflow", "switchnorm",
|
||||
"cleanup", "splitcopy", "splitpointer", "merge", "dynamic", "casts", "analysis",
|
||||
"fixateglobals", "fixateproto",
|
||||
"fixateglobals", "fixateproto", "constsequence",
|
||||
"segment", "returnsplit", "nodejoin", "doubleload", "doubleprecis",
|
||||
"unreachable", "subvar", "floatprecision",
|
||||
"conditionalexe", "" };
|
||||
|
@ -5585,6 +5582,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||
actfullloop->addAction( new ActionActiveReturn("protorecovery") );
|
||||
}
|
||||
act->addAction( actfullloop );
|
||||
act->addAction( new ActionMappedLocalSync("localrecovery") );
|
||||
act->addAction( new ActionStartCleanUp("cleanup") );
|
||||
{
|
||||
actcleanup = new ActionPool(Action::rule_repeatapply,"cleanup");
|
||||
|
@ -5599,6 +5597,7 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||
actcleanup->addRule( new RuleSplitCopy("splitcopy") );
|
||||
actcleanup->addRule( new RuleSplitLoad("splitpointer") );
|
||||
actcleanup->addRule( new RuleSplitStore("splitpointer") );
|
||||
actcleanup->addRule( new RuleStringSequence("constsequence"));
|
||||
}
|
||||
act->addAction( actcleanup );
|
||||
|
||||
|
@ -5620,7 +5619,6 @@ void ActionDatabase::universalAction(Architecture *conf)
|
|||
act->addAction( new ActionCopyMarker("merge") );
|
||||
act->addAction( new ActionOutputPrototype("localrecovery") );
|
||||
act->addAction( new ActionInputPrototype("fixateproto") );
|
||||
act->addAction( new ActionRestructureHigh("localrecovery") );
|
||||
act->addAction( new ActionMapGlobals("fixateglobals") );
|
||||
act->addAction( new ActionDynamicSymbols("dynamic") );
|
||||
act->addAction( new ActionNameVars("merge") );
|
||||
|
|
|
@ -844,15 +844,15 @@ public:
|
|||
virtual int4 apply(Funcdata &data);
|
||||
};
|
||||
|
||||
/// \brief Create symbols that map out the local stack-frame for the function.
|
||||
/// \brief Do final synchronization of symbols in the local scope with Varnodes
|
||||
///
|
||||
/// This produces the final set of symbols on the stack.
|
||||
class ActionRestructureHigh : public Action {
|
||||
/// Push data-types from the last local scope restructuring onto Varnodes
|
||||
class ActionMappedLocalSync : public Action {
|
||||
public:
|
||||
ActionRestructureHigh(const string &g) : Action(0,"restructure_high",g) {} ///< Constructor
|
||||
ActionMappedLocalSync(const string &g) : Action(0,"mapped_local_sync",g) {} ///< Constructor
|
||||
virtual Action *clone(const ActionGroupList &grouplist) const {
|
||||
if (!grouplist.contains(getGroup())) return (Action *)0;
|
||||
return new ActionRestructureHigh(getGroup());
|
||||
return new ActionMappedLocalSync(getGroup());
|
||||
}
|
||||
virtual int4 apply(Funcdata &data);
|
||||
};
|
||||
|
|
|
@ -2837,6 +2837,8 @@ void ScopeInternal::setCategory(Symbol *sym,int4 cat,int4 ind)
|
|||
while(category.size() <= sym->category)
|
||||
category.push_back(vector<Symbol *>());
|
||||
vector<Symbol *> &list(category[sym->category]);
|
||||
if (cat > 0)
|
||||
sym->catindex = list.size();
|
||||
while(list.size() <= sym->catindex)
|
||||
list.push_back((Symbol *)0);
|
||||
list[sym->catindex] = sym;
|
||||
|
|
|
@ -213,7 +213,8 @@ public:
|
|||
no_category = -1, ///< Symbol is not in a special category
|
||||
function_parameter = 0, ///< The Symbol is a parameter to a function
|
||||
equate = 1, ///< The Symbol holds \e equate information about a constant
|
||||
union_facet = 2 ///< Symbol holding read or write facing union field information
|
||||
union_facet = 2, ///< Symbol holding read or write facing union field information
|
||||
fake_input = 3 ///< Temporary placeholder for an input symbol prior to formalizing parameters
|
||||
};
|
||||
|
||||
Symbol(Scope *sc,const string &nm,Datatype *ct); ///< Construct given a name and data-type
|
||||
|
@ -735,7 +736,7 @@ public:
|
|||
///
|
||||
/// \param sym is the given Symbol
|
||||
/// \param cat is the \e category to set for the Symbol
|
||||
/// \param ind is the index position to set (within the category)
|
||||
/// \param ind is, for the function_parameter category, the index position to set, and is unused for other categories
|
||||
virtual void setCategory(Symbol *sym,int4 cat,int4 ind)=0;
|
||||
|
||||
virtual SymbolEntry *addSymbol(const string &nm,Datatype *ct,
|
||||
|
|
|
@ -330,8 +330,7 @@ PcodeOp *FlowInfo::xrefControlFlow(list<PcodeOp *>::const_iterator oiter,bool &s
|
|||
break;
|
||||
case CPUI_CALLOTHER:
|
||||
{
|
||||
InjectedUserOp *userop = dynamic_cast<InjectedUserOp *>(glb->userops.getOp(op->getIn(0)->getOffset()));
|
||||
if (userop != (InjectedUserOp *)0)
|
||||
if (glb->userops.getOp(op->getIn(0)->getOffset())->getType() == UserPcodeOp::injected)
|
||||
injectlist.push_back(op);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -297,6 +297,47 @@ Varnode *Funcdata::findSpacebaseInput(AddrSpace *id) const
|
|||
return vn;
|
||||
}
|
||||
|
||||
/// \brief If it doesn't exist, create an input Varnode of the base register corresponding to the given address space
|
||||
///
|
||||
/// The address space must have a base register associated with it or an exception is thrown.
|
||||
/// If a Varnode representing the incoming base register already exists, it is returned. Otherwise
|
||||
/// a new Varnode is created and returned. In either case, the Varnode will have the TypeSpacebase data-type set.
|
||||
/// \param id is the given address space
|
||||
/// \return the input Varnode corresponding to the base register
|
||||
Varnode *Funcdata::constructSpacebaseInput(AddrSpace *id)
|
||||
|
||||
{
|
||||
Varnode *spacePtr = findSpacebaseInput(id);
|
||||
if (spacePtr != (Varnode *)0)
|
||||
return spacePtr;
|
||||
if (id->numSpacebase() == 0)
|
||||
throw LowlevelError("Unable to construct pointer into space: "+id->getName());
|
||||
const VarnodeData &point(id->getSpacebase(0));
|
||||
Datatype *ct = glb->types->getTypeSpacebase(id,getAddress());
|
||||
Datatype *ptr = glb->types->getTypePointer(point.size,ct,id->getWordSize());
|
||||
spacePtr = newVarnode(point.size, point.getAddr(), ptr);
|
||||
spacePtr = setInputVarnode(spacePtr);
|
||||
spacePtr->setFlags(Varnode::spacebase);
|
||||
spacePtr->updateType(ptr, true, true);
|
||||
return spacePtr;
|
||||
}
|
||||
|
||||
/// \brief Create a constant representing the \e base of the given global address space
|
||||
///
|
||||
/// The constant will have the TypeSpacebase data-type set.
|
||||
/// \param id is the given address space
|
||||
/// \return the constant base Varnode
|
||||
Varnode *Funcdata::constructConstSpacebase(AddrSpace *id)
|
||||
|
||||
{
|
||||
Datatype *ct = glb->types->getTypeSpacebase(id,Address());
|
||||
Datatype *ptr = glb->types->getTypePointer(id->getAddrSize(),ct,id->getWordSize());
|
||||
Varnode *spacePtr = newConstant(id->getAddrSize(),0);
|
||||
spacePtr->updateType(ptr,true,true);
|
||||
spacePtr->setFlags(Varnode::spacebase);
|
||||
return spacePtr;
|
||||
}
|
||||
|
||||
/// \brief Convert a constant pointer into a \e ram CPUI_PTRSUB
|
||||
///
|
||||
/// A constant known to be a pointer into an address space like \b ram is converted
|
||||
|
|
|
@ -224,6 +224,8 @@ public:
|
|||
void spacebase(void); ///< Mark registers that map to a virtual address space
|
||||
Varnode *newSpacebasePtr(AddrSpace *id); ///< Construct a new \e spacebase register for a given address space
|
||||
Varnode *findSpacebaseInput(AddrSpace *id) const;
|
||||
Varnode *constructSpacebaseInput(AddrSpace *id);
|
||||
Varnode *constructConstSpacebase(AddrSpace *id);
|
||||
void spacebaseConstant(PcodeOp *op,int4 slot,SymbolEntry *entry,const Address &rampoint,uintb origval,int4 origsize);
|
||||
|
||||
int4 getHeritagePass(void) const { return heritage.getPass(); } ///< Get overall count of heritage passes
|
||||
|
@ -428,6 +430,7 @@ public:
|
|||
bool attemptDynamicMapping(SymbolEntry *entry,DynamicHash &dhash);
|
||||
bool attemptDynamicMappingLate(SymbolEntry *entry,DynamicHash &dhash);
|
||||
Merge &getMerge(void) { return covermerge; } ///< Get the Merge object for \b this function
|
||||
Varnode *getInternalString(const uint1 *buf,int4 size,Datatype *ptrType,PcodeOp *readOp);
|
||||
|
||||
// op routines
|
||||
PcodeOp *newOp(int4 inputs,const Address &pc); /// Allocate a new PcodeOp with Address
|
||||
|
|
|
@ -573,12 +573,12 @@ JumpTable::RecoveryMode Funcdata::earlyJumpTableFail(PcodeOp *op)
|
|||
OpCode opc = op->code();
|
||||
if (opc == CPUI_CALLOTHER) {
|
||||
int4 id = (int4)op->getIn(0)->getOffset();
|
||||
UserPcodeOp *userOp = glb->userops.getOp(id);
|
||||
if (dynamic_cast<InjectedUserOp *>(userOp) != (InjectedUserOp *)0)
|
||||
uint4 userOpType = glb->userops.getOp(id)->getType();
|
||||
if (userOpType == UserPcodeOp::injected)
|
||||
return JumpTable::success; // Don't try to back track through injection
|
||||
if (dynamic_cast<JumpAssistOp *>(userOp) != (JumpAssistOp *)0)
|
||||
if (userOpType == UserPcodeOp::jumpassist)
|
||||
return JumpTable::success;
|
||||
if (dynamic_cast<SegmentOp *>(userOp) != (SegmentOp *)0)
|
||||
if (userOpType == UserPcodeOp::segment)
|
||||
return JumpTable::success;
|
||||
if (outhit)
|
||||
return JumpTable::fail_callother; // Address formed via uninjected CALLOTHER, analysis will fail
|
||||
|
|
|
@ -632,7 +632,7 @@ bool Funcdata::replaceVolatile(Varnode *vn)
|
|||
{
|
||||
PcodeOp *newop;
|
||||
if (vn->isWritten()) { // A written value
|
||||
VolatileWriteOp *vw_op = glb->userops.getVolatileWrite();
|
||||
UserPcodeOp *vw_op = glb->userops.registerBuiltin(UserPcodeOp::BUILTIN_VOLATILE_WRITE);
|
||||
if (!vn->hasNoDescend()) throw LowlevelError("Volatile memory was propagated");
|
||||
PcodeOp *defop = vn->getDef();
|
||||
newop = newOp(3,defop->getAddr());
|
||||
|
@ -651,7 +651,7 @@ bool Funcdata::replaceVolatile(Varnode *vn)
|
|||
opInsertAfter(newop,defop); // Insert after defining op
|
||||
}
|
||||
else { // A read value
|
||||
VolatileReadOp *vr_op = glb->userops.getVolatileRead();
|
||||
UserPcodeOp *vr_op = glb->userops.registerBuiltin(UserPcodeOp::BUILTIN_VOLATILE_READ);
|
||||
if (vn->hasNoDescend()) return false; // Dead
|
||||
PcodeOp *readop = vn->loneDescend();
|
||||
if (readop == (PcodeOp *)0)
|
||||
|
@ -1002,7 +1002,6 @@ bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4
|
|||
if (ct != (Datatype *)0) {
|
||||
if (vn->updateType(ct,false,false))
|
||||
updateoccurred = true;
|
||||
vn->getHigh()->finalizeDatatype(ct); // Permanently set the data-type on the HighVariable
|
||||
}
|
||||
} while(iter != enditer);
|
||||
return updateoccurred;
|
||||
|
@ -1312,6 +1311,41 @@ bool Funcdata::attemptDynamicMappingLate(SymbolEntry *entry,DynamicHash &dhash)
|
|||
return true;
|
||||
}
|
||||
|
||||
/// \brief Create Varnode (and associated PcodeOp) that will display as a string constant
|
||||
///
|
||||
/// The raw data for the encoded string is given. If the data encodes a legal string, the string
|
||||
/// is stored in the StringManager, and a Varnode is created that will display in output as the
|
||||
/// quoted string. A given pointer data-type is assigned to the new Varnode and also indicates
|
||||
/// the character data-type associated with the encoding. Internally, a \e stringdata user-op is
|
||||
/// also created and its output is the Varnode actually returned.
|
||||
/// \param buf is the raw bytes of the encoded string
|
||||
/// \param size is the number of bytes
|
||||
/// \param ptrType is the given pointer to character data-type
|
||||
/// \param readOp is the PcodeOp that will read the new Varnode
|
||||
/// \return the new Varnode or null is the encoding isn't a legal string
|
||||
Varnode *Funcdata::getInternalString(const uint1 *buf,int4 size,Datatype *ptrType,PcodeOp *readOp)
|
||||
|
||||
{
|
||||
if (ptrType->getMetatype() != TYPE_PTR)
|
||||
return (Varnode *)0;
|
||||
Datatype *charType = ((TypePointer *)ptrType)->getPtrTo();
|
||||
|
||||
const Address &addr(readOp->getAddr());
|
||||
uint8 hash = glb->stringManager->registerInternalStringData(addr, buf, size, charType);
|
||||
if (hash == 0)
|
||||
return (Varnode *)0;
|
||||
glb->userops.registerBuiltin(UserPcodeOp::BUILTIN_STRINGDATA);
|
||||
PcodeOp *stringOp = newOp(2,addr);
|
||||
opSetOpcode(stringOp, CPUI_CALLOTHER);
|
||||
stringOp->clearFlag(PcodeOp::call);
|
||||
opSetInput(stringOp, newConstant(4, UserPcodeOp::BUILTIN_STRINGDATA), 0);
|
||||
opSetInput(stringOp, newConstant(8, hash), 1);
|
||||
Varnode *resVn = newUniqueOut(ptrType->getSize(), stringOp);
|
||||
resVn->updateType(ptrType, true, false);
|
||||
opInsertBefore(stringOp, readOp);
|
||||
return resVn;
|
||||
};
|
||||
|
||||
/// Follow the Varnode back to see if it comes from the return address for \b this function.
|
||||
/// If so, return \b true. The return address can flow through COPY, INDIRECT, and AND operations.
|
||||
/// If there are any other operations in the flow path, or if a standard storage location for the
|
||||
|
|
|
@ -2082,8 +2082,10 @@ bool JumpAssisted::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint
|
|||
if (assistOp->code() != CPUI_CALLOTHER) return false;
|
||||
if (assistOp->numInput() < 3) return false;
|
||||
int4 index = assistOp->getIn(0)->getOffset();
|
||||
userop = dynamic_cast<JumpAssistOp *>(fd->getArch()->userops.getOp(index));
|
||||
if (userop == (JumpAssistOp *)0) return false;
|
||||
UserPcodeOp *tmpOp = fd->getArch()->userops.getOp(index);
|
||||
if (tmpOp->getType() != UserPcodeOp::jumpassist)
|
||||
return false;
|
||||
userop = (JumpAssistOp *)tmpOp;
|
||||
|
||||
switchvn = assistOp->getIn(1); // The switch variable
|
||||
for(int4 i=2;i<assistOp->numInput();++i)
|
||||
|
|
|
@ -1384,7 +1384,7 @@ void Merge::groupPartialRoot(Varnode *vn)
|
|||
baseOffset = entry->getOffset();
|
||||
}
|
||||
|
||||
PieceNode::gatherPieces(pieces, vn, vn->getDef(), baseOffset);
|
||||
PieceNode::gatherPieces(pieces, vn, vn->getDef(), baseOffset, baseOffset);
|
||||
bool throwOut = false;
|
||||
for(int4 i=0;i<pieces.size();++i) {
|
||||
Varnode *nodeVn = pieces[i].getVarnode();
|
||||
|
@ -1496,6 +1496,14 @@ void Merge::markInternalCopies(void)
|
|||
if (p2->getOffset() != p1->getOffset() + v3->getSize()) break;
|
||||
}
|
||||
data.opMarkNonPrinting(op);
|
||||
if (v2->isImplied()) {
|
||||
v2->clearImplied();
|
||||
v2->setExplicit();
|
||||
}
|
||||
if (v3->isImplied()) {
|
||||
v3->clearImplied();
|
||||
v3->setExplicit();
|
||||
}
|
||||
break;
|
||||
case CPUI_SUBPIECE:
|
||||
v1 = op->getOut();
|
||||
|
@ -1513,6 +1521,10 @@ void Merge::markInternalCopies(void)
|
|||
if (p2->getOffset() + val != p1->getOffset()) break;
|
||||
}
|
||||
data.opMarkNonPrinting(op);
|
||||
if (v2->isImplied()) {
|
||||
v2->clearImplied();
|
||||
v2->setExplicit();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -756,9 +756,9 @@ int4 PcodeOp::compareOrder(const PcodeOp *bop) const
|
|||
/// whether a Varnode is a leaf of this tree.
|
||||
/// \param rootVn is the given root of the CONCAT tree
|
||||
/// \param vn is the Varnode to test as a leaf
|
||||
/// \param typeOffset is byte offset of the test Varnode within fully concatenated value
|
||||
/// \param relOffset is byte offset of the test Varnode within fully concatenated value (rooted at \b rootVn)
|
||||
/// \return \b true is the test Varnode is a leaf of the tree
|
||||
bool PieceNode::isLeaf(Varnode *rootVn,Varnode *vn,int4 typeOffset)
|
||||
bool PieceNode::isLeaf(Varnode *rootVn,Varnode *vn,int4 relOffset)
|
||||
|
||||
{
|
||||
if (vn->isMapped() && rootVn->getSymbolEntry() != vn->getSymbolEntry()) {
|
||||
|
@ -770,7 +770,7 @@ bool PieceNode::isLeaf(Varnode *rootVn,Varnode *vn,int4 typeOffset)
|
|||
PcodeOp *op = vn->loneDescend();
|
||||
if (op == (PcodeOp *)0) return true;
|
||||
if (vn->isAddrTied()) {
|
||||
Address addr = rootVn->getAddr() + typeOffset;
|
||||
Address addr = rootVn->getAddr() + relOffset;
|
||||
if (vn->getAddr() != addr) return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -820,17 +820,18 @@ Varnode *PieceNode::findRoot(Varnode *vn)
|
|||
/// \param stack holds the markup for each node of the tree
|
||||
/// \param rootVn is the given root of the tree
|
||||
/// \param op is the current PIECE op to explore as part of the tree
|
||||
/// \param baseOffset is the offset associated with the output of the current PIECE op
|
||||
void PieceNode::gatherPieces(vector<PieceNode> &stack,Varnode *rootVn,PcodeOp *op,int4 baseOffset)
|
||||
/// \param baseOffset is the offset associated with the output of the current PIECE op wihtin the data-type
|
||||
/// \param rootOffset is the offset of the \b rootVn within the data-type
|
||||
void PieceNode::gatherPieces(vector<PieceNode> &stack,Varnode *rootVn,PcodeOp *op,int4 baseOffset,int4 rootOffset)
|
||||
|
||||
{
|
||||
for(int4 i=0;i<2;++i) {
|
||||
Varnode *vn = op->getIn(i);
|
||||
int4 offset = (rootVn->getSpace()->isBigEndian() == (i==1)) ? baseOffset + op->getIn(1-i)->getSize() : baseOffset;
|
||||
bool res = isLeaf(rootVn,vn,offset);
|
||||
bool res = isLeaf(rootVn,vn,offset-rootOffset);
|
||||
stack.emplace_back(op,i,offset,res);
|
||||
if (!res)
|
||||
gatherPieces(stack,rootVn,vn->getDef(),offset);
|
||||
gatherPieces(stack,rootVn,vn->getDef(),offset,rootOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ public:
|
|||
Varnode *getVarnode(void) const { return pieceOp->getIn(slot); } ///< Get the Varnode representing \b this piece
|
||||
static bool isLeaf(Varnode *rootVn,Varnode *vn,int4 typeOffset);
|
||||
static Varnode *findRoot(Varnode *vn);
|
||||
static void gatherPieces(vector<PieceNode> &stack,Varnode *rootVn,PcodeOp *op,int4 baseOffset);
|
||||
static void gatherPieces(vector<PieceNode> &stack,Varnode *rootVn,PcodeOp *op,int4 baseOffset,int4 rootOffset);
|
||||
};
|
||||
|
||||
/// A map from sequence number (SeqNum) to PcodeOp
|
||||
|
|
|
@ -675,15 +675,7 @@ void PrintC::opCallother(const PcodeOp *op)
|
|||
{
|
||||
UserPcodeOp *userop = glb->userops.getOp(op->getIn(0)->getOffset());
|
||||
uint4 display = userop->getDisplay();
|
||||
if (display == UserPcodeOp::annotation_assignment) {
|
||||
pushOp(&assignment,op);
|
||||
pushVn(op->getIn(2),op,mods);
|
||||
pushVn(op->getIn(1),op,mods);
|
||||
}
|
||||
else if (display == UserPcodeOp::no_operator) {
|
||||
pushVn(op->getIn(1),op,mods);
|
||||
}
|
||||
else { // Emit using functional syntax
|
||||
if (display == 0) { // Emit using functional syntax
|
||||
string nm = op->getOpcode()->getOperatorName(op);
|
||||
pushOp(&function_call,op);
|
||||
pushAtom(Atom(nm,optoken,EmitMarkup::funcname_color,op));
|
||||
|
@ -698,6 +690,28 @@ void PrintC::opCallother(const PcodeOp *op)
|
|||
else
|
||||
pushAtom(Atom(EMPTY_STRING,blanktoken,EmitMarkup::no_color)); // Push empty token for void
|
||||
}
|
||||
else if (display == UserPcodeOp::annotation_assignment) {
|
||||
pushOp(&assignment,op);
|
||||
pushVn(op->getIn(2),op,mods);
|
||||
pushVn(op->getIn(1),op,mods);
|
||||
}
|
||||
else if (display == UserPcodeOp::no_operator) {
|
||||
pushVn(op->getIn(1),op,mods);
|
||||
}
|
||||
else if (display == UserPcodeOp::display_string) {
|
||||
const Varnode *vn = op->getOut();
|
||||
Datatype *ct = vn->getType();
|
||||
ostringstream str;
|
||||
if (ct->getMetatype() == TYPE_PTR) {
|
||||
ct = ((TypePointer *)ct)->getPtrTo();
|
||||
if (!printCharacterConstant(str,op->getIn(1)->getAddr(),ct))
|
||||
str << "\"badstring\"";
|
||||
}
|
||||
else
|
||||
str << "\"badstring\"";
|
||||
|
||||
pushAtom(Atom(str.str(),vartoken,EmitMarkup::const_color,op,vn));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintC::opConstructor(const PcodeOp *op,bool withNew)
|
||||
|
|
|
@ -7060,7 +7060,7 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data)
|
|||
|
||||
vector<PieceNode> stack;
|
||||
for(;;) {
|
||||
PieceNode::gatherPieces(stack, outvn, op, baseOffset);
|
||||
PieceNode::gatherPieces(stack, outvn, op, baseOffset, baseOffset);
|
||||
if (!findReplaceZext(stack, ct, data)) // Check for INT_ZEXT leaves that need to be converted to PIECEs
|
||||
break;
|
||||
stack.clear(); // If we found some, regenerate the tree
|
||||
|
|
|
@ -272,7 +272,7 @@ AddrSpace *SleighBase::decodeSlaSpace(Decoder &decoder,const Translate *trans)
|
|||
else if (attribId == sla::ATTRIB_SIZE)
|
||||
addressSize = decoder.readSignedInteger();
|
||||
else if (attribId == sla::ATTRIB_WORDSIZE)
|
||||
wordsize = decoder.readUnsignedInteger();
|
||||
wordsize = decoder.readSignedInteger();
|
||||
else if (attribId == sla::ATTRIB_BIGENDIAN) {
|
||||
bigEnd = decoder.readBool();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
#include "stringmanage.hh"
|
||||
#include "architecture.hh"
|
||||
#include "crc32.hh"
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
|
@ -24,6 +25,85 @@ ElementId ELEM_BYTES = ElementId("bytes",83);
|
|||
ElementId ELEM_STRING = ElementId("string",84);
|
||||
ElementId ELEM_STRINGMANAGE = ElementId("stringmanage",85);
|
||||
|
||||
/// Assume the buffer contains a null terminated unicode encoded string.
|
||||
/// Write the characters out (as UTF8) to the stream.
|
||||
/// \param s is the output stream
|
||||
/// \param buffer is the given byte buffer
|
||||
/// \param size is the number of bytes in the buffer
|
||||
/// \param charsize specifies the encoding (1=UTF8 2=UTF16 4=UTF32)
|
||||
/// \param bigend is \b true if (UTF16 and UTF32) are big endian encoded
|
||||
/// \return \b true if the byte array contains valid unicode
|
||||
bool StringManager::writeUnicode(ostream &s,const uint1 *buffer,int4 size,int4 charsize,bool bigend)
|
||||
|
||||
{
|
||||
int4 i=0;
|
||||
int4 count=0;
|
||||
int4 skip = charsize;
|
||||
while(i<size) {
|
||||
int4 codepoint = getCodepoint(buffer+i,charsize,bigend,skip);
|
||||
if (codepoint < 0) return false;
|
||||
if (codepoint == 0) break; // Terminator
|
||||
writeUtf8(s, codepoint);
|
||||
i += skip;
|
||||
count += 1;
|
||||
if (count >= maximumChars)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Translate and assign raw string data to a StringData object
|
||||
///
|
||||
/// The string data is provided as raw bytes. The data is translated to UTF-8 and truncated
|
||||
/// to the \b maximumChars allowed by the manager. The encoding must be legal unicode as performed
|
||||
/// by checkCharacters().
|
||||
/// \param data is the StringData object to populate
|
||||
/// \param buf is the raw byte array
|
||||
/// \param size is the number of bytes in the array
|
||||
/// \param charsize is the size of unicode encoding
|
||||
/// \param numChars is the number of characters in the encoding as returned by checkCharacters()
|
||||
/// \param bigend is \b true if UTF-16 and UTF-32 elements are big endian encoded
|
||||
void StringManager::assignStringData(StringData &data,const uint1 *buf,int4 size,int4 charsize,int4 numChars,bool bigend)
|
||||
|
||||
{
|
||||
if (charsize == 1 && numChars < maximumChars) {
|
||||
data.byteData.reserve(size);
|
||||
data.byteData.assign(buf,buf+size);
|
||||
}
|
||||
else {
|
||||
// We need to translate to UTF8 and/or truncate
|
||||
ostringstream s;
|
||||
if (!writeUnicode(s, buf, size, charsize, bigend))
|
||||
return;
|
||||
string resString = s.str();
|
||||
int4 newSize = resString.size();
|
||||
data.byteData.reserve(newSize + 1);
|
||||
const uint1 *ptr = (const uint1 *)resString.c_str();
|
||||
data.byteData.assign(ptr,ptr+newSize);
|
||||
data.byteData[newSize] = 0; // Make sure there is a null terminator
|
||||
}
|
||||
data.isTruncated = (numChars >= maximumChars);
|
||||
}
|
||||
|
||||
/// \brief Calculate hash of a specific Address and contents of a byte array
|
||||
///
|
||||
/// Calculate a 32-bit CRC of the bytes and XOR into the upper part of the Address offset.
|
||||
/// \param addr is the specific Address
|
||||
/// \param buf is a pointer to the array of bytes
|
||||
/// \param size is the number of bytes in the array
|
||||
/// \return the 64-bit hash
|
||||
uint8 StringManager::calcInternalHash(const Address &addr,const uint1 *buf,int4 size)
|
||||
|
||||
{
|
||||
uint4 reg = 0x7b7c66a9;
|
||||
for(int4 i=0;i<size;++i) {
|
||||
reg = crc_update(reg, buf[i]);
|
||||
}
|
||||
uint8 res = addr.getOffset();
|
||||
res ^= ((uint8)reg) << 32;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// \param max is the maximum number of characters to allow before truncating string
|
||||
StringManager::StringManager(int4 max)
|
||||
|
||||
|
@ -91,6 +171,33 @@ bool StringManager::isString(const Address &addr,Datatype *charType)
|
|||
return !buffer.empty();
|
||||
}
|
||||
|
||||
/// \brief Associate string data at a code address or other location that doesn't hold string data normally
|
||||
///
|
||||
/// The given byte buffer is decoded, and if it represents a legal string, a non-zero hash is returned,
|
||||
/// constructed from an Address associated with the string and the string data itself. The registered string
|
||||
/// can be retrieved via the getStringData() method using this hash as a constant Address. If the string is not
|
||||
/// legal, 0 is returned.
|
||||
/// \param addr is the address to associate with the string data
|
||||
/// \param buf is a pointer to the array of raw bytes encoding the string
|
||||
/// \param size is the number of bytes in the array
|
||||
/// \param charType is a character data-type indicating the encoding
|
||||
/// \return a hash associated with the string or 0
|
||||
uint8 StringManager::registerInternalStringData(const Address &addr,const uint1 *buf,int4 size,Datatype *charType)
|
||||
|
||||
{
|
||||
int4 charsize = charType->getSize();
|
||||
int4 numChars = checkCharacters(buf, size, charsize, addr.isBigEndian());
|
||||
if (numChars < 0)
|
||||
return 0; // Not a legal encoding
|
||||
uint8 hash = calcInternalHash(addr, buf, size);
|
||||
Address constAddr = addr.getSpace()->getManager()->getConstant(hash);
|
||||
StringData &stringData( stringMap[constAddr] );
|
||||
stringData.byteData.clear();
|
||||
stringData.isTruncated = false;
|
||||
assignStringData(stringData, buf, size, charsize, numChars, addr.isBigEndian());
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// Encode \<stringmanage> element, with \<string> children.
|
||||
/// \param encoder is the stream encoder
|
||||
void StringManager::encode(Encoder &encoder) const
|
||||
|
@ -204,6 +311,33 @@ inline int4 StringManager::readUtf16(const uint1 *buf,bool bigend)
|
|||
return codepoint;
|
||||
}
|
||||
|
||||
/// \brief Make sure buffer has valid bounded set of unicode
|
||||
///
|
||||
/// Check that the given buffer contains valid unicode.
|
||||
/// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check
|
||||
/// per character. For UTF16, the surrogate reserved area gives at least some check.
|
||||
/// \param buf is the byte array to check
|
||||
/// \param size is the size of the buffer in bytes
|
||||
/// \param charsize is the UTF encoding (1=UTF8, 2=UTF16, 4=UTF32)
|
||||
/// \param bigend is \b true if the (UTF16 and UTF32) characters are big endian encoded
|
||||
/// \return the number of characters or -1 if there is an invalid encoding
|
||||
int4 StringManager::checkCharacters(const uint1 *buf,int4 size,int4 charsize,bool bigend)
|
||||
|
||||
{
|
||||
if (buf == (const uint1 *)0) return -1;
|
||||
int4 i=0;
|
||||
int4 count=0;
|
||||
int4 skip = charsize;
|
||||
while(i<size) {
|
||||
int4 codepoint = getCodepoint(buf+i,charsize,bigend,skip);
|
||||
if (codepoint < 0) return -1;
|
||||
if (codepoint == 0) break;
|
||||
count += 1;
|
||||
i += skip;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// One or more bytes is consumed from the array, and the number of bytes used is passed back.
|
||||
/// \param buf is a pointer to the bytes in the character array
|
||||
/// \param charsize is 1 for UTF8, 2 for UTF16, or 4 for UTF32
|
||||
|
@ -328,80 +462,12 @@ const vector<uint1> &StringManagerUnicode::getStringData(const Address &addr,Dat
|
|||
return stringData.byteData; // Return the empty buffer
|
||||
}
|
||||
|
||||
int4 numChars = checkCharacters(testBuffer, curBufferSize, charsize);
|
||||
int4 numChars = checkCharacters(testBuffer, curBufferSize, charsize, addr.isBigEndian());
|
||||
if (numChars < 0)
|
||||
return stringData.byteData; // Return the empty buffer (invalid encoding)
|
||||
if (charsize == 1 && numChars < maximumChars) {
|
||||
stringData.byteData.reserve(curBufferSize);
|
||||
stringData.byteData.assign(testBuffer,testBuffer+curBufferSize);
|
||||
}
|
||||
else {
|
||||
// We need to translate to UTF8 and/or truncate
|
||||
ostringstream s;
|
||||
if (!writeUnicode(s, testBuffer, curBufferSize, charsize))
|
||||
return stringData.byteData; // Return the empty buffer
|
||||
string resString = s.str();
|
||||
int4 newSize = resString.size();
|
||||
stringData.byteData.reserve(newSize + 1);
|
||||
const uint1 *ptr = (const uint1 *)resString.c_str();
|
||||
stringData.byteData.assign(ptr,ptr+newSize);
|
||||
stringData.byteData[newSize] = 0; // Make sure there is a null terminator
|
||||
}
|
||||
stringData.isTruncated = (numChars >= maximumChars);
|
||||
assignStringData(stringData, testBuffer, curBufferSize, charsize, numChars, addr.isBigEndian());
|
||||
isTrunc = stringData.isTruncated;
|
||||
return stringData.byteData;
|
||||
}
|
||||
|
||||
/// Check that the given buffer contains valid unicode.
|
||||
/// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check
|
||||
/// per character. For UTF16, the surrogate reserved area gives at least some check.
|
||||
/// \param buf is the byte array to check
|
||||
/// \param size is the size of the buffer in bytes
|
||||
/// \param charsize is the UTF encoding (1=UTF8, 2=UTF16, 4=UTF32)
|
||||
/// \return the number of characters or -1 if there is an invalid encoding
|
||||
int4 StringManagerUnicode::checkCharacters(const uint1 *buf,int4 size,int4 charsize) const
|
||||
|
||||
{
|
||||
if (buf == (const uint1 *)0) return -1;
|
||||
bool bigend = glb->translate->isBigEndian();
|
||||
int4 i=0;
|
||||
int4 count=0;
|
||||
int4 skip = charsize;
|
||||
while(i<size) {
|
||||
int4 codepoint = getCodepoint(buf+i,charsize,bigend,skip);
|
||||
if (codepoint < 0) return -1;
|
||||
if (codepoint == 0) break;
|
||||
count += 1;
|
||||
i += skip;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Assume the buffer contains a null terminated unicode encoded string.
|
||||
/// Write the characters out (as UTF8) to the stream.
|
||||
/// \param s is the output stream
|
||||
/// \param buffer is the given byte buffer
|
||||
/// \param size is the number of bytes in the buffer
|
||||
/// \param charsize specifies the encoding (1=UTF8 2=UTF16 4=UTF32)
|
||||
/// \return \b true if the byte array contains valid unicode
|
||||
bool StringManagerUnicode::writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize)
|
||||
|
||||
{
|
||||
bool bigend = glb->translate->isBigEndian();
|
||||
int4 i=0;
|
||||
int4 count=0;
|
||||
int4 skip = charsize;
|
||||
while(i<size) {
|
||||
int4 codepoint = getCodepoint(buffer+i,charsize,bigend,skip);
|
||||
if (codepoint < 0) return false;
|
||||
if (codepoint == 0) break; // Terminator
|
||||
writeUtf8(s, codepoint);
|
||||
i += skip;
|
||||
count += 1;
|
||||
if (count >= maximumChars)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -33,9 +33,10 @@ extern ElementId ELEM_STRINGMANAGE; ///< Marshaling element \<stringmanage>
|
|||
|
||||
/// \brief Storage for decoding and storing strings associated with an address
|
||||
///
|
||||
/// Looks at data in the loadimage to determine if it represents a "string".
|
||||
/// Decodes the string for presentation in the output.
|
||||
/// Stores the decoded string until its needed for presentation.
|
||||
/// Looks at data in the loadimage to determine if it represents a "string". Decodes the string for
|
||||
/// presentation in the output. Stores the decoded string until its needed for presentation. Strings are
|
||||
/// associated with their starting address in memory. An \e internal string (that is not in the loadimage) can
|
||||
/// be registered with the manager and will be associated with a constant.
|
||||
class StringManager {
|
||||
protected:
|
||||
/// \brief String data (a sequence of bytes) stored by StringManager
|
||||
|
@ -46,6 +47,9 @@ protected:
|
|||
};
|
||||
map<Address,StringData> stringMap; ///< Map from address to string data
|
||||
int4 maximumChars; ///< Maximum characters in a string before truncating
|
||||
bool writeUnicode(ostream &s,const uint1 *buffer,int4 size,int4 charsize,bool bigend); ///< Translate/copy unicode to UTF8
|
||||
void assignStringData(StringData &data,const uint1 *buf,int4 size,int4 charsize,int4 numChars,bool bigend);
|
||||
static uint8 calcInternalHash(const Address &addr,const uint1 *buf,int4 size);
|
||||
public:
|
||||
StringManager(int4 max); ///< Constructor
|
||||
virtual ~StringManager(void); ///< Destructor
|
||||
|
@ -64,12 +68,14 @@ public:
|
|||
/// \return the byte array of UTF8 data
|
||||
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType,bool &isTrunc)=0;
|
||||
|
||||
uint8 registerInternalStringData(const Address &addr,const uint1 *buf,int4 size,Datatype *charType);
|
||||
void encode(Encoder &encoder) const; ///< Encode cached strings to a stream
|
||||
void decode(Decoder &decoder); ///< Restore string cache from a stream
|
||||
|
||||
static bool hasCharTerminator(const uint1 *buffer,int4 size,int4 charsize); ///< Check for a unicode string terminator
|
||||
static int4 readUtf16(const uint1 *buf,bool bigend); ///< Read a UTF16 code point from a byte array
|
||||
static void writeUtf8(ostream &s,int4 codepoint); ///< Write unicode character to stream in UTF8 encoding
|
||||
static int4 checkCharacters(const uint1 *buf,int4 size,int4 charsize,bool bigend);
|
||||
static int4 getCodepoint(const uint1 *buf,int4 charsize,bool bigend,int4 &skip); ///< Extract next \e unicode \e codepoint
|
||||
};
|
||||
|
||||
|
@ -80,13 +86,11 @@ public:
|
|||
class StringManagerUnicode : public StringManager {
|
||||
Architecture *glb; ///< Underlying architecture
|
||||
uint1 *testBuffer; ///< Temporary buffer for pulling in loadimage bytes
|
||||
int4 checkCharacters(const uint1 *buf,int4 size,int4 charsize) const; ///< Make sure buffer has valid bounded set of unicode
|
||||
public:
|
||||
StringManagerUnicode(Architecture *g,int4 max); ///< Constructor
|
||||
virtual ~StringManagerUnicode(void);
|
||||
|
||||
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType,bool &isTrunc);
|
||||
bool writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize); ///< Translate/copy unicode to UTF8
|
||||
};
|
||||
|
||||
} // End namespace ghidra
|
||||
|
|
|
@ -2846,7 +2846,11 @@ TransformVar *LaneDivide::setReplacement(Varnode *vn,int4 numLanes,int4 skipLane
|
|||
// if (vn->isFree())
|
||||
// return (TransformVar *)0;
|
||||
|
||||
if (vn->isTypeLock() && vn->getType()->getMetatype() != TYPE_PARTIALSTRUCT) {
|
||||
if (vn->isTypeLock()) {
|
||||
type_metatype meta = vn->getType()->getMetatype();
|
||||
if (meta > TYPE_ARRAY)
|
||||
return (TransformVar *)0; // Don't split a primitive type
|
||||
if (meta == TYPE_STRUCT || meta == TYPE_UNION)
|
||||
return (TransformVar *)0;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,10 +30,10 @@ 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_ENUMSIGNED = AttributeId("enumsigned",52);
|
||||
AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53);
|
||||
AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54);
|
||||
AttributeId ATTRIB_LONGSIZE = AttributeId("longsize",55);
|
||||
//AttributeId ATTRIB_ENUMSIGNED = AttributeId("enumsigned",52); // deprecated
|
||||
//AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53); // deprecated
|
||||
//AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54); // deprecated
|
||||
//AttributeId ATTRIB_LONGSIZE = AttributeId("longsize",55); // deprecated
|
||||
AttributeId ATTRIB_OPAQUESTRING = AttributeId("opaquestring",56);
|
||||
AttributeId ATTRIB_SIGNED = AttributeId("signed",57);
|
||||
AttributeId ATTRIB_STRUCTALIGN = AttributeId("structalign",58);
|
||||
|
@ -42,7 +42,7 @@ AttributeId ATTRIB_VARLENGTH = AttributeId("varlength",60);
|
|||
|
||||
//ElementId ELEM_ABSOLUTE_MAX_ALIGNMENT = ElementId("absolute_max_alignment", 37);
|
||||
//ElementId ELEM_BITFIELD_PACKING = ElementId("bitfield_packing", 38);
|
||||
//ElementId ELEM_CHAR_SIZE = ElementId("char_size", 39);
|
||||
ElementId ELEM_CHAR_SIZE = ElementId("char_size", 39);
|
||||
//ElementId ELEM_CHAR_TYPE = ElementId("char_type", 40);
|
||||
ElementId ELEM_CORETYPES = ElementId("coretypes",41);
|
||||
ElementId ELEM_DATA_ORGANIZATION = ElementId("data_organization", 42);
|
||||
|
@ -68,7 +68,7 @@ ElementId ELEM_TYPE = ElementId("type",60);
|
|||
ElementId ELEM_TYPEGRP = ElementId("typegrp",62);
|
||||
ElementId ELEM_TYPEREF = ElementId("typeref",63);
|
||||
//ElementId ELEM_USE_MS_CONVENTION = ElementId("use_MS_convention", 64);
|
||||
//ElementId ELEM_WCHAR_SIZE = ElementId("wchar_size", 65);
|
||||
ElementId ELEM_WCHAR_SIZE = ElementId("wchar_size", 65);
|
||||
//ElementId ELEM_ZERO_LENGTH_BOUNDARY = ElementId("zero_length_boundary", 66);
|
||||
|
||||
// Some default routines for displaying data
|
||||
|
@ -211,8 +211,8 @@ Datatype *Datatype::nearestArrayedComponentBackward(int8 off,int8 *newoff,int8 *
|
|||
int4 Datatype::compare(const Datatype &op,int4 level) const
|
||||
|
||||
{
|
||||
if (size != op.size) return (op.size - size);
|
||||
if (submeta != op.submeta) return (submeta < op.submeta) ? -1 : 1;
|
||||
if (size != op.size) return (op.size - size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2898,6 +2898,8 @@ TypeFactory::TypeFactory(Architecture *g)
|
|||
glb = g;
|
||||
sizeOfInt = 0;
|
||||
sizeOfLong = 0;
|
||||
sizeOfChar = 0;
|
||||
sizeOfWChar = 0;
|
||||
sizeOfPointer = 0;
|
||||
sizeOfAltPointer = 0;
|
||||
enumsize = 0;
|
||||
|
@ -2916,6 +2918,8 @@ void TypeFactory::clearCache(void)
|
|||
typecache10 = (Datatype *)0;
|
||||
typecache16 = (Datatype *)0;
|
||||
type_nochar = (Datatype *)0;
|
||||
for(i=0;i<5;++i)
|
||||
charcache[i] = (Datatype *)0;
|
||||
}
|
||||
|
||||
/// Set up default values for size of "int", structure alignment, and enums
|
||||
|
@ -2935,6 +2939,10 @@ void TypeFactory::setupSizes(void)
|
|||
if (sizeOfLong == 0) {
|
||||
sizeOfLong = (sizeOfInt == 4) ? 8 : sizeOfInt;
|
||||
}
|
||||
if (sizeOfChar == 0)
|
||||
sizeOfChar = 1;
|
||||
if (sizeOfWChar == 0)
|
||||
sizeOfWChar = 2;
|
||||
if (sizeOfPointer == 0)
|
||||
sizeOfPointer = glb->getDefaultDataSpace()->getAddrSize();
|
||||
SegmentOp *segOp = glb->getSegmentOp(glb->getDefaultDataSpace());
|
||||
|
@ -3003,11 +3011,15 @@ void TypeFactory::cacheCoreTypes(void)
|
|||
// fallthru
|
||||
case TYPE_UINT:
|
||||
if (ct->isEnumType()) break; // Conceivably an enumeration
|
||||
if (ct->isASCII()) { // Char is preferred over other int types
|
||||
typecache[ct->getSize()][ct->getMetatype()-TYPE_FLOAT] = ct;
|
||||
if (ct->isCharPrint()) {
|
||||
if (ct->getSize() < 5)
|
||||
charcache[ct->getSize()] = ct;
|
||||
if (ct->isASCII()) { // Char is preferred over other int types
|
||||
typecache[ct->getSize()][ct->getMetatype()-TYPE_FLOAT] = ct;
|
||||
}
|
||||
// Other character types (UTF16,UTF32) are not preferred
|
||||
break;
|
||||
}
|
||||
if (ct->isCharPrint()) break; // Other character types (UTF16,UTF32) are not preferred
|
||||
// fallthru
|
||||
case TYPE_VOID:
|
||||
case TYPE_UNKNOWN:
|
||||
|
@ -3519,6 +3531,20 @@ Datatype *TypeFactory::getBase(int4 s,type_metatype m,const string &n)
|
|||
return findAdd(tmp);
|
||||
}
|
||||
|
||||
/// If a \e core character data-type of the given size exists, it is returned.
|
||||
/// Otherwise an exception is thrown
|
||||
/// \param s is the size in bytes of the desired character data-type
|
||||
Datatype *TypeFactory::getTypeChar(int4 s)
|
||||
|
||||
{
|
||||
if (s < 5) {
|
||||
Datatype *res = charcache[s];
|
||||
if (res != (Datatype *)0)
|
||||
return res;
|
||||
}
|
||||
throw LowlevelError("Request for unsupported character data-type");
|
||||
}
|
||||
|
||||
/// Retrieve or create the core "code" Datatype object
|
||||
/// This has no prototype attached to it and is appropriate for anonymous function pointers.
|
||||
/// \return the TypeCode object
|
||||
|
@ -3983,10 +4009,6 @@ void TypeFactory::encode(Encoder &encoder) const
|
|||
|
||||
dependentOrder(deporder); // Put types in correct order
|
||||
encoder.openElement(ELEM_TYPEGRP);
|
||||
encoder.writeSignedInteger(ATTRIB_INTSIZE, sizeOfInt);
|
||||
encoder.writeSignedInteger(ATTRIB_LONGSIZE, sizeOfLong);
|
||||
encoder.writeSignedInteger(ATTRIB_ENUMSIZE, enumsize);
|
||||
encoder.writeBool(ATTRIB_ENUMSIGNED, (enumtype==TYPE_INT));
|
||||
for(iter=deporder.begin();iter!=deporder.end();++iter) {
|
||||
if ((*iter)->getName().size()==0) continue; // Don't save anonymous types
|
||||
if ((*iter)->isCoreType()) { // If this would be saved as a coretype
|
||||
|
@ -4305,15 +4327,7 @@ void TypeFactory::decode(Decoder &decoder)
|
|||
|
||||
{
|
||||
uint4 elemId = decoder.openElement(ELEM_TYPEGRP);
|
||||
string metastring;
|
||||
|
||||
sizeOfInt = decoder.readSignedInteger(ATTRIB_INTSIZE);
|
||||
sizeOfLong = decoder.readSignedInteger(ATTRIB_LONGSIZE);
|
||||
enumsize = decoder.readSignedInteger(ATTRIB_ENUMSIZE);
|
||||
if (decoder.readBool(ATTRIB_ENUMSIGNED))
|
||||
enumtype = TYPE_INT;
|
||||
else
|
||||
enumtype = TYPE_UINT;
|
||||
while(decoder.peekElement() != 0)
|
||||
decodeTypeNoRef(decoder,false);
|
||||
decoder.closeElement(elemId);
|
||||
|
@ -4355,6 +4369,12 @@ void TypeFactory::decodeDataOrganization(Decoder &decoder)
|
|||
else if (subId == ELEM_POINTER_SIZE) {
|
||||
sizeOfPointer = decoder.readSignedInteger(ATTRIB_VALUE);
|
||||
}
|
||||
else if (subId == ELEM_CHAR_SIZE) {
|
||||
sizeOfChar = decoder.readSignedInteger(ATTRIB_VALUE);
|
||||
}
|
||||
else if (subId == ELEM_WCHAR_SIZE) {
|
||||
sizeOfWChar = decoder.readSignedInteger(ATTRIB_VALUE);
|
||||
}
|
||||
else if (subId == ELEM_SIZE_ALIGNMENT_MAP) {
|
||||
decodeAlignmentMap(decoder);
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ 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_ENUMSIGNED; ///< Marshaling attribute "enumsigned"
|
||||
extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize"
|
||||
extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize"
|
||||
extern AttributeId ATTRIB_LONGSIZE; ///< Marshaling attribute "longsize"
|
||||
//extern AttributeId ATTRIB_ENUMSIGNED; ///< Marshaling attribute "enumsigned" deprecated
|
||||
//extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize" deprecated
|
||||
//extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize" deprecated
|
||||
//extern AttributeId ATTRIB_LONGSIZE; ///< Marshaling attribute "longsize" deprecated
|
||||
extern AttributeId ATTRIB_OPAQUESTRING; ///< Marshaling attribute "opaquestring"
|
||||
extern AttributeId ATTRIB_SIGNED; ///< Marshaling attribute "signed"
|
||||
extern AttributeId ATTRIB_STRUCTALIGN; ///< Marshaling attribute "structalign"
|
||||
|
@ -40,7 +40,7 @@ extern AttributeId ATTRIB_VARLENGTH; ///< Marshaling attribute "varlength"
|
|||
|
||||
//extern ElementId ELEM_ABSOLUTE_MAX_ALIGNMENT; ///< Marshaling element \<absolute_max_alignment>
|
||||
//extern ElementId ELEM_BITFIELD_PACKING; ///< Marshaling element \<bitfield_packing>
|
||||
//extern ElementId ELEM_CHAR_SIZE; ///< Marshaling element \<char_size>
|
||||
extern ElementId ELEM_CHAR_SIZE; ///< Marshaling element \<char_size>
|
||||
//extern ElementId ELEM_CHAR_TYPE; ///< Marshaling element \<char_type>
|
||||
extern ElementId ELEM_CORETYPES; ///< Marshaling element \<coretypes>
|
||||
extern ElementId ELEM_DATA_ORGANIZATION; ///< Marshaling element \<data_organization>
|
||||
|
@ -66,7 +66,7 @@ extern ElementId ELEM_TYPE; ///< Marshaling element \<type>
|
|||
extern ElementId ELEM_TYPEGRP; ///< Marshaling element \<typegrp>
|
||||
extern ElementId ELEM_TYPEREF; ///< Marshaling element \<typeref>
|
||||
//extern ElementId ELEM_USE_MS_CONVENTION; ///< Marshaling element \<use_MS_convention>
|
||||
//extern ElementId ELEM_WCHAR_SIZE; ///< Marshaling element \<wchar_size>
|
||||
extern ElementId ELEM_WCHAR_SIZE; ///< Marshaling element \<wchar_size>
|
||||
//extern ElementId ELEM_ZERO_LENGTH_BOUNDARY; ///< Marshaling element \<zero_length_boundary>
|
||||
|
||||
/// Print a hex dump of a data buffer to stream
|
||||
|
@ -555,6 +555,7 @@ class TypePartialStruct : public Datatype {
|
|||
public:
|
||||
TypePartialStruct(const TypePartialStruct &op); ///< Construct from another TypePartialStruct
|
||||
TypePartialStruct(Datatype *contain,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 container; } ///< Get the data-type containing \b this piece
|
||||
virtual void printRaw(ostream &s) const;
|
||||
virtual Datatype *getSubType(int8 off,int8 *newoff) const;
|
||||
|
@ -580,6 +581,7 @@ protected:
|
|||
public:
|
||||
TypePartialUnion(const TypePartialUnion &op); ///< Construct from another TypePartialUnion
|
||||
TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor
|
||||
int4 getOffset(void) const { return offset; } ///< Get the byte offset into the containing data-type
|
||||
TypeUnion *getParentUnion(void) const { return container; } ///< Get the union which \b this is part of
|
||||
virtual void printRaw(ostream &s) const;
|
||||
virtual const TypeField *findTruncation(int8 off,int4 sz,const PcodeOp *op,int4 slot,int8 &newoff) const;
|
||||
|
@ -697,8 +699,10 @@ public:
|
|||
|
||||
/// \brief Container class for all Datatype objects in an Architecture
|
||||
class TypeFactory {
|
||||
int4 sizeOfInt; ///< Size of the core "int" datatype
|
||||
int4 sizeOfLong; ///< Size of the core "long" datatype
|
||||
int4 sizeOfInt; ///< Size of the core "int" data-type
|
||||
int4 sizeOfLong; ///< Size of the core "long" data-type
|
||||
int4 sizeOfChar; ///< Size of the core "char" data-type
|
||||
int4 sizeOfWChar; ///< Size of the core "wchar_t" data-type
|
||||
int4 sizeOfPointer; ///< Size of pointers (into default data address space)
|
||||
int4 sizeOfAltPointer; ///< Size of alternate pointers used by architecture (if not 0)
|
||||
int4 enumsize; ///< Size of an enumerated type
|
||||
|
@ -710,6 +714,7 @@ class TypeFactory {
|
|||
Datatype *typecache10; ///< Specially cached 10-byte float type
|
||||
Datatype *typecache16; ///< Specially cached 16-byte float type
|
||||
Datatype *type_nochar; ///< Same dimensions as char but acts and displays as an INT
|
||||
Datatype *charcache[5]; ///< Cached character data-types
|
||||
Datatype *findNoName(Datatype &ct); ///< Find data-type (in this container) by function
|
||||
void insert(Datatype *newtype); ///< Insert pointer into the cross-reference sets
|
||||
Datatype *findAdd(Datatype &ct); ///< Find data-type in this container or add it
|
||||
|
@ -740,6 +745,8 @@ public:
|
|||
int4 getPrimitiveAlignSize(uint4 size) const; ///< Get the aligned size of a primitive data-type
|
||||
int4 getSizeOfInt(void) const { return sizeOfInt; } ///< Get the size of the default "int"
|
||||
int4 getSizeOfLong(void) const { return sizeOfLong; } ///< Get the size of the default "long"
|
||||
int4 getSizeOfChar(void) const { return sizeOfChar; } ///< Get the size of the default "char"
|
||||
int4 getSizeOfWChar(void) const { return sizeOfWChar; } ///< Get the size of the default "wchar_t"
|
||||
int4 getSizeOfPointer(void) const { return sizeOfPointer; } ///< Get the size of pointers
|
||||
int4 getSizeOfAltPointer(void) const { return sizeOfAltPointer; } ///< Get size of alternate pointers (or 0)
|
||||
Architecture *getArch(void) const { return glb; } ///< Get the Architecture object
|
||||
|
@ -759,6 +766,7 @@ public:
|
|||
Datatype *getBaseNoChar(int4 s,type_metatype m); ///< Get atomic type excluding "char"
|
||||
Datatype *getBase(int4 s,type_metatype m); ///< Get atomic type
|
||||
Datatype *getBase(int4 s,type_metatype m,const string &n); ///< Get named atomic type
|
||||
Datatype *getTypeChar(int4 s); ///< Get a character data-type by size
|
||||
TypeCode *getTypeCode(void); ///< Get an "anonymous" function data-type
|
||||
TypePointer *getTypePointerStripArray(int4 s,Datatype *pt,uint4 ws); ///< Construct a pointer data-type, stripping an ARRAY level
|
||||
TypePointer *getTypePointer(int4 s,Datatype *pt,uint4 ws); ///< Construct an absolute pointer data-type
|
||||
|
|
|
@ -767,42 +767,20 @@ string TypeOpCallother::getOperatorName(const PcodeOp *op) const
|
|||
Datatype *TypeOpCallother::getInputLocal(const PcodeOp *op,int4 slot) const
|
||||
|
||||
{
|
||||
if (!op->doesSpecialPropagation())
|
||||
return TypeOp::getInputLocal(op,slot);
|
||||
Architecture *glb = tlst->getArch();
|
||||
VolatileWriteOp *vw_op = glb->userops.getVolatileWrite(); // Check if this a volatile write op
|
||||
if ((vw_op->getIndex() == op->getIn(0)->getOffset()) && (slot == 2)) { // And we are requesting slot 2
|
||||
const Address &addr ( op->getIn(1)->getAddr() ); // Address of volatile memory
|
||||
int4 size = op->getIn(2)->getSize(); // Size of memory being written
|
||||
uint4 vflags = 0;
|
||||
SymbolEntry *entry = glb->symboltab->getGlobalScope()->queryProperties(addr,size,op->getAddr(),vflags);
|
||||
if (entry != (SymbolEntry *)0) {
|
||||
Datatype *res = entry->getSizedType(addr,size);
|
||||
if (res != (Datatype *)0)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
UserPcodeOp *userOp = tlst->getArch()->userops.getOp(op->getIn(0)->getOffset());
|
||||
Datatype *res = userOp->getInputLocal(op, slot);
|
||||
if (res != (Datatype *)0)
|
||||
return res;
|
||||
return TypeOp::getInputLocal(op,slot);
|
||||
}
|
||||
|
||||
Datatype *TypeOpCallother::getOutputLocal(const PcodeOp *op) const
|
||||
|
||||
{
|
||||
if (!op->doesSpecialPropagation())
|
||||
return TypeOp::getOutputLocal(op);
|
||||
Architecture *glb = tlst->getArch();
|
||||
VolatileReadOp *vr_op = glb->userops.getVolatileRead(); // Check if this a volatile read op
|
||||
if (vr_op->getIndex() == op->getIn(0)->getOffset()) {
|
||||
const Address &addr ( op->getIn(1)->getAddr() ); // Address of volatile memory
|
||||
int4 size = op->getOut()->getSize(); // Size of memory being written
|
||||
uint4 vflags = 0;
|
||||
SymbolEntry *entry = glb->symboltab->getGlobalScope()->queryProperties(addr,size,op->getAddr(),vflags);
|
||||
if (entry != (SymbolEntry *)0) {
|
||||
Datatype *res = entry->getSizedType(addr,size);
|
||||
if (res != (Datatype *)0)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
UserPcodeOp *userOp = tlst->getArch()->userops.getOp(op->getIn(0)->getOffset());
|
||||
Datatype *res = userOp->getOutputLocal(op);
|
||||
if (res != (Datatype *)0)
|
||||
return res;
|
||||
return TypeOp::getOutputLocal(op);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,61 @@ ElementId ELEM_CONSTRESOLVE = ElementId("constresolve",127);
|
|||
ElementId ELEM_JUMPASSIST = ElementId("jumpassist",128);
|
||||
ElementId ELEM_SEGMENTOP = ElementId("segmentop",129);
|
||||
|
||||
const uint4 UserPcodeOp::BUILTIN_STRINGDATA = 0x10000000;
|
||||
const uint4 UserPcodeOp::BUILTIN_VOLATILE_READ = 0x10000001;
|
||||
const uint4 UserPcodeOp::BUILTIN_VOLATILE_WRITE = 0x10000002;
|
||||
const uint4 UserPcodeOp::BUILTIN_MEMCPY = 0x10000003;
|
||||
const uint4 UserPcodeOp::BUILTIN_STRNCPY = 0x10000004;
|
||||
const uint4 UserPcodeOp::BUILTIN_WCSNCPY = 0x10000005;
|
||||
|
||||
int4 UserPcodeOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
|
||||
|
||||
{
|
||||
throw LowlevelError("Unexpected annotation input for CALLOTHER " + name);
|
||||
}
|
||||
|
||||
/// \brief Constructor given specific input/output data-types
|
||||
///
|
||||
/// An optional output data-type for the CALLOTHER can be specified and up to 4 input data-types
|
||||
/// associated with the first 4 inputs to the CALLOTHER (after the userop id in slot 0).
|
||||
/// \param nm is the name to associate with the user-op
|
||||
/// \param g is the Architecture owning the new user-op
|
||||
/// \param ind is the id associated with the user-op
|
||||
/// \param out is the data-type to associate with the CALLOTHER output (may be null)
|
||||
/// \param in0 is the first input data-type (may be null)
|
||||
/// \param in1 is the second input data-type (may be null)
|
||||
/// \param in2 is the third input data-type (may be null)
|
||||
/// \param in3 is the fourth input data-type (may be null)
|
||||
DatatypeUserOp::DatatypeUserOp(const string &nm,Architecture *g,int4 ind,Datatype *out,
|
||||
Datatype *in0,Datatype *in1,Datatype *in2,Datatype *in3)
|
||||
: UserPcodeOp(nm,g,datatype,ind)
|
||||
{
|
||||
outType = out;
|
||||
if (in0 != (Datatype *)0)
|
||||
inTypes.push_back(in0);
|
||||
if (in1 != (Datatype *)0)
|
||||
inTypes.push_back(in1);
|
||||
if (in2 != (Datatype *)0)
|
||||
inTypes.push_back(in2);
|
||||
if (in3 != (Datatype *)0)
|
||||
inTypes.push_back(in3);
|
||||
}
|
||||
|
||||
Datatype *DatatypeUserOp::getOutputLocal(const PcodeOp *op) const
|
||||
|
||||
{
|
||||
return outType;
|
||||
}
|
||||
|
||||
Datatype *DatatypeUserOp::getInputLocal(const PcodeOp *op,int4 slot) const
|
||||
|
||||
{
|
||||
slot -= 1;
|
||||
if (slot >= 0 && slot < inTypes.size())
|
||||
return inTypes[slot];
|
||||
return (Datatype *)0;
|
||||
}
|
||||
|
||||
void InjectedUserOp::decode(Decoder &decoder)
|
||||
|
||||
{
|
||||
|
@ -76,6 +125,21 @@ string VolatileReadOp::getOperatorName(const PcodeOp *op) const
|
|||
return appendSize(name,op->getOut()->getSize());
|
||||
}
|
||||
|
||||
Datatype *VolatileReadOp::getOutputLocal(const PcodeOp *op) const
|
||||
|
||||
{
|
||||
if (!op->doesSpecialPropagation())
|
||||
return (Datatype *)0;
|
||||
const Address &addr ( op->getIn(1)->getAddr() ); // Address of volatile memory
|
||||
int4 size = op->getOut()->getSize(); // Size of memory being written
|
||||
uint4 vflags = 0;
|
||||
SymbolEntry *entry = glb->symboltab->getGlobalScope()->queryProperties(addr,size,op->getAddr(),vflags);
|
||||
if (entry != (SymbolEntry *)0) {
|
||||
return entry->getSizedType(addr,size);
|
||||
}
|
||||
return (Datatype *)0;
|
||||
}
|
||||
|
||||
int4 VolatileReadOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
|
||||
|
||||
{
|
||||
|
@ -92,17 +156,32 @@ string VolatileWriteOp::getOperatorName(const PcodeOp *op) const
|
|||
return appendSize(name,op->getIn(2)->getSize());
|
||||
}
|
||||
|
||||
Datatype *VolatileWriteOp::getInputLocal(const PcodeOp *op,int4 slot) const
|
||||
|
||||
{
|
||||
if (!op->doesSpecialPropagation() || slot != 2)
|
||||
return (Datatype *)0;
|
||||
const Address &addr ( op->getIn(1)->getAddr() ); // Address of volatile memory
|
||||
int4 size = op->getIn(2)->getSize(); // Size of memory being written
|
||||
uint4 vflags = 0;
|
||||
SymbolEntry *entry = glb->symboltab->getGlobalScope()->queryProperties(addr,size,op->getAddr(),vflags);
|
||||
if (entry != (SymbolEntry *)0) {
|
||||
return entry->getSizedType(addr,size);
|
||||
}
|
||||
return (Datatype *)0;
|
||||
}
|
||||
|
||||
int4 VolatileWriteOp::extractAnnotationSize(const Varnode *vn,const PcodeOp *op)
|
||||
|
||||
{
|
||||
return op->getIn(2)->getSize(); // Get size from the 3rd parameter of write function
|
||||
}
|
||||
|
||||
/// \param g is the owning Architecture for this instance of the segment operation
|
||||
/// \param nm is the low-level name of the segment operation
|
||||
/// \param g is the owning Architecture for this instance of the segment operation
|
||||
/// \param ind is the constant id identifying the specific CALLOTHER variant
|
||||
SegmentOp::SegmentOp(Architecture *g,const string &nm,int4 ind)
|
||||
: TermPatternOp(g,nm,ind)
|
||||
SegmentOp::SegmentOp(const string &nm,Architecture *g,int4 ind)
|
||||
: TermPatternOp(nm,g,segment,ind)
|
||||
{
|
||||
constresolve.space = (AddrSpace *)0;
|
||||
}
|
||||
|
@ -212,7 +291,7 @@ void SegmentOp::decode(Decoder &decoder)
|
|||
|
||||
/// \param g is the Architecture owning this set of jump assist scripts
|
||||
JumpAssistOp::JumpAssistOp(Architecture *g)
|
||||
: UserPcodeOp(g,"",0)
|
||||
: UserPcodeOp("",g,jumpassist,0)
|
||||
{
|
||||
index2case = -1;
|
||||
index2addr = -1;
|
||||
|
@ -273,11 +352,22 @@ void JumpAssistOp::decode(Decoder &decoder)
|
|||
useropindex = base->getIndex(); // Get the index from the core userop
|
||||
}
|
||||
|
||||
InternalStringOp::InternalStringOp(Architecture *g)
|
||||
: UserPcodeOp("stringdata",g,string_data,BUILTIN_STRINGDATA)
|
||||
{
|
||||
flags |= display_string;
|
||||
}
|
||||
|
||||
Datatype *InternalStringOp::getOutputLocal(const PcodeOp *op) const
|
||||
|
||||
{
|
||||
return op->getOut()->getType();
|
||||
}
|
||||
|
||||
UserOpManage::UserOpManage(void)
|
||||
|
||||
{
|
||||
vol_read = (VolatileReadOp *)0;
|
||||
vol_write = (VolatileWriteOp *)0;
|
||||
glb = (Architecture *)0;
|
||||
}
|
||||
|
||||
UserOpManage::~UserOpManage(void)
|
||||
|
@ -290,37 +380,38 @@ UserOpManage::~UserOpManage(void)
|
|||
if (userop != (UserPcodeOp *)0)
|
||||
delete userop;
|
||||
}
|
||||
map<uint4,UserPcodeOp *>::iterator oiter;
|
||||
for(oiter=builtinmap.begin();oiter!=builtinmap.end();++oiter) {
|
||||
delete (*oiter).second;
|
||||
}
|
||||
}
|
||||
|
||||
/// Every user defined p-code op is initially assigned an UnspecializedPcodeOp description,
|
||||
/// which may get overridden later.
|
||||
/// \param glb is the Architecture from which to draw user defined operations
|
||||
void UserOpManage::initialize(Architecture *glb)
|
||||
/// \param g is the Architecture from which to draw user defined operations
|
||||
void UserOpManage::initialize(Architecture *g)
|
||||
|
||||
{
|
||||
glb = g;
|
||||
vector<string> basicops;
|
||||
glb->translate->getUserOpNames(basicops);
|
||||
for(uint4 i=0;i<basicops.size();++i) {
|
||||
if (basicops[i].size()==0) continue;
|
||||
UserPcodeOp *userop = new UnspecializedPcodeOp(glb,basicops[i],i);
|
||||
UserPcodeOp *userop = new UnspecializedPcodeOp(basicops[i],glb,i);
|
||||
registerOp(userop);
|
||||
}
|
||||
}
|
||||
|
||||
/// Establish defaults for necessary operators not already defined.
|
||||
/// Currently this forces volatile read/write operations to exist.
|
||||
/// \param glb is the owning Architecture
|
||||
void UserOpManage::setDefaults(Architecture *glb)
|
||||
|
||||
{
|
||||
if (vol_read == (VolatileReadOp *)0) {
|
||||
VolatileReadOp *volread = new VolatileReadOp(glb,"read_volatile",useroplist.size(), false);
|
||||
registerOp(volread);
|
||||
}
|
||||
if (vol_write == (VolatileWriteOp *)0) {
|
||||
VolatileWriteOp *volwrite = new VolatileWriteOp(glb,"write_volatile",useroplist.size(), false);
|
||||
registerOp(volwrite);
|
||||
}
|
||||
/// Retrieve a user-op description object by index
|
||||
/// \param i is the index
|
||||
/// \return the indicated user-op description
|
||||
UserPcodeOp *UserOpManage::getOp(uint4 i) const {
|
||||
if (i<useroplist.size())
|
||||
return useroplist[i];
|
||||
map<uint4,UserPcodeOp *>::const_iterator iter = builtinmap.find(i);
|
||||
if (iter == builtinmap.end())
|
||||
return (UserPcodeOp *)0;
|
||||
return ((*iter).second);
|
||||
}
|
||||
|
||||
/// \param nm is the low-level operation name
|
||||
|
@ -334,6 +425,64 @@ UserPcodeOp *UserOpManage::getOp(const string &nm) const
|
|||
return (*iter).second;
|
||||
}
|
||||
|
||||
/// Retrieve a built-in user-op given its id. If user-op record does not already exist,
|
||||
/// instantiate a default form of the record.
|
||||
/// \param i is the index associated
|
||||
/// \return the matching user-op record
|
||||
UserPcodeOp *UserOpManage::registerBuiltin(uint4 i)
|
||||
|
||||
{
|
||||
map<uint4,UserPcodeOp *>::const_iterator iter = builtinmap.find(i);
|
||||
if (iter != builtinmap.end())
|
||||
return (*iter).second;
|
||||
UserPcodeOp *res;
|
||||
switch(i) {
|
||||
case UserPcodeOp::BUILTIN_STRINGDATA:
|
||||
res = new InternalStringOp(glb);
|
||||
break;
|
||||
case UserPcodeOp::BUILTIN_VOLATILE_READ:
|
||||
res = new VolatileReadOp("read_volatile",glb,false);
|
||||
break;
|
||||
case UserPcodeOp::BUILTIN_VOLATILE_WRITE:
|
||||
res = new VolatileWriteOp("write_volatile",glb,false);
|
||||
break;
|
||||
case UserPcodeOp::BUILTIN_MEMCPY:
|
||||
{
|
||||
int4 ptrSize = glb->types->getSizeOfPointer();
|
||||
int4 wordSize = glb->getDefaultDataSpace()->getWordSize();
|
||||
Datatype *vType = glb->types->getTypeVoid();
|
||||
Datatype *ptrType = glb->types->getTypePointer(ptrSize,vType,wordSize);
|
||||
Datatype *intType = glb->types->getBase(4,TYPE_INT);
|
||||
res = new DatatypeUserOp("builtin_memcpy",glb,UserPcodeOp::BUILTIN_MEMCPY,ptrType,ptrType,ptrType,intType);
|
||||
break;
|
||||
}
|
||||
case UserPcodeOp::BUILTIN_STRNCPY: // Copy "char" elements
|
||||
{
|
||||
int4 ptrSize = glb->types->getSizeOfPointer();
|
||||
int4 wordSize = glb->getDefaultDataSpace()->getWordSize();
|
||||
Datatype *cType = glb->types->getTypeChar(glb->types->getSizeOfChar());
|
||||
Datatype *ptrType = glb->types->getTypePointer(ptrSize,cType,wordSize);
|
||||
Datatype *intType = glb->types->getBase(4,TYPE_INT);
|
||||
res = new DatatypeUserOp("builtin_strncpy",glb,UserPcodeOp::BUILTIN_STRNCPY,ptrType,ptrType,ptrType,intType);
|
||||
break;
|
||||
}
|
||||
case UserPcodeOp::BUILTIN_WCSNCPY: // Copy "wchar_t" elements
|
||||
{
|
||||
int4 ptrSize = glb->types->getSizeOfPointer();
|
||||
int4 wordSize = glb->getDefaultDataSpace()->getWordSize();
|
||||
Datatype *cType = glb->types->getTypeChar(glb->types->getSizeOfWChar());
|
||||
Datatype *ptrType = glb->types->getTypePointer(ptrSize,cType,wordSize);
|
||||
Datatype *intType = glb->types->getBase(4,TYPE_INT);
|
||||
res = new DatatypeUserOp("builtin_wcsncpy",glb,UserPcodeOp::BUILTIN_WCSNCPY,ptrType,ptrType,ptrType,intType);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw LowlevelError("Bad built-in userop id");
|
||||
}
|
||||
builtinmap[i] = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Add the description to the mapping by index and the mapping by name. Make same basic
|
||||
/// sanity checks for conflicting values and duplicate operations and throw an
|
||||
/// exception if there's a problem.
|
||||
|
@ -375,19 +524,6 @@ void UserOpManage::registerOp(UserPcodeOp *op)
|
|||
segmentop[index] = s_op;
|
||||
return;
|
||||
}
|
||||
VolatileReadOp *tmpVolRead = dynamic_cast<VolatileReadOp *>(op);
|
||||
if (tmpVolRead != (VolatileReadOp *)0) {
|
||||
if (vol_read != (VolatileReadOp *)0)
|
||||
throw LowlevelError("Multiple volatile reads registered");
|
||||
vol_read = tmpVolRead;
|
||||
return;
|
||||
}
|
||||
VolatileWriteOp *tmpVolWrite = dynamic_cast<VolatileWriteOp *>(op);
|
||||
if (tmpVolWrite != (VolatileWriteOp *)0) {
|
||||
if (vol_write != (VolatileWriteOp *)0)
|
||||
throw LowlevelError("Multiple volatile writes registered");
|
||||
vol_write = tmpVolWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a SegmentOp description object based on the element and
|
||||
|
@ -398,7 +534,7 @@ void UserOpManage::decodeSegmentOp(Decoder &decoder,Architecture *glb)
|
|||
|
||||
{
|
||||
SegmentOp *s_op;
|
||||
s_op = new SegmentOp(glb,"",useroplist.size());
|
||||
s_op = new SegmentOp("",glb,useroplist.size());
|
||||
try {
|
||||
s_op->decode(decoder);
|
||||
registerOp(s_op);
|
||||
|
@ -435,20 +571,15 @@ void UserOpManage::decodeVolatile(Decoder &decoder,Architecture *glb)
|
|||
}
|
||||
if (readOpName.size() == 0 || writeOpName.size() == 0)
|
||||
throw LowlevelError("Missing inputop/outputop attributes in <volatile> element");
|
||||
VolatileReadOp *vr_op = new VolatileReadOp(glb,readOpName,useroplist.size(),functionalDisplay);
|
||||
try {
|
||||
registerOp(vr_op);
|
||||
} catch(LowlevelError &err) {
|
||||
delete vr_op;
|
||||
throw err;
|
||||
}
|
||||
VolatileWriteOp *vw_op = new VolatileWriteOp(glb,writeOpName,useroplist.size(),functionalDisplay);
|
||||
try {
|
||||
registerOp(vw_op);
|
||||
} catch(LowlevelError &err) {
|
||||
delete vw_op;
|
||||
throw err;
|
||||
}
|
||||
map<uint4,UserPcodeOp *>::const_iterator iter;
|
||||
if (builtinmap.find(UserPcodeOp::BUILTIN_VOLATILE_READ) != builtinmap.end())
|
||||
throw LowlevelError("read_volatile user-op registered more than once");
|
||||
if (builtinmap.find(UserPcodeOp::BUILTIN_VOLATILE_WRITE) != builtinmap.end())
|
||||
throw LowlevelError("write_volatile user-op registered more than once");
|
||||
VolatileReadOp *vr_op = new VolatileReadOp(readOpName,glb,functionalDisplay);
|
||||
builtinmap[UserPcodeOp::BUILTIN_VOLATILE_READ] = vr_op;
|
||||
VolatileWriteOp *vw_op = new VolatileWriteOp(writeOpName,glb,functionalDisplay);
|
||||
builtinmap[UserPcodeOp::BUILTIN_VOLATILE_WRITE] = vw_op;
|
||||
}
|
||||
|
||||
/// Create an InjectedUserOp description object based on the element
|
||||
|
@ -458,7 +589,7 @@ void UserOpManage::decodeVolatile(Decoder &decoder,Architecture *glb)
|
|||
void UserOpManage::decodeCallOtherFixup(Decoder &decoder,Architecture *glb)
|
||||
|
||||
{
|
||||
InjectedUserOp *op = new InjectedUserOp(glb,"",0,0);
|
||||
InjectedUserOp *op = new InjectedUserOp("",glb,0,0);
|
||||
try {
|
||||
op->decode(decoder);
|
||||
registerOp(op);
|
||||
|
@ -505,7 +636,7 @@ void UserOpManage::manualCallOtherFixup(const string &useropname,const string &o
|
|||
throw LowlevelError("Cannot fixup userop: "+useropname);
|
||||
|
||||
int4 injectid = glb->pcodeinjectlib->manualCallOtherFixup(useropname,outname,inname,snippet);
|
||||
InjectedUserOp *op = new InjectedUserOp(glb,useropname,userop->getIndex(),injectid);
|
||||
InjectedUserOp *op = new InjectedUserOp(useropname,glb,userop->getIndex(),injectid);
|
||||
try {
|
||||
registerOp(op);
|
||||
} catch(LowlevelError &err) {
|
||||
|
|
|
@ -49,19 +49,40 @@ public:
|
|||
/// \brief Enumeration of different boolean properties that can be assigned to a CALLOTHER
|
||||
enum userop_flags {
|
||||
annotation_assignment = 1, ///< Displayed as assignment, `in1 = in2`, where the first parameter is an annotation
|
||||
no_operator = 2 ///< Don't emit special token, just emit the first input parameter as expression
|
||||
no_operator = 2, ///< Don't emit special token, just emit the first input parameter as expression
|
||||
display_string = 4 ///< Emit as a string constant
|
||||
};
|
||||
/// \brief User-op class encoded as an enum
|
||||
enum userop_type {
|
||||
unspecialized = 1, ///< Encoding for UnspecializedPcodeOp
|
||||
injected = 2, ///< InjectedUserOp
|
||||
volatile_read = 3, ///< VolatileReadOp
|
||||
volatile_write = 4, ///< VolatileWriteOp
|
||||
segment = 5, ///< SegmentOp
|
||||
jumpassist = 6, ///< JumpAssistOp
|
||||
string_data = 7, ///< InternalStringOp
|
||||
datatype = 8 ///< DatatypeUserOp
|
||||
};
|
||||
static const uint4 BUILTIN_STRINGDATA; ///< Built-in id for the InternalStringOp
|
||||
static const uint4 BUILTIN_VOLATILE_READ; ///< Built-in id for VolatileReadOp
|
||||
static const uint4 BUILTIN_VOLATILE_WRITE; ///< Built-in id for VolatileWriteOp
|
||||
static const uint4 BUILTIN_MEMCPY; ///< Built-in id for memcpy
|
||||
static const uint4 BUILTIN_STRNCPY; ///< Built-in id for strcpy
|
||||
static const uint4 BUILTIN_WCSNCPY; ///< Built-in id for wcsncpy
|
||||
protected:
|
||||
string name; ///< Low-level name of p-code operator
|
||||
int4 useropindex; ///< Index passed in the CALLOTHER op
|
||||
Architecture *glb; ///< Architecture owning the user defined op
|
||||
uint4 type; ///< Encoded class type (userop_type)
|
||||
int4 useropindex; ///< Index passed in the CALLOTHER op
|
||||
uint4 flags; ///< Boolean attributes of the CALLOTHER
|
||||
public:
|
||||
UserPcodeOp(Architecture *g,const string &nm,int4 ind) {
|
||||
name = nm; useropindex = ind; glb = g; flags = 0; } ///< Construct from name and index
|
||||
UserPcodeOp(const string &nm,Architecture *g,uint4 tp,int4 ind) {
|
||||
name = nm; glb = g; type = tp; useropindex = ind; flags = 0; } ///< Construct from name and index
|
||||
const string &getName(void) const { return name; } ///< Get the low-level name of the p-code op
|
||||
uint4 getType(void) const { return type; } ///< Get the encoded class type
|
||||
int4 getIndex(void) const { return useropindex; } ///< Get the constant id of the op
|
||||
uint4 getDisplay(void) const { return (flags & (annotation_assignment | no_operator)); } ///< Get display type (0=functional)
|
||||
uint4 getDisplay(void) const {
|
||||
return (flags & (annotation_assignment | no_operator | display_string)); } ///< Get display type (0=functional)
|
||||
virtual ~UserPcodeOp(void) {} ///< Destructor
|
||||
|
||||
/// \brief Get the symbol representing this operation in decompiled code
|
||||
|
@ -73,6 +94,19 @@ public:
|
|||
virtual string getOperatorName(const PcodeOp *op) const {
|
||||
return name; }
|
||||
|
||||
/// \brief Return the output data-type of the user-op if specified
|
||||
///
|
||||
/// \param op is the instantiation of the user-op
|
||||
/// \return the data-type or null to indicate the data-type is unspecified
|
||||
virtual Datatype *getOutputLocal(const PcodeOp *op) const { return (Datatype *)0; }
|
||||
|
||||
/// \brief Return the input data-type to the user-op in the given slot
|
||||
///
|
||||
/// \param op if the instantiation of the user-op
|
||||
/// \param slot is the given input slot
|
||||
/// \return the data-type or null to indicate the data-type is unspecified
|
||||
virtual Datatype *getInputLocal(const PcodeOp *op,int4 slot) const { return (Datatype *)0; }
|
||||
|
||||
/// \brief Assign a size to an annotation input to \b this userop
|
||||
///
|
||||
/// Assuming an annotation refers to a special symbol accessed by \b this operation, retrieve the
|
||||
|
@ -95,8 +129,23 @@ public:
|
|||
/// but still has an unknown effect.
|
||||
class UnspecializedPcodeOp : public UserPcodeOp {
|
||||
public:
|
||||
UnspecializedPcodeOp(Architecture *g,const string &nm,int4 ind)
|
||||
: UserPcodeOp(g,nm,ind) {} ///< Constructor
|
||||
UnspecializedPcodeOp(const string &nm,Architecture *g,int4 ind)
|
||||
: UserPcodeOp(nm,g,unspecialized,ind) {} ///< Constructor
|
||||
virtual void decode(Decoder &decoder) {}
|
||||
};
|
||||
|
||||
/// \brief Generic user defined operation that provides input/output data-types
|
||||
///
|
||||
/// The CALLOTHER acts a source of data-type information within data-flow.
|
||||
class DatatypeUserOp : public UserPcodeOp {
|
||||
Datatype *outType; ///< Data-type of the output
|
||||
vector<Datatype *> inTypes; ///< Data-type of the input(s)
|
||||
public:
|
||||
DatatypeUserOp(const string &nm,Architecture *g,int4 ind,Datatype *out,
|
||||
Datatype *in0=(Datatype *)0,Datatype *in1=(Datatype *)0,
|
||||
Datatype *in2=(Datatype *)0,Datatype *in3=(Datatype *)0);
|
||||
virtual Datatype *getOutputLocal(const PcodeOp *op) const;
|
||||
virtual Datatype *getInputLocal(const PcodeOp *op,int4 slot) const;
|
||||
virtual void decode(Decoder &decoder) {}
|
||||
};
|
||||
|
||||
|
@ -109,8 +158,8 @@ public:
|
|||
class InjectedUserOp : public UserPcodeOp {
|
||||
uint4 injectid; ///< The id of the injection object (to which this op maps)
|
||||
public:
|
||||
InjectedUserOp(Architecture *g,const string &nm,int4 ind,int4 injid)
|
||||
: UserPcodeOp(g,nm,ind) { injectid = injid; } ///< Constructor
|
||||
InjectedUserOp(const string &nm,Architecture *g,int4 ind,int4 injid)
|
||||
: UserPcodeOp(nm,g,injected,ind) { injectid = injid; } ///< Constructor
|
||||
uint4 getInjectId(void) const { return injectid; } ///< Get the id of the injection object
|
||||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
@ -126,8 +175,8 @@ class VolatileOp : public UserPcodeOp {
|
|||
protected:
|
||||
static string appendSize(const string &base,int4 size); ///< Append a suffix to a string encoding a specific size
|
||||
public:
|
||||
VolatileOp(Architecture *g,const string &nm,int4 ind)
|
||||
: UserPcodeOp(g,nm,ind) { } ///< Constructor
|
||||
VolatileOp(const string &nm,Architecture *g,uint4 tp,int4 ind)
|
||||
: UserPcodeOp(nm,g,tp,ind) { } ///< Constructor
|
||||
virtual void decode(Decoder &decoder) {} ///< Currently volatile ops only need their name
|
||||
};
|
||||
|
||||
|
@ -138,9 +187,10 @@ public:
|
|||
/// is the actual value read from memory.
|
||||
class VolatileReadOp : public VolatileOp {
|
||||
public:
|
||||
VolatileReadOp(Architecture *g,const string &nm,int4 ind,bool functional)
|
||||
: VolatileOp(g,nm,ind) { flags = functional ? 0 : no_operator; } ///< Constructor
|
||||
VolatileReadOp(const string &nm,Architecture *g,bool functional)
|
||||
: VolatileOp(nm,g,volatile_read,BUILTIN_VOLATILE_READ) { flags = functional ? 0 : no_operator; } ///< Constructor
|
||||
virtual string getOperatorName(const PcodeOp *op) const;
|
||||
virtual Datatype *getOutputLocal(const PcodeOp *op) const;
|
||||
virtual int4 extractAnnotationSize(const Varnode *vn,const PcodeOp *op);
|
||||
};
|
||||
|
||||
|
@ -152,9 +202,10 @@ public:
|
|||
/// - The Varnode value being written to the memory
|
||||
class VolatileWriteOp : public VolatileOp {
|
||||
public:
|
||||
VolatileWriteOp(Architecture *g,const string &nm,int4 ind,bool functional)
|
||||
: VolatileOp(g,nm,ind) { flags = functional ? 0 : annotation_assignment; } ///< Constructor
|
||||
VolatileWriteOp(const string &nm,Architecture *g,bool functional)
|
||||
: VolatileOp(nm,g,volatile_write,BUILTIN_VOLATILE_WRITE) { flags = functional ? 0 : annotation_assignment; } ///< Constructor
|
||||
virtual string getOperatorName(const PcodeOp *op) const;
|
||||
virtual Datatype *getInputLocal(const PcodeOp *op,int4 slot) const;
|
||||
virtual int4 extractAnnotationSize(const Varnode *vn,const PcodeOp *op);
|
||||
};
|
||||
|
||||
|
@ -170,7 +221,7 @@ public:
|
|||
/// constant inputs (matching the format determined by unify()).
|
||||
class TermPatternOp : public UserPcodeOp {
|
||||
public:
|
||||
TermPatternOp(Architecture *g,const string &nm,int4 ind) : UserPcodeOp(g,nm,ind) {} ///< Constructor
|
||||
TermPatternOp(const string &nm,Architecture *g,uint4 tp,int4 ind) : UserPcodeOp(nm,g,tp,ind) {} ///< Constructor
|
||||
virtual int4 getNumVariableTerms(void) const=0; ///< Get the number of input Varnodes expected
|
||||
|
||||
/// \brief Gather the formal input Varnode objects given the root PcodeOp
|
||||
|
@ -218,7 +269,7 @@ class SegmentOp : public TermPatternOp {
|
|||
bool supportsfarpointer; ///< Is \b true if the joined pair base:near acts as a \b far pointer
|
||||
VarnodeData constresolve; ///< How to resolve constant near pointers
|
||||
public:
|
||||
SegmentOp(Architecture *g,const string &nm,int4 ind); ///< Constructor
|
||||
SegmentOp(const string &nm,Architecture *g,int4 ind); ///< Constructor
|
||||
AddrSpace *getSpace(void) const { return spc; } ///< Get the address space being pointed to
|
||||
bool hasFarPointerSupport(void) const { return supportsfarpointer; } ///< Return \b true, if \b this op supports far pointers
|
||||
int4 getBaseSize(void) const { return baseinsize; } ///< Get size in bytes of the base/segment value
|
||||
|
@ -254,6 +305,17 @@ public:
|
|||
virtual void decode(Decoder &decoder);
|
||||
};
|
||||
|
||||
/// \brief An op that displays as an internal string
|
||||
///
|
||||
/// The user op takes no input parameters. In the decompiler output, it displays as a quoted string. The
|
||||
/// string is associated with the address assigned to the user op and is pulled from StringManager as \e internal.
|
||||
class InternalStringOp : public UserPcodeOp {
|
||||
public:
|
||||
InternalStringOp(Architecture *g); ///< Constructor
|
||||
virtual Datatype *getOutputLocal(const PcodeOp *op) const;
|
||||
virtual void decode(Decoder &decoder) {}
|
||||
};
|
||||
|
||||
/// \brief Manager/container for description objects (UserPcodeOp) of user defined p-code ops
|
||||
///
|
||||
/// The description objects are referenced by the CALLOTHER constant id, (or by name during initialization).
|
||||
|
@ -262,28 +324,22 @@ public:
|
|||
/// may reassign a more specialized description object by parsing specific tags using
|
||||
/// on of \b this class's parse* methods.
|
||||
class UserOpManage {
|
||||
Architecture *glb; ///< Architecture this manager is associated with
|
||||
vector<UserPcodeOp *> useroplist; ///< Description objects indexed by CALLOTHER constant id
|
||||
map<uint4,UserPcodeOp *> builtinmap; ///< Map from builtin ids to description objects
|
||||
map<string,UserPcodeOp *> useropmap; ///< A map from the name of the user defined operation to a description object
|
||||
vector<SegmentOp *> segmentop; ///< Segment operations supported by this Architecture
|
||||
VolatileReadOp *vol_read; ///< (Single) volatile read operation
|
||||
VolatileWriteOp *vol_write; ///< (Single) volatile write operation
|
||||
void registerOp(UserPcodeOp *op); ///< Insert a new UserPcodeOp description object in the map(s)
|
||||
public:
|
||||
UserOpManage(void); ///< Construct an empty manager
|
||||
~UserOpManage(void); ///< Destructor
|
||||
void initialize(Architecture *glb); ///< Initialize description objects for all user defined ops
|
||||
void setDefaults(Architecture *glb); ///< Create any required operations if they weren't explicitly defined
|
||||
void initialize(Architecture *g); ///< Initialize description objects for all user defined ops
|
||||
int4 numSegmentOps(void) const { return segmentop.size(); } ///< Number of segment operations supported
|
||||
|
||||
/// Retrieve a user-op description object by index
|
||||
/// \param i is the index
|
||||
/// \return the indicated user-op description
|
||||
UserPcodeOp *getOp(int4 i) const {
|
||||
if (i>=useroplist.size()) return (UserPcodeOp *)0;
|
||||
return useroplist[i];
|
||||
}
|
||||
UserPcodeOp *getOp(uint4 i) const; ///< Retrieve a user-op description object by index
|
||||
UserPcodeOp *getOp(const string &nm) const; ///< Retrieve description by name
|
||||
|
||||
UserPcodeOp *getOp(const string &nm) const; ///< Retrieve description by name
|
||||
UserPcodeOp *registerBuiltin(uint4 i); ///< Make sure an active record exists for the given built-in op
|
||||
|
||||
/// Retrieve a segment-op description object by index
|
||||
/// \param i is the index
|
||||
|
@ -293,8 +349,6 @@ public:
|
|||
return segmentop[i];
|
||||
}
|
||||
|
||||
VolatileReadOp *getVolatileRead(void) const { return vol_read; } ///< Get (the) volatile read description
|
||||
VolatileWriteOp *getVolatileWrite(void) const { return vol_write; } ///< Get (the) volatile write description
|
||||
void decodeSegmentOp(Decoder &decoder,Architecture *glb); ///< Parse a \<segmentop> element
|
||||
void decodeVolatile(Decoder &decoder,Architecture *glb); ///< Parse a \<volatile> element
|
||||
void decodeCallOtherFixup(Decoder &decoder,Architecture *glb); ///< Parse a \<callotherfixup> element
|
||||
|
|
|
@ -533,16 +533,25 @@ SymbolEntry *HighVariable::getSymbolEntry(void) const
|
|||
return (SymbolEntry *)0;
|
||||
}
|
||||
|
||||
/// The data-type its dirtying mechanism is disabled. The data-type will not change, unless
|
||||
/// this method is called again.
|
||||
/// \param tp is the data-type to set
|
||||
void HighVariable::finalizeDatatype(Datatype *tp)
|
||||
/// If there is an associated Symbol, its data-type (or the appropriate piece) is assigned
|
||||
/// to \b this. The dirtying mechanism is disabled so that data-type cannot change.
|
||||
/// \param typeFactory is the factory used to construct any required piece
|
||||
void HighVariable::finalizeDatatype(TypeFactory *typeFactory)
|
||||
|
||||
{
|
||||
if (symbol == (Symbol *)0) return;
|
||||
Datatype *cur = symbol->getType();
|
||||
int4 off = symboloffset;
|
||||
if (off < 0)
|
||||
off = 0;
|
||||
int4 sz = inst[0]->getSize();
|
||||
Datatype *tp = typeFactory->getExactPiece(cur, off, sz);
|
||||
if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN)
|
||||
return;
|
||||
type = tp;
|
||||
if (type->hasStripped()) {
|
||||
if (type->getMetatype() == TYPE_PARTIALUNION) {
|
||||
if (symbol != (Symbol *)0 && symboloffset != -1) {
|
||||
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
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
int4 getSymbolOffset(void) const { return symboloffset; } ///< Get the Symbol offset associated with \b this
|
||||
int4 numInstances(void) const { return inst.size(); } ///< Get the number of member Varnodes \b this has
|
||||
Varnode *getInstance(int4 i) const { return inst[i]; } ///< Get the i-th member Varnode
|
||||
void finalizeDatatype(Datatype *tp); ///< Set a final datatype for \b this variable
|
||||
void finalizeDatatype(TypeFactory *typeFactory); ///< Set a final data-type matching the associated Symbol
|
||||
void groupWith(int4 off,HighVariable *hi2); ///< Put \b this and another HighVariable in the same intersection group
|
||||
void establishGroupSymbolOffset(void); ///< Transfer \b symbol offset of \b this to the VariableGroup
|
||||
|
||||
|
|
|
@ -23,6 +23,35 @@ AttributeId ATTRIB_MAIN = AttributeId("main",134);
|
|||
|
||||
ElementId ELEM_LOCALDB = ElementId("localdb",228);
|
||||
|
||||
/// This is assumed to be \e open. If \b this is a primitive integer or float, and if the other range
|
||||
/// is just a constant being COPYed, return \b true, even if the constant is bigger.
|
||||
/// \param b is the other range to test for absorption
|
||||
/// \return \b true if the other range can be absorbed as a constant
|
||||
bool RangeHint::isConstAbsorbable(const RangeHint *b) const
|
||||
|
||||
{
|
||||
if ((b->flags & copy_constant) == 0)
|
||||
return false;
|
||||
if (b->isTypeLock())
|
||||
return false;
|
||||
if (b->size < size)
|
||||
return false;
|
||||
type_metatype meta = type->getMetatype();
|
||||
if (meta != TYPE_INT && meta != TYPE_UINT && meta != TYPE_BOOL && meta != TYPE_FLOAT)
|
||||
return false;
|
||||
type_metatype bMeta = b->type->getMetatype();
|
||||
if (bMeta != TYPE_UNKNOWN && bMeta != TYPE_INT && bMeta != TYPE_UINT)
|
||||
return false;
|
||||
intb end = sstart;
|
||||
if (highind > 0)
|
||||
end += highind * type->getAlignSize();
|
||||
else
|
||||
end += size;
|
||||
if (b->sstart > end)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Can the given intersecting RangeHint coexist with \b this at their given offsets
|
||||
///
|
||||
/// Determine if the data-type information in the two ranges \e line \e up
|
||||
|
@ -44,21 +73,26 @@ bool RangeHint::reconcile(const RangeHint *b) const
|
|||
mod += a->type->getAlignSize();
|
||||
|
||||
Datatype *sub = a->type;
|
||||
while((sub!=(Datatype *)0)&&(sub->getAlignSize() > b->type->getAlignSize()))
|
||||
while((sub!=(Datatype *)0)&&(sub->getAlignSize() > b->type->getAlignSize())) {
|
||||
sub = sub->getSubType(mod,&mod);
|
||||
}
|
||||
|
||||
if (sub == (Datatype *)0) return false;
|
||||
if (mod != 0) return false;
|
||||
if (sub->getAlignSize() == b->type->getAlignSize()) return true;
|
||||
if ((b->flags & Varnode::typelock)!=0) return false;
|
||||
// If we reach here, component sizes do not match
|
||||
// Check for data-types we want to protect more
|
||||
if (sub != (Datatype *)0) {
|
||||
if (sub->getAlignSize() == b->type->getAlignSize()) return true;
|
||||
// If we reach here, b overlaps multiple components of a
|
||||
}
|
||||
|
||||
// If we reach here, component sizes do not match. Check for data-types we want to protect more
|
||||
|
||||
if (b->rangeType == RangeType::open && b->isConstAbsorbable(a))
|
||||
return true;
|
||||
if (b->isTypeLock()) return false;
|
||||
type_metatype meta = a->type->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION) {
|
||||
if (meta != TYPE_ARRAY || ((TypeArray *)(a->type))->getBase()->getMetatype() == TYPE_UNKNOWN)
|
||||
if (meta != TYPE_ARRAY || ((TypeArray *)(a->type))->getBase()->getMetatype() != TYPE_UNKNOWN)
|
||||
return false;
|
||||
}
|
||||
// For structures, unions, and arrays, test if b looks like a partial data-type
|
||||
// For structures, unions, and arrays, test if b looks like a partial/combined data-type
|
||||
meta = b->type->getMetatype();
|
||||
if (meta == TYPE_UNKNOWN || meta == TYPE_INT || meta == TYPE_UINT) {
|
||||
return true;
|
||||
|
@ -95,19 +129,29 @@ bool RangeHint::preferred(const RangeHint *b,bool reconcile) const
|
|||
if (start != b->start)
|
||||
return true; // Something must occupy a->start to b->start
|
||||
// Prefer the locked type
|
||||
if ((b->flags & Varnode::typelock)!=0) {
|
||||
if ((flags & Varnode::typelock)==0)
|
||||
if (b->isTypeLock()) {
|
||||
if (!isTypeLock())
|
||||
return false;
|
||||
}
|
||||
else if ((flags & Varnode::typelock)!=0)
|
||||
else if (isTypeLock())
|
||||
return true;
|
||||
|
||||
if (!reconcile) { // If the ranges don't reconcile
|
||||
if (rangeType == open && b->rangeType != open) // Throw out the open range
|
||||
return false;
|
||||
if (b->rangeType == open && rangeType != open)
|
||||
if (rangeType == open && b->rangeType != open) {
|
||||
if (!reconcile)
|
||||
return false; // Throw out open range
|
||||
if (isConstAbsorbable(b))
|
||||
return true;
|
||||
}
|
||||
else if (b->rangeType == open && rangeType != open) {
|
||||
if (!reconcile)
|
||||
return true; // Throw out open range
|
||||
if (b->isConstAbsorbable(this))
|
||||
return false;
|
||||
}
|
||||
else if (rangeType == fixed && b->rangeType == fixed) {
|
||||
if (size != b->size && !reconcile)
|
||||
return (size > b->size);
|
||||
}
|
||||
|
||||
return (0>type->typeOrder(*b->type)); // Prefer the more specific
|
||||
}
|
||||
|
@ -127,8 +171,12 @@ bool RangeHint::attemptJoin(RangeHint *b)
|
|||
|
||||
{
|
||||
if (rangeType != open) return false;
|
||||
if (highind < 0) return false;
|
||||
if (b->rangeType == endpoint) return false; // Don't merge with bounding range
|
||||
if (isConstAbsorbable(b)) {
|
||||
absorb(b);
|
||||
return true;
|
||||
}
|
||||
if (highind < 0) return false;
|
||||
Datatype *settype = type; // Assume we will keep this data-type
|
||||
if (settype->getAlignSize() != b->type->getAlignSize()) return false;
|
||||
if (settype != b->type) {
|
||||
|
@ -151,9 +199,8 @@ bool RangeHint::attemptJoin(RangeHint *b)
|
|||
else if (aTestType != bTestType) // If they are both not unknown, they must be the same
|
||||
return false;
|
||||
}
|
||||
if ((flags & Varnode::typelock)!=0) return false;
|
||||
if ((b->flags & Varnode::typelock)!=0) return false;
|
||||
if (flags != b->flags) return false;
|
||||
if (isTypeLock()) return false;
|
||||
if (b->isTypeLock()) return false;
|
||||
intb diffsz = b->sstart - sstart;
|
||||
if ((diffsz % settype->getAlignSize()) != 0) return false;
|
||||
diffsz /= settype->getAlignSize();
|
||||
|
@ -170,16 +217,34 @@ bool RangeHint::attemptJoin(RangeHint *b)
|
|||
void RangeHint::absorb(RangeHint *b)
|
||||
|
||||
{
|
||||
if (b->rangeType == open && type->getAlignSize() == b->type->getAlignSize()) {
|
||||
rangeType = open;
|
||||
if (0 <= b->highind) { // If b has array indexing
|
||||
intb diffsz = b->sstart - sstart;
|
||||
diffsz /= type->getAlignSize();
|
||||
int4 trialhi = b->highind + diffsz;
|
||||
if (b->rangeType == open) {
|
||||
if (type->getAlignSize() == b->type->getAlignSize()) { // Compatible element data-type
|
||||
rangeType = open;
|
||||
if (0 <= b->highind) { // If b has array indexing
|
||||
intb diffsz = b->sstart - sstart;
|
||||
diffsz /= type->getAlignSize();
|
||||
int4 trialhi = b->highind + diffsz;
|
||||
if (highind < trialhi)
|
||||
highind = trialhi;
|
||||
}
|
||||
}
|
||||
else if (start == b->start) {
|
||||
type_metatype meta = type->getMetatype();
|
||||
if (meta != TYPE_STRUCT && meta != TYPE_UNION)
|
||||
rangeType = open;
|
||||
}
|
||||
}
|
||||
else if ((b->flags & copy_constant)!=0 && rangeType == open) {
|
||||
intb diffsz = b->sstart - sstart + b->size;
|
||||
if (diffsz > size) {
|
||||
int4 trialhi = diffsz / type->getAlignSize();
|
||||
if (highind < trialhi)
|
||||
highind = trialhi;
|
||||
}
|
||||
}
|
||||
if ((flags & copy_constant)!=0 && (b->flags & copy_constant)==0) {
|
||||
flags ^= copy_constant;
|
||||
}
|
||||
}
|
||||
|
||||
/// Given that \b this and the other RangeHint intersect, redefine \b this so that it
|
||||
|
@ -206,12 +271,12 @@ bool RangeHint::merge(RangeHint *b,AddrSpace *space,TypeFactory *typeFactory)
|
|||
}
|
||||
else {
|
||||
didReconcile = false;
|
||||
resType = ((flags & Varnode::typelock) != 0) ? 0 : 2;
|
||||
resType = isTypeLock() ? 0 : 2;
|
||||
}
|
||||
// Check for really problematic cases
|
||||
if (!didReconcile) {
|
||||
if ((flags & Varnode::typelock)!=0) {
|
||||
if ((b->flags & Varnode::typelock)!=0)
|
||||
if (isTypeLock()) {
|
||||
if (b->isTypeLock())
|
||||
throw LowlevelError("Overlapping forced variable types : " + type->getName() + " " + b->type->getName());
|
||||
if (start != b->start)
|
||||
return false; // Discard b entirely
|
||||
|
@ -219,8 +284,7 @@ bool RangeHint::merge(RangeHint *b,AddrSpace *space,TypeFactory *typeFactory)
|
|||
}
|
||||
|
||||
if (resType == 0) {
|
||||
if (didReconcile)
|
||||
absorb(b);
|
||||
absorb(b);
|
||||
}
|
||||
else if (resType == 1) {
|
||||
RangeHint copyRange = *this;
|
||||
|
@ -250,7 +314,7 @@ bool RangeHint::merge(RangeHint *b,AddrSpace *space,TypeFactory *typeFactory)
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Compare (signed) offset, size, RangeType, type lock, and high index, in that order.
|
||||
/// Compare (signed) offset, size, RangeType, flags, and high index, in that order.
|
||||
/// Datatype is \e not compared.
|
||||
/// \param op2 is the other RangeHint to compare with \b this
|
||||
/// \return -1, 0, or 1 depending on if \b this comes before, is equal to, or comes after
|
||||
|
@ -263,10 +327,8 @@ int4 RangeHint::compare(const RangeHint &op2) const
|
|||
return (size < op2.size) ? -1 : 1; // Small sizes come first
|
||||
if (rangeType != op2.rangeType)
|
||||
return (rangeType < op2.rangeType) ? -1 : 1;
|
||||
uint4 thisLock = flags & Varnode::typelock;
|
||||
uint4 op2Lock = op2.flags & Varnode::typelock;
|
||||
if (thisLock != op2Lock)
|
||||
return (thisLock < op2Lock) ? -1 : 1;
|
||||
if (flags != op2.flags)
|
||||
return (flags < op2.flags) ? -1 : 1;
|
||||
if (highind != op2.highind)
|
||||
return (highind < op2.highind) ? -1 : 1;
|
||||
return 0;
|
||||
|
@ -284,6 +346,7 @@ ScopeLocal::ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g) : S
|
|||
maxParamOffset = 0;
|
||||
rangeLocked = false;
|
||||
stackGrowsNegative = true;
|
||||
overlapProblems = false;
|
||||
restrictScope(fd);
|
||||
}
|
||||
|
||||
|
@ -473,6 +536,9 @@ void ScopeLocal::markNotMapped(AddrSpace *spc,uintb first,int4 sz,bool parameter
|
|||
fd->warningHeader("Variable defined which should be unmapped: "+sym->getName());
|
||||
return;
|
||||
}
|
||||
else if (sym->getCategory() == Symbol::fake_input) {
|
||||
return; // Inputs in the stack space should not be unmapped
|
||||
}
|
||||
removeSymbol(sym);
|
||||
overlap = findOverlap(addr,sz);
|
||||
}
|
||||
|
@ -522,7 +588,7 @@ bool ScopeLocal::adjustFit(RangeHint &a) const
|
|||
|
||||
{
|
||||
if (a.size==0) return false; // Nothing to fit
|
||||
if ((a.flags & Varnode::typelock)!=0) return false; // Already entered
|
||||
if (a.isTypeLock()) return false; // Already entered
|
||||
Address addr(space,a.start);
|
||||
uintb maxsize = getRangeTree().longestFit(addr,a.size);
|
||||
if (maxsize==0) return false;
|
||||
|
@ -773,11 +839,14 @@ uintb AliasChecker::gatherOffset(Varnode *vn)
|
|||
case CPUI_PTRADD:
|
||||
othervn = def->getIn(2);
|
||||
retval = gatherOffset(def->getIn(0));
|
||||
// We need to treat PTRADD exactly as if it were encoded as an ADD and MULT
|
||||
// Because a plain MULT truncates the ADD tree
|
||||
// We only follow getIn(1) if the PTRADD multiply is by 1
|
||||
if (othervn->isConstant() && (othervn->getOffset()==1))
|
||||
retval = retval + gatherOffset(def->getIn(1));
|
||||
if (def->getIn(1)->isConstant())
|
||||
retval = retval + def->getIn(1)->getOffset() * othervn->getOffset();
|
||||
else if (othervn->getOffset()==1) {
|
||||
// We need to treat PTRADD exactly as if it were encoded as an ADD and MULT
|
||||
// Because a plain MULT truncates the ADD tree
|
||||
// We only follow getIn(1) if the PTRADD multiply is by 1
|
||||
retval = retval + gatherOffset(def->getIn(1));
|
||||
}
|
||||
break;
|
||||
case CPUI_SEGMENTOP:
|
||||
retval = gatherOffset(def->getIn(2));
|
||||
|
@ -849,6 +918,43 @@ void MapState::addRange(uintb st,Datatype *ct,uint4 fl,RangeHint::RangeType rt,i
|
|||
#endif
|
||||
}
|
||||
|
||||
/// If the data-type is an array, partial struct, or partial union, the reference may be added as \e open.
|
||||
/// \param start is the starting offset of the range
|
||||
/// \param ct is the data-type
|
||||
/// \param flags indicates any boolean properties applied to the range
|
||||
/// \param types is the TypeFactory used to construct unknown data-types
|
||||
void MapState::addFixedType(uintb start,Datatype *ct,uint4 flags,TypeFactory *types)
|
||||
|
||||
{
|
||||
if (ct->getMetatype() == TYPE_PARTIALSTRUCT) {
|
||||
TypePartialStruct *tps = (TypePartialStruct *)ct;
|
||||
ct = tps->getParent();
|
||||
if (ct->getMetatype() == TYPE_STRUCT && tps->getOffset() == 0) { // If initial fields of TYPE_STRUCT are moved here
|
||||
addRange(start,ct,0,RangeHint::open,-1); // Treat as an open reference
|
||||
}
|
||||
else if (ct->getMetatype() == TYPE_ARRAY) { // If elements of an array are moved here
|
||||
ct = ((TypeArray *)ct)->getBase();
|
||||
if (ct->getMetatype() != TYPE_UNKNOWN)
|
||||
addRange(start,ct,0,RangeHint::open,-1); // Treat as an open reference
|
||||
}
|
||||
// If the Varnode is a constant COPY, generate a fixed reference as well
|
||||
if (flags != 0) {
|
||||
ct = types->getBase(tps->getSize(), TYPE_UNKNOWN);
|
||||
addRange(start,ct,flags,RangeHint::fixed,-1);
|
||||
}
|
||||
}
|
||||
else if (ct->getMetatype() == TYPE_PARTIALUNION) {
|
||||
TypePartialUnion *tpu = (TypePartialUnion *)ct;
|
||||
if (tpu->getOffset() == 0) { // If the initial fields of TYPE_UNION are moved here
|
||||
ct = tpu->getParentUnion();
|
||||
addRange(start,ct,0,RangeHint::open,-1); // Treat as an open reference
|
||||
}
|
||||
}
|
||||
else {
|
||||
addRange(start,ct,flags,RangeHint::fixed,-1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Assuming a sorted list, from among a sequence of RangeHints with the same start and size, select
|
||||
/// the most specific data-type. Set all elements to use this data-type, and eliminate duplicates.
|
||||
void MapState::reconcileDatatypes(void)
|
||||
|
@ -863,7 +969,7 @@ void MapState::reconcileDatatypes(void)
|
|||
int4 curPos = 1;
|
||||
while(curPos < maplist.size()) {
|
||||
RangeHint *curHint = maplist[curPos++];
|
||||
if (curHint->start == startHint->start && curHint->size == startHint->size) {
|
||||
if (curHint->start == startHint->start && curHint->size == startHint->size && curHint->flags == startHint->flags) {
|
||||
Datatype *curDatatype = curHint->type;
|
||||
if (curDatatype->typeOrder(*startDatatype) < 0) // Take the most specific variant of data-type
|
||||
startDatatype = curDatatype;
|
||||
|
@ -947,7 +1053,8 @@ void MapState::gatherSymbols(const EntryMap *rangemap)
|
|||
// if ((*iter).isPiece()) continue; // This should probably never happen
|
||||
uintb start = (*riter).getAddr().getOffset();
|
||||
Datatype *ct = sym->getType();
|
||||
addRange(start,ct,sym->getFlags(),RangeHint::fixed,-1);
|
||||
uint4 flags = sym->isTypeLocked() ? RangeHint::typelock : 0;
|
||||
addRange(start,ct,flags,RangeHint::fixed,-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -974,6 +1081,42 @@ bool MapState::initialize(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Filter out INDIRECT, MULTIEQUAL, and PIECE operations that are just copying between the same storage location.
|
||||
/// If there is another operation reading the Varnode, return \b true, otherwise return \b false.
|
||||
/// \param vn is the given Varnode to test
|
||||
/// \return \b true if there is an active operation reading the Varnode
|
||||
bool MapState::isReadActive(Varnode *vn)
|
||||
|
||||
{
|
||||
list<PcodeOp *>::const_iterator iter;
|
||||
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
|
||||
PcodeOp *op = *iter;
|
||||
if (op->isMarker()) {
|
||||
if (vn->getAddr() != op->getOut()->getAddr())
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
OpCode opc = op->code();
|
||||
if (opc == CPUI_PIECE) {
|
||||
Address addr = op->getOut()->getAddr();
|
||||
int4 slot = addr.isBigEndian() ? 0 : 1;
|
||||
if (op->getIn(slot) != vn) {
|
||||
addr = addr + op->getIn(slot)->getSize();
|
||||
}
|
||||
if (vn->getAddr() != addr)
|
||||
return true;
|
||||
}
|
||||
else if (opc == CPUI_SUBPIECE) {
|
||||
// Any data-type information comes from the output Varnode, so we ignore input
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Add a RangeHint corresponding to each Varnode stored in the address space
|
||||
/// for the given function. The current knowledge of the Varnode's data-type
|
||||
/// is included as part of the hint.
|
||||
|
@ -983,53 +1126,85 @@ void MapState::gatherVarnodes(const Funcdata &fd)
|
|||
{
|
||||
VarnodeLocSet::const_iterator riter,iterend;
|
||||
Varnode *vn;
|
||||
TypeFactory *types = fd.getArch()->types;
|
||||
riter = fd.beginLoc(spaceid);
|
||||
iterend = fd.endLoc(spaceid);
|
||||
while(riter != iterend) {
|
||||
vn = *riter++;
|
||||
if (vn->isFree()) continue;
|
||||
uintb start = vn->getOffset();
|
||||
Datatype *ct = vn->getType();
|
||||
// Assume parents are present so partials aren't needed
|
||||
if (ct->getMetatype() == TYPE_PARTIALSTRUCT) continue;
|
||||
if (ct->getMetatype() == TYPE_PARTIALUNION) continue;
|
||||
// Do not force Varnode flags on the entry
|
||||
// as the flags were inherited from the previous
|
||||
// (now obsolete) entry
|
||||
addRange(start,ct,0,RangeHint::fixed,-1);
|
||||
// Do not force Varnode flags on the entry
|
||||
// as the flags were inherited from the previous
|
||||
// (now obsolete) entry
|
||||
if (!vn->isWritten()) {
|
||||
if (isReadActive(vn))
|
||||
addFixedType(vn->getOffset(), vn->getType(), 0, types);
|
||||
continue;
|
||||
}
|
||||
PcodeOp *op = vn->getDef();
|
||||
switch(op->code()) {
|
||||
case CPUI_INDIRECT:
|
||||
{
|
||||
Varnode *invn = op->getIn(0);
|
||||
if (vn->getAddr() != invn->getAddr() || isReadActive(vn)) {
|
||||
addFixedType(vn->getOffset(), vn->getType(), 0, types);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CPUI_MULTIEQUAL:
|
||||
{
|
||||
int4 i;
|
||||
for(i=0;i<op->numInput();++i) {
|
||||
Varnode *invn = op->getIn(i);
|
||||
if (vn->getAddr() != invn->getAddr())
|
||||
break;
|
||||
}
|
||||
if (i != op->numInput() || isReadActive(vn))
|
||||
addFixedType(vn->getOffset(), vn->getType(), 0, types);
|
||||
break;
|
||||
}
|
||||
case CPUI_PIECE:
|
||||
{
|
||||
// Treat PIECE as two COPYs
|
||||
Address addr = vn->getAddr();
|
||||
int4 slot = addr.isBigEndian() ? 0 : 1;
|
||||
Varnode *inFirst = op->getIn(slot);
|
||||
if (inFirst->getAddr() != addr)
|
||||
addFixedType(addr.getOffset(),inFirst->getType(), 0, types);
|
||||
addr = addr + inFirst->getSize();
|
||||
Varnode *inSecond = op->getIn(1-slot);
|
||||
if (inSecond->getAddr() != addr)
|
||||
addFixedType(addr.getOffset(),inSecond->getType(), 0, types);
|
||||
if (isReadActive(vn))
|
||||
addFixedType(vn->getOffset(),vn->getType(), 0, types);
|
||||
break;
|
||||
}
|
||||
case CPUI_SUBPIECE:
|
||||
{
|
||||
// Don't treat SUBPIECE as an active write if it is just copying to the same storage location
|
||||
Address addr = op->getIn(0)->getAddr();
|
||||
int4 trunc;
|
||||
if (addr.isBigEndian()) {
|
||||
trunc = op->getIn(0)->getSize() - vn->getSize() - (int4)op->getIn(1)->getOffset();
|
||||
}
|
||||
else {
|
||||
trunc = (int4)op->getIn(1)->getOffset();
|
||||
}
|
||||
addr = addr + trunc;
|
||||
if (addr != vn->getAddr() || isReadActive(vn)) {
|
||||
addFixedType(vn->getOffset(), vn->getType(), 0, types);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CPUI_COPY:
|
||||
addFixedType(vn->getOffset(), vn->getType(), op->getIn(0)->isConstant() ? RangeHint::copy_constant : 0, types);
|
||||
break;
|
||||
default:
|
||||
addFixedType(vn->getOffset(), vn->getType(), 0, types);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a RangeHint corresponding to each HighVariable that is mapped to our
|
||||
/// address space for the given function.
|
||||
/// \param fd is the given function
|
||||
void MapState::gatherHighs(const Funcdata &fd)
|
||||
|
||||
{
|
||||
vector<HighVariable *> varvec;
|
||||
VarnodeLocSet::const_iterator riter,iterend;
|
||||
Varnode *vn;
|
||||
HighVariable *high;
|
||||
riter = fd.beginLoc(spaceid);
|
||||
iterend = fd.endLoc(spaceid);
|
||||
while(riter != iterend) {
|
||||
vn = *riter++;
|
||||
high = vn->getHigh();
|
||||
if (high == (HighVariable *)0) continue;
|
||||
if (high->isMark()) continue;
|
||||
if (!high->isAddrTied()) continue;
|
||||
vn = high->getTiedVarnode(); // Original vn may not be good representative
|
||||
high->setMark();
|
||||
varvec.push_back(high);
|
||||
uintb start = vn->getOffset();
|
||||
Datatype *ct = high->getType(); // Get type from high
|
||||
if (ct->getMetatype() == TYPE_PARTIALUNION) continue;
|
||||
addRange(start,ct,0,RangeHint::fixed,-1);
|
||||
}
|
||||
for(int4 i=0;i<varvec.size();++i)
|
||||
varvec[i]->clearMark();
|
||||
}
|
||||
|
||||
/// For any Varnode that looks like a pointer into our address space, create an
|
||||
/// \e open RangeHint. The size of the object may not be known.
|
||||
/// \param fd is the given function
|
||||
|
@ -1092,12 +1267,13 @@ void ScopeLocal::restructureVarnode(bool aliasyes)
|
|||
state.gatherVarnodes(*fd); // Gather stack type information from varnodes
|
||||
state.gatherOpen(*fd);
|
||||
state.gatherSymbols(maptable[space->getIndex()]);
|
||||
restructure(state);
|
||||
overlapProblems = restructure(state);
|
||||
|
||||
// At some point, processing mapped input symbols may be folded
|
||||
// into the above gather/restructure process, but for now
|
||||
// we just define fake symbols so that mark_unaliased will work
|
||||
clearUnlockedCategory(0);
|
||||
clearUnlockedCategory(Symbol::function_parameter);
|
||||
clearCategory(Symbol::fake_input);
|
||||
fakeInputSymbols();
|
||||
|
||||
state.sortAlias();
|
||||
|
@ -1109,30 +1285,6 @@ void ScopeLocal::restructureVarnode(bool aliasyes)
|
|||
annotateRawStackPtr(); // Add a special placeholder PTRSUB
|
||||
}
|
||||
|
||||
/// Define stack Symbols based on HighVariables.
|
||||
/// This method is called once at the end of decompilation to create the final set of stack Symbols after
|
||||
/// all data-type propagation has settled. It creates a consistent data-type for all Varnode instances of
|
||||
/// a HighVariable.
|
||||
void ScopeLocal::restructureHigh(void)
|
||||
|
||||
{ // Define stack mapping based on highs
|
||||
clearUnlockedCategory(-1); // Clear out any unlocked entries
|
||||
MapState state(space,getRangeTree(),fd->getFuncProto().getParamRange(),
|
||||
glb->types->getBase(1,TYPE_UNKNOWN)); // Organize list of ranges to insert
|
||||
|
||||
#ifdef OPACTION_DEBUG
|
||||
if (debugon)
|
||||
state.turnOnDebug(glb);
|
||||
#endif
|
||||
state.gatherHighs(*fd); // Gather stack type information from highs
|
||||
state.gatherOpen(*fd);
|
||||
state.gatherSymbols(maptable[space->getIndex()]);
|
||||
bool overlapProblems = restructure(state);
|
||||
|
||||
if (overlapProblems)
|
||||
fd->warningHeader("Could not reconcile some variable overlaps");
|
||||
}
|
||||
|
||||
/// RangeHints from the given collection are merged into a definitive set of Symbols
|
||||
/// for \b this scope. Overlapping or open RangeHints are adjusted to form a disjoint
|
||||
/// cover of the mapped portion of the address space. Names for the disjoint cover elements
|
||||
|
@ -1285,12 +1437,12 @@ void ScopeLocal::fakeInputSymbols(void)
|
|||
int4 size = (endpoint - addr.getOffset()) + 1;
|
||||
Datatype *ct = fd->getArch()->types->getBase(size,TYPE_UNKNOWN);
|
||||
try {
|
||||
addSymbol("",ct,addr,usepoint)->getSymbol();
|
||||
Symbol *sym = addSymbol("",ct,addr,usepoint)->getSymbol();
|
||||
setCategory(sym, Symbol::fake_input, -1);
|
||||
}
|
||||
catch(LowlevelError &err) {
|
||||
fd->warningHeader(err.explain);
|
||||
}
|
||||
// setCategory(sym,0,index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,11 @@ public:
|
|||
open = 1, ///< An array with a (possibly unknown) number of elements
|
||||
endpoint = 2 ///< An (artificial) boundary to the range of bytes getting analyzed
|
||||
};
|
||||
/// \brief Boolean properties for the range
|
||||
enum {
|
||||
typelock = 1, ///< Data-type for the range is locked
|
||||
copy_constant = 2 ///< Only a constant is COPYed into the range
|
||||
};
|
||||
private:
|
||||
uintb start; ///< Starting offset of \b this range of bytes
|
||||
int4 size; ///< Number of bytes in a single element of this range
|
||||
|
@ -109,6 +114,8 @@ public:
|
|||
RangeHint(void) {} ///< Uninitialized constructor
|
||||
RangeHint(uintb st,int4 sz,intb sst,Datatype *ct,uint4 fl,RangeType rt,int4 hi) {
|
||||
start=st; size=sz; sstart=sst; type=ct; flags=fl; rangeType = rt; highind=hi; } ///< Initialized constructor
|
||||
bool isTypeLock(void) const { return ((flags & typelock)!=0); } ///< Is the data-type for \b this range locked
|
||||
bool isConstAbsorbable(const RangeHint *b) const; ///< Can another range by absorbed into \b this as a constant
|
||||
bool reconcile(const RangeHint *b) const;
|
||||
bool contain(const RangeHint *b) const;
|
||||
bool preferred(const RangeHint *b,bool reconcile) const;
|
||||
|
@ -173,7 +180,9 @@ class MapState {
|
|||
AliasChecker checker; ///< A collection of pointer Varnodes into our address space
|
||||
void addGuard(const LoadGuard &guard,OpCode opc,TypeFactory *typeFactory); ///< Add LoadGuard record as a hint to the collection
|
||||
void addRange(uintb st,Datatype *ct,uint4 fl,RangeHint::RangeType rt,int4 hi); ///< Add a hint to the collection
|
||||
void addFixedType(uintb start,Datatype *ct,uint4 flags,TypeFactory *types); ///< Add a fixed reference to a specific data-type
|
||||
void reconcileDatatypes(void); ///< Decide on data-type for RangeHints at the same address
|
||||
static bool isReadActive(Varnode *vn); ///< Is the given Varnode read by an active operation
|
||||
public:
|
||||
#ifdef OPACTION_DEBUG
|
||||
mutable bool debugon;
|
||||
|
@ -188,7 +197,6 @@ public:
|
|||
const vector<uintb> &getAlias(void) { return checker.getAlias(); } ///< Get the list of alias starting offsets
|
||||
void gatherSymbols(const EntryMap *rangemap); ///< Add Symbol information as hints to the collection
|
||||
void gatherVarnodes(const Funcdata &fd); ///< Add stack Varnodes as hints to the collection
|
||||
void gatherHighs(const Funcdata &fd); ///< Add HighVariables as hints to the collection
|
||||
void gatherOpen(const Funcdata &fd); ///< Add pointer references as hints to the collection
|
||||
RangeHint *next(void) { return *iter; } ///< Get the current RangeHint in the collection
|
||||
bool getNext(void) { ++iter; if (iter==maplist.end()) return false; return true; } ///< Advance the iterator, return \b true if another hint is available
|
||||
|
@ -210,6 +218,7 @@ class ScopeLocal : public ScopeInternal {
|
|||
uintb maxParamOffset; ///< Maximum offset of parameter passed (to a called function) on the stack
|
||||
bool stackGrowsNegative; ///< Marked \b true if the stack is considered to \e grow towards smaller offsets
|
||||
bool rangeLocked; ///< True if the subset of addresses \e mapped to \b this scope has been locked
|
||||
bool overlapProblems; ///< True if the last \b restructure had overlapping variable problems
|
||||
bool adjustFit(RangeHint &a) const; ///< Make the given RangeHint fit in the current Symbol map
|
||||
void createEntry(const RangeHint &a); ///< Create a Symbol entry corresponding to the given (fitted) RangeHint
|
||||
bool restructure(MapState &state); ///< Merge hints into a formal Symbol layout of the address space
|
||||
|
@ -225,6 +234,9 @@ public:
|
|||
|
||||
AddrSpace *getSpaceId(void) const { return space; } ///< Get the associated (stack) address space
|
||||
|
||||
/// \brief Return \b true if \b restructure analysis discovered overlapping variables
|
||||
bool hasOverlapProbems(void) const { return overlapProblems; }
|
||||
|
||||
/// \brief Is this a storage location for \e unaffected registers
|
||||
///
|
||||
/// \param vn is the Varnode storing an \e unaffected register
|
||||
|
@ -245,7 +257,6 @@ public:
|
|||
int4 &index,uint4 flags) const;
|
||||
void resetLocalWindow(void); ///< Reset the set of addresses that are considered mapped by the scope to the default
|
||||
void restructureVarnode(bool aliasyes); ///< Layout mapped symbols based on Varnode information
|
||||
void restructureHigh(void); ///< Layout mapped symbols based on HighVariable information
|
||||
SymbolEntry *remapSymbol(Symbol *sym,const Address &addr,const Address &usepoint);
|
||||
SymbolEntry *remapSymbolDynamic(Symbol *sym,uint8 hash,const Address &usepoint);
|
||||
void recoverNameRecommendationsForSymbols(void);
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<decompilertest>
|
||||
<binaryimage arch="x86:LE:64:default:gcc">
|
||||
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||
f30f1efa4883ec28660f6f0540020000
|
||||
b8616c00004889e766894424140f2904
|
||||
24c744241062796520c64424166ce8cd
|
||||
0f0000488d7c240ce8c30f00004883c4
|
||||
28c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100050" readonly="true">
|
||||
f30f1efa48b868656c6c6f2077685548
|
||||
83ec10488d6c240348894424034889ef
|
||||
c744240b69726c00c644240f00e87e0f
|
||||
00004889efc644240f0048b877686972
|
||||
6c6564204889442403c744240b706561
|
||||
73e85a0f00004883c4105dc3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x1000b0" readonly="true">
|
||||
f30f1efa4883ec38660f6f05a0010000
|
||||
4889e70f290424660f6f05a10100000f
|
||||
29442410660f6f05a40100000f294424
|
||||
20e8220f00004883c438c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x1000f0" readonly="true">
|
||||
f30f1efa48b86f6e652074776f204883
|
||||
ec38893c244889e74889442408b84521
|
||||
0000c7442410544852456689442414c6
|
||||
4424160089742404e8e30e00004883c4
|
||||
38c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100140" readonly="true">
|
||||
f30f1efa55b8730000005389fb4883ec
|
||||
28660f6f05370100004889e566894424
|
||||
104889ef0f290424e8930e00004889ef
|
||||
885c2409e8870e00004883c4285b5dc3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100180" readonly="true">
|
||||
f30f1efa4883ec28b92000000048b84d
|
||||
6573736167653a4889042466894c2408
|
||||
85ff7524ba0a0000004889e7c7442409
|
||||
5a45524f668954240de8420e00004883
|
||||
c428c3000000000048b8504f53495449
|
||||
56454889e74889442409b80900000066
|
||||
89442411e8170e00004883c428c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x1001f0" readonly="true">
|
||||
f30f1efa83ff647e3748b852616e6765
|
||||
206578c6051c0e000000488905070e00
|
||||
00b865640000c705000e000063656564
|
||||
668905fd0d0000c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100230" readonly="true">
|
||||
48b876616c6964c2a300488905d70d00
|
||||
00c3
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100250" readonly="true">
|
||||
68656c6c6f20776f726c6400676f6f64
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100260" readonly="true">
|
||||
48000000450000004c0000004c000000
|
||||
4f00000020000000570000004f000000
|
||||
520000004c0000004400000000000000
|
||||
</bytechunk>
|
||||
<bytechunk space="ram" offset="0x100290" readonly="true">
|
||||
736c656570696e6720646f67206c6965
|
||||
</bytechunk>
|
||||
<symbol space="ram" offset="0x100000" name="nullbetween"/>
|
||||
<symbol space="ram" offset="0x100050" name="twomessages"/>
|
||||
<symbol space="ram" offset="0x1000b0" name="utf32message"/>
|
||||
<symbol space="ram" offset="0x1000f0" name="stringInStruct"/>
|
||||
<symbol space="ram" offset="0x100140" name="alterString"/>
|
||||
<symbol space="ram" offset="0x100180" name="conditionalString"/>
|
||||
<symbol space="ram" offset="0x1001f0" name="globalString"/>
|
||||
<symbol space="ram" offset="0x101000" name="customPrint"/>
|
||||
<symbol space="ram" offset="0x101008" name="customPrintWide"/>
|
||||
<symbol space="ram" offset="0x101010" name="receiveStruct"/>
|
||||
</binaryimage>
|
||||
<script>
|
||||
<com>option readonly on</com>
|
||||
<com>parse line struct stringstruct { int4 a; int4 b; char warning[32]; int4 c; };</com>
|
||||
<com>parse line extern void customPrint(char *);</com>
|
||||
<com>parse line extern void customPrintWide(wchar4 *);</com>
|
||||
<com>parse line extern void receiveStruct(stringstruct *);</com>
|
||||
<com>map addr r0x101018 char globstring[32]</com>
|
||||
<com>lo fu nullbetween</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu twomessages</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu utf32message</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu stringInStruct</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu alterString</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu conditionalString</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>lo fu globalString</com>
|
||||
<com>decompile</com>
|
||||
<com>print C</com>
|
||||
<com>quit</com>
|
||||
</script>
|
||||
<stringmatch name="Stack string #1" min="1" max="1">builtin_strncpy\(acStack_28.*"goodbye all",0xb\);</stringmatch>
|
||||
<stringmatch name="Stack string #2" min="1" max="1">builtin_strncpy\(acStack_28,.*"hello world",0xc\);</stringmatch>
|
||||
<stringmatch name="Stack string #3" min="1" max="1">builtin_strncpy\(acStack_15,.*"hello whirl",0xc\);</stringmatch>
|
||||
<stringmatch name="Stack string #4" min="1" max="1">builtin_strncpy\(acStack_15,.*"whirled peas",0xd\);</stringmatch>
|
||||
<stringmatch name="Stack string #5" min="1" max="1">acStack_15\[0xc\] = 0;</stringmatch>
|
||||
<stringmatch name="Stack string #6" min="1" max="1">builtin_wcsncpy\(awStack_38,.*"HELLO WORLD",0xc\);</stringmatch>
|
||||
<stringmatch name="Stack string #7" min="1" max="1">builtin_strncpy\(sStack_38\.warning,"one two THREE!",0xf\);</stringmatch>
|
||||
<stringmatch name="Stack string #8" min="1" max="1">builtin_strncpy\(acStack_38,"sleeping dog lies",0x12\);</stringmatch>
|
||||
<stringmatch name="Stack string #9" min="1" max="1">acStack_38\[9\] = param_1;</stringmatch>
|
||||
<stringmatch name="Stack string #10" min="1" max="1">builtin_strncpy\(acStack_28,"Message: ",9\);</stringmatch>
|
||||
<stringmatch name="Stack string #11" min="1" max="1">builtin_strncpy\(acStack_28 \+ 9,"ZERO\\n",6\);</stringmatch>
|
||||
<stringmatch name="Stack string #12" min="1" max="1">builtin_strncpy\(acStack_28 \+ 9,"POSITIVE\\t",10\);</stringmatch>
|
||||
<stringmatch name="Stack string #13" min="1" max="1">builtin_strncpy\(globstring,"Range exceeded",0xf\);</stringmatch>
|
||||
<stringmatch name="Stack string #14" min="1" max="1">builtin_strncpy\(globstring,"valid£",8\);</stringmatch>
|
||||
</decompilertest>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user