mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Compare commits
34 commits
c18c7a8f50
...
970c8fbdd5
Author | SHA1 | Date | |
---|---|---|---|
![]() |
970c8fbdd5 | ||
![]() |
bc6701a24d | ||
![]() |
e330fc2077 | ||
![]() |
6bba2ea685 | ||
![]() |
2993827690 | ||
![]() |
59bcbba8cf | ||
![]() |
272314d7af | ||
![]() |
eadb23c45a | ||
![]() |
89b6306aea | ||
![]() |
657ec39201 | ||
![]() |
df90de2367 | ||
![]() |
73bdee2546 | ||
![]() |
5fbb052b28 | ||
![]() |
4f164e68a6 | ||
![]() |
6d588dab00 | ||
![]() |
f781273d49 | ||
![]() |
dd6561807b | ||
![]() |
58ca21b8db | ||
![]() |
e5684417c0 | ||
![]() |
fca6104af0 | ||
![]() |
b5e91aeac0 | ||
![]() |
0613d364fc | ||
![]() |
deddc84205 | ||
![]() |
20314d087d | ||
![]() |
0645a51808 | ||
![]() |
221939c0a9 | ||
![]() |
5769aec3f3 | ||
![]() |
998bea8c50 | ||
![]() |
457d342887 | ||
![]() |
93f92fa879 | ||
![]() |
8272662627 | ||
![]() |
f5572ede99 | ||
![]() |
48754fb98d | ||
![]() |
d538513428 |
164 changed files with 6917 additions and 2170 deletions
|
@ -81,11 +81,11 @@ function Compute-Lldb-Usermode-Args {
|
||||||
function Compute-Lldb-Platform-Args {
|
function Compute-Lldb-Platform-Args {
|
||||||
param($TargetImage, $TargetType, $TargetUrl, $RmiAddress)
|
param($TargetImage, $TargetType, $TargetUrl, $RmiAddress)
|
||||||
|
|
||||||
$argslist = @("`"$Env:OPT_LLDB_PATH`"")
|
$arglist = @("`"$Env:OPT_LLDB_PATH`"")
|
||||||
Add-Lldb-Init-Args -ArgList ([ref]$arglist)
|
Add-Lldb-Init-Args -ArgList ([ref]$arglist)
|
||||||
$argslist+=("-o", "`"platform select '$TargetType'`"")
|
$arglist+=("-o", "`"platform select '$TargetType'`"")
|
||||||
$argslist+=("-o", "`"platform connect '$TargetUrl'`"")
|
$arglist+=("-o", "`"platform connect '$TargetUrl'`"")
|
||||||
Add-Lldb-Image-And-Args -ArgList ([ref]$arglistt) -TargetImage $TargetImage -TargetArgs $Env:OPT_TARGET_ARGS
|
Add-Lldb-Image-And-Args -ArgList ([ref]$arglist) -TargetImage $TargetImage -TargetArgs $Env:OPT_TARGET_ARGS
|
||||||
Add-Lldb-Connect-And-Sync -ArgList ([ref]$arglist) -Address $RmiAddress
|
Add-Lldb-Connect-And-Sync -ArgList ([ref]$arglist) -Address $RmiAddress
|
||||||
Add-Lldb-Start-If-Image -ArgList ([ref]$arglist) -TargetImage $TargetImage
|
Add-Lldb-Start-If-Image -ArgList ([ref]$arglist) -TargetImage $TargetImage
|
||||||
Add-Lldb-Tail-Args -ArgList ([ref]$arglist)
|
Add-Lldb-Tail-Args -ArgList ([ref]$arglist)
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
Module.manifest||GHIDRA||||END|
|
Module.manifest||GHIDRA||||END|
|
||||||
README.md||GHIDRA||||END|
|
README.md||GHIDRA||||END|
|
||||||
data/symbolic.summary.z3.theme.properties||GHIDRA||||END|
|
data/symbolic.summary.z3.theme.properties||GHIDRA||||END|
|
||||||
|
extension.properties||GHIDRA||||END|
|
||||||
|
|
5
Ghidra/Extensions/SymbolicSummaryZ3/extension.properties
Normal file
5
Ghidra/Extensions/SymbolicSummaryZ3/extension.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name=Symbolic Summarizer (Z3)
|
||||||
|
description=Plugin for emulating with Z3 symbols and displaying a summary
|
||||||
|
author=Ghidra Team et al
|
||||||
|
createdOn=6/26/2025
|
||||||
|
version=@extversion@
|
|
@ -184,6 +184,10 @@ public class ResolveX86orX64LinuxSyscallsScript extends GhidraScript {
|
||||||
funcName = syscallNumbersToNames.get(offset);
|
funcName = syscallNumbersToNames.get(offset);
|
||||||
}
|
}
|
||||||
callee = createFunction(callTarget, funcName);
|
callee = createFunction(callTarget, funcName);
|
||||||
|
if (callee == null) {
|
||||||
|
Msg.warn(this, "Unable to create function at "+callTarget);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
callee.setCallingConvention(callingConvention);
|
callee.setCallingConvention(callingConvention);
|
||||||
|
|
||||||
//check if the function name is one of the non-returning syscalls
|
//check if the function name is one of the non-returning syscalls
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.analysis;
|
package ghidra.app.plugin.core.analysis;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
public interface AutoAnalysisManagerListener {
|
public interface AutoAnalysisManagerListener {
|
||||||
|
|
||||||
public void analysisEnded(AutoAnalysisManager manager, boolean isCancelled);
|
public void analysisEnded(AutoAnalysisManager manager, boolean isCancelled);
|
||||||
|
|
|
@ -17,7 +17,7 @@ package ghidra.app.plugin.core.console;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.PrintWriter;
|
import java.io.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.text.*;
|
import javax.swing.text.*;
|
||||||
|
@ -29,6 +29,7 @@ import docking.widgets.FindDialog;
|
||||||
import docking.widgets.TextComponentSearcher;
|
import docking.widgets.TextComponentSearcher;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import generic.theme.Gui;
|
import generic.theme.Gui;
|
||||||
|
import ghidra.app.script.DecoratingPrintWriter;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.framework.main.ConsoleTextPane;
|
import ghidra.framework.main.ConsoleTextPane;
|
||||||
|
@ -95,8 +96,8 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
stderr = new PrintWriter(new ConsoleWriter(this, true));
|
stderr = new ConsolePrintWriter(true);
|
||||||
stdin = new PrintWriter(new ConsoleWriter(this, false));
|
stdin = new ConsolePrintWriter(false);
|
||||||
|
|
||||||
/* call this before build() -- we get our Font here */
|
/* call this before build() -- we get our Font here */
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
@ -230,6 +231,11 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
||||||
textPane.addPartialMessage(msg);
|
textPane.addPartialMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void print(String msg, Color c) {
|
||||||
|
checkVisible();
|
||||||
|
textPane.addPartialMessage(msg, c);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void printError(String errmsg) {
|
public void printError(String errmsg) {
|
||||||
checkVisible();
|
checkVisible();
|
||||||
|
@ -330,6 +336,84 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//=================================================================================================
|
//=================================================================================================
|
||||||
|
|
||||||
|
private class ConsolePrintWriter extends DecoratingPrintWriter {
|
||||||
|
|
||||||
|
private ColoringConsoleWriter writer;
|
||||||
|
|
||||||
|
ConsolePrintWriter(boolean error) {
|
||||||
|
this(new ColoringConsoleWriter(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConsolePrintWriter(ColoringConsoleWriter writer) {
|
||||||
|
super(writer);
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(String s, Color c) {
|
||||||
|
try {
|
||||||
|
writer.setColor(c);
|
||||||
|
print(s);
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.setColor(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(String s, Color c) {
|
||||||
|
try {
|
||||||
|
writer.setColor(c);
|
||||||
|
print(s);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.setColor(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ColoringConsoleWriter extends Writer {
|
||||||
|
|
||||||
|
private Color color;
|
||||||
|
private boolean error;
|
||||||
|
|
||||||
|
public ColoringConsoleWriter(boolean error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(Color color) {
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||||
|
String s = new String(cbuf, off, len);
|
||||||
|
if (error) {
|
||||||
|
printError(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == null) {
|
||||||
|
print(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(s, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
clearMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class GoToMouseListener extends MouseAdapter {
|
private class GoToMouseListener extends MouseAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
/* ###
|
|
||||||
* 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.
|
|
||||||
* 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.console;
|
|
||||||
|
|
||||||
import ghidra.app.services.ConsoleService;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
class ConsoleWriter extends Writer {
|
|
||||||
private ConsoleService console;
|
|
||||||
private boolean error;
|
|
||||||
|
|
||||||
ConsoleWriter(ConsoleService console, boolean error) {
|
|
||||||
super();
|
|
||||||
this.console = console;
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.io.Writer#close()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
console.clearMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.io.Writer#flush()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void flush() throws IOException {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.io.Writer#write(char[], int, int)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
|
||||||
String str = new String(cbuf, off, len);
|
|
||||||
if (error) {
|
|
||||||
console.printError(str);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.print(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -35,7 +35,7 @@ import ghidra.app.plugin.core.console.CodeCompletion;
|
||||||
*/
|
*/
|
||||||
public class CodeCompletionWindow extends JDialog {
|
public class CodeCompletionWindow extends JDialog {
|
||||||
|
|
||||||
private static final String FONT_ID = "font.plugin.terminal.completion.list";
|
static final String FONT_ID = "font.plugin.terminal.completion.list";
|
||||||
|
|
||||||
protected final InterpreterPanel console;
|
protected final InterpreterPanel console;
|
||||||
protected final JTextPane outputTextField;
|
protected final JTextPane outputTextField;
|
||||||
|
@ -394,6 +394,10 @@ class CodeCompletionListSelectionModel extends DefaultListSelectionModel {
|
||||||
*/
|
*/
|
||||||
class CodeCompletionListCellRenderer extends GListCellRenderer<CodeCompletion> {
|
class CodeCompletionListCellRenderer extends GListCellRenderer<CodeCompletion> {
|
||||||
|
|
||||||
|
CodeCompletionListCellRenderer() {
|
||||||
|
setBaseFontId(CodeCompletionWindow.FONT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getItemText(CodeCompletion value) {
|
protected String getItemText(CodeCompletion value) {
|
||||||
return value.getDescription();
|
return value.getDescription();
|
||||||
|
@ -421,7 +425,6 @@ class CodeCompletionListCellRenderer extends GListCellRenderer<CodeCompletion> {
|
||||||
}
|
}
|
||||||
|
|
||||||
component.setEnabled(list.isEnabled());
|
component.setEnabled(list.isEnabled());
|
||||||
component.setFont(list.getFont());
|
|
||||||
component.setComponentOrientation(list.getComponentOrientation());
|
component.setComponentOrientation(list.getComponentOrientation());
|
||||||
Border border = null;
|
Border border = null;
|
||||||
if (cellHasFocus) {
|
if (cellHasFocus) {
|
||||||
|
|
|
@ -26,11 +26,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.text.*;
|
import javax.swing.text.*;
|
||||||
|
|
||||||
|
import org.apache.commons.io.output.WriterOutputStream;
|
||||||
|
|
||||||
import docking.DockingUtils;
|
import docking.DockingUtils;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
import generic.theme.*;
|
import generic.theme.*;
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
import ghidra.app.plugin.core.console.CodeCompletion;
|
import ghidra.app.plugin.core.console.CodeCompletion;
|
||||||
|
import ghidra.app.script.DecoratingPrintWriter;
|
||||||
import ghidra.framework.options.OptionsChangeListener;
|
import ghidra.framework.options.OptionsChangeListener;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
@ -69,8 +72,12 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
/* junit */ IPStdin stdin;
|
/* junit */ IPStdin stdin;
|
||||||
private OutputStream stdout;
|
private OutputStream stdout;
|
||||||
private OutputStream stderr;
|
private OutputStream stderr;
|
||||||
private PrintWriter outWriter;
|
private InterpreterPrintWriter outWriter;
|
||||||
private PrintWriter errWriter;
|
private InterpreterPrintWriter errWriter;
|
||||||
|
|
||||||
|
private AnsiRenderer stdErrRenderer = new AnsiRenderer();
|
||||||
|
private AnsiRenderer stdInRenderer = new AnsiRenderer();
|
||||||
|
private AnsiRenderer stdOutRenderer = new AnsiRenderer();
|
||||||
|
|
||||||
private SimpleAttributeSet STDOUT_SET;
|
private SimpleAttributeSet STDOUT_SET;
|
||||||
private SimpleAttributeSet STDERR_SET;
|
private SimpleAttributeSet STDERR_SET;
|
||||||
|
@ -129,11 +136,12 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
outputScrollPane.setFocusable(false);
|
outputScrollPane.setFocusable(false);
|
||||||
promptTextPane.setFocusable(false);
|
promptTextPane.setFocusable(false);
|
||||||
|
|
||||||
|
outWriter = new InterpreterPrintWriter(TextType.STDOUT);
|
||||||
|
errWriter = new InterpreterPrintWriter(TextType.STDERR);
|
||||||
|
|
||||||
stdin = new IPStdin();
|
stdin = new IPStdin();
|
||||||
stdout = new IPOut(TextType.STDOUT);
|
stdout = outWriter.asOutputStream();
|
||||||
stderr = new IPOut(TextType.STDERR);
|
stderr = errWriter.asOutputStream();
|
||||||
outWriter = new PrintWriter(stdout, true);
|
|
||||||
errWriter = new PrintWriter(stderr, true);
|
|
||||||
|
|
||||||
outputTextPane.setEditable(false);
|
outputTextPane.setEditable(false);
|
||||||
promptTextPane.setEditable(false);
|
promptTextPane.setEditable(false);
|
||||||
|
@ -270,7 +278,6 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
private void updateFontAttributes(Font font) {
|
private void updateFontAttributes(Font font) {
|
||||||
Font boldFont = font.deriveFont(Font.BOLD);
|
Font boldFont = font.deriveFont(Font.BOLD);
|
||||||
|
|
||||||
STDOUT_SET = new GAttributes(font, NORMAL_COLOR);
|
|
||||||
STDOUT_SET = new GAttributes(font, NORMAL_COLOR);
|
STDOUT_SET = new GAttributes(font, NORMAL_COLOR);
|
||||||
STDERR_SET = new GAttributes(font, ERROR_COLOR);
|
STDERR_SET = new GAttributes(font, ERROR_COLOR);
|
||||||
STDIN_SET = new GAttributes(boldFont, NORMAL_COLOR);
|
STDIN_SET = new GAttributes(boldFont, NORMAL_COLOR);
|
||||||
|
@ -410,11 +417,7 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
outputTextPane.setCaretPosition(Math.max(0, outputTextPane.getDocument().getLength()));
|
outputTextPane.setCaretPosition(Math.max(0, outputTextPane.getDocument().getLength()));
|
||||||
}
|
}
|
||||||
|
|
||||||
AnsiRenderer stdErrRenderer = new AnsiRenderer();
|
private void addText(String text, TextType type) {
|
||||||
AnsiRenderer stdInRenderer = new AnsiRenderer();
|
|
||||||
AnsiRenderer stdOutRenderer = new AnsiRenderer();
|
|
||||||
|
|
||||||
void addText(String text, TextType type) {
|
|
||||||
SimpleAttributeSet attributes;
|
SimpleAttributeSet attributes;
|
||||||
AnsiRenderer renderer;
|
AnsiRenderer renderer;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -438,29 +441,23 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
repositionScrollpane();
|
repositionScrollpane();
|
||||||
}
|
}
|
||||||
catch (BadLocationException e) {
|
catch (BadLocationException e) {
|
||||||
Msg.error(this, "internal document positioning error", e);
|
// shouldn't happen
|
||||||
|
Msg.error(this, "Document positioning error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IPOut extends OutputStream {
|
private void addText(String text, Color c) {
|
||||||
TextType type;
|
|
||||||
byte[] buffer = new byte[1];
|
|
||||||
|
|
||||||
IPOut(TextType type) {
|
SimpleAttributeSet attributes = new GAttributes(getFont(), c);
|
||||||
this.type = type;
|
|
||||||
|
try {
|
||||||
|
StyledDocument document = outputTextPane.getStyledDocument();
|
||||||
|
stdOutRenderer.renderString(document, text, attributes);
|
||||||
|
repositionScrollpane();
|
||||||
}
|
}
|
||||||
|
catch (BadLocationException e) {
|
||||||
@Override
|
// shouldn't happen
|
||||||
public void write(int b) throws IOException {
|
Msg.error(this, "Document positioning error", e);
|
||||||
buffer[0] = (byte) b;
|
|
||||||
String text = new String(buffer);
|
|
||||||
addText(text, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
|
||||||
String text = new String(b, off, len);
|
|
||||||
addText(text, type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,6 +562,102 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
private class InterpreterPrintWriter extends DecoratingPrintWriter {
|
||||||
|
|
||||||
|
private InterpreterConsoleWriter writer;
|
||||||
|
|
||||||
|
InterpreterPrintWriter(TextType type) {
|
||||||
|
this(new InterpreterConsoleWriter(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InterpreterPrintWriter(InterpreterConsoleWriter writer) {
|
||||||
|
super(writer);
|
||||||
|
this.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream asOutputStream() {
|
||||||
|
try {
|
||||||
|
return WriterOutputStream.builder().setWriter(writer).getOutputStream();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.error(this, "Unable to create output stream", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void println(String s, Color c) {
|
||||||
|
try {
|
||||||
|
writer.setColor(c);
|
||||||
|
print(s);
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.setColor(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(String s, Color c) {
|
||||||
|
try {
|
||||||
|
writer.setColor(c);
|
||||||
|
print(s);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.setColor(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InterpreterConsoleWriter extends Writer {
|
||||||
|
|
||||||
|
private Color color;
|
||||||
|
|
||||||
|
TextType type;
|
||||||
|
byte[] buffer = new byte[1];
|
||||||
|
|
||||||
|
public InterpreterConsoleWriter(TextType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(Color color) {
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
buffer[0] = (byte) b;
|
||||||
|
String text = new String(buffer);
|
||||||
|
if (color != null) {
|
||||||
|
addText(text, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addText(text, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(char[] b, int off, int len) throws IOException {
|
||||||
|
String text = new String(b, off, len);
|
||||||
|
if (color != null) {
|
||||||
|
addText(text, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addText(text, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link InputStream} that has as its source text strings being pushed into
|
* An {@link InputStream} that has as its source text strings being pushed into
|
||||||
* it by a thread, and being read by another thread.
|
* it by a thread, and being read by another thread.
|
||||||
|
|
|
@ -487,7 +487,6 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
|
||||||
// this will overwrite any changes--be sure to resolve that before calling this method!
|
// this will overwrite any changes--be sure to resolve that before calling this method!
|
||||||
try {
|
try {
|
||||||
loadScript(scriptSourceFile);
|
loadScript(scriptSourceFile);
|
||||||
fileHash = MD5Utilities.getMD5Hash(scriptSourceFile.getFile(false));
|
|
||||||
clearChanges();
|
clearChanges();
|
||||||
refreshAction();
|
refreshAction();
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,9 @@ public class SymbolNode extends SymbolTreeNode {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (o == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (getClass() != o.getClass()) {
|
if (getClass() != o.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* ###
|
||||||
|
* 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.script;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A print writer that allows clients to specify the text color.
|
||||||
|
*/
|
||||||
|
public abstract class DecoratingPrintWriter extends PrintWriter {
|
||||||
|
|
||||||
|
public DecoratingPrintWriter(Writer out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a line of text with the given color.
|
||||||
|
* @param s the text
|
||||||
|
* @param c the color
|
||||||
|
*/
|
||||||
|
public abstract void println(String s, Color c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print text with the given color.
|
||||||
|
* @param s the text
|
||||||
|
* @param c the color
|
||||||
|
*/
|
||||||
|
public abstract void print(String s, Color c);
|
||||||
|
}
|
|
@ -1042,6 +1042,58 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the {@link #decorateOutput optionally} {@link #decorate(String) decorated} message
|
||||||
|
* followed by a line feed to this script's {@code stdout} {@link PrintWriter}, which is set by
|
||||||
|
* {@link #set(GhidraState, ScriptControls)}.
|
||||||
|
* <p>
|
||||||
|
* Additionally, the always {@link #decorate(String) decorated} message is written to Ghidra's
|
||||||
|
* log.
|
||||||
|
*
|
||||||
|
* @param message the message to print
|
||||||
|
* @param color the color for the text
|
||||||
|
*/
|
||||||
|
public void println(String message, Color color) {
|
||||||
|
|
||||||
|
String decoratedMessage = decorate(message);
|
||||||
|
|
||||||
|
Msg.info(GhidraScript.class, new ScriptMessage(decoratedMessage));
|
||||||
|
|
||||||
|
if (writer instanceof DecoratingPrintWriter scriptWriter) {
|
||||||
|
scriptWriter.println(decorateOutput ? decoratedMessage : message, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writer != null) {
|
||||||
|
writer.println(decorateOutput ? decoratedMessage : message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the undecorated message with no newline to this script's {@code stdout}
|
||||||
|
* {@link PrintWriter}, which is set by {@link #set(GhidraState, ScriptControls)}.
|
||||||
|
* <p>
|
||||||
|
* Additionally, the undecorated message is written to Ghidra's log.
|
||||||
|
*
|
||||||
|
* @param message the message to print
|
||||||
|
* @param color the color for the text
|
||||||
|
*/
|
||||||
|
public void print(String message, Color color) {
|
||||||
|
|
||||||
|
String decoratedMessage = decorate(message);
|
||||||
|
|
||||||
|
Msg.info(GhidraScript.class, new ScriptMessage(decoratedMessage));
|
||||||
|
|
||||||
|
if (writer instanceof DecoratingPrintWriter scriptWriter) {
|
||||||
|
scriptWriter.print(decorateOutput ? decoratedMessage : message, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writer != null) {
|
||||||
|
writer.print(decorateOutput ? decoratedMessage : message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the undecorated {@link java.util.Formatter formatted message} to this script's
|
* Prints the undecorated {@link java.util.Formatter formatted message} to this script's
|
||||||
* {@code stdout} {@link PrintWriter}, which is set by
|
* {@code stdout} {@link PrintWriter}, which is set by
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class ScriptControls {
|
||||||
* @param monitor A cancellable monitor
|
* @param monitor A cancellable monitor
|
||||||
*/
|
*/
|
||||||
public ScriptControls(InterpreterConsole console, TaskMonitor monitor) {
|
public ScriptControls(InterpreterConsole console, TaskMonitor monitor) {
|
||||||
this(console.getStdOut(), console.getStdErr(), monitor);
|
this(console.getOutWriter(), console.getErrWriter(), monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.services;
|
package ghidra.app.services;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.console.ConsolePlugin;
|
import ghidra.app.plugin.core.console.ConsolePlugin;
|
||||||
import ghidra.framework.plugintool.ServiceInfo;
|
import ghidra.framework.plugintool.ServiceInfo;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic console interface allowing any plugin to print
|
* Generic console interface allowing any plugin to print
|
||||||
* messages to console window.
|
* messages to console window.
|
||||||
|
@ -109,8 +109,6 @@ public interface ConsoleService {
|
||||||
* please throw {@link UnsupportedOperationException}.
|
* please throw {@link UnsupportedOperationException}.
|
||||||
*
|
*
|
||||||
* @return number of characters >= 0
|
* @return number of characters >= 0
|
||||||
*
|
|
||||||
* @throws UnsupportedOperationException
|
|
||||||
*/
|
*/
|
||||||
public int getTextLength();
|
public int getTextLength();
|
||||||
|
|
||||||
|
@ -128,8 +126,6 @@ public interface ConsoleService {
|
||||||
* @param length the length of the desired string >= 0
|
* @param length the length of the desired string >= 0
|
||||||
*
|
*
|
||||||
* @return the text, in a String of length >= 0
|
* @return the text, in a String of length >= 0
|
||||||
*
|
|
||||||
* @throws UnsupportedOperationException
|
|
||||||
*/
|
*/
|
||||||
public String getText(int offset, int length);
|
public String getText(int offset, int length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1606,14 +1606,13 @@ public class ElfHeader implements StructConverter {
|
||||||
* @return the section header that contains the address
|
* @return the section header that contains the address
|
||||||
*/
|
*/
|
||||||
public ElfSectionHeader getSectionLoadHeaderContaining(long address) {
|
public ElfSectionHeader getSectionLoadHeaderContaining(long address) {
|
||||||
// FIXME: verify
|
|
||||||
for (ElfSectionHeader sectionHeader : sectionHeaders) {
|
for (ElfSectionHeader sectionHeader : sectionHeaders) {
|
||||||
if (!sectionHeader.isAlloc()) {
|
if (!sectionHeader.isAlloc()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
long start = sectionHeader.getAddress();
|
long start = sectionHeader.getAddress();
|
||||||
long end = start + sectionHeader.getSize();
|
long end = start + sectionHeader.getSize();
|
||||||
if (start <= address && address <= end) {
|
if (start <= address && address < end) {
|
||||||
return sectionHeader;
|
return sectionHeader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,8 +114,9 @@ class MemorySearchOptionsPanel extends JPanel {
|
||||||
innerPanel.add(label);
|
innerPanel.add(label);
|
||||||
|
|
||||||
Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 };
|
Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 };
|
||||||
|
int decimalByteSize = model.getDecimalByteSize();
|
||||||
decimalByteSizeCombo = new GComboBox<>(decimalSizes);
|
decimalByteSizeCombo = new GComboBox<>(decimalSizes);
|
||||||
decimalByteSizeCombo.setSelectedItem(4);
|
decimalByteSizeCombo.setSelectedItem(decimalByteSize);
|
||||||
decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged);
|
decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged);
|
||||||
decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes");
|
decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes");
|
||||||
innerPanel.add(decimalByteSizeCombo);
|
innerPanel.add(decimalByteSizeCombo);
|
||||||
|
@ -126,6 +127,7 @@ class MemorySearchOptionsPanel extends JPanel {
|
||||||
"Sets whether decimal values should be interpreted as unsigned values");
|
"Sets whether decimal values should be interpreted as unsigned values");
|
||||||
decimalUnsignedCheckbox.addActionListener(
|
decimalUnsignedCheckbox.addActionListener(
|
||||||
e -> model.setDecimalUnsigned(decimalUnsignedCheckbox.isSelected()));
|
e -> model.setDecimalUnsigned(decimalUnsignedCheckbox.isSelected()));
|
||||||
|
decimalUnsignedCheckbox.setSelected(model.isDecimalUnsigned());
|
||||||
|
|
||||||
panel.add(decimalUnsignedCheckbox);
|
panel.add(decimalUnsignedCheckbox);
|
||||||
return panel;
|
return panel;
|
||||||
|
@ -205,9 +207,10 @@ class MemorySearchOptionsPanel extends JPanel {
|
||||||
Charset[] supportedCharsets =
|
Charset[] supportedCharsets =
|
||||||
{ StandardCharsets.US_ASCII, StandardCharsets.UTF_8, StandardCharsets.UTF_16 };
|
{ StandardCharsets.US_ASCII, StandardCharsets.UTF_8, StandardCharsets.UTF_16 };
|
||||||
|
|
||||||
|
Charset charSet = model.getStringCharset();
|
||||||
charsetCombo = new GComboBox<>(supportedCharsets);
|
charsetCombo = new GComboBox<>(supportedCharsets);
|
||||||
charsetCombo.setName("Encoding Options");
|
charsetCombo.setName("Encoding Options");
|
||||||
charsetCombo.setSelectedIndex(0);
|
charsetCombo.setSelectedItem(charSet);
|
||||||
charsetCombo.addItemListener(this::encodingComboChanged);
|
charsetCombo.addItemListener(this::encodingComboChanged);
|
||||||
charsetCombo.setToolTipText("Character encoding for translating strings to bytes");
|
charsetCombo.setToolTipText("Character encoding for translating strings to bytes");
|
||||||
|
|
||||||
|
|
|
@ -27,14 +27,23 @@ import ghidra.features.base.memsearch.gui.SearchSettings;
|
||||||
*/
|
*/
|
||||||
public abstract class ByteMatcher {
|
public abstract class ByteMatcher {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
private final String input;
|
private final String input;
|
||||||
private final SearchSettings settings;
|
private final SearchSettings settings;
|
||||||
|
|
||||||
protected ByteMatcher(String input, SearchSettings settings) {
|
protected ByteMatcher(String name, String input, SearchSettings settings) {
|
||||||
|
this.name = name;
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the name of this byte matcher.}
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original input text that generated this ByteMatacher.
|
* Returns the original input text that generated this ByteMatacher.
|
||||||
* @return the original input text that generated this BytesMatcher
|
* @return the original input text that generated this BytesMatcher
|
||||||
|
@ -120,7 +129,10 @@ public abstract class ByteMatcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record class to contain a match specification.
|
* Record class to contain a match specification.
|
||||||
|
* @param start the offset in the buffer where the match starts
|
||||||
|
* @param length the length of the match
|
||||||
|
* @param matcher the matcher the found the match
|
||||||
*/
|
*/
|
||||||
public record ByteMatch(int start, int length) {}
|
public record ByteMatch(int start, int length, ByteMatcher matcher) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.memsearch.matcher;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.features.base.memsearch.bytesequence.ExtendedByteSequence;
|
||||||
|
import ghidra.features.base.memsearch.gui.SearchSettings;
|
||||||
|
import ghidra.features.base.memsearch.searcher.MemorySearcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ByteMatcher that searches an input sequence for matches from multiple patterns. This is
|
||||||
|
* useful for using with the {@link MemorySearcher} so that multiple patterns can be searched with
|
||||||
|
* only one pass through memory, thus paying the memory I/O costs only once. The resulting matches
|
||||||
|
* will contain the sub ByteMatcher that matched so that it is easy to know which of the multiple
|
||||||
|
* patterns matched.
|
||||||
|
*/
|
||||||
|
public class CombinedByteMatcher extends ByteMatcher {
|
||||||
|
|
||||||
|
private List<ByteMatcher> matchers;
|
||||||
|
|
||||||
|
public CombinedByteMatcher(List<ByteMatcher> matchers, SearchSettings settings) {
|
||||||
|
super("Multi-Pattern Matcher", null, settings);
|
||||||
|
this.matchers = matchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<ByteMatch> match(ExtendedByteSequence bytes) {
|
||||||
|
return new MultiMatcherIterator(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolTip() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MultiMatcherIterator implements Iterable<ByteMatch>, Iterator<ByteMatch> {
|
||||||
|
|
||||||
|
private Iterator<ByteMatcher> matcherIterator;
|
||||||
|
private Iterator<ByteMatch> currentMatchIterator;
|
||||||
|
private ExtendedByteSequence bytes;
|
||||||
|
|
||||||
|
MultiMatcherIterator(ExtendedByteSequence bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
matcherIterator = matchers.iterator();
|
||||||
|
currentMatchIterator = getNextMatchIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
while (currentMatchIterator != null && !currentMatchIterator.hasNext()) {
|
||||||
|
currentMatchIterator = getNextMatchIterator();
|
||||||
|
}
|
||||||
|
return currentMatchIterator != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterator<ByteMatch> getNextMatchIterator() {
|
||||||
|
if (matcherIterator.hasNext()) {
|
||||||
|
ByteMatcher matcher = matcherIterator.next();
|
||||||
|
return matcher.match(bytes).iterator();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteMatch next() {
|
||||||
|
if (hasNext()) {
|
||||||
|
return currentMatchIterator.next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ByteMatch> iterator() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ public class InvalidByteMatcher extends ByteMatcher {
|
||||||
* a negative number.
|
* a negative number.
|
||||||
*/
|
*/
|
||||||
public InvalidByteMatcher(String errorMessage, boolean isValidInput) {
|
public InvalidByteMatcher(String errorMessage, boolean isValidInput) {
|
||||||
super(null, null);
|
super("Invalid", null, null);
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
this.isValidInput = isValidInput;
|
this.isValidInput = isValidInput;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class MaskedByteSequenceByteMatcher extends ByteMatcher {
|
||||||
*/
|
*/
|
||||||
public MaskedByteSequenceByteMatcher(String input, byte[] bytes, byte[] masks,
|
public MaskedByteSequenceByteMatcher(String input, byte[] bytes, byte[] masks,
|
||||||
SearchSettings settings) {
|
SearchSettings settings) {
|
||||||
super(input, settings);
|
super("Masked Byte Sequence Matcher", input, settings);
|
||||||
|
|
||||||
if (masks == null) {
|
if (masks == null) {
|
||||||
masks = new byte[bytes.length];
|
masks = new byte[bytes.length];
|
||||||
|
@ -145,7 +145,8 @@ public class MaskedByteSequenceByteMatcher extends ByteMatcher {
|
||||||
while (nextPossibleStart >= 0) {
|
while (nextPossibleStart >= 0) {
|
||||||
startIndex = nextPossibleStart + 1;
|
startIndex = nextPossibleStart + 1;
|
||||||
if (isValidMatch(nextPossibleStart)) {
|
if (isValidMatch(nextPossibleStart)) {
|
||||||
return new ByteMatch(nextPossibleStart, searchBytes.length);
|
return new ByteMatch(nextPossibleStart, searchBytes.length,
|
||||||
|
MaskedByteSequenceByteMatcher.this);
|
||||||
}
|
}
|
||||||
nextPossibleStart = findNextPossibleStart(startIndex);
|
nextPossibleStart = findNextPossibleStart(startIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,11 @@ public class RegExByteMatcher extends ByteMatcher {
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
|
|
||||||
public RegExByteMatcher(String input, SearchSettings settings) {
|
public RegExByteMatcher(String input, SearchSettings settings) {
|
||||||
super(input, settings);
|
this("Regex Matcher", input, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegExByteMatcher(String name, String input, SearchSettings settings) {
|
||||||
|
super(name, input, settings);
|
||||||
// without DOTALL mode, bytes that match line terminator characters will cause
|
// without DOTALL mode, bytes that match line terminator characters will cause
|
||||||
// the regular expression pattern to not match.
|
// the regular expression pattern to not match.
|
||||||
this.pattern = Pattern.compile(input, Pattern.DOTALL);
|
this.pattern = Pattern.compile(input, Pattern.DOTALL);
|
||||||
|
@ -133,7 +137,7 @@ public class RegExByteMatcher extends ByteMatcher {
|
||||||
if (start >= byteSequence.getLength()) {
|
if (start >= byteSequence.getLength()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ByteMatch(start, end - start);
|
return new ByteMatch(start, end - start, RegExByteMatcher.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ public class MemorySearcher {
|
||||||
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
||||||
Address address = searchBytes.getAddress(byteMatch.start());
|
Address address = searchBytes.getAddress(byteMatch.start());
|
||||||
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
||||||
MemoryMatch match = new MemoryMatch(address, bytes, matcher);
|
MemoryMatch match = new MemoryMatch(address, bytes, byteMatch.matcher());
|
||||||
if (filter.test(match)) {
|
if (filter.test(match)) {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ public class MemorySearcher {
|
||||||
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
||||||
Address address = searchBytes.getAddress(byteMatch.start());
|
Address address = searchBytes.getAddress(byteMatch.start());
|
||||||
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
||||||
MemoryMatch match = new MemoryMatch(address, bytes, matcher);
|
MemoryMatch match = new MemoryMatch(address, bytes, byteMatch.matcher());
|
||||||
if (filter.test(match)) {
|
if (filter.test(match)) {
|
||||||
last = match;
|
last = match;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ public class MemorySearcher {
|
||||||
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
for (ByteMatch byteMatch : matcher.match(searchSequence)) {
|
||||||
Address address = searchBytes.getAddress(byteMatch.start());
|
Address address = searchBytes.getAddress(byteMatch.start());
|
||||||
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
byte[] bytes = searchSequence.getBytes(byteMatch.start(), byteMatch.length());
|
||||||
MemoryMatch match = new MemoryMatch(address, bytes, matcher);
|
MemoryMatch match = new MemoryMatch(address, bytes, byteMatch.matcher());
|
||||||
if (filter.test(match)) {
|
if (filter.test(match)) {
|
||||||
if (accumulator.size() >= searchLimit) {
|
if (accumulator.size() >= searchLimit) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.main;
|
package ghidra.framework.main;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
import javax.swing.text.*;
|
import javax.swing.text.*;
|
||||||
|
@ -83,6 +85,10 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
||||||
doAddMessage(new MessageWrapper(message));
|
doAddMessage(new MessageWrapper(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addPartialMessage(String message, Color c) {
|
||||||
|
doAddMessage(new MessageWrapper(message, getFont(), c));
|
||||||
|
}
|
||||||
|
|
||||||
public void addErrorMessage(String message) {
|
public void addErrorMessage(String message) {
|
||||||
doAddMessage(new ErrorMessage(message));
|
doAddMessage(new ErrorMessage(message));
|
||||||
}
|
}
|
||||||
|
@ -280,15 +286,21 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
private static class MessageWrapper {
|
private static class MessageWrapper {
|
||||||
private final StringBuilder message;
|
protected final StringBuilder message;
|
||||||
|
private Color color;
|
||||||
|
private Font font;
|
||||||
|
|
||||||
private MessageWrapper(String message) {
|
private MessageWrapper(String message) {
|
||||||
if (message == null) {
|
Objects.requireNonNull(message, "Attempted to log a null message");
|
||||||
throw new AssertException("Attempted to log a null message.");
|
|
||||||
}
|
|
||||||
this.message = new StringBuilder(message);
|
this.message = new StringBuilder(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MessageWrapper(String message, Font font, Color color) {
|
||||||
|
this(message);
|
||||||
|
this.font = Objects.requireNonNull(font);
|
||||||
|
this.color = Objects.requireNonNull(color);
|
||||||
|
}
|
||||||
|
|
||||||
CharSequence getMessage() {
|
CharSequence getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -297,13 +309,31 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
||||||
if (getClass() != other.getClass()) {
|
if (getClass() != other.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(color, other.color)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
message.append(other.message);
|
message.append(other.message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributeSet getAttributes() {
|
AttributeSet getAttributes() {
|
||||||
|
if (color != null) {
|
||||||
|
GAttributes attrs = new GAttributes(font, color);
|
||||||
|
attrs.addAttribute(CUSTOM_ATTRIBUTE_KEY, OUTPUT_ATTRIBUTE_VALUE);
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
return outputAttributes;
|
return outputAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (color == null) {
|
||||||
|
return message.toString();
|
||||||
|
}
|
||||||
|
return "[color=" + color + "] " + message.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ErrorMessage extends MessageWrapper {
|
private static class ErrorMessage extends MessageWrapper {
|
||||||
|
@ -315,6 +345,10 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
||||||
AttributeSet getAttributes() {
|
AttributeSet getAttributes() {
|
||||||
return errorAttributes;
|
return errorAttributes;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[error] " + message.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -538,7 +538,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
||||||
DockingWindowManager.setMouseOverAction(windowMenuAction);
|
DockingWindowManager.setMouseOverAction(windowMenuAction);
|
||||||
|
|
||||||
performLaunchKeyStrokeDialogAction();
|
performLaunchKeyStrokeDialogAction();
|
||||||
DialogComponentProvider warningDialog = waitForDialogComponent("Unable to Set Keybinding");
|
DialogComponentProvider warningDialog = waitForDialogComponent("Unable to Set Key Binding");
|
||||||
close(warningDialog);
|
close(warningDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1199,6 +1199,34 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||||
assertTrue("Timed-out waiting for cancelled script to complete", success);
|
assertTrue("Timed-out waiting for cancelled script to complete", success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void runScript(ResourceFile scriptFile) throws Exception {
|
||||||
|
|
||||||
|
GhidraScriptProvider scriptProvider = GhidraScriptUtil.getProvider(scriptFile);
|
||||||
|
GhidraScript script =
|
||||||
|
scriptProvider.getScriptInstance(scriptFile, new PrintWriter(System.err));
|
||||||
|
|
||||||
|
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||||
|
task.addTaskListener(provider.getTaskListener());
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
task.addTaskListener(new TaskListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void taskCompleted(Task t) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void taskCancelled(Task t) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TaskLauncher.launch(task);
|
||||||
|
|
||||||
|
latch.await(TASK_RUN_SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
protected void startRunScriptTask(GhidraScript script) throws Exception {
|
protected void startRunScriptTask(GhidraScript script) throws Exception {
|
||||||
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||||
task.addTaskListener(provider.getTaskListener());
|
task.addTaskListener(provider.getTaskListener());
|
||||||
|
|
|
@ -17,9 +17,12 @@ package ghidra.app.plugin.core.script;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import javax.swing.text.*;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -490,6 +493,113 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
||||||
"*2*", output);
|
"*2*", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScriptPrintWithColor() throws Exception {
|
||||||
|
|
||||||
|
// create a script
|
||||||
|
ResourceFile newScriptFile = createTempScriptFile("LineColoringScript");
|
||||||
|
String filename = newScriptFile.getName();
|
||||||
|
String className = filename.replaceAll("\\.java", "");
|
||||||
|
|
||||||
|
String text1 = "This is black, ";
|
||||||
|
String text2 = "this is blue, and ";
|
||||||
|
String text3 = "this is red.\\n";
|
||||||
|
String line2 = "This is the default color.";
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String newScript = """
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
public class %s extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
|
||||||
|
print("%s");
|
||||||
|
print("%s", Color.BLUE);
|
||||||
|
print("%s", Color.RED);
|
||||||
|
print("%s");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
""".formatted(className, text1, text2, text3, line2);
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
writeStringToFile(newScriptFile, newScript);
|
||||||
|
|
||||||
|
runScript(newScriptFile);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
assertConsoleTextColor(text1, Color.BLACK);
|
||||||
|
assertConsoleTextColor(text2, Color.BLUE);
|
||||||
|
assertConsoleTextColor(text3, Color.RED);
|
||||||
|
assertConsoleTextColor(text2, Color.BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScriptPrintlnWithColor() throws Exception {
|
||||||
|
|
||||||
|
// create a script
|
||||||
|
ResourceFile newScriptFile = createTempScriptFile("LineColoringScript");
|
||||||
|
String filename = newScriptFile.getName();
|
||||||
|
String className = filename.replaceAll("\\.java", "");
|
||||||
|
|
||||||
|
String line1 = "1 This is a default line";
|
||||||
|
String line2 = "2 This is a blue line";
|
||||||
|
String line3 = "3 This is a red line";
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String newScript = """
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
public class %s extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
|
||||||
|
println("%s");
|
||||||
|
println("%s", Color.BLUE);
|
||||||
|
println("%s", Color.RED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
""".formatted(className, line1, line2, line3);
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
writeStringToFile(newScriptFile, newScript);
|
||||||
|
|
||||||
|
runScript(newScriptFile);
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
assertConsoleTextColor(line1, Color.BLACK);
|
||||||
|
assertConsoleTextColor(line2, Color.BLUE);
|
||||||
|
assertConsoleTextColor(line3, Color.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertConsoleTextColor(String text, Color expectedFgColor) {
|
||||||
|
String fullText = runSwing(() -> consoleTextPane.getText());
|
||||||
|
|
||||||
|
// We have 2 layers of newlines in the test. A '\\n' that gets written to file as Java
|
||||||
|
// code. That then gets compiled and written out as a newline. Our 'text' value passed
|
||||||
|
// here is that original '\\n'. We are trying to compare that against what ends up in the
|
||||||
|
// console, which has gone through 2 string interpretations to end up as a standard newline.
|
||||||
|
// Strip off the '\\n' from the original input text before looking for it in the console.
|
||||||
|
String visibleText = text.replaceAll("\\\\n", "");
|
||||||
|
int start = fullText.indexOf(visibleText);
|
||||||
|
int end = visibleText.length();
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
StyledDocument styledDocument = (StyledDocument) consoleTextPane.getDocument();
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
Element element = styledDocument.getCharacterElement(i);
|
||||||
|
AttributeSet attrs = element.getAttributes();
|
||||||
|
Color actualFgColor = (Color) attrs.getAttribute(StyleConstants.Foreground);
|
||||||
|
assertEquals(expectedFgColor, actualFgColor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Path getBinDirFromScriptFile(ResourceFile sourceFile) {
|
private Path getBinDirFromScriptFile(ResourceFile sourceFile) {
|
||||||
ResourceFile tmpSourceDir = sourceFile.getParentFile();
|
ResourceFile tmpSourceDir = sourceFile.getParentFile();
|
||||||
String tmpSymbolicName = GhidraSourceBundle.sourceDirHash(tmpSourceDir);
|
String tmpSymbolicName = GhidraSourceBundle.sourceDirHash(tmpSourceDir);
|
||||||
|
|
|
@ -51,7 +51,6 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private KeyBindingsPanel panel;
|
private KeyBindingsPanel panel;
|
||||||
private JTable table;
|
private JTable table;
|
||||||
private TableModel model;
|
private TableModel model;
|
||||||
private JTextField keyField;
|
|
||||||
private JTextPane statusPane;
|
private JTextPane statusPane;
|
||||||
private JDialog dialog;
|
private JDialog dialog;
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
setUpDialog();
|
setUpDialog();
|
||||||
|
|
||||||
grabActionsWithoutKeybinding();
|
grabActionsWithoutKeyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -82,8 +81,8 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testKeyBindingsDisplay() throws Exception {
|
public void testKeyBindingsDisplay() throws Exception {
|
||||||
|
|
||||||
assertEquals(3, model.getColumnCount());
|
assertEquals(3, table.getColumnCount());
|
||||||
String[] ids = new String[] { "Action Name", "KeyBinding", "Plugin Name" };
|
String[] ids = new String[] { "Action Name", "Key Binding", "Owner" };
|
||||||
TableColumnModel m = table.getColumnModel();
|
TableColumnModel m = table.getColumnModel();
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (int i = 0; i < ids.length; i++) {
|
||||||
TableColumn c = m.getColumn(i);
|
TableColumn c = m.getColumn(i);
|
||||||
|
@ -91,15 +90,9 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
assertTrue(model.getRowCount() > 0);
|
assertTrue(model.getRowCount() > 0);
|
||||||
|
|
||||||
// look for the info panel
|
assertMessage("Select an action to change a keybinding");
|
||||||
MultiLineLabel label = findComponent(panel, MultiLineLabel.class);
|
|
||||||
String str = "To add or change a key binding, select an action\n" +
|
|
||||||
"and type any key combination.";
|
|
||||||
|
|
||||||
assertEquals(str, label.getLabel());
|
|
||||||
|
|
||||||
// verify that the description is displayed for the selected action
|
// verify that the description is displayed for the selected action
|
||||||
|
|
||||||
selectRowForAction(action1);
|
selectRowForAction(action1);
|
||||||
|
|
||||||
String actualText = getText(statusPane);
|
String actualText = getText(statusPane);
|
||||||
|
@ -130,6 +123,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertNotNull("Could not find edit key binding action.", action);
|
assertNotNull("Could not find edit key binding action.", action);
|
||||||
|
|
||||||
selectRowForAction(action);
|
selectRowForAction(action);
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
triggerText(keyField, "z");
|
triggerText(keyField, "z");
|
||||||
assertKeyFieldText("Z");
|
assertKeyFieldText("Z");
|
||||||
|
|
||||||
|
@ -148,14 +142,16 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerText(keyField, "z");
|
JTextField keyField = getKeyField();
|
||||||
assertMessage("No action is selected.");
|
assertNull(keyField);
|
||||||
|
assertMessage("Select an action to change a keybinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetKeyBinding() throws Exception {
|
public void testSetKeyBinding() throws Exception {
|
||||||
// set a key binding on an action that does not have a key binding
|
// set a key binding on an action that does not have a key binding
|
||||||
selectRowForAction(action1);
|
selectRowForAction(action1);
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
triggerActionKey(keyField, InputEvent.CTRL_DOWN_MASK, KeyEvent.VK_X);
|
triggerActionKey(keyField, InputEvent.CTRL_DOWN_MASK, KeyEvent.VK_X);
|
||||||
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK);
|
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK);
|
||||||
assertKeyFieldText(KeyBindingUtils.parseKeyStroke(ks));
|
assertKeyFieldText(KeyBindingUtils.parseKeyStroke(ks));
|
||||||
|
@ -168,6 +164,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testSetKeyBinding2() throws Exception {
|
public void testSetKeyBinding2() throws Exception {
|
||||||
|
|
||||||
selectRowForAction(action1);
|
selectRowForAction(action1);
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
triggerText(keyField, "x");
|
triggerText(keyField, "x");
|
||||||
assertKeyFieldText("X");
|
assertKeyFieldText("X");
|
||||||
|
|
||||||
|
@ -394,6 +391,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNoKeyStrokeText() {
|
private void assertNoKeyStrokeText() {
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
assertEquals("", keyField.getText());
|
assertEquals("", keyField.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +400,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertKeyFieldText(String s) {
|
private void assertKeyFieldText(String s) {
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
assertEquals(s, runSwing(() -> keyField.getText()));
|
assertEquals(s, runSwing(() -> keyField.getText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +409,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void typeKeyStroke(KeyStroke ks) {
|
private void typeKeyStroke(KeyStroke ks) {
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
triggerKey(keyField, ks);
|
triggerKey(keyField, ks);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
@ -419,6 +419,7 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void typeKeyStroke(int modifiers, int keyCode) {
|
private void typeKeyStroke(int modifiers, int keyCode) {
|
||||||
|
JTextField keyField = getKeyField();
|
||||||
triggerKey(keyField, modifiers, keyCode, KeyEvent.CHAR_UNDEFINED);
|
triggerKey(keyField, modifiers, keyCode, KeyEvent.CHAR_UNDEFINED);
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
@ -538,14 +539,18 @@ public class KeyBindingsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
});
|
});
|
||||||
table = findComponent(panel, JTable.class);
|
table = findComponent(panel, JTable.class);
|
||||||
keyField = (JTextField) findComponentByName(panel, "Key Entry Text Field");
|
|
||||||
statusPane = findComponent(panel, JTextPane.class);
|
statusPane = findComponent(panel, JTextPane.class);
|
||||||
model = table.getModel();
|
model = table.getModel();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JTextField getKeyField() {
|
||||||
|
JTextField keyField = (JTextField) findComponentByName(panel, "Key Entry Text Field");
|
||||||
|
return keyField;
|
||||||
|
}
|
||||||
|
|
||||||
// find 2 actions that do not have key bindings so that we can add and change the values
|
// find 2 actions that do not have key bindings so that we can add and change the values
|
||||||
private void grabActionsWithoutKeybinding() {
|
private void grabActionsWithoutKeyBinding() {
|
||||||
Set<DockingActionIf> list = tool.getAllActions();
|
Set<DockingActionIf> list = tool.getAllActions();
|
||||||
for (DockingActionIf action : list) {
|
for (DockingActionIf action : list) {
|
||||||
if (ignoreAction(action)) {
|
if (ignoreAction(action)) {
|
||||||
|
|
|
@ -980,8 +980,6 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
selectRowForAction(panel, actionName, actionOwner);
|
selectRowForAction(panel, actionName, actionOwner);
|
||||||
|
|
||||||
setToggleButtonSelected(panel, "Enter Mouse Binding", true);
|
|
||||||
|
|
||||||
JPanel actionBindingPanel = (JPanel) getInstanceField("actionBindingPanel", panel);
|
JPanel actionBindingPanel = (JPanel) getInstanceField("actionBindingPanel", panel);
|
||||||
JTextField textField = (JTextField) getInstanceField("mouseEntryField", actionBindingPanel);
|
JTextField textField = (JTextField) getInstanceField("mouseEntryField", actionBindingPanel);
|
||||||
|
|
||||||
|
@ -1008,8 +1006,6 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
selectRowForAction(panel, actionName, actionOwner);
|
selectRowForAction(panel, actionName, actionOwner);
|
||||||
|
|
||||||
setToggleButtonSelected(panel, "Enter Mouse Binding", false);
|
|
||||||
|
|
||||||
JPanel actionBindingPanel = (JPanel) getInstanceField("actionBindingPanel", panel);
|
JPanel actionBindingPanel = (JPanel) getInstanceField("actionBindingPanel", panel);
|
||||||
KeyEntryPanel keyEntryPanel =
|
KeyEntryPanel keyEntryPanel =
|
||||||
(KeyEntryPanel) getInstanceField("keyEntryPanel", actionBindingPanel);
|
(KeyEntryPanel) getInstanceField("keyEntryPanel", actionBindingPanel);
|
||||||
|
@ -1031,8 +1027,6 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
selectRowForAction(panel, actionName, actionOwner);
|
selectRowForAction(panel, actionName, actionOwner);
|
||||||
|
|
||||||
setToggleButtonSelected(panel, "Enter Mouse Binding", false);
|
|
||||||
|
|
||||||
pressButtonByName(panel, "Clear Key Binding");
|
pressButtonByName(panel, "Clear Key Binding");
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/* ###
|
||||||
|
* 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.features.base.memsearch.bytesequence;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.features.base.memsearch.matcher.*;
|
||||||
|
import ghidra.features.base.memsearch.matcher.ByteMatcher.ByteMatch;
|
||||||
|
|
||||||
|
public class CombinedByteMatcherTest {
|
||||||
|
private ByteMatcher xxxByteMatcher;
|
||||||
|
private ByteMatcher yyyByteMatcher;
|
||||||
|
private ByteMatcher zzzByteMatcher;
|
||||||
|
private CombinedByteMatcher multiMatcher;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
|
||||||
|
xxxByteMatcher = new RegExByteMatcher("xxx", null);
|
||||||
|
yyyByteMatcher = new RegExByteMatcher("yyy", null);
|
||||||
|
zzzByteMatcher = new RegExByteMatcher("zzz", null);
|
||||||
|
multiMatcher =
|
||||||
|
new CombinedByteMatcher(List.of(xxxByteMatcher, yyyByteMatcher, zzzByteMatcher), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void textFindsOneEachPatterns() {
|
||||||
|
List<ByteMatch> results = match("fooxxxbar, fooyyybar, foozzzbar");
|
||||||
|
assertEquals(3, results.size());
|
||||||
|
assertEquals(new ByteMatch(3, 3, xxxByteMatcher), results.get(0));
|
||||||
|
assertEquals(new ByteMatch(15, 3, yyyByteMatcher), results.get(1));
|
||||||
|
assertEquals(new ByteMatch(26, 3, zzzByteMatcher), results.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void textFindsMutliplePatterns() {
|
||||||
|
List<ByteMatch> results = match("xxxyyyzzzxxxyyyzzz");
|
||||||
|
assertEquals(6, results.size());
|
||||||
|
assertEquals(new ByteMatch(0, 3, xxxByteMatcher), results.get(0));
|
||||||
|
assertEquals(new ByteMatch(9, 3, xxxByteMatcher), results.get(1));
|
||||||
|
assertEquals(new ByteMatch(3, 3, yyyByteMatcher), results.get(2));
|
||||||
|
assertEquals(new ByteMatch(12, 3, yyyByteMatcher), results.get(3));
|
||||||
|
assertEquals(new ByteMatch(6, 3, zzzByteMatcher), results.get(4));
|
||||||
|
assertEquals(new ByteMatch(15, 3, zzzByteMatcher), results.get(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoMatches() {
|
||||||
|
List<ByteMatch> results = match("There are no matches here!");
|
||||||
|
assertEquals(0, results.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ByteMatch> match(String s) {
|
||||||
|
ExtendedByteSequence sequence = createByteSequence(s);
|
||||||
|
Iterable<ByteMatch> match = multiMatcher.match(sequence);
|
||||||
|
return StreamSupport.stream(match.spliterator(), false).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtendedByteSequence createByteSequence(String s) {
|
||||||
|
ByteSequence main = new ByteArrayByteSequence(makeBytes(s));
|
||||||
|
ByteSequence extra = new ByteArrayByteSequence(makeBytes(""));
|
||||||
|
return new ExtendedByteSequence(main, extra, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] makeBytes(String string) {
|
||||||
|
byte[] bytes = new byte[string.length()];
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = (byte) string.charAt(i);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,10 +49,10 @@ public class MaskedBytesSequenceByteMatcherTest {
|
||||||
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
||||||
|
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertEquals(new ByteMatch(2, 3), it.next());
|
assertEquals(new ByteMatch(2, 3, byteMatcher), it.next());
|
||||||
|
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertEquals(new ByteMatch(9, 3), it.next());
|
assertEquals(new ByteMatch(9, 3, byteMatcher), it.next());
|
||||||
|
|
||||||
assertFalse(it.hasNext());
|
assertFalse(it.hasNext());
|
||||||
|
|
||||||
|
@ -66,8 +66,8 @@ public class MaskedBytesSequenceByteMatcherTest {
|
||||||
|
|
||||||
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
||||||
|
|
||||||
assertEquals(new ByteMatch(2, 3), it.next());
|
assertEquals(new ByteMatch(2, 3, byteMatcher), it.next());
|
||||||
assertEquals(new ByteMatch(9, 3), it.next());
|
assertEquals(new ByteMatch(9, 3, byteMatcher), it.next());
|
||||||
assertNull(it.next());
|
assertNull(it.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +81,9 @@ public class MaskedBytesSequenceByteMatcherTest {
|
||||||
|
|
||||||
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
||||||
|
|
||||||
assertEquals(new ByteMatch(1, 3), it.next());
|
assertEquals(new ByteMatch(1, 3, byteMatcher), it.next());
|
||||||
assertEquals(new ByteMatch(6, 3), it.next());
|
assertEquals(new ByteMatch(6, 3, byteMatcher), it.next());
|
||||||
assertEquals(new ByteMatch(8, 3), it.next());
|
assertEquals(new ByteMatch(8, 3, byteMatcher), it.next());
|
||||||
assertNull(it.next());
|
assertNull(it.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@ public class RegExByteMatcherTest {
|
||||||
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
||||||
|
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertEquals(new ByteMatch(4, 3), it.next());
|
assertEquals(new ByteMatch(4, 3, byteMatcher), it.next());
|
||||||
|
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertEquals(new ByteMatch(14, 3), it.next());
|
assertEquals(new ByteMatch(14, 3, byteMatcher), it.next());
|
||||||
|
|
||||||
assertFalse(it.hasNext());
|
assertFalse(it.hasNext());
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ public class RegExByteMatcherTest {
|
||||||
|
|
||||||
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
Iterator<ByteMatch> it = byteMatcher.match(byteSequence).iterator();
|
||||||
|
|
||||||
assertEquals(new ByteMatch(4, 3), it.next());
|
assertEquals(new ByteMatch(4, 3, byteMatcher), it.next());
|
||||||
assertEquals(new ByteMatch(14, 3), it.next());
|
assertEquals(new ByteMatch(14, 3, byteMatcher), it.next());
|
||||||
assertNull(it.next());
|
assertNull(it.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,3 +8,5 @@ src/main/help/help/topics/DecompilerTaint/DecompilerTaint.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html||GHIDRA||||END|
|
src/main/help/help/topics/DecompilerTextFinderPlugin/Decompiler_Text_Finder.html||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png||GHIDRA||||END|
|
src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderDialog.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png||GHIDRA||||END|
|
src/main/help/help/topics/DecompilerTextFinderPlugin/images/DecompilerTextFinderResultsTable.png||GHIDRA||||END|
|
||||||
|
src/main/resources/images/default-query.png||GHIDRA||||END|
|
||||||
|
src/main/resources/images/gate-set.png||GHIDRA||||END|
|
||||||
|
|
|
@ -8,5 +8,7 @@ color.bg.decompiler.highlights.sourcesink = color.palette.darkcyan
|
||||||
color.bg.decompiler.highlights.path = color.palette.yellow
|
color.bg.decompiler.highlights.path = color.palette.yellow
|
||||||
|
|
||||||
icon.plugin.decompiler.text.finder.select.functions = icon.make.selection {FunctionScope.gif[size(12,12)][move(6,6)]}
|
icon.plugin.decompiler.text.finder.select.functions = icon.make.selection {FunctionScope.gif[size(12,12)][move(6,6)]}
|
||||||
|
icon.plugin.decompiler.taint.gate.set = gate-set.png
|
||||||
|
icon.plugin.decompiler.taint.default.query = default-query.png
|
||||||
|
|
||||||
[Dark Defaults]
|
[Dark Defaults]
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompiler.absint;
|
||||||
|
|
||||||
|
import ghidra.framework.plugintool.ServiceInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AbstractInterpretationService provides a general service for generating results from an external engine
|
||||||
|
*/
|
||||||
|
@ServiceInfo(description = "supply abstract interpretation")
|
||||||
|
public interface AbstractInterpretationService {
|
||||||
|
|
||||||
|
public String getActiveQueryName();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.HighVariable;
|
import ghidra.program.model.pcode.HighVariable;
|
||||||
import ghidra.program.model.pcode.PcodeException;
|
import ghidra.program.model.pcode.PcodeException;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
import sarif.SarifService;
|
import sarif.SarifService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,10 +60,11 @@ public abstract class AbstractTaintState implements TaintState {
|
||||||
protected TaintOptions taintOptions;
|
protected TaintOptions taintOptions;
|
||||||
protected TaintPlugin plugin;
|
protected TaintPlugin plugin;
|
||||||
protected boolean usesIndex = true;
|
protected boolean usesIndex = true;
|
||||||
private boolean cancellation;
|
protected TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||||
|
|
||||||
private TaskType taskType = TaskType.SET_TAINT;
|
private TaskType taskType = TaskType.SET_TAINT;
|
||||||
|
|
||||||
|
|
||||||
public AbstractTaintState(TaintPlugin plugin) {
|
public AbstractTaintState(TaintPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
@ -83,13 +85,22 @@ public abstract class AbstractTaintState implements TaintState {
|
||||||
protected abstract void writeFooter(PrintWriter writer);
|
protected abstract void writeFooter(PrintWriter writer);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean wasCancelled() {
|
public void setMonitor(TaskMonitor monitor) {
|
||||||
return this.cancellation;
|
if (monitor != null) {
|
||||||
|
monitor.setIndeterminate(true);
|
||||||
|
monitor.setShowProgressValue(false);
|
||||||
|
}
|
||||||
|
this.monitor = monitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCancellation(boolean status) {
|
public boolean isCancelled() {
|
||||||
this.cancellation = status;
|
return monitor != null && monitor.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
monitor.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -320,6 +331,10 @@ public abstract class AbstractTaintState implements TaintState {
|
||||||
pb.redirectError(Redirect.INHERIT);
|
pb.redirectError(Redirect.INHERIT);
|
||||||
Process p = pb.start();
|
Process p = pb.start();
|
||||||
|
|
||||||
|
monitor.addCancelledListener(() -> {
|
||||||
|
p.destroyForcibly();
|
||||||
|
});
|
||||||
|
|
||||||
readQueryResultsIntoDataFrame(program, p.getInputStream());
|
readQueryResultsIntoDataFrame(program, p.getInputStream());
|
||||||
|
|
||||||
// We wait for the process to finish after starting to read the input stream,
|
// We wait for the process to finish after starting to read the input stream,
|
||||||
|
@ -555,4 +570,9 @@ public abstract class AbstractTaintState implements TaintState {
|
||||||
return ENGINE_NAME;
|
return ENGINE_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class TaintDecompilerMarginProvider extends JPanel
|
||||||
|
|
||||||
private Icon sourceIcon = new GIcon("icon.plugin.scriptmanager.run");
|
private Icon sourceIcon = new GIcon("icon.plugin.scriptmanager.run");
|
||||||
private Icon sinkIcon = new GIcon("icon.stop");
|
private Icon sinkIcon = new GIcon("icon.stop");
|
||||||
private Icon gateIcon = new GIcon("icon.debugger.breakpoint.set");
|
private Icon gateIcon = new GIcon("icon.plugin.decompiler.taint.gate.set");
|
||||||
|
|
||||||
public TaintDecompilerMarginProvider(TaintPlugin plugin) {
|
public TaintDecompilerMarginProvider(TaintPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
|
@ -160,15 +160,16 @@ public class TaintLabelsTableProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
TaintState state = plugin.getTaintState();
|
TaintState state = plugin.getTaintState();
|
||||||
|
|
||||||
Task queryTask = new Task("Source-Sink Query Task", true, true, true, true) {
|
Task queryTask = new Task("Source-Sink Query Task", true, false, false, true) {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
state.setCancellation(false);
|
|
||||||
monitor.initialize(program.getFunctionManager().getFunctionCount());
|
monitor.initialize(program.getFunctionManager().getFunctionCount());
|
||||||
// query index NOT the default query; use table data.
|
// query index NOT the default query; use table data.
|
||||||
boolean successful =
|
boolean successful =
|
||||||
state.queryIndex(currentProgram, tool, QueryType.SRCSINK);
|
state.queryIndex(currentProgram, tool, QueryType.SRCSINK);
|
||||||
state.setCancellation(!successful || monitor.isCancelled());
|
if (!successful) {
|
||||||
|
state.cancel();
|
||||||
|
}
|
||||||
monitor.clearCancelled();
|
monitor.clearCancelled();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -180,7 +181,7 @@ public class TaintLabelsTableProvider extends ComponentProviderAdapter {
|
||||||
// 1. Query Index.
|
// 1. Query Index.
|
||||||
tool.execute(queryTask);
|
tool.execute(queryTask);
|
||||||
|
|
||||||
if (!state.wasCancelled()) {
|
if (!state.isCancelled()) {
|
||||||
// 2. Show Table.
|
// 2. Show Table.
|
||||||
SarifService sarifService = plugin.getSarifService();
|
SarifService sarifService = plugin.getSarifService();
|
||||||
sarifService.getController()
|
sarifService.getController()
|
||||||
|
@ -192,8 +193,6 @@ public class TaintLabelsTableProvider extends ComponentProviderAdapter {
|
||||||
TaintProvider provider = plugin.getProvider();
|
TaintProvider provider = plugin.getProvider();
|
||||||
provider.setTaint();
|
provider.setTaint();
|
||||||
plugin.consoleMessage("query complete");
|
plugin.consoleMessage("query complete");
|
||||||
state.setCancellation(false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
plugin.consoleMessage("Source-Sink query was cancelled.");
|
plugin.consoleMessage("Source-Sink query was cancelled.");
|
||||||
|
|
|
@ -265,7 +265,7 @@ public class TaintProvider extends ComponentProviderAdapter implements OptionsCh
|
||||||
|
|
||||||
state.setTaskType(taskType);
|
state.setTaskType(taskType);
|
||||||
AddressSet taintAddressSet = state.getTaintAddressSet();
|
AddressSet taintAddressSet = state.getTaintAddressSet();
|
||||||
Msg.info(this, "setTaint(): " + taintAddressSet.toString());
|
//Msg.info(this, "setTaint(): " + taintAddressSet.toString());
|
||||||
|
|
||||||
// sets the selection in the LISTING?
|
// sets the selection in the LISTING?
|
||||||
// TODO: should we not set select and only highlight in the decompilation.
|
// TODO: should we not set select and only highlight in the decompilation.
|
||||||
|
|
|
@ -119,6 +119,9 @@ public record TaintQueryResult(String name,String fqname, Address iaddr, Address
|
||||||
if (fqname.contains(":"+hvName)) {
|
if (fqname.contains(":"+hvName)) {
|
||||||
return hvName;
|
return hvName;
|
||||||
}
|
}
|
||||||
|
if (fqname.contains(":"+ast.getAddress())) {
|
||||||
|
return hvName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import ghidra.program.model.symbol.SymbolTable;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface for the methods that collect desired taint information from the decompiler window and store them
|
* The interface for the methods that collect desired taint information from the decompiler window and store them
|
||||||
|
@ -92,6 +93,8 @@ public interface TaintState extends ExtensionPoint {
|
||||||
*/
|
*/
|
||||||
public boolean queryIndex(Program program, PluginTool tool, QueryType queryType);
|
public boolean queryIndex(Program program, PluginTool tool, QueryType queryType);
|
||||||
|
|
||||||
|
public String getQueryName();
|
||||||
|
|
||||||
public TaintLabel toggleMark(MarkType mtype, ClangToken token) throws PcodeException;
|
public TaintLabel toggleMark(MarkType mtype, ClangToken token) throws PcodeException;
|
||||||
|
|
||||||
public Set<TaintLabel> getTaintLabels(MarkType mtype);
|
public Set<TaintLabel> getTaintLabels(MarkType mtype);
|
||||||
|
@ -121,9 +124,11 @@ public interface TaintState extends ExtensionPoint {
|
||||||
// predicate that indicates there are sources, sinks, or gates.
|
// predicate that indicates there are sources, sinks, or gates.
|
||||||
public boolean hasMarks();
|
public boolean hasMarks();
|
||||||
|
|
||||||
public boolean wasCancelled();
|
public void setMonitor(TaskMonitor monitor);
|
||||||
|
|
||||||
public void setCancellation(boolean status);
|
public boolean isCancelled();
|
||||||
|
|
||||||
|
public void cancel();
|
||||||
|
|
||||||
public void setTaintVarnodeMap(Map<Address, Set<TaintQueryResult>> vmap, TaskType delta);
|
public void setTaintVarnodeMap(Map<Address, Set<TaintQueryResult>> vmap, TaskType delta);
|
||||||
|
|
||||||
|
|
|
@ -76,11 +76,9 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
TaintState state = plugin.getTaintState();
|
TaintState state = plugin.getTaintState();
|
||||||
state.setCancellation(false);
|
state.setMonitor(monitor);
|
||||||
monitor.initialize(program.getFunctionManager().getFunctionCount());
|
|
||||||
state.queryIndex(program, tool, queryType);
|
state.queryIndex(program, tool, queryType);
|
||||||
state.setCancellation(monitor.isCancelled());
|
state.setMonitor(null);
|
||||||
monitor.clearCancelled();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,11 +89,15 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc
|
||||||
tool.execute(defaultQueryTask);
|
tool.execute(defaultQueryTask);
|
||||||
|
|
||||||
TaintState state = plugin.getTaintState();
|
TaintState state = plugin.getTaintState();
|
||||||
if (!state.wasCancelled()) {
|
if (!defaultQueryTask.isCancelled()) {
|
||||||
TaintFormat format = state.getOptions().getTaintOutputForm();
|
TaintFormat format = state.getOptions().getTaintOutputForm();
|
||||||
if (!format.equals(TaintFormat.NONE)) {
|
if (!format.equals(TaintFormat.NONE)) {
|
||||||
SarifService sarifService = plugin.getSarifService();
|
SarifService sarifService = plugin.getSarifService();
|
||||||
sarifService.getController().setDefaultGraphHander(SarifTaintGraphRunHandler.class);
|
sarifService.getController().setDefaultGraphHander(SarifTaintGraphRunHandler.class);
|
||||||
|
String queryName = state.getQueryName();
|
||||||
|
if (queryName != null) {
|
||||||
|
desc = queryName;
|
||||||
|
}
|
||||||
sarifService.showSarif(desc, state.getData());
|
sarifService.showSarif(desc, state.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,11 +106,9 @@ public abstract class TaintAbstractQueryAction extends TaintAbstractDecompilerAc
|
||||||
provider.setTaint();
|
provider.setTaint();
|
||||||
|
|
||||||
plugin.consoleMessage("query complete");
|
plugin.consoleMessage("query complete");
|
||||||
state.setCancellation(false);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
plugin.consoleMessage("Source-Sink query was cancelled.");
|
plugin.consoleMessage("Source-Sink query was cancelled.");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class TaintQueryDefaultAction extends TaintAbstractQueryAction {
|
||||||
|
|
||||||
public TaintQueryDefaultAction(TaintPlugin plugin) {
|
public TaintQueryDefaultAction(TaintPlugin plugin) {
|
||||||
super(plugin, "DefaultQuery", "Default Taint Query", "Run default taint query");
|
super(plugin, "DefaultQuery", "Default Taint Query", "Run default taint query");
|
||||||
executeTaintQueryIconString = "icon.version.tracking.markup.status.conflict";
|
executeTaintQueryIconString = "icon.plugin.decompiler.taint.default.query";
|
||||||
executeTaintQueryIcon = new GIcon(executeTaintQueryIconString);
|
executeTaintQueryIcon = new GIcon(executeTaintQueryIconString);
|
||||||
queryType = QueryType.DEFAULT;
|
queryType = QueryType.DEFAULT;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import ghidra.app.plugin.core.decompiler.taint.TaintLabel;
|
||||||
import ghidra.app.plugin.core.decompiler.taint.TaintPlugin;
|
import ghidra.app.plugin.core.decompiler.taint.TaintPlugin;
|
||||||
import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType;
|
import ghidra.app.plugin.core.decompiler.taint.TaintState.MarkType;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.UndefinedFunction;
|
import ghidra.util.UndefinedFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +47,7 @@ public class TaintSetSizeAction extends TaintAbstractDecompilerAction {
|
||||||
|
|
||||||
public TaintSetSizeAction(TaintPlugin plugin) {
|
public TaintSetSizeAction(TaintPlugin plugin) {
|
||||||
super("Set length");
|
super("Set length");
|
||||||
|
setHelpLocation(new HelpLocation(TaintPlugin.HELP_LOCATION, TaintPlugin.HELP_LOCATION));
|
||||||
// Taint Menu -> Source sub item.
|
// Taint Menu -> Source sub item.
|
||||||
setPopupMenuData(new MenuData(new String[] { "Taint", "Set length" }, "Decompile"));
|
setPopupMenuData(new MenuData(new String[] { "Taint", "Set length" }, "Decompile"));
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
|
@ -216,6 +216,7 @@ public class SarifTaintResultHandler extends SarifResultHandler {
|
||||||
protected void doRun(TaskMonitor monitor) {
|
protected void doRun(TaskMonitor monitor) {
|
||||||
int[] selected = tableProvider.filterTable.getTable().getSelectedRows();
|
int[] selected = tableProvider.filterTable.getTable().getSelectedRows();
|
||||||
Map<Address, Set<TaintQueryResult>> map = new HashMap<>();
|
Map<Address, Set<TaintQueryResult>> map = new HashMap<>();
|
||||||
|
AddressSet set = new AddressSet();
|
||||||
for (int row : selected) {
|
for (int row : selected) {
|
||||||
Map<String, Object> r = tableProvider.getRow(row);
|
Map<String, Object> r = tableProvider.getRow(row);
|
||||||
String kind = (String) r.get("kind");
|
String kind = (String) r.get("kind");
|
||||||
|
@ -225,12 +226,17 @@ public class SarifTaintResultHandler extends SarifResultHandler {
|
||||||
if (kind.equals("variable")) {
|
if (kind.equals("variable")) {
|
||||||
getTaintedVariable(map, r);
|
getTaintedVariable(map, r);
|
||||||
}
|
}
|
||||||
|
Address addr = (Address) r.get("Address");
|
||||||
|
if (addr != null) {
|
||||||
|
set.add(addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginTool tool = tableProvider.getController().getPlugin().getTool();
|
PluginTool tool = tableProvider.getController().getPlugin().getTool();
|
||||||
TaintService service = tool.getService(TaintService.class);
|
TaintService service = tool.getService(TaintService.class);
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
service.setVarnodeMap(map, true, taskType);
|
service.setVarnodeMap(map, true, taskType);
|
||||||
|
service.setAddressSet(set, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 778 B |
Binary file not shown.
After Width: | Height: | Size: 561 B |
|
@ -1,246 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// VxWorksSymTab_5_4 is a copy of VxWorksSymTab_6_1 with a different value for SYM_ENTRY_SIZE
|
|
||||||
// It was replaced at the request of a customer who tested that it worked with the slight modification
|
|
||||||
// VxWorksSymTab_6_1 is an adaptation of the vxWorksSymTab script. It was modified by a customer
|
|
||||||
// to use a single loop, instead of two. It also added demangling of C++ symbol names - at least
|
|
||||||
// those that Ghidra knows how to demangle.
|
|
||||||
//
|
|
||||||
// Extracts all symbols in a VxWorks symbol table and disassembles
|
|
||||||
// the global functions. Any existing symbols in the Ghidra symbol table
|
|
||||||
// that collide with symbols defined in the VxWorks symbol table are deleted.
|
|
||||||
//
|
|
||||||
// The VxWorks symbol table is an array of symbol table entries [0..n-1]
|
|
||||||
// followed by a 32-bit value that is equal to n (number of sym tbl entries).
|
|
||||||
// Each entry in the array has the following structure:
|
|
||||||
//
|
|
||||||
// // Total size: 0x18 (24) bytes
|
|
||||||
// 0x00 int NULL
|
|
||||||
// 0x04 char *symNameAddr // symbol name
|
|
||||||
// 0x08 void *symLocAddr // location of object named by symbol
|
|
||||||
// 0x0c int NULL
|
|
||||||
// 0x10 int NULL
|
|
||||||
// 0x14 uchar symType // see switch statement below
|
|
||||||
// 0x15 uchar fill[3]
|
|
||||||
//
|
|
||||||
// The script requests:
|
|
||||||
// - Output file name: Each symbol name and address is recorded here.
|
|
||||||
// (Errors are also logged to this file.)
|
|
||||||
// - Address of "number of symbols" value: At the end of the symbol table,
|
|
||||||
// its length is recorded as a 32-bit integer. The
|
|
||||||
// script needs the address of that value to calculate
|
|
||||||
// the symbol table's start address.
|
|
||||||
//
|
|
||||||
// @category Customer Submission.vxWorks
|
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.app.cmd.label.DemanglerCmd;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
|
||||||
import ghidra.app.util.demangler.DemangledException;
|
|
||||||
import ghidra.app.util.demangler.MangledContext;
|
|
||||||
import ghidra.app.util.demangler.gnu.GnuDemangler;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.mem.Memory;
|
|
||||||
import ghidra.program.model.symbol.*;
|
|
||||||
|
|
||||||
public class VxWorksSymTab_5_4 extends GhidraScript {
|
|
||||||
|
|
||||||
static final int SYM_ENTRY_SIZE = 16;
|
|
||||||
static final int SYM_NAME_OFF = 4;
|
|
||||||
static final int SYM_LOC_OFF = 8;
|
|
||||||
static final int SYM_TYPE_OFF = 0x14;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() throws Exception {
|
|
||||||
|
|
||||||
// Get Memory and SymbolTable objects (used later)
|
|
||||||
Memory mem = currentProgram.getMemory();
|
|
||||||
SymbolTable ghidraSymTbl = currentProgram.getSymbolTable();
|
|
||||||
|
|
||||||
// Open output file
|
|
||||||
// All symbols found (address and name) will be logged to this file
|
|
||||||
try (PrintWriter output =
|
|
||||||
new PrintWriter(new FileOutputStream(askFile("vxWorks Symbol Table Parser",
|
|
||||||
"Output file name?")))) {
|
|
||||||
|
|
||||||
// Get address of "total number of sym tbl entries" value
|
|
||||||
Address vxNumSymEntriesAddr =
|
|
||||||
askAddress("vxWorks Symbol Table Parser",
|
|
||||||
"Address of \"total number of symbol table entries\" value?");
|
|
||||||
int vxNumSymEntries = mem.getInt(vxNumSymEntriesAddr);
|
|
||||||
println("VxWorks symbol table has " + vxNumSymEntries + " entries");
|
|
||||||
|
|
||||||
// Create a GNU demangler instance
|
|
||||||
GnuDemangler demangler = new GnuDemangler();
|
|
||||||
if (!demangler.canDemangle(currentProgram)) {
|
|
||||||
println("Unable to create demangler.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process entries in VxWorks symbol table
|
|
||||||
Address vxSymTbl = vxNumSymEntriesAddr.subtract(vxNumSymEntries * SYM_ENTRY_SIZE);
|
|
||||||
for (int i = 0; i < vxNumSymEntries; i++) {
|
|
||||||
|
|
||||||
if (monitor.isCancelled()) {
|
|
||||||
return; // check for cancel button
|
|
||||||
}
|
|
||||||
println("i=" + i); // visual counter
|
|
||||||
|
|
||||||
// Extract symbol table entry values
|
|
||||||
Address symEntry = vxSymTbl.add(i * SYM_ENTRY_SIZE);
|
|
||||||
Address symNameAddr = toAddr(mem.getInt(symEntry.add(SYM_NAME_OFF)));
|
|
||||||
Address symLocAddr = toAddr(mem.getInt(symEntry.add(SYM_LOC_OFF)));
|
|
||||||
byte symType = mem.getByte(symEntry.add(SYM_TYPE_OFF));
|
|
||||||
println("symNameAddr: 0x" + symNameAddr.toString() + ", symLocAddr: 0x" +
|
|
||||||
symLocAddr.toString() + ", symType: " + symType);
|
|
||||||
|
|
||||||
// Remove any data or instructions that overlap this symName
|
|
||||||
// (May happen if disassembly creates invalid references)
|
|
||||||
Address a;
|
|
||||||
String symName;
|
|
||||||
for (a = symNameAddr; mem.getByte(a) != 0; a = a.add(1)) {
|
|
||||||
if (getDataAt(a) != null) {
|
|
||||||
removeDataAt(a);
|
|
||||||
}
|
|
||||||
if (getInstructionAt(a) != null) {
|
|
||||||
removeInstructionAt(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (getDataAt(a) != null) {
|
|
||||||
removeDataAt(a);
|
|
||||||
}
|
|
||||||
if (getInstructionAt(a) != null) {
|
|
||||||
removeInstructionAt(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn *symNameAddr into a string and store it in symName
|
|
||||||
try {
|
|
||||||
symName = (String) createAsciiString(symNameAddr).getValue();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
println("createAsciiString: caught exception...");
|
|
||||||
println(e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println("symName: " + symName);
|
|
||||||
|
|
||||||
// Demangle symName
|
|
||||||
String symDemangledName = null;
|
|
||||||
try {
|
|
||||||
// if successful, symDemangledName will be non-NULL
|
|
||||||
MangledContext mangledContext = demangler.createMangledContext(symDemangledName,
|
|
||||||
null, currentProgram, symNameAddr);
|
|
||||||
symDemangledName = demangler.demangle(mangledContext).getSignature(false);
|
|
||||||
}
|
|
||||||
catch (DemangledException e) {
|
|
||||||
// if symName wasn't a mangled name, silently continue
|
|
||||||
if (!e.isInvalidMangledName()) {
|
|
||||||
println("demangle: Demangling error");
|
|
||||||
output.println("demangle: Demangling error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
println("demangle: Caught runtime exception");
|
|
||||||
output.println("demangle: Caught runtime exception");
|
|
||||||
}
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
println("symDemangledName: " + symDemangledName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any symbol in the Ghidra symbol table with the same name
|
|
||||||
SymbolIterator syms = ghidraSymTbl.getSymbols(symName);
|
|
||||||
Symbol sym;
|
|
||||||
while (syms.hasNext()) {
|
|
||||||
sym = syms.next();
|
|
||||||
println("Deleting matching Ghidra symbol: " + sym.getName());
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any symbol in the Ghidra symbol table at the same address
|
|
||||||
if ((sym = getSymbolAt(symLocAddr)) != null) {
|
|
||||||
println("Deleting symbol at target address: " + sym.getName());
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (symType) {
|
|
||||||
case 0: // Undefined Symbol
|
|
||||||
println("NULL symType!");
|
|
||||||
break;
|
|
||||||
case 2: // Local Absolute
|
|
||||||
case 3: // Global Absolute
|
|
||||||
case 6: // Local Data
|
|
||||||
case 7: // Global Data
|
|
||||||
case 8: // Local BSS
|
|
||||||
case 9: // Global BSS
|
|
||||||
// Data: log the symbol & create a Ghidra symbol at symLocAddr
|
|
||||||
output.println(symLocAddr.toString() + "\t" + symName);
|
|
||||||
createLabel(symLocAddr, symName, true);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram, monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4: // Local .text
|
|
||||||
case 5: // Global .text
|
|
||||||
// Code: log the symbol, disassemble, & create/name function
|
|
||||||
output.println(symLocAddr.toString() + "\t" + symName);
|
|
||||||
goTo(symLocAddr);
|
|
||||||
disassemble(symLocAddr);
|
|
||||||
createFunction(symLocAddr, symName);
|
|
||||||
if (getFunctionAt(symLocAddr) != null) {
|
|
||||||
getFunctionAt(symLocAddr).setName(symName, SourceType.USER_DEFINED);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram,
|
|
||||||
monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println("createFunction: Failed to create function");
|
|
||||||
output.println("createFunction: Failed to create function");
|
|
||||||
createLabel(symLocAddr, symName, true);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram,
|
|
||||||
monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
println("Invalid symType!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
symEntry = symEntry.add(SYM_ENTRY_SIZE); // goto next entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,242 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
// VxWorksSymTab_6_1 is an adaptation of the vxWorksSymTab script. It was modified by a customer
|
|
||||||
// to use a single loop, instead of two. It also added demangling of C++ symbol names - at least
|
|
||||||
// those that Ghidra knows how to demangle.
|
|
||||||
//
|
|
||||||
// Extracts all symbols in a VxWorks symbol table and disassembles
|
|
||||||
// the global functions. Any existing symbols in the Ghidra symbol table
|
|
||||||
// that collide with symbols defined in the VxWorks symbol table are deleted.
|
|
||||||
//
|
|
||||||
// The VxWorks symbol table is an array of symbol table entries [0..n-1]
|
|
||||||
// followed by a 32-bit value that is equal to n (number of sym tbl entries).
|
|
||||||
// Each entry in the array has the following structure:
|
|
||||||
//
|
|
||||||
// // Total size: 0x18 (24) bytes
|
|
||||||
// 0x00 int NULL
|
|
||||||
// 0x04 char *symNameAddr // symbol name
|
|
||||||
// 0x08 void *symLocAddr // location of object named by symbol
|
|
||||||
// 0x0c int NULL
|
|
||||||
// 0x10 int NULL
|
|
||||||
// 0x14 uchar symType // see switch statement below
|
|
||||||
// 0x15 uchar fill[3]
|
|
||||||
//
|
|
||||||
// The script requests:
|
|
||||||
// - Output file name: Each symbol name and address is recorded here.
|
|
||||||
// (Errors are also logged to this file.)
|
|
||||||
// - Address of "number of symbols" value: At the end of the symbol table,
|
|
||||||
// its length is recorded as a 32-bit integer. The
|
|
||||||
// script needs the address of that value to calculate
|
|
||||||
// the symbol table's start address.
|
|
||||||
//
|
|
||||||
// @category Customer Submission.vxWorks
|
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.app.cmd.label.DemanglerCmd;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
|
||||||
import ghidra.app.util.demangler.DemangledException;
|
|
||||||
import ghidra.app.util.demangler.MangledContext;
|
|
||||||
import ghidra.app.util.demangler.gnu.GnuDemangler;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.mem.Memory;
|
|
||||||
import ghidra.program.model.symbol.*;
|
|
||||||
|
|
||||||
public class VxWorksSymTab_6_1 extends GhidraScript {
|
|
||||||
|
|
||||||
static final int SYM_ENTRY_SIZE = 24;
|
|
||||||
static final int SYM_NAME_OFF = 4;
|
|
||||||
static final int SYM_LOC_OFF = 8;
|
|
||||||
static final int SYM_TYPE_OFF = 0x14;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() throws Exception {
|
|
||||||
|
|
||||||
// Get Memory and SymbolTable objects (used later)
|
|
||||||
Memory mem = currentProgram.getMemory();
|
|
||||||
SymbolTable ghidraSymTbl = currentProgram.getSymbolTable();
|
|
||||||
|
|
||||||
// Open output file
|
|
||||||
// All symbols found (address and name) will be logged to this file
|
|
||||||
try (PrintWriter output = new PrintWriter(
|
|
||||||
new FileOutputStream(askFile("vxWorks Symbol Table Parser", "Output file name?")))) {
|
|
||||||
|
|
||||||
// Get address of "total number of sym tbl entries" value
|
|
||||||
Address vxNumSymEntriesAddr = askAddress("vxWorks Symbol Table Parser",
|
|
||||||
"Address of \"total number of symbol table entries\" value?");
|
|
||||||
int vxNumSymEntries = mem.getInt(vxNumSymEntriesAddr);
|
|
||||||
println("VxWorks symbol table has " + vxNumSymEntries + " entries");
|
|
||||||
|
|
||||||
// Create a GNU demangler instance
|
|
||||||
GnuDemangler demangler = new GnuDemangler();
|
|
||||||
if (!demangler.canDemangle(currentProgram)) {
|
|
||||||
println("Unable to create demangler.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process entries in VxWorks symbol table
|
|
||||||
Address vxSymTbl = vxNumSymEntriesAddr.subtract(vxNumSymEntries * SYM_ENTRY_SIZE);
|
|
||||||
for (int i = 0; i < vxNumSymEntries; i++) {
|
|
||||||
|
|
||||||
if (monitor.isCancelled()) {
|
|
||||||
return; // check for cancel button
|
|
||||||
}
|
|
||||||
println("i=" + i); // visual counter
|
|
||||||
|
|
||||||
// Extract symbol table entry values
|
|
||||||
Address symEntry = vxSymTbl.add(i * SYM_ENTRY_SIZE);
|
|
||||||
Address symNameAddr = toAddr(mem.getInt(symEntry.add(SYM_NAME_OFF)));
|
|
||||||
Address symLocAddr = toAddr(mem.getInt(symEntry.add(SYM_LOC_OFF)));
|
|
||||||
byte symType = mem.getByte(symEntry.add(SYM_TYPE_OFF));
|
|
||||||
println("symNameAddr: 0x" + symNameAddr.toString() + ", symLocAddr: 0x" +
|
|
||||||
symLocAddr.toString() + ", symType: " + symType);
|
|
||||||
|
|
||||||
// Remove any data or instructions that overlap this symName
|
|
||||||
// (May happen if disassembly creates invalid references)
|
|
||||||
Address a;
|
|
||||||
String symName;
|
|
||||||
for (a = symNameAddr; mem.getByte(a) != 0; a = a.add(1)) {
|
|
||||||
if (getDataAt(a) != null) {
|
|
||||||
removeDataAt(a);
|
|
||||||
}
|
|
||||||
if (getInstructionAt(a) != null) {
|
|
||||||
removeInstructionAt(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (getDataAt(a) != null) {
|
|
||||||
removeDataAt(a);
|
|
||||||
}
|
|
||||||
if (getInstructionAt(a) != null) {
|
|
||||||
removeInstructionAt(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn *symNameAddr into a string and store it in symName
|
|
||||||
try {
|
|
||||||
symName = (String) createAsciiString(symNameAddr).getValue();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
println("createAsciiString: caught exception...");
|
|
||||||
println(e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println("symName: " + symName);
|
|
||||||
|
|
||||||
// Demangle symName
|
|
||||||
String symDemangledName = null;
|
|
||||||
try {
|
|
||||||
// if successful, symDemangledName will be non-NULL
|
|
||||||
MangledContext mangledContext = demangler.createMangledContext(symDemangledName,
|
|
||||||
null, currentProgram, symNameAddr);
|
|
||||||
symDemangledName = demangler.demangle(mangledContext).getSignature(false);
|
|
||||||
}
|
|
||||||
catch (DemangledException e) {
|
|
||||||
// if symName wasn't a mangled name, silently continue
|
|
||||||
if (!e.isInvalidMangledName()) {
|
|
||||||
println("demangle: Demangling error");
|
|
||||||
output.println("demangle: Demangling error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
println("demangle: Caught runtime exception");
|
|
||||||
output.println("demangle: Caught runtime exception");
|
|
||||||
}
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
println("symDemangledName: " + symDemangledName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any symbol in the Ghidra symbol table with the same name
|
|
||||||
SymbolIterator syms = ghidraSymTbl.getSymbols(symName);
|
|
||||||
Symbol sym;
|
|
||||||
while (syms.hasNext()) {
|
|
||||||
sym = syms.next();
|
|
||||||
println("Deleting matching Ghidra symbol: " + sym.getName());
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any symbol in the Ghidra symbol table at the same address
|
|
||||||
if ((sym = getSymbolAt(symLocAddr)) != null) {
|
|
||||||
println("Deleting symbol at target address: " + sym.getName());
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (symType) {
|
|
||||||
case 0: // Undefined Symbol
|
|
||||||
println("NULL symType!");
|
|
||||||
break;
|
|
||||||
case 2: // Local Absolute
|
|
||||||
case 3: // Global Absolute
|
|
||||||
case 6: // Local Data
|
|
||||||
case 7: // Global Data
|
|
||||||
case 8: // Local BSS
|
|
||||||
case 9: // Global BSS
|
|
||||||
// Data: log the symbol & create a Ghidra symbol at symLocAddr
|
|
||||||
output.println(symLocAddr.toString() + "\t" + symName);
|
|
||||||
createLabel(symLocAddr, symName, true);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram, monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4: // Local .text
|
|
||||||
case 5: // Global .text
|
|
||||||
// Code: log the symbol, disassemble, & create/name function
|
|
||||||
output.println(symLocAddr.toString() + "\t" + symName);
|
|
||||||
goTo(symLocAddr);
|
|
||||||
disassemble(symLocAddr);
|
|
||||||
createFunction(symLocAddr, symName);
|
|
||||||
if (getFunctionAt(symLocAddr) != null) {
|
|
||||||
getFunctionAt(symLocAddr).setName(symName, SourceType.USER_DEFINED);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram,
|
|
||||||
monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println("createFunction: Failed to create function");
|
|
||||||
output.println("createFunction: Failed to create function");
|
|
||||||
createLabel(symLocAddr, symName, true);
|
|
||||||
if (symDemangledName != null) {
|
|
||||||
new DemanglerCmd(symLocAddr, symName).applyTo(currentProgram,
|
|
||||||
monitor);
|
|
||||||
List<Symbol> symbols =
|
|
||||||
getSymbols(symName, currentProgram.getGlobalNamespace());
|
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
ghidraSymTbl.removeSymbolSpecial(symbols.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
println("Invalid symType!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
symEntry = symEntry.add(SYM_ENTRY_SIZE); // goto next entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,24 +39,25 @@
|
||||||
// - Modify getVxSymbolClass() to recognize your program's VxWorks
|
// - Modify getVxSymbolClass() to recognize your program's VxWorks
|
||||||
// symbol table entry structure, if necessary
|
// symbol table entry structure, if necessary
|
||||||
//
|
//
|
||||||
// @category Customer Submission.vxWorks
|
// @category VxWorks
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.cmd.data.CreateDataCmd;
|
||||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||||
import ghidra.app.cmd.label.DemanglerCmd;
|
import ghidra.app.cmd.label.DemanglerCmd;
|
||||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.util.demangler.DemangledException;
|
import ghidra.app.util.PseudoDisassembler;
|
||||||
import ghidra.app.util.demangler.MangledContext;
|
import ghidra.app.util.demangler.*;
|
||||||
import ghidra.app.util.demangler.gnu.GnuDemangler;
|
import ghidra.app.util.demangler.gnu.GnuDemangler;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.Instruction;
|
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
|
|
||||||
public class VxWorksSymTab_Finder extends GhidraScript {
|
public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
|
|
||||||
|
@ -86,7 +87,8 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
|
|
||||||
private int getFieldOffset(StructureDataType dataType, String name) {
|
private int getFieldOffset(StructureDataType dataType, String name) {
|
||||||
for (DataTypeComponent comp : dataType.getComponents()) {
|
for (DataTypeComponent comp : dataType.getComponents()) {
|
||||||
if (comp.getFieldName().equals(name)) {
|
String fieldName = comp.getFieldName();
|
||||||
|
if (name.equals(fieldName)) {
|
||||||
return comp.getOffset();
|
return comp.getOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,9 +432,14 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
// return false;
|
// return false;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// symType field must be recognized type code (this test is weak)
|
// symType field must be recognized type code
|
||||||
byte symType = getByte(entry.add(vxSymbol.typeOffset()));
|
byte symType = getByte(entry.add(vxSymbol.typeOffset()));
|
||||||
if (!isValidSymType(symType)) {
|
byte zeroByte = 0;
|
||||||
|
if (vxSymbol.typeOffset+1 <= vxSymbol.length()) {
|
||||||
|
// type is always at end of symbol entry, if padded make sure is zero
|
||||||
|
zeroByte = getByte(entry.add(vxSymbol.typeOffset()+1));
|
||||||
|
}
|
||||||
|
if (!isValidSymType(symType) || zeroByte != 0) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
println("5: " + entry + " --> " + symType);
|
println("5: " + entry + " --> " + symType);
|
||||||
}
|
}
|
||||||
|
@ -454,50 +461,20 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
case 9: // Global BSS
|
case 9: // Global BSS
|
||||||
case 4: // Local .text
|
case 4: // Local .text
|
||||||
case 5: // Global .text
|
case 5: // Global .text
|
||||||
case 0x11: // External ref
|
case 0x10: // Local BSS 6.8
|
||||||
|
case 0x11: // Global BSS 6.8
|
||||||
|
case 0x12: // Local Common
|
||||||
|
case 0x13: // Global Common
|
||||||
|
case 0x20: // Local Common 6.8
|
||||||
|
case 0x21: // Global Common 6.8
|
||||||
|
case 0x40: // Local Symbols 6.8
|
||||||
|
case 0x41: // Global Symbols 6.8
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
// isStringPointerTable
|
|
||||||
//
|
|
||||||
// Check to see if the candidate symbol table is just a string pointer
|
|
||||||
// table.
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
private boolean isStringPointerTable(Address offset, int table_size) throws Exception {
|
|
||||||
if (debug) {
|
|
||||||
printf("Checking for string pointer table at 0x%x\n", offset.getOffset());
|
|
||||||
}
|
|
||||||
// Skip the first offset in the table because it can be null as a symbol table
|
|
||||||
Address cursor = offset.add(4);
|
|
||||||
long end = offset.add(table_size).getOffset();
|
|
||||||
|
|
||||||
while (cursor.getOffset() < end) {
|
|
||||||
long value = getInt(cursor) & 0xffffffffL;
|
|
||||||
if (isAddress(value)) {
|
|
||||||
if (!isValidSymbolString(toAddr(value))) {
|
|
||||||
if (debug) {
|
|
||||||
printf("Found non-string pointer in table at 0x%x (0x%x)\n",
|
|
||||||
cursor.getOffset(), value);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cursor = cursor.add(4);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (debug) {
|
|
||||||
printf("Found non-address in table at 0x%x", cursor.getOffset());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
// findSymTbl
|
// findSymTbl
|
||||||
//
|
//
|
||||||
|
@ -556,9 +533,6 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
}
|
}
|
||||||
if (i == testLen) {
|
if (i == testLen) {
|
||||||
// May have symbol table -- verify length
|
// May have symbol table -- verify length
|
||||||
int table_size = vxSymbol.length() * i;
|
|
||||||
|
|
||||||
if (!isStringPointerTable(cursor, table_size)) {
|
|
||||||
if (getSymTblLen(cursor, vxSymbol) != 0) {
|
if (getSymTblLen(cursor, vxSymbol) != 0) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
|
@ -568,15 +542,6 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
printf("Possible symbol table at " + cursor + " has length error\n");
|
printf("Possible symbol table at " + cursor + " has length error\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (debug) {
|
|
||||||
printf("False-positive: String pointer table at %s, skipping\n",
|
|
||||||
cursor.toString());
|
|
||||||
}
|
|
||||||
cursor = cursor.add(table_size);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor = cursor.add(4);
|
cursor = cursor.add(4);
|
||||||
}
|
}
|
||||||
|
@ -659,7 +624,12 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
if (symTblLenPtr != null) {
|
if (symTblLenPtr != null) {
|
||||||
removeConflictingSymbols("vxSymTblLen", symTblLenPtr);
|
removeConflictingSymbols("vxSymTblLen", symTblLenPtr);
|
||||||
createLabel(symTblLenPtr, "vxSymTblLen", true);
|
createLabel(symTblLenPtr, "vxSymTblLen", true);
|
||||||
createDWord(symTblLenPtr);
|
|
||||||
|
CreateDataCmd dtCmd = new CreateDataCmd(symTblLenPtr, false, DWordDataType.dataType);
|
||||||
|
boolean created = dtCmd.applyTo(currentProgram);
|
||||||
|
if (!created) {
|
||||||
|
println("Warning: Symbol Table size could not be created");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
println("Warning: Symbol Table Size not found before of after table");
|
println("Warning: Symbol Table Size not found before of after table");
|
||||||
|
@ -695,12 +665,11 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
private void applyDemangled(Address addr, String mangled, String demangled) {
|
private void applyDemangled(Address addr, String mangled, String demangled) {
|
||||||
|
|
||||||
if (demangled != null) {
|
if (demangled != null) {
|
||||||
new DemanglerCmd(addr, mangled).applyTo(currentProgram, monitor);
|
DemanglerOptions options = new DemanglerOptions();
|
||||||
List<Symbol> symbols =
|
options.setApplySignature(true);
|
||||||
getSymbols(mangled, currentProgram.getGlobalNamespace());
|
options.setApplyCallingConvention(true);
|
||||||
if (!symbols.isEmpty()) {
|
options.setDemangleOnlyKnownPatterns(false);
|
||||||
currentProgram.getSymbolTable().removeSymbolSpecial(symbols.get(0));
|
new DemanglerCmd(addr, mangled, options).applyTo(currentProgram, monitor);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -714,11 +683,22 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
// allows auto-analysis to operate with more information (and code/data
|
// allows auto-analysis to operate with more information (and code/data
|
||||||
// that isn't rapidly changing).
|
// that isn't rapidly changing).
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
private void doLocalDisassemble(Address addr) {
|
private boolean doLocalDisassemble(Address addr) {
|
||||||
|
|
||||||
// Only disassemble in memory blocks marked executable
|
// Only disassemble in memory blocks marked executable
|
||||||
if (!isExecute(addr)) {
|
if (!isExecute(addr)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PseudoDisassembler pdis = new PseudoDisassembler(currentProgram);
|
||||||
|
pdis.setMaxInstructions(20);
|
||||||
|
if (!pdis.checkValidSubroutine(addr, true, false, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be at least 2 contiguous instructions
|
||||||
|
if (pdis.getLastCheckValidInstructionCount()<2) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassembleCommand cmd = new DisassembleCommand(addr, null, true);
|
DisassembleCommand cmd = new DisassembleCommand(addr, null, true);
|
||||||
|
@ -728,7 +708,7 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
AddressSet set = cmd.getDisassembledAddressSet();
|
AddressSet set = cmd.getDisassembledAddressSet();
|
||||||
AutoAnalysisManager.getAnalysisManager(currentProgram).codeDefined(set);
|
AutoAnalysisManager.getAnalysisManager(currentProgram).codeDefined(set);
|
||||||
|
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
@ -764,6 +744,8 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
int symTblLen = getSymTblLen(symTbl, vxSymbol);
|
int symTblLen = getSymTblLen(symTbl, vxSymbol);
|
||||||
println("Symbol table at " + symTbl + " (" + symTblLen + " entries)");
|
println("Symbol table at " + symTbl + " (" + symTblLen + " entries)");
|
||||||
|
|
||||||
|
currentProgram.getOptions(Program.PROGRAM_INFO).setString("Framework", "vxWorks");
|
||||||
|
|
||||||
// Name the VxWorks symbol table
|
// Name the VxWorks symbol table
|
||||||
removeConflictingSymbols("vxSymTbl", symTbl);
|
removeConflictingSymbols("vxSymTbl", symTbl);
|
||||||
createLabel(symTbl, "vxSymTbl", true);
|
createLabel(symTbl, "vxSymTbl", true);
|
||||||
|
@ -836,9 +818,6 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
symType + ", name: " + symName);
|
symType + ", name: " + symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any conflicting symbols from the Ghidra symbol table
|
|
||||||
removeConflictingSymbols(symName, symLoc);
|
|
||||||
|
|
||||||
// If entry type is data, simply create a Ghidra symbol for it.
|
// If entry type is data, simply create a Ghidra symbol for it.
|
||||||
// If entry type is code, disassemble it and create function.
|
// If entry type is code, disassemble it and create function.
|
||||||
switch (symType) {
|
switch (symType) {
|
||||||
|
@ -855,27 +834,26 @@ public class VxWorksSymTab_Finder extends GhidraScript {
|
||||||
case 9: // Global BSS
|
case 9: // Global BSS
|
||||||
case 0x11: // External ref
|
case 0x11: // External ref
|
||||||
|
|
||||||
createLabel(symLoc, symName, true);
|
createLabel(symLoc, symName, true, SourceType.IMPORTED);
|
||||||
applyDemangled(symLoc, symName, symDemangledName);
|
applyDemangled(symLoc, symName, symDemangledName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: // Local .text
|
case 4: // Local .text
|
||||||
case 5: // Global .text
|
case 5: // Global .text
|
||||||
|
|
||||||
doLocalDisassemble(symLoc);
|
createLabel(symLoc, symName, true, SourceType.IMPORTED);
|
||||||
createFunction(symLoc, symName);
|
boolean isCode = doLocalDisassemble(symLoc);
|
||||||
if (getFunctionAt(symLoc) != null) {
|
if (isCode) {
|
||||||
getFunctionAt(symLoc).setName(symName, SourceType.USER_DEFINED);
|
Function function = createFunction(symLoc, symName);
|
||||||
applyDemangled(symLoc, symName, symDemangledName);
|
if (function == null) {
|
||||||
|
println("createFunction: Failed to create function " + symLoc);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
println("createFunction: Failed to create function");
|
|
||||||
createLabel(symLoc, symName, true);
|
|
||||||
applyDemangled(symLoc, symName, symDemangledName);
|
|
||||||
}
|
}
|
||||||
|
applyDemangled(symLoc, symName, symDemangledName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
createLabel(symLoc, symName, true, SourceType.IMPORTED);
|
||||||
println("Invalid symType " + symType + " !");
|
println("Invalid symType " + symType + " !");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,11 +141,15 @@ def program_context(
|
||||||
|
|
||||||
### pyghidra.analyze()
|
### pyghidra.analyze()
|
||||||
```python
|
```python
|
||||||
def analyze(program: "Program"):
|
def analyze(
|
||||||
|
program: "Program",
|
||||||
|
monitor: Optional["TaskMonitor"] = None
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Analyzes the given program.
|
Analyzes the given program.
|
||||||
|
|
||||||
:param program: The Ghidra program to analyze.
|
:param program: The Ghidra program to analyze.
|
||||||
|
:return: The analysis log.
|
||||||
"""
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -162,11 +162,12 @@ def program_context(
|
||||||
def analyze(
|
def analyze(
|
||||||
program: "Program",
|
program: "Program",
|
||||||
monitor: Optional["TaskMonitor"] = None
|
monitor: Optional["TaskMonitor"] = None
|
||||||
):
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Analyzes the given program.
|
Analyzes the given program.
|
||||||
|
|
||||||
:param program: The Ghidra program to analyze.
|
:param program: The Ghidra program to analyze.
|
||||||
|
:return: The analysis log.
|
||||||
"""
|
"""
|
||||||
from ghidra.app.script import GhidraScriptUtil
|
from ghidra.app.script import GhidraScriptUtil
|
||||||
from ghidra.program.util import GhidraProgramUtilities
|
from ghidra.program.util import GhidraProgramUtilities
|
||||||
|
@ -181,12 +182,16 @@ def analyze(
|
||||||
mgr: AutoAnalysisManager = AutoAnalysisManager.getAnalysisManager(program);
|
mgr: AutoAnalysisManager = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
mgr.initializeOptions();
|
mgr.initializeOptions();
|
||||||
mgr.reAnalyzeAll(None);
|
mgr.reAnalyzeAll(None);
|
||||||
analysisTool = mgr.getAnalysisTool();
|
mgr_log = ""
|
||||||
if analysisTool is None or analysisTool.threadIsBackgroundTaskThread():
|
def get_log(manager, _is_cancelled):
|
||||||
|
nonlocal mgr_log
|
||||||
|
mgr_log += manager.getMessageLog().toString()
|
||||||
|
mgr.addListener(get_log)
|
||||||
mgr.startAnalysis(monitor, True); # yields to analysis
|
mgr.startAnalysis(monitor, True); # yields to analysis
|
||||||
else:
|
if not monitor.isCancelled():
|
||||||
mgr.waitForAnalysis(None, monitor); # waits for all analysis to complete
|
monitor.cancel()
|
||||||
GhidraProgramUtilities.markProgramAnalyzed(program)
|
GhidraProgramUtilities.markProgramAnalyzed(program)
|
||||||
|
return mgr_log
|
||||||
finally:
|
finally:
|
||||||
GhidraScriptUtil.releaseBundleHostReference()
|
GhidraScriptUtil.releaseBundleHostReference()
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,10 @@ public class SarifController implements ObjectSelectedListener<Map<String, Objec
|
||||||
|
|
||||||
public void showTable(String logName, SarifSchema210 sarif) {
|
public void showTable(String logName, SarifSchema210 sarif) {
|
||||||
SarifDataFrame df = new SarifDataFrame(sarif, this, false);
|
SarifDataFrame df = new SarifDataFrame(sarif, this, false);
|
||||||
|
int size = df.getTableResults().size();
|
||||||
|
if (size != 0) {
|
||||||
|
logName += " ["+size+"]";
|
||||||
|
}
|
||||||
SarifResultsTableProvider provider =
|
SarifResultsTableProvider provider =
|
||||||
new SarifResultsTableProvider(logName, getPlugin(), this, df);
|
new SarifResultsTableProvider(logName, getPlugin(), this, df);
|
||||||
provider.filterTable.addSelectionListener(this);
|
provider.filterTable.addSelectionListener(this);
|
||||||
|
@ -278,7 +282,6 @@ public class SarifController implements ObjectSelectedListener<Map<String, Objec
|
||||||
return defaultGraphHandler;
|
return defaultGraphHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void setDefaultGraphHander(Class<? extends SarifGraphRunHandler> clazz) {
|
public void setDefaultGraphHander(Class<? extends SarifGraphRunHandler> clazz) {
|
||||||
defaultGraphHandler = clazz;
|
defaultGraphHandler = clazz;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,31 @@ public class SarifUtils {
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsonArray setLocation(Address addr, String kind, String uri, String name, String fqname, int index) {
|
||||||
|
JsonArray locations = new JsonArray();
|
||||||
|
JsonObject element = new JsonObject();
|
||||||
|
locations.add(element);
|
||||||
|
JsonObject ploc = new JsonObject();
|
||||||
|
JsonArray lloc = new JsonArray();
|
||||||
|
element.add("physicalLocation", ploc);
|
||||||
|
element.add("logicalLocations", lloc);
|
||||||
|
JsonObject artifact = new JsonObject();
|
||||||
|
artifact.addProperty("uri", uri);
|
||||||
|
JsonObject address = new JsonObject();
|
||||||
|
ploc.add("artifactLocation", artifact);
|
||||||
|
ploc.add("address", address);
|
||||||
|
address.addProperty("absoluteAddress", addr.getOffset());
|
||||||
|
if (name != null) {
|
||||||
|
address.addProperty("name", name);
|
||||||
|
}
|
||||||
|
address.addProperty("kind", kind);
|
||||||
|
address.addProperty("fullyQualifiedName", fqname);
|
||||||
|
JsonObject ll = new JsonObject();
|
||||||
|
lloc.add(ll);
|
||||||
|
ll.addProperty("index", index);
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static AddressSet getLocations(Map<String, Object> result, Program program, AddressSet set)
|
public static AddressSet getLocations(Map<String, Object> result, Program program, AddressSet set)
|
||||||
throws AddressOverflowException {
|
throws AddressOverflowException {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* ###
|
||||||
|
* 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 sarif.export;
|
||||||
|
|
||||||
|
import ghidra.program.model.data.ISF.IsfObject;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
|
||||||
|
public class ExtLogicalLocation implements IsfObject {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String kind;
|
||||||
|
String decoratedName;
|
||||||
|
String fullyQualifiedName;
|
||||||
|
String uri;
|
||||||
|
|
||||||
|
public ExtLogicalLocation(String key, Function function, String location, String op) {
|
||||||
|
this.name = key;
|
||||||
|
this.kind = "variable";
|
||||||
|
this.decoratedName = op;
|
||||||
|
this.fullyQualifiedName = location + ":" + name;
|
||||||
|
this.uri = function.getProgram().getExecutablePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDecoratedName() {
|
||||||
|
return decoratedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullyQualfiedName() {
|
||||||
|
return fullyQualifiedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -67,6 +67,13 @@ public class SarifObject implements IsfObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SarifObject(String key, String ruleKey, ExtLogicalLocation lloc, JsonElement tree, Address addr, int index) {
|
||||||
|
this(key, ruleKey, tree);
|
||||||
|
if (addr != null) {
|
||||||
|
locations = SarifUtils.setLocation(addr, "data", lloc.uri, lloc.name, lloc.fullyQualifiedName, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void writeLocations(Address min, Address max) {
|
protected void writeLocations(Address min, Address max) {
|
||||||
if (SARIF) {
|
if (SARIF) {
|
||||||
locations = SarifUtils.setLocations(min, max);
|
locations = SarifUtils.setLocations(min, max);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* ###
|
||||||
|
* 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 sarif.export;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
|
public class WrappedLogicalLocation {
|
||||||
|
|
||||||
|
private ExtLogicalLocation lloc;
|
||||||
|
private Address addr;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public WrappedLogicalLocation(ExtLogicalLocation lloc, Address addr) {
|
||||||
|
this.lloc = lloc;
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtLogicalLocation getLogicalLocation() {
|
||||||
|
return lloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -191,6 +191,11 @@ public interface EmuSyscallLibrary<T> extends PcodeUseropLibrary<T> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getOutputType() {
|
||||||
|
return void.class;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PcodeUseropLibrary<?> getDefiningLibrary() {
|
public PcodeUseropLibrary<?> getDefiningLibrary() {
|
||||||
return syslib;
|
return syslib;
|
||||||
|
|
|
@ -123,6 +123,8 @@ icon.widget.imagepanel.zoom.out = icon.zoom.out
|
||||||
icon.widget.filterpanel.filter.off = filter_off.png
|
icon.widget.filterpanel.filter.off = filter_off.png
|
||||||
icon.widget.filterpanel.filter.on = filter_on.png
|
icon.widget.filterpanel.filter.on = filter_on.png
|
||||||
|
|
||||||
|
icon.text.field.clear = icon.delete[size(12,12)]
|
||||||
|
|
||||||
icon.widget.pathmanager.reset = trash-empty.png
|
icon.widget.pathmanager.reset = trash-empty.png
|
||||||
|
|
||||||
icon.widget.table.header.help = info_small.png
|
icon.widget.table.header.help = info_small.png
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.EmptyBorderButton;
|
||||||
|
import docking.widgets.label.GLabel;
|
||||||
|
import generic.theme.GIcon;
|
||||||
import gui.event.MouseBinding;
|
import gui.event.MouseBinding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,9 +32,7 @@ public class ActionBindingPanel extends JPanel {
|
||||||
private static final String DISABLED_HINT = "Select an action";
|
private static final String DISABLED_HINT = "Select an action";
|
||||||
|
|
||||||
private KeyEntryPanel keyEntryPanel;
|
private KeyEntryPanel keyEntryPanel;
|
||||||
private JCheckBox useMouseBindingCheckBox;
|
|
||||||
private MouseEntryTextField mouseEntryField;
|
private MouseEntryTextField mouseEntryField;
|
||||||
private JPanel textFieldPanel;
|
|
||||||
|
|
||||||
private DockingActionInputBindingListener listener;
|
private DockingActionInputBindingListener listener;
|
||||||
|
|
||||||
|
@ -47,42 +46,31 @@ public class ActionBindingPanel extends JPanel {
|
||||||
|
|
||||||
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||||
|
|
||||||
textFieldPanel = new JPanel(new BorderLayout());
|
|
||||||
|
|
||||||
keyEntryPanel = new KeyEntryPanel(20, ks -> listener.keyStrokeChanged(ks));
|
keyEntryPanel = new KeyEntryPanel(20, ks -> listener.keyStrokeChanged(ks));
|
||||||
keyEntryPanel.setDisabledHint(DISABLED_HINT);
|
keyEntryPanel.setDisabledHint(DISABLED_HINT);
|
||||||
keyEntryPanel.setEnabled(false); // enabled on action selection
|
keyEntryPanel.setEnabled(false); // enabled on action selection
|
||||||
|
|
||||||
mouseEntryField = new MouseEntryTextField(20, mb -> listener.mouseBindingChanged(mb));
|
mouseEntryField = new MouseEntryTextField(20, mb -> listener.mouseBindingChanged(mb));
|
||||||
mouseEntryField.setDisabledHint(DISABLED_HINT);
|
mouseEntryField.setDisabledHint(DISABLED_HINT);
|
||||||
mouseEntryField.setEnabled(false); // enabled on action selection
|
|
||||||
|
|
||||||
textFieldPanel.add(keyEntryPanel, BorderLayout.NORTH);
|
JButton clearMouseButton = new EmptyBorderButton(new GIcon("icon.text.field.clear"));
|
||||||
|
clearMouseButton.setName("Clear Mouse Binding");
|
||||||
|
clearMouseButton.addActionListener(e -> mouseEntryField.clearMouseBinding());
|
||||||
|
|
||||||
String checkBoxText = "Enter Mouse Binding";
|
GLabel keyBindingLabel = new GLabel("Key Binding: ");
|
||||||
useMouseBindingCheckBox = new GCheckBox(checkBoxText);
|
JTextField tf = keyEntryPanel.getTextField();
|
||||||
useMouseBindingCheckBox
|
keyBindingLabel.setLabelFor(tf);
|
||||||
.setToolTipText("When checked, the text field accepts mouse buttons");
|
|
||||||
useMouseBindingCheckBox.setName(checkBoxText);
|
|
||||||
useMouseBindingCheckBox.addItemListener(e -> updateTextField());
|
|
||||||
|
|
||||||
add(textFieldPanel);
|
GLabel mouseBindingLabel = new GLabel("Mouse Binding: ");
|
||||||
add(Box.createHorizontalStrut(5));
|
mouseBindingLabel.setLabelFor(mouseBindingLabel);
|
||||||
add(useMouseBindingCheckBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextField() {
|
add(keyBindingLabel);
|
||||||
|
add(keyEntryPanel);
|
||||||
if (useMouseBindingCheckBox.isSelected()) {
|
add(Box.createHorizontalStrut(30));
|
||||||
textFieldPanel.remove(keyEntryPanel);
|
add(mouseBindingLabel);
|
||||||
textFieldPanel.add(mouseEntryField, BorderLayout.NORTH);
|
add(mouseEntryField);
|
||||||
}
|
add(Box.createHorizontalStrut(2));
|
||||||
else {
|
add(clearMouseButton);
|
||||||
textFieldPanel.remove(mouseEntryField);
|
|
||||||
textFieldPanel.add(keyEntryPanel, BorderLayout.NORTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
validate();
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyBindingData(KeyStroke ks, MouseBinding mb) {
|
public void setKeyBindingData(KeyStroke ks, MouseBinding mb) {
|
||||||
|
@ -113,11 +101,6 @@ public class ActionBindingPanel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearMouseBinding() {
|
public void clearMouseBinding() {
|
||||||
mouseEntryField.clearField();
|
mouseEntryField.clearMouseBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMouseBinding() {
|
|
||||||
return useMouseBindingCheckBox.isSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package docking;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.widgets.EmptyBorderButton;
|
import docking.widgets.EmptyBorderButton;
|
||||||
import resources.Icons;
|
import generic.theme.GIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A panel that holds a {@link KeyEntryTextField} and a button for clearing the current key binding.
|
* A panel that holds a {@link KeyEntryTextField} and a button for clearing the current key binding.
|
||||||
|
@ -41,7 +41,7 @@ public class KeyEntryPanel extends JPanel {
|
||||||
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||||
|
|
||||||
keyEntryField = new KeyEntryTextField(columns, listener);
|
keyEntryField = new KeyEntryTextField(columns, listener);
|
||||||
clearButton = new EmptyBorderButton(Icons.DELETE_ICON);
|
clearButton = new EmptyBorderButton(new GIcon("icon.text.field.clear"));
|
||||||
clearButton.setName("Clear Key Binding");
|
clearButton.setName("Clear Key Binding");
|
||||||
clearButton.addActionListener(e -> keyEntryField.clearKeyStroke());
|
clearButton.addActionListener(e -> keyEntryField.clearKeyStroke());
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.*;
|
||||||
import java.awt.event.KeyListener;
|
import java.awt.event.FocusEvent.Cause;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
|
@ -47,7 +47,21 @@ public class KeyEntryTextField extends HintTextField {
|
||||||
getAccessibleContext().setAccessibleName(getName());
|
getAccessibleContext().setAccessibleName(getName());
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
|
// remove the default mouse listeners to prevent pasting
|
||||||
|
MouseListener[] oldListeners1 = getMouseListeners();
|
||||||
|
for (MouseListener l : oldListeners1) {
|
||||||
|
removeMouseListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
addKeyListener(new MyKeyListener());
|
addKeyListener(new MyKeyListener());
|
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
requestFocusInWindow(Cause.MOUSE_EVENT);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package docking;
|
package docking;
|
||||||
|
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.awt.event.FocusEvent.Cause;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -37,6 +38,12 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
getAccessibleContext().setAccessibleName(getName());
|
getAccessibleContext().setAccessibleName(getName());
|
||||||
this.listener = Objects.requireNonNull(listener);
|
this.listener = Objects.requireNonNull(listener);
|
||||||
|
|
||||||
|
// remove the default mouse listeners to prevent pasting
|
||||||
|
MouseListener[] oldListeners1 = getMouseListeners();
|
||||||
|
for (MouseListener l : oldListeners1) {
|
||||||
|
removeMouseListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
addMouseListener(new MyMouseListener());
|
addMouseListener(new MyMouseListener());
|
||||||
addKeyListener(new MyKeyListener());
|
addKeyListener(new MyKeyListener());
|
||||||
}
|
}
|
||||||
|
@ -63,10 +70,23 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
processMouseBinding(mb, false);
|
processMouseBinding(mb, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the state of this class, but does not notify listeners. This allows clients to
|
||||||
|
* control the state of the field without having a callback change the client state.
|
||||||
|
*/
|
||||||
public void clearField() {
|
public void clearField() {
|
||||||
processMouseBinding(null, false);
|
processMouseBinding(null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the state of this class and notifies this client. This effectively allows for the
|
||||||
|
* programmatic setting of the mouse binding in use to be null, or in the 'no mouse binding set'
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
public void clearMouseBinding() {
|
||||||
|
processMouseBinding(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void processMouseBinding(MouseBinding mb, boolean notify) {
|
private void processMouseBinding(MouseBinding mb, boolean notify) {
|
||||||
|
|
||||||
this.mouseBinding = mb;
|
this.mouseBinding = mb;
|
||||||
|
@ -90,6 +110,8 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestFocusInWindow(Cause.MOUSE_EVENT);
|
||||||
|
|
||||||
int modifiersEx = e.getModifiersEx();
|
int modifiersEx = e.getModifiersEx();
|
||||||
int button = e.getButton();
|
int button = e.getButton();
|
||||||
|
|
||||||
|
@ -102,6 +124,7 @@ public class MouseEntryTextField extends HintTextField {
|
||||||
}
|
}
|
||||||
|
|
||||||
processMouseBinding(new MouseBinding(button, modifiersEx), true);
|
processMouseBinding(new MouseBinding(button, modifiersEx), true);
|
||||||
|
|
||||||
e.consume();
|
e.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package docking.action;
|
package docking.action;
|
||||||
|
|
||||||
import java.awt.event.InputEvent;
|
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -103,9 +102,6 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
|
|
||||||
// map standard keystroke to action
|
// map standard keystroke to action
|
||||||
doAddKeyBinding(provider, action, keyStroke);
|
doAddKeyBinding(provider, action, keyStroke);
|
||||||
|
|
||||||
// map workaround keystroke to action
|
|
||||||
fixupAltGraphKeyStrokeMapping(provider, action, keyStroke);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String validateActionKeyBinding(DockingActionIf dockingAction, KeyStroke ks) {
|
public String validateActionKeyBinding(DockingActionIf dockingAction, KeyStroke ks) {
|
||||||
|
@ -145,24 +141,6 @@ public class KeyBindingsManager implements PropertyChangeListener {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fixupAltGraphKeyStrokeMapping(ComponentProvider provider, DockingActionIf action,
|
|
||||||
KeyStroke keyStroke) {
|
|
||||||
|
|
||||||
// special case
|
|
||||||
int modifiers = keyStroke.getModifiers();
|
|
||||||
if ((modifiers & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK) {
|
|
||||||
//
|
|
||||||
// Also register the 'Alt' binding with the 'Alt Graph' mask. This fixes the but
|
|
||||||
// on Windows (https://bugs.openjdk.java.net/browse/JDK-8194873)
|
|
||||||
// that have different key codes for the left and right Alt keys.
|
|
||||||
//
|
|
||||||
modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
|
|
||||||
KeyStroke updateKeyStroke =
|
|
||||||
KeyStroke.getKeyStroke(keyStroke.getKeyCode(), modifiers, false);
|
|
||||||
doAddKeyBinding(provider, action, updateKeyStroke, keyStroke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doAddKeyBinding(ComponentProvider provider, DockingActionIf action,
|
private void doAddKeyBinding(ComponentProvider provider, DockingActionIf action,
|
||||||
KeyStroke keyStroke) {
|
KeyStroke keyStroke) {
|
||||||
doAddKeyBinding(provider, action, keyStroke, keyStroke);
|
doAddKeyBinding(provider, action, keyStroke, keyStroke);
|
||||||
|
|
|
@ -94,7 +94,7 @@ public class KeyBindingUtils {
|
||||||
public static ToolOptions importKeyBindings() {
|
public static ToolOptions importKeyBindings() {
|
||||||
// show a filechooser for the user to choose a location
|
// show a filechooser for the user to choose a location
|
||||||
InputStream inputStream = getInputStreamForFile(getStartingDir());
|
InputStream inputStream = getInputStreamForFile(getStartingDir());
|
||||||
return createOptionsforKeybindings(inputStream);
|
return createOptionsforKeyBindings(inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,7 +107,7 @@ public class KeyBindingUtils {
|
||||||
* @return An options object that is composed of key binding names and their
|
* @return An options object that is composed of key binding names and their
|
||||||
* associated keystrokes.
|
* associated keystrokes.
|
||||||
*/
|
*/
|
||||||
public static ToolOptions createOptionsforKeybindings(InputStream inputStream) {
|
public static ToolOptions createOptionsforKeyBindings(InputStream inputStream) {
|
||||||
if (inputStream == null) {
|
if (inputStream == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class SetKeyBindingAction extends DockingAction {
|
||||||
|
|
||||||
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
if (!action.getKeyBindingType().supportsKeyBindings()) {
|
||||||
Component parent = windowManager.getActiveComponent();
|
Component parent = windowManager.getActiveComponent();
|
||||||
Msg.showInfo(getClass(), parent, "Unable to Set Keybinding",
|
Msg.showInfo(getClass(), parent, "Unable to Set Key Binding",
|
||||||
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
"Action \"" + getActionName(action) + "\" does not support key bindings");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ public class ToolActions implements DockingToolActions, PropertyChangeListener {
|
||||||
private void loadKeyBindingFromOptions(DockingActionIf action, ActionTrigger actionTrigger) {
|
private void loadKeyBindingFromOptions(DockingActionIf action, ActionTrigger actionTrigger) {
|
||||||
|
|
||||||
String fullName = action.getFullName();
|
String fullName = action.getFullName();
|
||||||
String description = "Keybinding for " + fullName;
|
String description = "Key Binding for " + fullName;
|
||||||
options.registerOption(fullName, OptionType.ACTION_TRIGGER, actionTrigger, null,
|
options.registerOption(fullName, OptionType.ACTION_TRIGGER, actionTrigger, null,
|
||||||
description);
|
description);
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import docking.action.DockingActionIf;
|
||||||
import ghidra.docking.util.LookAndFeelUtils;
|
import ghidra.docking.util.LookAndFeelUtils;
|
||||||
import ghidra.util.StringUtilities;
|
import ghidra.util.StringUtilities;
|
||||||
|
|
||||||
class DockingToolBarUtils {
|
public class DockingToolBarUtils {
|
||||||
|
|
||||||
private static final String START_KEYBINDING_TEXT = "<BR><HR><CENTER>(";
|
private static final String START_KEYBINDING_TEXT = "<BR><HR><CENTER>(";
|
||||||
private static final String END_KEYBINDNIG_TEXT = ")</CENTER>";
|
private static final String END_KEYBINDNIG_TEXT = ")</CENTER>";
|
||||||
|
@ -37,16 +37,25 @@ class DockingToolBarUtils {
|
||||||
* @param button the button
|
* @param button the button
|
||||||
* @param action the action
|
* @param action the action
|
||||||
*/
|
*/
|
||||||
static void setToolTipText(JButton button, DockingActionIf action) {
|
public static void setToolTipText(JButton button, DockingActionIf action) {
|
||||||
|
String text = createToolTipText(button, action);
|
||||||
|
button.setToolTipText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates tooltip text for the given action and button. This is intended to be used for
|
||||||
|
* buttons that represent the given action.
|
||||||
|
* @param button the button that is the target for the text
|
||||||
|
* @param action the action that is the source of the button
|
||||||
|
* @return the text
|
||||||
|
*/
|
||||||
|
public static String createToolTipText(JButton button, DockingActionIf action) {
|
||||||
String toolTipText = getToolTipText(action);
|
String toolTipText = getToolTipText(action);
|
||||||
String keyBindingText = getKeyBindingAcceleratorText(button, action.getKeyBinding());
|
String keyBindingText = getKeyBindingAcceleratorText(button, action.getKeyBinding());
|
||||||
if (keyBindingText != null) {
|
if (keyBindingText != null) {
|
||||||
button.setToolTipText(combingToolTipTextWithKeyBinding(toolTipText, keyBindingText));
|
return combingToolTipTextWithKeyBinding(toolTipText, keyBindingText);
|
||||||
}
|
|
||||||
else {
|
|
||||||
button.setToolTipText(toolTipText);
|
|
||||||
}
|
}
|
||||||
|
return toolTipText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String combingToolTipTextWithKeyBinding(String toolTipText,
|
private static String combingToolTipTextWithKeyBinding(String toolTipText,
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class EmptyBorderButton extends JButton {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBorder(Border border) {
|
public void setBorder(Border border) {
|
||||||
// To keep UI from installing a non-appropriate border (such as when switching themes),
|
// To keep UI from installing an incorrect border (such as when switching themes),
|
||||||
// only allow borders created by this class to be set.
|
// only allow borders created by this class to be set.
|
||||||
if (border == RAISED_BUTTON_BORDER || border == LOWERED_BUTTON_BORDER ||
|
if (border == RAISED_BUTTON_BORDER || border == LOWERED_BUTTON_BORDER ||
|
||||||
border == FOCUSED_BUTTON_BORDER || border == NO_BUTTON_BORDER) {
|
border == FOCUSED_BUTTON_BORDER || border == NO_BUTTON_BORDER) {
|
||||||
|
@ -171,6 +171,12 @@ public class EmptyBorderButton extends JButton {
|
||||||
setBorder(NO_BUTTON_BORDER);
|
setBorder(NO_BUTTON_BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean b) {
|
||||||
|
setBorder(NO_BUTTON_BORDER);
|
||||||
|
super.setEnabled(b);
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateBorderBasedOnState() {
|
protected void updateBorderBasedOnState() {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,7 +26,7 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Class to render a String that has new line characters as a multiline
|
* Class to render a String that has new line characters as a multi-line
|
||||||
* label. Calculates the resizing and centering characteristics.
|
* label. Calculates the resizing and centering characteristics.
|
||||||
* <p>
|
* <p>
|
||||||
* Not affected by HTML formatting.
|
* Not affected by HTML formatting.
|
||||||
|
@ -49,12 +49,19 @@ public class MultiLineLabel extends JPanel {
|
||||||
protected String[] lines; // lines to text to display
|
protected String[] lines; // lines to text to display
|
||||||
protected int num_lines; // number of lines
|
protected int num_lines; // number of lines
|
||||||
protected int margin_width; // left and right margins
|
protected int margin_width; // left and right margins
|
||||||
protected int margin_height; // top and botton margins
|
protected int margin_height; // top and bottom margins
|
||||||
protected int line_height; // total height of font
|
protected int line_height; // total height of font
|
||||||
protected int line_ascent; // font height above baseline
|
protected int line_ascent; // font height above baseline
|
||||||
protected int[] line_widths; // how wide each line is
|
protected int[] line_widths; // how wide each line is
|
||||||
protected int max_width; // width of widest line
|
protected int max_width; // width of widest line
|
||||||
protected int alignment = CENTER; // default alignment of text
|
protected int alignment = CENTER; // default alignment of text
|
||||||
|
private VerticalAlignment verticalAlignment = VerticalAlignment.MIDDLE;
|
||||||
|
|
||||||
|
/** Values for controlling vertical alignment of the text */
|
||||||
|
public enum VerticalAlignment {
|
||||||
|
TOP,
|
||||||
|
MIDDLE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
|
@ -91,9 +98,9 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* breaks specified label into array of lines.
|
* Breaks specified label into array of lines.
|
||||||
*
|
*
|
||||||
*@param label String to display in canvas.
|
*@param label String to display.
|
||||||
*/
|
*/
|
||||||
protected void newLabel(String label) {
|
protected void newLabel(String label) {
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
|
@ -119,9 +126,6 @@ public class MultiLineLabel extends JPanel {
|
||||||
protected void measure() {
|
protected void measure() {
|
||||||
|
|
||||||
FontMetrics fm = this.getFontMetrics(this.getFont());
|
FontMetrics fm = this.getFontMetrics(this.getFont());
|
||||||
|
|
||||||
// if no font metrics yet, just return
|
|
||||||
|
|
||||||
if (fm == null) {
|
if (fm == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -139,9 +143,9 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a new label for JPanel
|
* Set a new label to display.
|
||||||
*
|
*
|
||||||
* @param label String to display in canvas
|
* @param label String to display
|
||||||
*/
|
*/
|
||||||
public void setLabel(String label) {
|
public void setLabel(String label) {
|
||||||
|
|
||||||
|
@ -163,7 +167,7 @@ public class MultiLineLabel extends JPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the label text.
|
* {@return the label text.}
|
||||||
*/
|
*/
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
@ -189,11 +193,6 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a new color for Canvas
|
|
||||||
*
|
|
||||||
*@param c Color to display in canvas
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setForeground(Color c) {
|
public void setForeground(Color c) {
|
||||||
super.setForeground(c);
|
super.setForeground(c);
|
||||||
|
@ -209,6 +208,15 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the vertical alignment of the text. The default is {@link VerticalAlignment#MIDDLE}.
|
||||||
|
* @param alignment the alignment
|
||||||
|
*/
|
||||||
|
public void setVerticalAlignment(VerticalAlignment alignment) {
|
||||||
|
this.verticalAlignment = alignment;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set margin width.
|
* Set margin width.
|
||||||
* @param mw the new margin width.
|
* @param mw the new margin width.
|
||||||
|
@ -227,29 +235,20 @@ public class MultiLineLabel extends JPanel {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get alignment for text, LEFT, CENTER, RIGHT.
|
|
||||||
*/
|
|
||||||
public final int getAlignment() {
|
public final int getAlignment() {
|
||||||
return alignment;
|
return alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get margin width.
|
|
||||||
*/
|
|
||||||
public final int getMarginWidth() {
|
public final int getMarginWidth() {
|
||||||
return margin_width;
|
return margin_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*Get margin height.
|
|
||||||
*/
|
|
||||||
public final int getMarginHeight() {
|
public final int getMarginHeight() {
|
||||||
return margin_height;
|
return margin_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is invoked after Canvas is first created
|
* This method is invoked after this class is first created
|
||||||
* but before it can be actually displayed. After we have
|
* but before it can be actually displayed. After we have
|
||||||
* invoked our superclass's addNotify() method, we have font
|
* invoked our superclass's addNotify() method, we have font
|
||||||
* metrics and can successfully call measure() to figure out
|
* metrics and can successfully call measure() to figure out
|
||||||
|
@ -257,48 +256,38 @@ public class MultiLineLabel extends JPanel {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addNotify() {
|
public void addNotify() {
|
||||||
|
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
measure();
|
measure();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called by a layout manager when it wants
|
|
||||||
* to know how big we'd like to be
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public java.awt.Dimension getPreferredSize() {
|
public Dimension getPreferredSize() {
|
||||||
return new Dimension(max_width + 2 * margin_width,
|
return new Dimension(max_width + 2 * margin_width,
|
||||||
num_lines * line_height + 2 * margin_height);
|
num_lines * line_height + 2 * margin_height);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when layout manager wants to
|
|
||||||
* know the bare minimum amount of space we need to get by.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public java.awt.Dimension getMinimumSize() {
|
public Dimension getMinimumSize() {
|
||||||
|
|
||||||
return new Dimension(max_width, num_lines * line_height);
|
return new Dimension(max_width, num_lines * line_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method draws label (applets use same method).
|
|
||||||
* Note that it handles the margins and the alignment, but
|
|
||||||
* that is does not have to worry about the color or font --
|
|
||||||
* the superclass takes care of setting those in the Graphics
|
|
||||||
* object we've passed.
|
|
||||||
* @param g the graphics context to paint with.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void paint(Graphics g) {
|
public void paint(Graphics g) {
|
||||||
|
|
||||||
int x, y;
|
paintBorder(g);
|
||||||
Dimension d = this.getSize();
|
|
||||||
// g.clearRect(0, 0, d.width, d.height);
|
|
||||||
|
|
||||||
|
Dimension d = this.getSize();
|
||||||
|
|
||||||
|
int y;
|
||||||
|
if (verticalAlignment == VerticalAlignment.MIDDLE) {
|
||||||
y = line_ascent + (d.height - num_lines * line_height) / 2;
|
y = line_ascent + (d.height - num_lines * line_height) / 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y = margin_height + line_ascent;
|
||||||
|
}
|
||||||
|
int x;
|
||||||
for (int i = 0; i < num_lines; i++, y += line_height) {
|
for (int i = 0; i < num_lines; i++, y += line_height) {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case LEFT:
|
case LEFT:
|
||||||
|
@ -313,14 +302,11 @@ public class MultiLineLabel extends JPanel {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsUtils.drawString(this, g, lines[i], x, y);
|
GraphicsUtils.drawString(this, g, lines[i], x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple test for the MultiLineLabel class.
|
|
||||||
* @param args not used
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
MultiLineLabel mlab = new MultiLineLabel(
|
MultiLineLabel mlab = new MultiLineLabel(
|
||||||
|
|
|
@ -15,36 +15,45 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.filechooser;
|
package docking.widgets.filechooser;
|
||||||
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.Color;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import generic.theme.GColor;
|
||||||
import generic.theme.GThemeDefaults.Colors;
|
import generic.theme.GThemeDefaults.Colors;
|
||||||
|
|
||||||
public class FileChooserToggleButton extends JToggleButton {
|
public class FileChooserToggleButton extends JToggleButton {
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
static final Border RAISED_BORDER = BorderFactory.createCompoundBorder(
|
//
|
||||||
BorderFactory.createRaisedBevelBorder(),
|
// All border sizes are based on trial-and-error, adjusted to prevent the UI from moving as the
|
||||||
BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
// user hovers and moves around with the keyboard.
|
||||||
|
//
|
||||||
|
private static final Border RAISED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createRaisedBevelBorder(), BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
|
||||||
static final Border NO_BORDER = new EmptyBorder(RAISED_BORDER.getBorderInsets(new JButton()));
|
private static final Border LOWERED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createLoweredBevelBorder(), BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
|
||||||
static final Border LOWERED_BORDER = BorderFactory.createCompoundBorder(
|
// The focused border is a blue line with some padding on the outside so it is easy to see when
|
||||||
BorderFactory.createLoweredBevelBorder(),
|
// the button has focus. This is similar to other buttons in the system.
|
||||||
BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
private static final Color FOCUS_COLOR = new GColor("color.border.button.focused");
|
||||||
|
private static final Border FOCUSED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createEmptyBorder(1, 1, 1, 1), BorderFactory.createLineBorder(FOCUS_COLOR));
|
||||||
|
private static final Border UNFOCUSED_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
|
||||||
|
|
||||||
public FileChooserToggleButton(String text) {
|
private static final Border NO_BORDER = new EmptyBorder(4, 4, 4, 4);
|
||||||
|
|
||||||
|
private GhidraFileChooser fileChooser;
|
||||||
|
|
||||||
|
public FileChooserToggleButton(String text, GhidraFileChooser fileChooser) {
|
||||||
super(text);
|
super(text);
|
||||||
initBorder();
|
this.fileChooser = fileChooser;
|
||||||
}
|
|
||||||
|
|
||||||
public FileChooserToggleButton(Action action) {
|
|
||||||
super(action);
|
|
||||||
initBorder();
|
initBorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,88 +68,92 @@ public class FileChooserToggleButton extends JToggleButton {
|
||||||
setContentAreaFilled(false);
|
setContentAreaFilled(false);
|
||||||
|
|
||||||
// changes the border on hover and click
|
// changes the border on hover and click
|
||||||
addMouseListener(new ButtonMouseListener());
|
addChangeListener(new ButtonStateListener());
|
||||||
|
|
||||||
// works in conjunction with the mouse listener to properly set the border
|
// works in conjunction with the mouse listener to properly set the border
|
||||||
addChangeListener(e -> {
|
addChangeListener(e -> updateBorderBasedOnState());
|
||||||
if (isSelected()) {
|
|
||||||
setBorder(LOWERED_BORDER);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setBorder(NO_BORDER);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setFocusable(false); // this prevents the focus box from being drawn over the button
|
addFocusListener(new ButtonFocusListener());
|
||||||
|
|
||||||
|
updateBorderBasedOnState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearBorder() {
|
@Override
|
||||||
|
public void setBorder(Border border) {
|
||||||
|
// To keep UI from installing an incorrect border (such as when switching themes),
|
||||||
|
// only allow borders created by this class to be set.
|
||||||
|
if (border == RAISED_BORDER || border == LOWERED_BORDER || border == NO_BORDER ||
|
||||||
|
border instanceof FocusedBorder) {
|
||||||
|
super.setBorder(border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearBorder() {
|
||||||
setBorder(NO_BORDER);
|
setBorder(NO_BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the directory with which this button is associated. */
|
/** {@return Returns the directory with which this button is associated.} */
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ButtonMouseListener extends MouseAdapter {
|
private void updateBorderBasedOnState() {
|
||||||
private boolean inside = false;
|
if (!isEnabled()) {
|
||||||
|
|
||||||
private Border defaultBorder;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered(MouseEvent me) {
|
|
||||||
if (isSelected()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBorder = getBorder();
|
ButtonModel buttonModel = getModel();
|
||||||
setBorder(RAISED_BORDER);
|
boolean pressed = buttonModel.isPressed();
|
||||||
inside = true;
|
boolean rollover = buttonModel.isRollover();
|
||||||
|
boolean armed = buttonModel.isArmed();
|
||||||
|
boolean selected = buttonModel.isSelected();
|
||||||
|
|
||||||
|
Border border = NO_BORDER;
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
border = LOWERED_BORDER;
|
||||||
|
}
|
||||||
|
else if (pressed && (rollover || armed)) {
|
||||||
|
border = LOWERED_BORDER;
|
||||||
|
}
|
||||||
|
else if (rollover) {
|
||||||
|
border = RAISED_BORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
border = createFocusedBorder(border, isFocusOwner());
|
||||||
|
|
||||||
|
setBorder(border);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Border createFocusedBorder(Border outside, boolean isFocused) {
|
||||||
|
Border inside = isFocused ? FOCUSED_BORDER : UNFOCUSED_BORDER;
|
||||||
|
return new FocusedBorder(outside, inside);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ButtonStateListener implements ChangeListener {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
updateBorderBasedOnState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ButtonFocusListener implements FocusListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
updateBorderBasedOnState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseExited(MouseEvent me) {
|
public void focusLost(FocusEvent e) {
|
||||||
if (isSelected()) {
|
updateBorderBasedOnState();
|
||||||
return;
|
fileChooser.updateShortcutPanel();
|
||||||
}
|
|
||||||
|
|
||||||
inside = false;
|
|
||||||
restoreBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
if (isSelected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
setBorder(LOWERED_BORDER);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class FocusedBorder extends CompoundBorder {
|
||||||
public void mouseReleased(MouseEvent e) {
|
FocusedBorder(Border outsideBorder, Border insideBorder) {
|
||||||
if (isSelected()) {
|
super(outsideBorder, insideBorder);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inside) {
|
|
||||||
setBorder(RAISED_BORDER);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
restoreBorder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreBorder() {
|
|
||||||
if (defaultBorder != null) {
|
|
||||||
setBorder(defaultBorder);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setBorder(NO_BORDER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import javax.swing.*;
|
||||||
|
|
||||||
import docking.ReusableDialogComponentProvider;
|
import docking.ReusableDialogComponentProvider;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.label.GLabel;
|
|
||||||
import ghidra.framework.preferences.Preferences;
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
|
|
||||||
|
@ -47,17 +46,13 @@ class GFileChooserOptionsDialog extends ReusableDialogComponentProvider {
|
||||||
private JComponent buildComponent() {
|
private JComponent buildComponent() {
|
||||||
JPanel panel = new JPanel(new PairLayout());
|
JPanel panel = new JPanel(new PairLayout());
|
||||||
|
|
||||||
showDotFilesCheckBox = new GCheckBox();
|
showDotFilesCheckBox = new GCheckBox("Show '.' files");
|
||||||
|
showDotFilesCheckBox.setToolTipText("When toggled on the file chooser will show files " +
|
||||||
|
"with names that begin with a '.' character");
|
||||||
showDotFilesCheckBox.getAccessibleContext().setAccessibleName("Show Dot Files");
|
showDotFilesCheckBox.getAccessibleContext().setAccessibleName("Show Dot Files");
|
||||||
showDotFilesCheckBox.setSelected(true);
|
showDotFilesCheckBox.setSelected(true);
|
||||||
|
|
||||||
JLabel label = new GLabel("Show '.' files");
|
|
||||||
label.getAccessibleContext().setAccessibleName("Show Files");
|
|
||||||
label.setToolTipText("When toggled on the file chooser will show files " +
|
|
||||||
"with names that begin with a '.' character");
|
|
||||||
|
|
||||||
panel.add(showDotFilesCheckBox);
|
panel.add(showDotFilesCheckBox);
|
||||||
panel.add(label);
|
|
||||||
panel.getAccessibleContext().setAccessibleName("GFile Chooser Options");
|
panel.getAccessibleContext().setAccessibleName("GFile Chooser Options");
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ package docking.widgets.filechooser;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -35,7 +37,11 @@ import javax.swing.text.DefaultFormatterFactory;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import docking.menu.DockingToolBarUtils;
|
||||||
import docking.widgets.*;
|
import docking.widgets.*;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
|
@ -166,13 +172,19 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
|
|
||||||
private Component parent;
|
private Component parent;
|
||||||
private JPanel waitPanel;
|
private JPanel waitPanel;
|
||||||
private EmptyBorderButton backButton;
|
private JButton backButton;
|
||||||
private EmptyBorderButton forwardButton;
|
private JButton forwardButton;
|
||||||
private EmptyBorderButton upLevelButton;
|
private JButton upButton;
|
||||||
private EmptyBorderButton newFolderButton;
|
private JButton newFolderButton;
|
||||||
private EmptyBorderButton refreshButton;
|
private JButton refreshButton;
|
||||||
private EmptyBorderToggleButton detailsButton;
|
private EmptyBorderToggleButton detailsButton;
|
||||||
|
|
||||||
|
private DockingAction upAction;
|
||||||
|
private DockingAction backAction;
|
||||||
|
private DockingAction forwardAction;
|
||||||
|
private KeyBindingChangeListener keyBindingChangeListener = new KeyBindingChangeListener();
|
||||||
|
|
||||||
|
private JPanel shortcutPanel;
|
||||||
private UnselectableButtonGroup shortCutButtonGroup;
|
private UnselectableButtonGroup shortCutButtonGroup;
|
||||||
private FileChooserToggleButton myComputerButton;
|
private FileChooserToggleButton myComputerButton;
|
||||||
private FileChooserToggleButton desktopButton;
|
private FileChooserToggleButton desktopButton;
|
||||||
|
@ -261,12 +273,43 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
setPreferredSize(800, 600);
|
setPreferredSize(800, 600);
|
||||||
|
|
||||||
updateDirOnly(newModel.getHomeDirectory(), true);
|
updateDirOnly(newModel.getHomeDirectory(), true);
|
||||||
|
|
||||||
|
createActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Setup Methods
|
// Setup Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
private void createActions() {
|
||||||
|
|
||||||
|
String owner = getClass().getSimpleName();
|
||||||
|
upAction = new ActionBuilder("Up One Level", owner)
|
||||||
|
.keyBinding("Alt Up")
|
||||||
|
.onAction(c -> goUp())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
backAction = new ActionBuilder("Last Folder Visited", owner)
|
||||||
|
.keyBinding("Alt Left")
|
||||||
|
.onAction(c -> goBack())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
forwardAction = new ActionBuilder("Previous Folder Visited", owner)
|
||||||
|
.keyBinding("Alt Right")
|
||||||
|
.onAction(c -> goForward())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
upAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
backAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
forwardAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
|
||||||
|
addAction(upAction);
|
||||||
|
addAction(backAction);
|
||||||
|
addAction(forwardAction);
|
||||||
|
|
||||||
|
updateNavigationButtonToolTips();
|
||||||
|
}
|
||||||
|
|
||||||
private JComponent buildWorkPanel() {
|
private JComponent buildWorkPanel() {
|
||||||
buildWaitPanel();
|
buildWaitPanel();
|
||||||
|
|
||||||
|
@ -302,7 +345,8 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildShortCutPanel() {
|
private JPanel buildShortCutPanel() {
|
||||||
myComputerButton = new FileChooserToggleButton("My Computer") {
|
|
||||||
|
myComputerButton = new FileChooserToggleButton("My Computer", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return MY_COMPUTER;
|
return MY_COMPUTER;
|
||||||
|
@ -314,7 +358,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
myComputerButton.addActionListener(e -> updateMyComputer());
|
myComputerButton.addActionListener(e -> updateMyComputer());
|
||||||
myComputerButton.setForeground(FOREROUND_COLOR);
|
myComputerButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
desktopButton = new FileChooserToggleButton("Desktop") {
|
desktopButton = new FileChooserToggleButton("Desktop", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getDesktopDirectory();
|
return fileChooserModel.getDesktopDirectory();
|
||||||
|
@ -327,7 +371,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
desktopButton.setForeground(FOREROUND_COLOR);
|
desktopButton.setForeground(FOREROUND_COLOR);
|
||||||
desktopButton.setEnabled(fileChooserModel.getDesktopDirectory() != null);
|
desktopButton.setEnabled(fileChooserModel.getDesktopDirectory() != null);
|
||||||
|
|
||||||
homeButton = new FileChooserToggleButton("Home") {
|
homeButton = new FileChooserToggleButton("Home", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getHomeDirectory();
|
return fileChooserModel.getHomeDirectory();
|
||||||
|
@ -339,7 +383,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
homeButton.addActionListener(e -> updateHome());
|
homeButton.addActionListener(e -> updateHome());
|
||||||
homeButton.setForeground(FOREROUND_COLOR);
|
homeButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
downloadsButton = new FileChooserToggleButton("Downloads") {
|
downloadsButton = new FileChooserToggleButton("Downloads", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getDownloadsDirectory();
|
return fileChooserModel.getDownloadsDirectory();
|
||||||
|
@ -350,7 +394,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
downloadsButton.addActionListener(e -> updateDownloads());
|
downloadsButton.addActionListener(e -> updateDownloads());
|
||||||
downloadsButton.setForeground(FOREROUND_COLOR);
|
downloadsButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
recentButton = new FileChooserToggleButton("Recent") {
|
recentButton = new FileChooserToggleButton("Recent", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return RECENT;
|
return RECENT;
|
||||||
|
@ -369,27 +413,30 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
shortCutButtonGroup.add(downloadsButton);
|
shortCutButtonGroup.add(downloadsButton);
|
||||||
shortCutButtonGroup.add(recentButton);
|
shortCutButtonGroup.add(recentButton);
|
||||||
|
|
||||||
JPanel shortCutPanel = new JPanel(new GridLayout(0, 1));
|
shortcutPanel = new JPanel(new GridLayout(0, 1));
|
||||||
shortCutPanel.getAccessibleContext().setAccessibleName("Short Cut");
|
shortcutPanel.getAccessibleContext().setAccessibleName("Short Cut");
|
||||||
DockingUtils.setTransparent(shortCutPanel);
|
DockingUtils.setTransparent(shortcutPanel);
|
||||||
shortCutPanel.add(myComputerButton);
|
shortcutPanel.add(myComputerButton);
|
||||||
shortCutPanel.add(desktopButton);
|
shortcutPanel.add(desktopButton);
|
||||||
shortCutPanel.add(homeButton);
|
shortcutPanel.add(homeButton);
|
||||||
shortCutPanel.add(downloadsButton);
|
shortcutPanel.add(downloadsButton);
|
||||||
shortCutPanel.add(recentButton);
|
shortcutPanel.add(recentButton);
|
||||||
|
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||||
panel.setBackground(SHORTCUT_BACKGROUND_COLOR);
|
panel.setBackground(SHORTCUT_BACKGROUND_COLOR);
|
||||||
panel.add(shortCutPanel, BorderLayout.NORTH);
|
panel.add(shortcutPanel, BorderLayout.NORTH);
|
||||||
panel.getAccessibleContext().setAccessibleName("Short Cut");
|
panel.getAccessibleContext().setAccessibleName("Short Cut");
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildFileNamePanel() {
|
private JPanel buildFileNamePanel() {
|
||||||
JLabel filenameLabel = new GDLabel("File name:");
|
JLabel filenameLabel = new GDLabel("Filename:");
|
||||||
|
|
||||||
FileDropDownSelectionDataModel model = new FileDropDownSelectionDataModel(this);
|
FileDropDownSelectionDataModel model = new FileDropDownSelectionDataModel(this);
|
||||||
filenameTextField = new DropDownSelectionTextField<>(model);
|
filenameTextField = new DropDownSelectionTextField<>(model);
|
||||||
|
filenameLabel.setLabelFor(filenameTextField);
|
||||||
filenameTextField.setMatchingWindowHeight(200);
|
filenameTextField.setMatchingWindowHeight(200);
|
||||||
filenameTextField.getAccessibleContext().setAccessibleName("Filename");
|
filenameTextField.getAccessibleContext().setAccessibleName("Filename");
|
||||||
filenameTextField.addCellEditorListener(new CellEditorListener() {
|
filenameTextField.addCellEditorListener(new CellEditorListener() {
|
||||||
|
@ -427,9 +474,10 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
JLabel filterLabel = new GLabel("Type:");
|
JLabel filterLabel = new GLabel("Type:");
|
||||||
filterLabel.getAccessibleContext().setAccessibleName("Filter");
|
filterLabel.getAccessibleContext().setAccessibleName("Filter");
|
||||||
filterCombo = new GComboBox<>();
|
filterCombo = new GComboBox<>();
|
||||||
|
filterLabel.setLabelFor(filterCombo);
|
||||||
filterCombo.setRenderer(GListCellRenderer.createDefaultTextRenderer(
|
filterCombo.setRenderer(GListCellRenderer.createDefaultTextRenderer(
|
||||||
fileFilter -> fileFilter != null ? fileFilter.getDescription() : ""));
|
fileFilter -> fileFilter != null ? fileFilter.getDescription() : ""));
|
||||||
filterCombo.getAccessibleContext().setAccessibleName("Filter");
|
filterCombo.getAccessibleContext().setAccessibleName("File Type Filter");
|
||||||
filterModel = (DefaultComboBoxModel<GhidraFileFilter>) filterCombo.getModel();
|
filterModel = (DefaultComboBoxModel<GhidraFileFilter>) filterCombo.getModel();
|
||||||
addFileFilter(GhidraFileFilter.ALL);
|
addFileFilter(GhidraFileFilter.ALL);
|
||||||
filterCombo.addItemListener(e -> rescanCurrentDirectory());
|
filterCombo.addItemListener(e -> rescanCurrentDirectory());
|
||||||
|
@ -444,22 +492,12 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return filenamePanel;
|
return filenamePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectionListener<T> implements DropDownSelectionChoiceListener<File> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void selectionChanged(File file) {
|
|
||||||
// take the selection and close the dialog
|
|
||||||
worker.schedule(new SetSelectedFileAndAcceptSelection(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JPanel buildHeaderPanel() {
|
private JPanel buildHeaderPanel() {
|
||||||
|
|
||||||
JPanel headerPanel = new JPanel(new GridBagLayout());
|
JPanel headerPanel = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
|
||||||
gbc.gridx = 0;
|
gbc.gridx = 0;
|
||||||
// gbc.insets = new Insets(PAD, PAD, PAD, PAD);
|
|
||||||
JButton[] navButtons = buildNavigationButtons();
|
JButton[] navButtons = buildNavigationButtons();
|
||||||
for (JButton element : navButtons) {
|
for (JButton element : navButtons) {
|
||||||
headerPanel.add(element, gbc);
|
headerPanel.add(element, gbc);
|
||||||
|
@ -613,6 +651,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private JButton[] buildNavigationButtons() {
|
private JButton[] buildNavigationButtons() {
|
||||||
|
|
||||||
backButton = new EmptyBorderButton(ICON_BACK);
|
backButton = new EmptyBorderButton(ICON_BACK);
|
||||||
backButton.setName("BACK_BUTTON");
|
backButton.setName("BACK_BUTTON");
|
||||||
backButton.setEnabled(false);
|
backButton.setEnabled(false);
|
||||||
|
@ -625,12 +664,13 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
forwardButton.setToolTipText("Go to previous folder visited");
|
forwardButton.setToolTipText("Go to previous folder visited");
|
||||||
forwardButton.addActionListener(e -> goForward());
|
forwardButton.addActionListener(e -> goForward());
|
||||||
|
|
||||||
upLevelButton = new EmptyBorderButton(ICON_UP);
|
upButton = new EmptyBorderButton(ICON_UP);
|
||||||
upLevelButton.setName(UP_BUTTON_NAME);
|
upButton.setName(UP_BUTTON_NAME);
|
||||||
upLevelButton.setToolTipText("Up one level");
|
upButton.setEnabled(false);
|
||||||
upLevelButton.addActionListener(e -> goUpOneDirectoryLevel());
|
upButton.setToolTipText("Up one level");
|
||||||
|
upButton.addActionListener(e -> goUp());
|
||||||
|
|
||||||
return new JButton[] { backButton, forwardButton, upLevelButton };
|
return new JButton[] { backButton, forwardButton, upButton };
|
||||||
}
|
}
|
||||||
|
|
||||||
private JButton[] buildNonNavigationButtons() {
|
private JButton[] buildNonNavigationButtons() {
|
||||||
|
@ -1382,15 +1422,17 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doSetSelectedFileAndUpdateDisplay(File file) {
|
private void doSetSelectedFileAndUpdateDisplay(File file) {
|
||||||
if (lastInputFocus != null) {
|
|
||||||
lastInputFocus.requestFocusInWindow();
|
Component toFocus = getRestoreFocusComponent();
|
||||||
|
if (toFocus != null) {
|
||||||
|
toFocus.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCR 4513 - exception if we don't cancel edits before changing the display
|
// exception if we don't cancel edits before changing the display
|
||||||
cancelEdits();
|
cancelEdits();
|
||||||
selectedFiles.setFile(file);
|
selectedFiles.setFile(file);
|
||||||
updateTextFieldForFile(file);
|
updateTextFieldForFile(file);
|
||||||
|
@ -1398,6 +1440,22 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
directoryModel.setSelectedFile(file); // the list or table display
|
directoryModel.setSelectedFile(file); // the list or table display
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Component getRestoreFocusComponent() {
|
||||||
|
// ensure we transfer focus to the directory or table when the view switches
|
||||||
|
if (isTableShowing()) {
|
||||||
|
if (lastInputFocus == directoryList) {
|
||||||
|
lastInputFocus = directoryTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lastInputFocus == directoryTable) {
|
||||||
|
lastInputFocus = directoryList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastInputFocus;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateTextFieldForFile(File file) {
|
private void updateTextFieldForFile(File file) {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1452,7 +1510,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goUpOneDirectoryLevel() {
|
private void goUp() {
|
||||||
cancelEdits();
|
cancelEdits();
|
||||||
|
|
||||||
if (currentDirectory() == null) {
|
if (currentDirectory() == null) {
|
||||||
|
@ -1521,8 +1579,16 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
updateDirAndSelectFile(currentDir, currentSelectedFile, true, false);
|
updateDirAndSelectFile(currentDir, currentSelectedFile, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShortcutPanel() {
|
void updateShortcutPanel() {
|
||||||
// make sure that if one of the shortcut buttons is selected, the directory matches that button
|
|
||||||
|
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
Component focusOwner = kfm.getFocusOwner();
|
||||||
|
if (focusOwner != null && !SwingUtilities.isDescendingFrom(focusOwner, shortcutPanel)) {
|
||||||
|
// only synchronize the button state if the user is not interacting with the buttons
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the selected button matches the current directory
|
||||||
File currentDirectory = currentDirectory();
|
File currentDirectory = currentDirectory();
|
||||||
checkShortCutButton(myComputerButton, currentDirectory);
|
checkShortCutButton(myComputerButton, currentDirectory);
|
||||||
checkShortCutButton(homeButton, currentDirectory);
|
checkShortCutButton(homeButton, currentDirectory);
|
||||||
|
@ -1556,13 +1622,24 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
history.clear();
|
history.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateNavigationButtonToolTips() {
|
||||||
|
String tip = DockingToolBarUtils.createToolTipText(backButton, backAction);
|
||||||
|
backButton.setToolTipText(tip);
|
||||||
|
|
||||||
|
tip = DockingToolBarUtils.createToolTipText(forwardButton, forwardAction);
|
||||||
|
forwardButton.setToolTipText(tip);
|
||||||
|
|
||||||
|
tip = DockingToolBarUtils.createToolTipText(upButton, upAction);
|
||||||
|
upButton.setToolTipText(tip);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateNavigationButtons() {
|
private void updateNavigationButtons() {
|
||||||
backButton.setEnabled(history.hasPrevious());
|
backButton.setEnabled(history.hasPrevious());
|
||||||
forwardButton.setEnabled(history.hasNext());
|
forwardButton.setEnabled(history.hasNext());
|
||||||
|
|
||||||
File dir = currentDirectory();
|
File dir = currentDirectory();
|
||||||
boolean enable = dir != null && dir.getParentFile() != null;
|
boolean enable = dir != null && dir.getParentFile() != null;
|
||||||
upLevelButton.setEnabled(enable);
|
upButton.setEnabled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHistoryWithSelectedFiles(HistoryEntry historyEntry) {
|
private void updateHistoryWithSelectedFiles(HistoryEntry historyEntry) {
|
||||||
|
@ -2235,7 +2312,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
public void runSwing() {
|
public void runSwing() {
|
||||||
setDirectoryList(myComputerFile, roots);
|
setDirectoryList(myComputerFile, roots);
|
||||||
setWaitPanelVisible(false);
|
setWaitPanelVisible(false);
|
||||||
Swing.runLater(() -> doSetSelectedFileAndUpdateDisplay(null));
|
setSelectedFileAndUpdateDisplay(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,6 +2331,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
setCurrentDirectoryDisplay(recentFile, addToHistory);
|
setCurrentDirectoryDisplay(recentFile, addToHistory);
|
||||||
List<File> list = CollectionUtils.asList(recentList, File.class);
|
List<File> list = CollectionUtils.asList(recentList, File.class);
|
||||||
setDirectoryList(recentFile, list);
|
setDirectoryList(recentFile, list);
|
||||||
|
setSelectedFileAndUpdateDisplay(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2430,4 +2508,26 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return parentDir.getName() + selectedFilesText;
|
return parentDir.getName() + selectedFilesText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SelectionListener<T> implements DropDownSelectionChoiceListener<File> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectionChanged(File file) {
|
||||||
|
// take the selection and close the dialog
|
||||||
|
worker.schedule(new SetSelectedFileAndAcceptSelection(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyBindingChangeListener implements PropertyChangeListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent e) {
|
||||||
|
String name = e.getPropertyName();
|
||||||
|
if (name.equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||||
|
updateNavigationButtonToolTips();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,8 @@ import org.jdesktop.animation.timing.interpolation.PropertySetter;
|
||||||
|
|
||||||
import docking.util.AnimationUtils;
|
import docking.util.AnimationUtils;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
|
import generic.theme.GIcon;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import resources.Icons;
|
|
||||||
import resources.ResourceManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A label that displays an icon that, when clicked, will clear the contents of the
|
* A label that displays an icon that, when clicked, will clear the contents of the
|
||||||
|
@ -39,8 +38,7 @@ import resources.ResourceManager;
|
||||||
*/
|
*/
|
||||||
public class ClearFilterLabel extends GIconLabel {
|
public class ClearFilterLabel extends GIconLabel {
|
||||||
|
|
||||||
private Icon RAW_ICON = Icons.DELETE_ICON;
|
private Icon ICON = new GIcon("icon.text.field.clear");
|
||||||
private Icon ICON = ResourceManager.getScaledIcon(RAW_ICON, 10, 10);
|
|
||||||
|
|
||||||
private static final float FULLY_TRANSPARENT = 0F;
|
private static final float FULLY_TRANSPARENT = 0F;
|
||||||
private static final float FULLY_OPAQUE = .6F;
|
private static final float FULLY_OPAQUE = .6F;
|
||||||
|
|
|
@ -203,7 +203,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelCurrentWorkerJob() {
|
private void cancelCurrentWorkerJob() {
|
||||||
if (worker != null && worker.isBusy()) {
|
if (worker != null) {
|
||||||
worker.clearAllJobsWithInterrupt_IKnowTheRisks();
|
worker.clearAllJobsWithInterrupt_IKnowTheRisks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,6 +145,11 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getOutputType() {
|
||||||
|
return void.class;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Method getJavaMethod() {
|
public Method getJavaMethod() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.pcode.emu.jit.analysis;
|
package ghidra.pcode.emu.jit.analysis;
|
||||||
|
|
||||||
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.*;
|
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.maxAddr;
|
||||||
|
import static ghidra.pcode.emu.jit.analysis.JitVarScopeModel.overlapsLeft;
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.collections4.iterators.ReverseListIterator;
|
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||||
|
@ -33,6 +33,7 @@ import ghidra.pcode.emu.jit.gen.GenConsts;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.var.*;
|
import ghidra.pcode.emu.jit.var.*;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -258,6 +259,13 @@ public class JitAllocationModel {
|
||||||
generateLoadCode(rv);
|
generateLoadCode(rv);
|
||||||
VarGen.generateValWriteCodeDirect(gen, type, vn, rv);
|
VarGen.generateValWriteCodeDirect(gen, type, vn, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the maximum address that would be occupied by the full primitive type}
|
||||||
|
*/
|
||||||
|
public Address maxPrimAddr() {
|
||||||
|
return vn.getAddress().add(type.ext().size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -296,9 +304,10 @@ public class JitAllocationModel {
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param type the p-code type of the value expected on the JVM stack by the proceeding
|
* @param type the p-code type of the value expected on the JVM stack by the proceeding
|
||||||
* bytecode
|
* bytecode
|
||||||
|
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*/
|
*/
|
||||||
void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv);
|
void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext, MethodVisitor rv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit bytecode to load the varnode's value onto the JVM stack.
|
* Emit bytecode to load the varnode's value onto the JVM stack.
|
||||||
|
@ -306,9 +315,10 @@ public class JitAllocationModel {
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param type the p-code type of the value produced on the JVM stack by the preceding
|
* @param type the p-code type of the value produced on the JVM stack by the preceding
|
||||||
* bytecode
|
* bytecode
|
||||||
|
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*/
|
*/
|
||||||
void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv);
|
void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext, MethodVisitor rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -334,14 +344,16 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
default void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
|
MethodVisitor rv) {
|
||||||
local().generateLoadCode(rv);
|
local().generateLoadCode(rv);
|
||||||
TypeConversions.generate(gen, this.type(), type, rv);
|
TypeConversions.generate(gen, this.type(), type, ext, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
default void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
TypeConversions.generate(gen, type, this.type(), rv);
|
MethodVisitor rv) {
|
||||||
|
TypeConversions.generate(gen, type, this.type(), ext, rv);
|
||||||
local().generateStoreCode(rv);
|
local().generateStoreCode(rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -387,9 +399,9 @@ public class JitAllocationModel {
|
||||||
* shifted to the right to place it into position.
|
* shifted to the right to place it into position.
|
||||||
*
|
*
|
||||||
* @param local the local variable allocated to this part
|
* @param local the local variable allocated to this part
|
||||||
* @param shift the number of bytes and direction to shift
|
* @param shift the number of bytes and direction to shift (+ is right)
|
||||||
*/
|
*/
|
||||||
public record MultiLocalPart(JvmLocal local, int shift) {
|
public record MultiLocalSub(JvmLocal local, int shift) {
|
||||||
private JitType chooseLargerType(JitType t1, JitType t2) {
|
private JitType chooseLargerType(JitType t1, JitType t2) {
|
||||||
return t1.size() > t2.size() ? t1 : t2;
|
return t1.size() > t2.size() ? t1 : t2;
|
||||||
}
|
}
|
||||||
|
@ -405,15 +417,17 @@ public class JitAllocationModel {
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param type the p-code type of the value expected on the stack by the proceeding
|
* @param type the p-code type of the value expected on the stack by the proceeding
|
||||||
* bytecode, which may be to load additional parts
|
* bytecode, which may be to load additional parts
|
||||||
|
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*
|
*
|
||||||
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
||||||
* expected type, otherwise bits may get dropped while positioning the value.
|
* expected type, otherwise bits may get dropped while positioning the value.
|
||||||
*/
|
*/
|
||||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
|
MethodVisitor rv) {
|
||||||
local.generateLoadCode(rv);
|
local.generateLoadCode(rv);
|
||||||
JitType tempType = chooseLargerType(local.type, type);
|
JitType tempType = chooseLargerType(local.type, type);
|
||||||
TypeConversions.generate(gen, local.type, tempType, rv);
|
TypeConversions.generate(gen, local.type, tempType, ext, rv);
|
||||||
if (shift > 0) {
|
if (shift > 0) {
|
||||||
switch (tempType) {
|
switch (tempType) {
|
||||||
case IntJitType t -> {
|
case IntJitType t -> {
|
||||||
|
@ -440,7 +454,7 @@ public class JitAllocationModel {
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TypeConversions.generate(gen, tempType, type, rv);
|
TypeConversions.generate(gen, tempType, type, ext, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -454,14 +468,16 @@ public class JitAllocationModel {
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param type the p-code type of the value expected on the stack by the proceeding
|
* @param type the p-code type of the value expected on the stack by the proceeding
|
||||||
* bytecode, which may be to load additional parts
|
* bytecode, which may be to load additional parts
|
||||||
|
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||||
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
* @param rv the visitor for the {@link JitCompiledPassage#run(int) run} method
|
||||||
*
|
*
|
||||||
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
* @implNote We must keep temporary values in a variable of the larger of the local's or the
|
||||||
* expected type, otherwise bits may get dropped while positioning the value.
|
* expected type, otherwise bits may get dropped while positioning the value.
|
||||||
*/
|
*/
|
||||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
|
MethodVisitor rv) {
|
||||||
JitType tempType = chooseLargerType(local.type, type);
|
JitType tempType = chooseLargerType(local.type, type);
|
||||||
TypeConversions.generate(gen, type, tempType, rv);
|
TypeConversions.generate(gen, type, tempType, ext, rv);
|
||||||
switch (tempType) {
|
switch (tempType) {
|
||||||
case IntJitType t -> {
|
case IntJitType t -> {
|
||||||
if (shift > 0) {
|
if (shift > 0) {
|
||||||
|
@ -483,9 +499,9 @@ public class JitAllocationModel {
|
||||||
rv.visitInsn(LUSHR);
|
rv.visitInsn(LUSHR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError("tempType = " + tempType);
|
||||||
}
|
}
|
||||||
TypeConversions.generate(gen, tempType, local.type, rv);
|
TypeConversions.generate(gen, tempType, local.type, ext, rv);
|
||||||
switch (local.type) {
|
switch (local.type) {
|
||||||
case IntJitType t -> {
|
case IntJitType t -> {
|
||||||
int mask = -1 >>> (Integer.SIZE - Byte.SIZE * type.size());
|
int mask = -1 >>> (Integer.SIZE - Byte.SIZE * type.size());
|
||||||
|
@ -524,6 +540,34 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record MultiLocalPart(List<MultiLocalSub> subs, SimpleJitType type) {
|
||||||
|
public void generateLoadCode(JitCodeGenerator gen, Ext ext, MethodVisitor rv) {
|
||||||
|
subs.get(0).generateLoadCode(gen, this.type, ext, rv);
|
||||||
|
for (MultiLocalSub sub : subs.subList(1, subs.size())) {
|
||||||
|
sub.generateLoadCode(gen, this.type, ext, rv);
|
||||||
|
switch (this.type) {
|
||||||
|
case IntJitType t -> rv.visitInsn(IOR);
|
||||||
|
case LongJitType t -> rv.visitInsn(LOR);
|
||||||
|
default -> throw new AssertionError("this.type = " + this.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeConversions.generate(gen, this.type, type, ext, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateStoreCode(JitCodeGenerator gen, Ext ext, MethodVisitor rv) {
|
||||||
|
TypeConversions.generate(gen, type, this.type, ext, rv);
|
||||||
|
for (MultiLocalSub sub : subs.subList(1, subs.size()).reversed()) {
|
||||||
|
switch (this.type) {
|
||||||
|
case IntJitType t -> rv.visitInsn(DUP);
|
||||||
|
case LongJitType t -> rv.visitInsn(DUP2);
|
||||||
|
default -> throw new AssertionError("this.type = " + this.type);
|
||||||
|
}
|
||||||
|
sub.generateStoreCode(gen, this.type, ext, rv);
|
||||||
|
}
|
||||||
|
subs.get(0).generateStoreCode(gen, this.type, ext, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The handler for a variable allocated in a composition of locals
|
* The handler for a variable allocated in a composition of locals
|
||||||
*
|
*
|
||||||
|
@ -537,6 +581,7 @@ public class JitAllocationModel {
|
||||||
*/
|
*/
|
||||||
public record MultiLocalVarHandler(List<MultiLocalPart> parts, JitType type)
|
public record MultiLocalVarHandler(List<MultiLocalPart> parts, JitType type)
|
||||||
implements VarHandler {
|
implements VarHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateInitCode(JitCodeGenerator gen, MethodVisitor iv) {
|
public void generateInitCode(JitCodeGenerator gen, MethodVisitor iv) {
|
||||||
// Generator calls local inits directly
|
// Generator calls local inits directly
|
||||||
|
@ -549,31 +594,23 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
parts.get(0).generateLoadCode(gen, this.type, rv);
|
MethodVisitor rv) {
|
||||||
for (MultiLocalPart part : parts.subList(1, parts.size())) {
|
for (MultiLocalPart part : parts) {
|
||||||
part.generateLoadCode(gen, this.type, rv);
|
part.generateLoadCode(gen, ext, rv);
|
||||||
switch (this.type) {
|
// TODO: Optimize case where last sub of cur is first sub of next
|
||||||
case IntJitType t -> rv.visitInsn(IOR);
|
|
||||||
case LongJitType t -> rv.visitInsn(LOR);
|
|
||||||
default -> throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
}
|
TypeConversions.generate(gen, this.type, type, ext, rv);
|
||||||
TypeConversions.generate(gen, this.type, type, rv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
TypeConversions.generate(gen, type, this.type, rv);
|
MethodVisitor rv) {
|
||||||
for (MultiLocalPart part : parts.subList(1, parts.size()).reversed()) {
|
TypeConversions.generate(gen, type, this.type, ext, rv);
|
||||||
switch (this.type) {
|
for (MultiLocalPart part : parts.reversed()) {
|
||||||
case IntJitType t -> rv.visitInsn(DUP);
|
part.generateStoreCode(gen, ext, rv);
|
||||||
case LongJitType t -> rv.visitInsn(DUP2);
|
// TODO: Optimize case where last sub of cur is first sub of next
|
||||||
default -> throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
part.generateStoreCode(gen, this.type, rv);
|
|
||||||
}
|
|
||||||
parts.get(0).generateStoreCode(gen, this.type, rv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,12 +636,14 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateLoadCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateLoadCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
|
MethodVisitor rv) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateStoreCode(JitCodeGenerator gen, JitType type, MethodVisitor rv) {
|
public void generateStoreCode(JitCodeGenerator gen, JitType type, Ext ext,
|
||||||
|
MethodVisitor rv) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,6 +750,15 @@ public class JitAllocationModel {
|
||||||
index());
|
index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the initialization of this variable.
|
||||||
|
*
|
||||||
|
* @param mv the method visitor
|
||||||
|
* @param nameThis the name of the class defining the containing method
|
||||||
|
*/
|
||||||
|
default void generateInitCode(MethodVisitor mv, String nameThis) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a load of this variable onto the JVM stack.
|
* Generate a load of this variable onto the JVM stack.
|
||||||
*
|
*
|
||||||
|
@ -816,9 +864,7 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateDeclCode(MethodVisitor mv, String nameThis, Label startLocals,
|
public void generateInitCode(MethodVisitor mv, String nameThis) {
|
||||||
Label endLocals) {
|
|
||||||
super.generateDeclCode(mv, nameThis, startLocals, endLocals);
|
|
||||||
mv.visitLdcInsn(0);
|
mv.visitLdcInsn(0);
|
||||||
mv.visitVarInsn(ISTORE, index());
|
mv.visitVarInsn(ISTORE, index());
|
||||||
}
|
}
|
||||||
|
@ -860,6 +906,54 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class JvmTempAlloc implements AutoCloseable {
|
||||||
|
final MethodVisitor mv;
|
||||||
|
final String prefix;
|
||||||
|
final Class<?> primitiveType;
|
||||||
|
final int startIndex;
|
||||||
|
final int count;
|
||||||
|
final int step;
|
||||||
|
final Label start;
|
||||||
|
final Label end;
|
||||||
|
|
||||||
|
JvmTempAlloc(MethodVisitor mv, String prefix, Class<?> primitiveType, int count,
|
||||||
|
int startIndex, int step, Label start, Label end) {
|
||||||
|
this.mv = mv;
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.primitiveType = primitiveType;
|
||||||
|
this.count = count;
|
||||||
|
this.startIndex = startIndex;
|
||||||
|
this.step = step;
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int idx(int i) {
|
||||||
|
if (i >= count) {
|
||||||
|
throw new IndexOutOfBoundsException(i);
|
||||||
|
}
|
||||||
|
return startIndex + i * step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitLocals() {
|
||||||
|
mv.visitLabel(end);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
String name = count == 1 ? prefix : (prefix + i);
|
||||||
|
mv.visitLocalVariable(name, Type.getDescriptor(primitiveType), null, start, end,
|
||||||
|
startIndex + step * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
releaseTemp(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final JitDataFlowModel dfm;
|
private final JitDataFlowModel dfm;
|
||||||
private final JitVarScopeModel vsm;
|
private final JitVarScopeModel vsm;
|
||||||
private final JitTypeModel tm;
|
private final JitTypeModel tm;
|
||||||
|
@ -871,12 +965,13 @@ public class JitAllocationModel {
|
||||||
private final Map<JitVal, VarHandler> handlers = new HashMap<>();
|
private final Map<JitVal, VarHandler> handlers = new HashMap<>();
|
||||||
private final Map<Varnode, VarHandler> handlersPerVarnode = new HashMap<>();
|
private final Map<Varnode, VarHandler> handlersPerVarnode = new HashMap<>();
|
||||||
private final NavigableMap<Address, JvmLocal> locals = new TreeMap<>();
|
private final NavigableMap<Address, JvmLocal> locals = new TreeMap<>();
|
||||||
|
private final Deque<JvmTempAlloc> tempAllocs = new LinkedList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the allocation model.
|
* Construct the allocation model.
|
||||||
*
|
*
|
||||||
* @param context the analysis context
|
* @param context the analysis context
|
||||||
* @param dfm the data flow moel
|
* @param dfm the data flow model
|
||||||
* @param vsm the variable scope model
|
* @param vsm the variable scope model
|
||||||
* @param tm the type model
|
* @param tm the type model
|
||||||
*/
|
*/
|
||||||
|
@ -912,18 +1007,70 @@ public class JitAllocationModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next free local index without reserving it
|
* Temporarily allocate the next {@code count} indices of local variables
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This should be used by operator code generators <em>after</em> all the
|
* These indices are reserved only within the scope of the {@code try-with-resources} block
|
||||||
* {@link JitBytesPcodeExecutorState state} bypassing local variables have been allocated. The
|
* creating the allocation. If the {@code primitiveType} is a {@code long} or {@code double},
|
||||||
* variables should be scoped to that operator only, so that the ids used are freed for the next
|
* then the number of actual indices allocated is multiplied by 2, such that the total number of
|
||||||
* operator.
|
* variables is given by {@code count}.
|
||||||
|
* <p>
|
||||||
|
* This should be used by operator code generators <em>after</em> all the local variables,
|
||||||
|
* including those used to bypass {@link JitBytesPcodeExecutorState state}, have been allocated,
|
||||||
|
* or else this may generate colliding indices. These variables ought to be released before the
|
||||||
|
* next operator's code generator is invoked.
|
||||||
|
* <p>
|
||||||
|
* <b>NOTE:</b> This will automatically invoke
|
||||||
|
* {@link MethodVisitor#visitLocalVariable(String, String, String, Label, Label, int)} and place
|
||||||
|
* the appropriate labels for you.
|
||||||
*
|
*
|
||||||
* @return the next id
|
* @param mv the method visitor
|
||||||
|
* @param prefix the name of the local variable, or its prefix if count > 1
|
||||||
|
* @param primitiveType the type of each variable. NOTE: If heterogeneous allocations are
|
||||||
|
* needed, invoke this method more than once in the {@code try-with-resources}
|
||||||
|
* assignment.
|
||||||
|
* @param count the number of variables to allocate
|
||||||
|
* @return the handle to the allocation.
|
||||||
*/
|
*/
|
||||||
public int nextFreeLocal() {
|
public JvmTempAlloc allocateTemp(MethodVisitor mv, String prefix, Class<?> primitiveType,
|
||||||
return nextLocal;
|
int count) {
|
||||||
|
if (count == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int startIndex = nextLocal;
|
||||||
|
int step = primitiveType == long.class || primitiveType == double.class ? 2 : 1;
|
||||||
|
int countIndices = count * step;
|
||||||
|
nextLocal += countIndices;
|
||||||
|
|
||||||
|
Label start = new Label();
|
||||||
|
Label end = new Label();
|
||||||
|
mv.visitLabel(start);
|
||||||
|
JvmTempAlloc temp =
|
||||||
|
new JvmTempAlloc(mv, prefix, primitiveType, count, startIndex, step, start, end);
|
||||||
|
tempAllocs.push(temp);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporarily allocate the next {@code count} indices of local {@code int} variables
|
||||||
|
*
|
||||||
|
* @param mv the method visitor
|
||||||
|
* @param prefix the name of the local variable, or its prefix if count > 1
|
||||||
|
* @param count the number of variables to allocate
|
||||||
|
* @return the handle to the allocation.
|
||||||
|
* @see #allocateTemp(MethodVisitor, String, Class, int)
|
||||||
|
*/
|
||||||
|
public JvmTempAlloc allocateTemp(MethodVisitor mv, String prefix, int count) {
|
||||||
|
return allocateTemp(mv, prefix, int.class, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseTemp(JvmTempAlloc alloc) {
|
||||||
|
JvmTempAlloc popped = tempAllocs.pop();
|
||||||
|
if (popped != alloc) {
|
||||||
|
throw new AssertionError("Temp allocations must obey stack semantics");
|
||||||
|
}
|
||||||
|
alloc.visitLocals();
|
||||||
|
nextLocal = alloc.startIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -934,12 +1081,10 @@ public class JitAllocationModel {
|
||||||
* @param desc the (whole) variable's descriptor
|
* @param desc the (whole) variable's descriptor
|
||||||
* @return the allocated JVM locals from most to least significant
|
* @return the allocated JVM locals from most to least significant
|
||||||
*/
|
*/
|
||||||
private List<JvmLocal> genFreeLocals(String name, List<SimpleJitType> types,
|
private List<JvmLocal> genFreeLocals(String name, List<? extends SimpleJitType> types,
|
||||||
VarDesc desc) {
|
VarDesc desc) {
|
||||||
JvmLocal[] result = new JvmLocal[types.size()];
|
JvmLocal[] result = new JvmLocal[types.size()];
|
||||||
Iterable<SimpleJitType> it = language.isBigEndian()
|
Iterable<? extends SimpleJitType> it = language.isBigEndian() ? types : types.reversed();
|
||||||
? types
|
|
||||||
: () -> new ReverseListIterator<SimpleJitType>(types);
|
|
||||||
long offset = desc.offset;
|
long offset = desc.offset;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (SimpleJitType t : it) {
|
for (SimpleJitType t : it) {
|
||||||
|
@ -1022,20 +1167,55 @@ public class JitAllocationModel {
|
||||||
* locals.
|
* locals.
|
||||||
*/
|
*/
|
||||||
private VarHandler createComplicatedHandler(Varnode vn) {
|
private VarHandler createComplicatedHandler(Varnode vn) {
|
||||||
Entry<Address, JvmLocal> leftEntry = locals.floorEntry(vn.getAddress());
|
JitType type = JitTypeBehavior.INTEGER.type(vn.getSize());
|
||||||
assert overlapsLeft(leftEntry.getValue().vn, vn);
|
NavigableMap<Varnode, MultiLocalPart> legs =
|
||||||
Address min = leftEntry.getKey();
|
new TreeMap<>(Comparator.comparing(Varnode::getAddress));
|
||||||
NavigableMap<Address, JvmLocal> sub = locals.subMap(min, true, maxAddr(vn), true);
|
switch (endian) {
|
||||||
|
case BIG -> {
|
||||||
List<MultiLocalPart> parts = new ArrayList<>();
|
Address address = vn.getAddress();
|
||||||
for (JvmLocal local : sub.values()) {
|
for (SimpleJitType legType : type.legTypes()) {
|
||||||
int offset = (int) switch (endian) {
|
Varnode legVn = new Varnode(address, legType.size());
|
||||||
case BIG -> maxAddr(leftEntry.getValue().vn).subtract(maxAddr(vn));
|
legs.put(legVn, new MultiLocalPart(new ArrayList<>(), legType));
|
||||||
case LITTLE -> vn.getAddress().subtract(leftEntry.getKey());
|
address = address.add(legType.size());
|
||||||
};
|
|
||||||
parts.add(new MultiLocalPart(local, offset));
|
|
||||||
}
|
}
|
||||||
return new MultiLocalVarHandler(parts, JitTypeBehavior.INTEGER.type(vn.getSize()));
|
}
|
||||||
|
case LITTLE -> {
|
||||||
|
Address address = maxAddr(vn);
|
||||||
|
for (SimpleJitType legType : type.legTypes()) {
|
||||||
|
address = address.subtract(legType.size() - 1);
|
||||||
|
Varnode legVn = new Varnode(address, legType.size());
|
||||||
|
legs.put(legVn, new MultiLocalPart(new ArrayList<>(), legType));
|
||||||
|
address = address.subtractWrap(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry<Address, JvmLocal> firstEntry = locals.floorEntry(vn.getAddress());
|
||||||
|
assert overlapsLeft(firstEntry.getValue().vn, vn);
|
||||||
|
Address min = firstEntry.getKey();
|
||||||
|
NavigableMap<Address, JvmLocal> sub = locals.subMap(min, true, maxAddr(vn), true);
|
||||||
|
for (JvmLocal local : sub.values()) {
|
||||||
|
Varnode startVn = legs.floorKey(local.vn);
|
||||||
|
if (startVn == null || !startVn.intersects(local.vn)) {
|
||||||
|
startVn = local.vn;
|
||||||
|
}
|
||||||
|
for (Entry<Varnode, MultiLocalPart> ent : legs.tailMap(startVn).entrySet()) {
|
||||||
|
Varnode legVn = ent.getKey();
|
||||||
|
if (!legVn.intersects(local.vn)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int offset = (int) switch (endian) {
|
||||||
|
case BIG -> maxAddr(local.vn).subtract(maxAddr(legVn));
|
||||||
|
case LITTLE -> legVn.getAddress().subtract(local.vn.getAddress());
|
||||||
|
};
|
||||||
|
ent.getValue().subs.add(new MultiLocalSub(local, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<MultiLocalPart> parts = List.copyOf(legs.values());
|
||||||
|
return new MultiLocalVarHandler(switch (endian) {
|
||||||
|
case BIG -> parts;
|
||||||
|
case LITTLE -> parts.reversed();
|
||||||
|
}, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -79,6 +79,10 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||||
return endian;
|
return endian;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Varnode truncVnFromRight(Varnode vn, int amt) {
|
||||||
|
return new Varnode(vn.getAddress(), vn.getSize() - amt);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove {@code amt} bytes from the right of the <em>varnode</em>.
|
* Remove {@code amt} bytes from the right of the <em>varnode</em>.
|
||||||
*
|
*
|
||||||
|
@ -94,10 +98,14 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||||
* @return the resulting value
|
* @return the resulting value
|
||||||
*/
|
*/
|
||||||
public JitVal truncFromRight(Varnode in1Vn, int amt, JitVal in1) {
|
public JitVal truncFromRight(Varnode in1Vn, int amt, JitVal in1) {
|
||||||
Varnode outVn = new Varnode(in1Vn.getAddress(), in1Vn.getSize() - amt);
|
Varnode outVn = truncVnFromRight(in1Vn, amt);
|
||||||
return subpiece(outVn, endian.isBigEndian() ? amt : 0, in1);
|
return subpiece(outVn, endian.isBigEndian() ? amt : 0, in1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Varnode truncVnFromLeft(Varnode vn, int amt) {
|
||||||
|
return new Varnode(vn.getAddress().add(amt), vn.getSize() - amt);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove {@code amt} bytes from the left of the <em>varnode</em>.
|
* Remove {@code amt} bytes from the left of the <em>varnode</em>.
|
||||||
*
|
*
|
||||||
|
@ -113,7 +121,7 @@ public class JitDataFlowArithmetic implements PcodeArithmetic<JitVal> {
|
||||||
* @return the resulting value
|
* @return the resulting value
|
||||||
*/
|
*/
|
||||||
public JitVal truncFromLeft(Varnode in1Vn, int amt, JitVal in1) {
|
public JitVal truncFromLeft(Varnode in1Vn, int amt, JitVal in1) {
|
||||||
Varnode outVn = new Varnode(in1Vn.getAddress().add(amt), in1Vn.getSize() - amt);
|
Varnode outVn = truncVnFromLeft(in1Vn, amt);
|
||||||
return subpiece(outVn, endian.isBigEndian() ? 0 : amt, in1);
|
return subpiece(outVn, endian.isBigEndian() ? 0 : amt, in1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,30 +224,40 @@ public class JitDataFlowState implements PcodeExecutorState<JitVal> {
|
||||||
*/
|
*/
|
||||||
protected List<JitVal> doGetDefinitions(NavigableMap<Long, JitVal> map, AddressSpace space,
|
protected List<JitVal> doGetDefinitions(NavigableMap<Long, JitVal> map, AddressSpace space,
|
||||||
long offset, int size) {
|
long offset, int size) {
|
||||||
|
long end = offset + size;
|
||||||
List<JitVal> result = new ArrayList<>();
|
List<JitVal> result = new ArrayList<>();
|
||||||
Entry<Long, JitVal> preEntry = map.lowerEntry(offset);
|
Entry<Long, JitVal> preEntry = map.lowerEntry(offset);
|
||||||
long cursor = offset;
|
long cursor = offset;
|
||||||
if (preEntry != null) {
|
if (preEntry != null) {
|
||||||
if (endOf(preEntry) > offset) {
|
if (endOf(preEntry) > offset) { // Do I intersect the lower entry?
|
||||||
JitVal preVal = preEntry.getValue();
|
JitVal preVal = preEntry.getValue();
|
||||||
Varnode preVn = new Varnode(space.getAddress(preEntry.getKey()), preVal.size());
|
Varnode preVn = new Varnode(space.getAddress(preEntry.getKey()), preVal.size());
|
||||||
int shave = (int) (offset - preEntry.getKey());
|
int shaveLeft = (int) (offset - preEntry.getKey());
|
||||||
JitVal truncVal = arithmetic.truncFromLeft(preVn, shave, preVal);
|
JitVal truncVal = arithmetic.truncFromLeft(preVn, shaveLeft, preVal);
|
||||||
|
if (endOf(preEntry) > end) { // Am I contained in the lower entry?
|
||||||
|
Varnode truncVn = arithmetic.truncVnFromLeft(preVn, shaveLeft);
|
||||||
|
int shaveRight = (int) (endOf(preEntry) - end);
|
||||||
|
truncVal = arithmetic.truncFromRight(truncVn, shaveRight, truncVal);
|
||||||
|
cursor = end;
|
||||||
|
result.add(truncVal);
|
||||||
|
}
|
||||||
|
else {
|
||||||
cursor = endOf(preEntry);
|
cursor = endOf(preEntry);
|
||||||
result.add(truncVal);
|
result.add(truncVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long end = offset + size;
|
}
|
||||||
for (Entry<Long, JitVal> entry : map.subMap(offset, end).entrySet()) {
|
for (Entry<Long, JitVal> entry : map.subMap(offset, end).entrySet()) {
|
||||||
if (entry.getKey() > cursor) {
|
if (entry.getKey() > cursor) {
|
||||||
result.add(new JitMissingVar(
|
result.add(new JitMissingVar(
|
||||||
new Varnode(space.getAddress(cursor), (int) (entry.getKey() - cursor))));
|
new Varnode(space.getAddress(cursor), (int) (entry.getKey() - cursor))));
|
||||||
}
|
}
|
||||||
if (endOf(entry) > end) {
|
if (endOf(entry) > end) { // Do I have off the end?
|
||||||
JitVal postVal = entry.getValue();
|
JitVal postVal = entry.getValue();
|
||||||
Varnode postVn = new Varnode(space.getAddress(entry.getKey()), postVal.size());
|
Varnode postVn = new Varnode(space.getAddress(entry.getKey()), postVal.size());
|
||||||
int shave = (int) (endOf(entry) - end);
|
int shave = (int) (endOf(entry) - end);
|
||||||
JitVal truncVal = arithmetic.truncFromRight(postVn, shave, postVal);
|
JitVal truncVal = arithmetic.truncFromRight(postVn, shave, postVal);
|
||||||
|
// NOTE: No need to check for contained here. Would have been caught above.
|
||||||
cursor = end;
|
cursor = end;
|
||||||
result.add(truncVal);
|
result.add(truncVal);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -152,17 +152,13 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||||
* Get the type behavior from the userop's Java method
|
* Get the type behavior from the userop's Java method
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If the userop is not backed by a Java method, or its return type is not supported, this
|
* If the userop is not backed by a Java method, or its output type is not supported, this
|
||||||
* return {@link JitTypeBehavior#ANY}.
|
* return {@link JitTypeBehavior#ANY}.
|
||||||
*
|
*
|
||||||
* @return the type behavior
|
* @return the type behavior
|
||||||
*/
|
*/
|
||||||
private JitTypeBehavior getReturnType() {
|
private JitTypeBehavior getOutputTypeBehavior() {
|
||||||
Method method = decOp.getJavaMethod();
|
return JitTypeBehavior.forJavaType(getOutputType());
|
||||||
if (method == null) {
|
|
||||||
return JitTypeBehavior.ANY;
|
|
||||||
}
|
|
||||||
return JitTypeBehavior.forJavaType(method.getReturnType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -210,8 +206,8 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
JitOutVar out = dfm.generateOutVar(outVn);
|
JitOutVar out = dfm.generateOutVar(outVn);
|
||||||
dfm.notifyOp(new JitCallOtherDefOp(op, out, getReturnType(), decOp, inVals, inTypes,
|
dfm.notifyOp(new JitCallOtherDefOp(op, out, getOutputTypeBehavior(), decOp, inVals,
|
||||||
state.captureState()));
|
inTypes, state.captureState()));
|
||||||
state.setVar(outVn, out);
|
state.setVar(outVn, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +232,11 @@ public class JitDataFlowUseropLibrary implements PcodeUseropLibrary<JitVal> {
|
||||||
return decOp.canInlinePcode();
|
return decOp.canInlinePcode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getOutputType() {
|
||||||
|
return decOp.getOutputType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Method getJavaMethod() {
|
public Method getJavaMethod() {
|
||||||
return decOp.getJavaMethod();
|
return decOp.getJavaMethod();
|
||||||
|
|
|
@ -21,6 +21,8 @@ import java.util.*;
|
||||||
|
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import ghidra.lifecycle.Unfinished;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The p-code type of an operand.
|
* The p-code type of an operand.
|
||||||
*
|
*
|
||||||
|
@ -124,6 +126,9 @@ public interface JitType {
|
||||||
* @return this type as an int
|
* @return this type as an int
|
||||||
*/
|
*/
|
||||||
SimpleJitType asInt();
|
SimpleJitType asInt();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SimpleJitType ext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,6 +206,11 @@ public interface JitType {
|
||||||
public IntJitType asInt() {
|
public IntJitType asInt() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IntJitType> legTypes() {
|
||||||
|
return List.of(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,6 +288,11 @@ public interface JitType {
|
||||||
public LongJitType asInt() {
|
public LongJitType asInt() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LongJitType> legTypes() {
|
||||||
|
return List.of(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -326,6 +341,11 @@ public interface JitType {
|
||||||
public IntJitType asInt() {
|
public IntJitType asInt() {
|
||||||
return IntJitType.I4;
|
return IntJitType.I4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FloatJitType> legTypes() {
|
||||||
|
return List.of(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -374,10 +394,20 @@ public interface JitType {
|
||||||
public LongJitType asInt() {
|
public LongJitType asInt() {
|
||||||
return LongJitType.I8;
|
return LongJitType.I8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DoubleJitType> legTypes() {
|
||||||
|
return List.of(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>WIP</b>: The p-code types for integers of size 9 and greater.
|
* The p-code types for integers of size 9 and greater.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* We take the strategy of inlined manipulation of int locals, composed to form the full
|
||||||
|
* variable. When stored on the stack, the least-significant portion is always toward the top,
|
||||||
|
* no matter the language endianness.
|
||||||
*
|
*
|
||||||
* @param size the size in bytes
|
* @param size the size in bytes
|
||||||
*/
|
*/
|
||||||
|
@ -432,22 +462,14 @@ public interface JitType {
|
||||||
return size % Integer.BYTES;
|
return size % Integer.BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Get the p-code type that describes the part of the variable in each leg
|
public List<IntJitType> legTypes() {
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Each whole leg will have the type {@link IntJitType#I4}, and the partial leg, if
|
|
||||||
* applicable, will have its appropriate smaller integer type.
|
|
||||||
*
|
|
||||||
* @return the list of types, each fitting in a JVM int.
|
|
||||||
*/
|
|
||||||
public List<SimpleJitType> legTypes() {
|
|
||||||
IntJitType[] types = new IntJitType[legsAlloc()];
|
IntJitType[] types = new IntJitType[legsAlloc()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
if (partialSize() != 0) {
|
if (partialSize() != 0) {
|
||||||
types[i++] = IntJitType.forSize(partialSize());
|
types[i++] = IntJitType.forSize(partialSize());
|
||||||
}
|
}
|
||||||
for (; i < legsWhole(); i++) {
|
for (; i < types.length; i++) {
|
||||||
types[i] = IntJitType.I4;
|
types[i] = IntJitType.I4;
|
||||||
}
|
}
|
||||||
return Arrays.asList(types);
|
return Arrays.asList(types);
|
||||||
|
@ -492,6 +514,11 @@ public interface JitType {
|
||||||
public MpFloatJitType ext() {
|
public MpFloatJitType ext() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SimpleJitType> legTypes() {
|
||||||
|
return Unfinished.TODO("MpFloat");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -526,4 +553,15 @@ public interface JitType {
|
||||||
* @return the extended type
|
* @return the extended type
|
||||||
*/
|
*/
|
||||||
JitType ext();
|
JitType ext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the p-code type that describes the part of the variable in each leg
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Each whole leg will have the type {@link IntJitType#I4}, and the partial leg, if applicable,
|
||||||
|
* will have its appropriate smaller integer type.
|
||||||
|
*
|
||||||
|
* @return the list of types, each fitting in a JVM int, in big-endian order.
|
||||||
|
*/
|
||||||
|
List<? extends SimpleJitType> legTypes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,6 +159,9 @@ public enum JitTypeBehavior {
|
||||||
if (cls == long.class) {
|
if (cls == long.class) {
|
||||||
return INTEGER;
|
return INTEGER;
|
||||||
}
|
}
|
||||||
|
if (cls == int[].class) {
|
||||||
|
return INTEGER;
|
||||||
|
}
|
||||||
if (cls == float.class) {
|
if (cls == float.class) {
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,6 +222,7 @@ public class JitTypeModel {
|
||||||
* @param c the number of votes cast
|
* @param c the number of votes cast
|
||||||
*/
|
*/
|
||||||
private void vote(JitTypeBehavior candidate, int c) {
|
private void vote(JitTypeBehavior candidate, int c) {
|
||||||
|
Objects.requireNonNull(candidate);
|
||||||
if (candidate == JitTypeBehavior.ANY || candidate == JitTypeBehavior.COPY) {
|
if (candidate == JitTypeBehavior.ANY || candidate == JitTypeBehavior.COPY) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,11 @@ public class DecoderUseropLibrary extends AnnotatedPcodeUseropLibrary<Object> {
|
||||||
return rtOp.canInlinePcode();
|
return rtOp.canInlinePcode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getOutputType() {
|
||||||
|
return rtOp.getOutputType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Method getJavaMethod() {
|
public Method getJavaMethod() {
|
||||||
return rtOp.getJavaMethod();
|
return rtOp.getJavaMethod();
|
||||||
|
|
|
@ -91,7 +91,7 @@ public interface GenConsts {
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class));
|
||||||
public static final String MDESC_INTEGER__BIT_COUNT =
|
public static final String MDESC_INTEGER__BIT_COUNT =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
||||||
public static final String MDESC_INTEGER__COMPARE_UNSIGNED =
|
public static final String MDESC_INTEGER__COMPARE =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||||
public static final String MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
|
public static final String MDESC_INTEGER__NUMBER_OF_LEADING_ZEROS =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE);
|
||||||
|
@ -135,6 +135,9 @@ public interface GenConsts {
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__INVOKE_USEROP =
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PcodeUseropDefinition.class),
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(PcodeUseropDefinition.class),
|
||||||
Type.getType(Varnode.class), Type.getType(Varnode[].class));
|
Type.getType(Varnode.class), Type.getType(Varnode[].class));
|
||||||
|
public static final String MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP =
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class),
|
||||||
|
Type.getType(int[].class), Type.getType(int[].class));
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_INTX =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE);
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__READ_LONGX =
|
||||||
|
@ -147,6 +150,9 @@ public interface GenConsts {
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_LONG_RAW =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_LONG_RAW =
|
||||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||||
|
public static final String MDESC_JIT_COMPILED_PASSAGE__S_CARRY_MP_INT =
|
||||||
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(int[].class),
|
||||||
|
Type.getType(int[].class), Type.INT_TYPE);
|
||||||
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
|
public static final String MDESC_JIT_COMPILED_PASSAGE__WRITE_INTX =
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(byte[].class),
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(byte[].class),
|
||||||
Type.INT_TYPE);
|
Type.INT_TYPE);
|
||||||
|
@ -182,10 +188,23 @@ public interface GenConsts {
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE);
|
||||||
public static final String MDESC_$LONG_BINOP =
|
public static final String MDESC_$LONG_BINOP =
|
||||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||||
|
public static final String MDESC_$SHIFT_AA =
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||||
|
Type.getType(int[].class), Type.getType(int[].class));
|
||||||
|
public static final String MDESC_$SHIFT_AJ =
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||||
|
Type.getType(int[].class), Type.LONG_TYPE);
|
||||||
|
public static final String MDESC_$SHIFT_AI =
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.INT_TYPE,
|
||||||
|
Type.getType(int[].class), Type.INT_TYPE);
|
||||||
|
public static final String MDESC_$SHIFT_JA =
|
||||||
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.getType(int[].class));
|
||||||
public static final String MDESC_$SHIFT_JJ =
|
public static final String MDESC_$SHIFT_JJ =
|
||||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
|
||||||
public static final String MDESC_$SHIFT_JI =
|
public static final String MDESC_$SHIFT_JI =
|
||||||
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.INT_TYPE);
|
Type.getMethodDescriptor(Type.LONG_TYPE, Type.LONG_TYPE, Type.INT_TYPE);
|
||||||
|
public static final String MDESC_$SHIFT_IA =
|
||||||
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.getType(int[].class));
|
||||||
public static final String MDESC_$SHIFT_IJ =
|
public static final String MDESC_$SHIFT_IJ =
|
||||||
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
|
Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
|
||||||
public static final String MDESC_$SHIFT_II =
|
public static final String MDESC_$SHIFT_II =
|
||||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.EntryPoint;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage.ExitSlot;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassageClass;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.gen.var.ValGen;
|
import ghidra.pcode.emu.jit.gen.var.ValGen;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||||
|
@ -216,9 +217,6 @@ public class JitCodeGenerator {
|
||||||
private final MethodVisitor initMv;
|
private final MethodVisitor initMv;
|
||||||
private final MethodVisitor runMv;
|
private final MethodVisitor runMv;
|
||||||
|
|
||||||
private final Label startLocals = new Label();
|
|
||||||
private final Label endLocals = new Label();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a code generator for the given passage's target classfile
|
* Construct a code generator for the given passage's target classfile
|
||||||
*
|
*
|
||||||
|
@ -594,10 +592,11 @@ public class JitCodeGenerator {
|
||||||
*
|
*
|
||||||
* @param v the value to read
|
* @param v the value to read
|
||||||
* @param typeReq the required type of the value
|
* @param typeReq the required type of the value
|
||||||
|
* @param ext the kind of extension to apply when adjusting from JVM size to varnode size
|
||||||
* @return the actual type of the value on the stack
|
* @return the actual type of the value on the stack
|
||||||
*/
|
*/
|
||||||
public JitType generateValReadCode(JitVal v, JitTypeBehavior typeReq) {
|
public JitType generateValReadCode(JitVal v, JitTypeBehavior typeReq, Ext ext) {
|
||||||
return ValGen.lookup(v).generateValReadCode(this, v, typeReq, runMv);
|
return ValGen.lookup(v).generateValReadCode(this, v, typeReq, ext, runMv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -611,9 +610,10 @@ public class JitCodeGenerator {
|
||||||
*
|
*
|
||||||
* @param v the variable to write
|
* @param v the variable to write
|
||||||
* @param type the actual type of the value on the stack
|
* @param type the actual type of the value on the stack
|
||||||
|
* @param ext the kind of extension to apply when adjusting from varnode size to JVM size
|
||||||
*/
|
*/
|
||||||
public void generateVarWriteCode(JitVar v, JitType type) {
|
public void generateVarWriteCode(JitVar v, JitType type, Ext ext) {
|
||||||
VarGen.lookup(v).generateVarWriteCode(this, v, type, runMv);
|
VarGen.lookup(v).generateVarWriteCode(this, v, type, ext, runMv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -858,20 +858,9 @@ public class JitCodeGenerator {
|
||||||
*/
|
*/
|
||||||
protected void generateRunCode() {
|
protected void generateRunCode() {
|
||||||
runMv.visitCode();
|
runMv.visitCode();
|
||||||
|
final Label startLocals = new Label();
|
||||||
runMv.visitLabel(startLocals);
|
runMv.visitLabel(startLocals);
|
||||||
|
|
||||||
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
|
||||||
fixed.generateDeclCode(runMv, nameThis, startLocals, endLocals);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JvmLocal local : am.allLocals()) {
|
|
||||||
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
|
||||||
}
|
|
||||||
// TODO: This for loop doesn't actually do anything....
|
|
||||||
for (JitVal v : dfm.allValuesSorted()) {
|
|
||||||
VarHandler handler = am.getHandler(v);
|
|
||||||
handler.generateDeclCode(this, startLocals, endLocals, runMv);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* NB. opIdx starts at 1, because JVM will ignore "Line number 0"
|
* NB. opIdx starts at 1, because JVM will ignore "Line number 0"
|
||||||
*/
|
*/
|
||||||
|
@ -886,6 +875,10 @@ public class JitCodeGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||||
|
fixed.generateInitCode(runMv, nameThis);
|
||||||
|
}
|
||||||
|
|
||||||
// []
|
// []
|
||||||
RunFixedLocal.BLOCK_ID.generateLoadCode(runMv);
|
RunFixedLocal.BLOCK_ID.generateLoadCode(runMv);
|
||||||
// [blockId]
|
// [blockId]
|
||||||
|
@ -929,6 +922,22 @@ public class JitCodeGenerator {
|
||||||
for (ExceptionHandler handler : excHandlers.values()) {
|
for (ExceptionHandler handler : excHandlers.values()) {
|
||||||
handler.generateRunCode(this, runMv);
|
handler.generateRunCode(this, runMv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Label endLocals = new Label();
|
||||||
|
runMv.visitLabel(endLocals);
|
||||||
|
|
||||||
|
for (FixedLocal fixed : RunFixedLocal.ALL) {
|
||||||
|
fixed.generateDeclCode(runMv, nameThis, startLocals, endLocals);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JvmLocal local : am.allLocals()) {
|
||||||
|
local.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||||
|
}
|
||||||
|
// TODO: This for loop doesn't actually do anything....
|
||||||
|
for (JitVal v : dfm.allValuesSorted()) {
|
||||||
|
VarHandler handler = am.getHandler(v);
|
||||||
|
handler.generateDeclCode(this, startLocals, endLocals, runMv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -953,8 +962,9 @@ public class JitCodeGenerator {
|
||||||
dest.getParentFile().mkdirs();
|
dest.getParentFile().mkdirs();
|
||||||
try (OutputStream os = new FileOutputStream(dest)) {
|
try (OutputStream os = new FileOutputStream(dest)) {
|
||||||
os.write(bytes);
|
os.write(bytes);
|
||||||
|
new ProcessBuilder("javap", "-c", "-l", dest.getPath()).inheritIO().start().waitFor();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException | InterruptedException e) {
|
||||||
Msg.warn(this, "Could not dump class file: " + nameThis + " (" + e + ")");
|
Msg.warn(this, "Could not dump class file: " + nameThis + " (" + e + ")");
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
|
@ -989,7 +999,6 @@ public class JitCodeGenerator {
|
||||||
initMv.visitMaxs(20, 20);
|
initMv.visitMaxs(20, 20);
|
||||||
initMv.visitEnd();
|
initMv.visitEnd();
|
||||||
|
|
||||||
runMv.visitLabel(endLocals);
|
|
||||||
try {
|
try {
|
||||||
runMv.visitMaxs(20, 20);
|
runMv.visitMaxs(20, 20);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.pcode.emu.jit.gen.op;
|
package ghidra.pcode.emu.jit.gen.op;
|
||||||
|
|
||||||
|
import static ghidra.pcode.emu.jit.gen.GenConsts.MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP;
|
||||||
|
import static ghidra.pcode.emu.jit.gen.GenConsts.NAME_JIT_COMPILED_PASSAGE;
|
||||||
|
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +37,134 @@ import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||||
*/
|
*/
|
||||||
public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A choice of static method parameter to take as operator output
|
||||||
|
*/
|
||||||
|
enum TakeOut {
|
||||||
|
/**
|
||||||
|
* The out (first) parameter
|
||||||
|
*/
|
||||||
|
OUT,
|
||||||
|
/**
|
||||||
|
* The left (second) parameter
|
||||||
|
*/
|
||||||
|
LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit bytecode that implements an mp-int binary operator via delegation to a static method on
|
||||||
|
* {@link JitCompiledPassage}. The method must have the signature:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* void method(int[] out, int[] inL, int[] inR);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method presumes that the left operand's legs are at the top of the stack,
|
||||||
|
* least-significant leg on top, followed by the right operand legs, also least-significant leg
|
||||||
|
* on top. This will allocate the output array, move the operands into their respective input
|
||||||
|
* arrays, invoke the method, and then place the result legs on the stack, least-significant leg
|
||||||
|
* on top.
|
||||||
|
*
|
||||||
|
* @param gen the code generator
|
||||||
|
* @param type the type of the operands
|
||||||
|
* @param methodName the name of the method in {@link JitCompiledPassage} to invoke
|
||||||
|
* @param mv the method visitor
|
||||||
|
* @param overProvisionLeft the number of extra ints to allocate for the left operand's array.
|
||||||
|
* This is to facilitate Knuth's division algorithm, which may require an extra
|
||||||
|
* leading leg in the dividend after normalization.
|
||||||
|
* @param takeOut indicates which operand of the static method to actually take for the output.
|
||||||
|
* This is to facilitate the remainder operator, because Knuth's algorithm leaves the
|
||||||
|
* remainder where there dividend was.
|
||||||
|
*/
|
||||||
|
static void generateMpDelegationToStaticMethod(JitCodeGenerator gen, MpIntJitType type,
|
||||||
|
String methodName, MethodVisitor mv, int overProvisionLeft, TakeOut takeOut) {
|
||||||
|
/**
|
||||||
|
* The strategy here will be to allocate an array for each of the operands (output and 2
|
||||||
|
* inputs) and then invoke a static method to do the actual operation. It might be nice to
|
||||||
|
* generate inline code for small multiplications, but we're going to leave that for later.
|
||||||
|
*/
|
||||||
|
// [lleg1,...,llegN,rleg1,...,rlegN]
|
||||||
|
JitAllocationModel am = gen.getAllocationModel();
|
||||||
|
int legCount = type.legsAlloc();
|
||||||
|
try (
|
||||||
|
JvmTempAlloc tmpL = am.allocateTemp(mv, "tmpL", legCount);
|
||||||
|
JvmTempAlloc tmpR = am.allocateTemp(mv, "tmpR", legCount)) {
|
||||||
|
// [rleg1,...,rlegN,lleg1,...,llegN]
|
||||||
|
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||||
|
// [lleg1,...,llegN]
|
||||||
|
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||||
|
// []
|
||||||
|
|
||||||
|
switch (takeOut) {
|
||||||
|
case OUT -> {
|
||||||
|
// []
|
||||||
|
mv.visitLdcInsn(legCount);
|
||||||
|
// [count:INT]
|
||||||
|
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||||
|
// [out:INT[count]]
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
// [out,out]
|
||||||
|
|
||||||
|
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
|
||||||
|
// [inL,out,out]
|
||||||
|
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||||
|
// [inR,inL,out,out]
|
||||||
|
}
|
||||||
|
case LEFT -> {
|
||||||
|
// []
|
||||||
|
mv.visitLdcInsn(legCount);
|
||||||
|
// [count:INT]
|
||||||
|
mv.visitIntInsn(NEWARRAY, T_INT);
|
||||||
|
// [out]
|
||||||
|
OpGen.generateMpLegsIntoArray(tmpL, legCount + overProvisionLeft, legCount, mv);
|
||||||
|
// [inL,out]
|
||||||
|
mv.visitInsn(DUP_X1);
|
||||||
|
// [inL,out,inL]
|
||||||
|
OpGen.generateMpLegsIntoArray(tmpR, legCount, legCount, mv);
|
||||||
|
// [inR,inL,out,inL]
|
||||||
|
}
|
||||||
|
default -> throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mv.visitMethodInsn(INVOKESTATIC, NAME_JIT_COMPILED_PASSAGE, methodName,
|
||||||
|
MDESC_JIT_COMPILED_PASSAGE__MP_INT_BINOP, true);
|
||||||
|
// [out||inL:INT[count]]
|
||||||
|
|
||||||
|
// Push the result back, in reverse order
|
||||||
|
OpGen.generateMpLegsFromArray(legCount, mv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this operator is signed
|
||||||
|
* <p>
|
||||||
|
* In many cases, the operator itself is not affected by the signedness of the operands;
|
||||||
|
* however, if size adjustments to the operands are needed, this can determine how those
|
||||||
|
* operands are extended.
|
||||||
|
*
|
||||||
|
* @return true for signed, false if not
|
||||||
|
*/
|
||||||
|
boolean isSigned();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When loading and storing variables, the kind of extension to apply
|
||||||
|
*
|
||||||
|
* @return the extension kind
|
||||||
|
*/
|
||||||
|
default Ext ext() {
|
||||||
|
return Ext.forSigned(isSigned());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When loading the right operand, the kind of extension to apply
|
||||||
|
*
|
||||||
|
* @return the extension kind
|
||||||
|
*/
|
||||||
|
default Ext rExt() {
|
||||||
|
return ext();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit code between reading the left and right operands
|
* Emit code between reading the left and right operands
|
||||||
*
|
*
|
||||||
|
@ -79,12 +215,12 @@ public interface BinOpGen<T extends JitBinOp> extends OpGen<T> {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
|
default void generateRunCode(JitCodeGenerator gen, T op, JitBlock block, MethodVisitor rv) {
|
||||||
JitType lType = gen.generateValReadCode(op.l(), op.lType());
|
JitType lType = gen.generateValReadCode(op.l(), op.lType(), ext());
|
||||||
JitType rType = op.rType().resolve(gen.getTypeModel().typeOf(op.r()));
|
JitType rType = op.rType().resolve(gen.getTypeModel().typeOf(op.r()));
|
||||||
lType = afterLeft(gen, op, lType, rType, rv);
|
lType = afterLeft(gen, op, lType, rType, rv);
|
||||||
JitType checkRType = gen.generateValReadCode(op.r(), op.rType());
|
JitType checkRType = gen.generateValReadCode(op.r(), op.rType(), rExt());
|
||||||
assert checkRType == rType;
|
assert checkRType == rType;
|
||||||
JitType outType = generateBinOpRunCode(gen, op, block, lType, rType, rv);
|
JitType outType = generateBinOpRunCode(gen, op, block, lType, rType, rv);
|
||||||
gen.generateVarWriteCode(op.out(), outType);
|
gen.generateVarWriteCode(op.out(), outType, Ext.ZERO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,16 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||||
|
|
||||||
import static ghidra.lifecycle.Unfinished.TODO;
|
import static ghidra.lifecycle.Unfinished.TODO;
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.op.JitBinOp;
|
import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +34,11 @@ import ghidra.pcode.emu.jit.op.JitBinOp;
|
||||||
*
|
*
|
||||||
* @param <T> the class of p-code op node in the use-def graph
|
* @param <T> the class of p-code op node in the use-def graph
|
||||||
*/
|
*/
|
||||||
public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
public interface BitwiseBinOpGen<T extends JitBinOp> extends IntBinOpGen<T> {
|
||||||
|
@Override
|
||||||
|
default boolean isSigned() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JVM opcode to implement this operator with int operands on the stack.
|
* The JVM opcode to implement this operator with int operands on the stack.
|
||||||
|
@ -49,7 +55,7 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||||
int longOpcode();
|
int longOpcode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>WIP</b>: The implementation for multi-precision ints.
|
* The implementation for multi-precision ints.
|
||||||
*
|
*
|
||||||
* @param gen the code generator
|
* @param gen the code generator
|
||||||
* @param type the type of each operand, including the reuslt
|
* @param type the type of each operand, including the reuslt
|
||||||
|
@ -66,37 +72,25 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||||
*/
|
*/
|
||||||
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
// [lleg1,...,llegN,rleg1,rlegN] (N is least-significant leg)
|
||||||
int legCount = type.legsAlloc();
|
int legCount = type.legsAlloc();
|
||||||
int firstIndex = gen.getAllocationModel().nextFreeLocal();
|
try (JvmTempAlloc result = gen.getAllocationModel().allocateTemp(mv, "result", legCount)) {
|
||||||
Label start = new Label();
|
OpGen.generateMpLegsIntoTemp(result, legCount, mv);
|
||||||
Label end = new Label();
|
|
||||||
mv.visitLabel(start);
|
|
||||||
for (int i = 0; i < legCount; i++) {
|
|
||||||
mv.visitLocalVariable("result" + i, Type.getDescriptor(int.class), null, start, end,
|
|
||||||
firstIndex + i);
|
|
||||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
|
||||||
// NOTE: More significant legs have higher indices (reverse of stack)
|
|
||||||
}
|
|
||||||
for (int i = 0; i < legCount; i++) {
|
for (int i = 0; i < legCount; i++) {
|
||||||
// [lleg1,...,llegN:INT]
|
// [lleg1,...,llegN:INT]
|
||||||
mv.visitVarInsn(ILOAD, firstIndex + i);
|
mv.visitVarInsn(ILOAD, result.idx(i));
|
||||||
// [lleg1,...,llegN:INT,rlegN:INT]
|
// [lleg1,...,llegN:INT,rlegN:INT]
|
||||||
mv.visitInsn(intOpcode());
|
mv.visitInsn(intOpcode());
|
||||||
// [lleg1,...,olegN:INT]
|
// [lleg1,...,olegN:INT]
|
||||||
mv.visitVarInsn(ISTORE, firstIndex + i);
|
mv.visitVarInsn(ISTORE, result.idx(i));
|
||||||
// [lleg1,...]
|
// [lleg1,...]
|
||||||
}
|
}
|
||||||
|
OpGen.generateMpLegsFromTemp(result, legCount, mv);
|
||||||
// Push it all back, in reverse order
|
|
||||||
for (int i = 0; i < legCount; i++) {
|
|
||||||
mv.visitVarInsn(ILOAD, firstIndex + legCount - i - 1);
|
|
||||||
}
|
}
|
||||||
mv.visitLabel(end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
return TypeConversions.forceUniform(gen, lType, rType, Ext.ZERO, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,7 +104,7 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||||
@Override
|
@Override
|
||||||
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
|
||||||
JitType rType, MethodVisitor rv) {
|
JitType rType, MethodVisitor rv) {
|
||||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
rType = TypeConversions.forceUniform(gen, rType, lType, Ext.ZERO, rv);
|
||||||
switch (rType) {
|
switch (rType) {
|
||||||
case IntJitType t -> rv.visitInsn(intOpcode());
|
case IntJitType t -> rv.visitInsn(intOpcode());
|
||||||
case LongJitType t -> rv.visitInsn(longOpcode());
|
case LongJitType t -> rv.visitInsn(longOpcode());
|
||||||
|
@ -118,6 +112,6 @@ public interface BitwiseBinOpGen<T extends JitBinOp> extends BinOpGen<T> {
|
||||||
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
case MpIntJitType t -> TODO("MpInt of differing sizes");
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
return lType;
|
return rType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ import ghidra.pcode.opbehavior.OpBehaviorBoolAnd;
|
||||||
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
* @implNote It is the responsibility of the slaspec author to ensure boolean values are 0 or 1.
|
||||||
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
* This allows us to use bitwise logic instead of having to check for any non-zero value,
|
||||||
* just like {@link OpBehaviorBoolAnd}. Thus, this is identical to {@link IntAndOpGen}.
|
* just like {@link OpBehaviorBoolAnd}. Thus, this is identical to {@link IntAndOpGen}.
|
||||||
|
* @implNote Because having bits other than the least significant set in the inputs is "undefined
|
||||||
|
* behavior," we could technically optimize this by only ANDing the least significant leg
|
||||||
|
* when we're dealing with mp-ints.
|
||||||
*/
|
*/
|
||||||
public enum BoolAndOpGen implements BitwiseBinOpGen<JitBoolAndOp> {
|
public enum BoolAndOpGen implements BitwiseBinOpGen<JitBoolAndOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.pcode.emu.jit.gen.op;
|
||||||
|
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.lifecycle.Unfinished;
|
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||||
|
@ -36,6 +35,11 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSigned() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitBoolNegateOp op, JitBlock block,
|
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitBoolNegateOp op, JitBlock block,
|
||||||
JitType uType, MethodVisitor rv) {
|
JitType uType, MethodVisitor rv) {
|
||||||
|
@ -48,7 +52,11 @@ public enum BoolNegateOpGen implements UnOpGen<JitBoolNegateOp> {
|
||||||
rv.visitLdcInsn(1L);
|
rv.visitLdcInsn(1L);
|
||||||
rv.visitInsn(LXOR);
|
rv.visitInsn(LXOR);
|
||||||
}
|
}
|
||||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
case MpIntJitType t -> {
|
||||||
|
// Least-sig leg is on top, and it's an int.
|
||||||
|
rv.visitLdcInsn(1);
|
||||||
|
rv.visitInsn(IXOR);
|
||||||
|
}
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
return uType;
|
return uType;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import ghidra.pcode.emu.jit.analysis.JitType.LongJitType;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.BranchGen;
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.BranchGen;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
|
import ghidra.pcode.emu.jit.op.JitBranchIndOp;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
|
@ -55,9 +56,9 @@ public enum BranchIndOpGen implements OpGen<JitBranchIndOp> {
|
||||||
JitBlock block, MethodVisitor rv) {
|
JitBlock block, MethodVisitor rv) {
|
||||||
gen.generatePassageExit(block, () -> {
|
gen.generatePassageExit(block, () -> {
|
||||||
// [...]
|
// [...]
|
||||||
JitType targetType = gen.generateValReadCode(op.target(), op.targetType());
|
JitType targetType = gen.generateValReadCode(op.target(), op.targetType(), Ext.ZERO);
|
||||||
// [...,target:?]
|
// [...,target:?]
|
||||||
TypeConversions.generateToLong(targetType, LongJitType.I8, rv);
|
TypeConversions.generateToLong(targetType, LongJitType.I8, Ext.ZERO, rv);
|
||||||
// [...,target:LONG]
|
// [...,target:LONG]
|
||||||
}, ctx, rv);
|
}, ctx, rv);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
|
||||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.ExtBranchGen;
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.ExtBranchGen;
|
||||||
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.IntBranchGen;
|
import ghidra.pcode.emu.jit.gen.op.BranchOpGen.IntBranchGen;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||||
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
import ghidra.pcode.emu.jit.op.JitCBranchOp;
|
||||||
|
@ -146,7 +147,7 @@ public enum CBranchOpGen implements OpGen<JitCBranchOp> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JitType cType = gen.generateValReadCode(op.cond(), op.condType());
|
JitType cType = gen.generateValReadCode(op.cond(), op.condType(), Ext.ZERO);
|
||||||
TypeConversions.generateIntToBool(cType, rv);
|
TypeConversions.generateIntToBool(cType, rv);
|
||||||
switch (op.branch()) {
|
switch (op.branch()) {
|
||||||
case RIntBranch ib -> IntCBranchGen.C_INT.generateCode(gen, op, ib, block, rv);
|
case RIntBranch ib -> IntCBranchGen.C_INT.generateCode(gen, op, ib, block, rv);
|
||||||
|
|
|
@ -19,23 +19,31 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Parameter;
|
import java.lang.reflect.Parameter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
import ghidra.pcode.emu.jit.JitBytesPcodeExecutorState;
|
||||||
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
import ghidra.pcode.emu.jit.JitPassage.DecodedPcodeOp;
|
||||||
import ghidra.pcode.emu.jit.analysis.*;
|
import ghidra.pcode.emu.jit.analysis.*;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.RunFixedLocal;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
|
import ghidra.pcode.emu.jit.analysis.JitType.MpIntJitType;
|
||||||
import ghidra.pcode.emu.jit.gen.*;
|
import ghidra.pcode.emu.jit.gen.*;
|
||||||
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
|
import ghidra.pcode.emu.jit.gen.JitCodeGenerator.RetireMode;
|
||||||
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
import ghidra.pcode.emu.jit.gen.tgt.JitCompiledPassage;
|
||||||
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
|
||||||
|
import ghidra.pcode.emu.jit.gen.type.TypeConversions.Ext;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
import ghidra.pcode.emu.jit.gen.var.VarGen;
|
||||||
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
import ghidra.pcode.emu.jit.gen.var.VarGen.BlockTransition;
|
||||||
import ghidra.pcode.emu.jit.op.JitCallOtherDefOp;
|
import ghidra.pcode.emu.jit.op.JitCallOtherDefOp;
|
||||||
import ghidra.pcode.emu.jit.op.JitCallOtherOpIf;
|
import ghidra.pcode.emu.jit.op.JitCallOtherOpIf;
|
||||||
import ghidra.pcode.emu.jit.var.JitVal;
|
import ghidra.pcode.emu.jit.var.JitVal;
|
||||||
|
import ghidra.pcode.exec.AnnotatedPcodeUseropLibrary.OpOutput;
|
||||||
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
import ghidra.pcode.exec.PcodeUseropLibrary.PcodeUseropDefinition;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
@ -153,6 +161,31 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||||
transition.generateInv(rv);
|
transition.generateInv(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Parameter findOutputParameter(Parameter[] parameters, Method method) {
|
||||||
|
List<Parameter> found =
|
||||||
|
Stream.of(parameters).filter(p -> p.getAnnotation(OpOutput.class) != null).toList();
|
||||||
|
return switch (found.size()) {
|
||||||
|
case 0 -> null;
|
||||||
|
case 1 -> {
|
||||||
|
Parameter p = found.get(0);
|
||||||
|
if (p.getType() == int[].class) {
|
||||||
|
yield p;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("""
|
||||||
|
@%s requires parameter to have type int[] when functional=true. \
|
||||||
|
Got %s (method %s)""".formatted(
|
||||||
|
OpOutput.class.getSimpleName(), p, method.getName()));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
throw new IllegalArgumentException("""
|
||||||
|
@%s can only be applied to one parameter of method %s. \
|
||||||
|
It is applied to: %s""".formatted(
|
||||||
|
OpOutput.class.getSimpleName(), method.getName(),
|
||||||
|
found.stream().map(Parameter::toString).collect(Collectors.joining(", "))));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit code to implement the Direct strategy (see the class documentation)
|
* Emit code to implement the Direct strategy (see the class documentation)
|
||||||
*
|
*
|
||||||
|
@ -175,6 +208,8 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||||
rv.visitTryCatchBlock(tryStart, tryEnd,
|
rv.visitTryCatchBlock(tryStart, tryEnd,
|
||||||
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).label(), NAME_THROWABLE);
|
gen.requestExceptionHandler((DecodedPcodeOp) op.op(), block).label(), NAME_THROWABLE);
|
||||||
|
|
||||||
|
JitAllocationModel am = gen.getAllocationModel();
|
||||||
|
|
||||||
// []
|
// []
|
||||||
useropField.generateLoadCode(gen, rv);
|
useropField.generateLoadCode(gen, rv);
|
||||||
// [userop]
|
// [userop]
|
||||||
|
@ -186,17 +221,66 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||||
rv.visitTypeInsn(CHECKCAST, owningLibName);
|
rv.visitTypeInsn(CHECKCAST, owningLibName);
|
||||||
// [library:OWNING_TYPE]
|
// [library:OWNING_TYPE]
|
||||||
Parameter[] parameters = method.getParameters();
|
Parameter[] parameters = method.getParameters();
|
||||||
for (int i = 0; i < op.args().size(); i++) {
|
Parameter outputParameter = findOutputParameter(parameters, method);
|
||||||
JitVal arg = op.args().get(i);
|
if (outputParameter != null && method.getReturnType() != void.class) {
|
||||||
Parameter p = parameters[i];
|
throw new IllegalArgumentException("""
|
||||||
|
@%s cannot be applied to any parameter of a method returning non-void. \
|
||||||
JitType type = gen.generateValReadCode(arg, JitTypeBehavior.ANY);
|
It's applied to %s of %s""".formatted(
|
||||||
if (p.getType() == boolean.class) {
|
OpOutput.class.getSimpleName(), outputParameter, method.getName()));
|
||||||
TypeConversions.generateIntToBool(type, rv);
|
}
|
||||||
|
try (JvmTempAlloc out =
|
||||||
|
am.allocateTemp(rv, "out", int[].class, outputParameter == null ? 0 : 1)) {
|
||||||
|
MpIntJitType outMpType;
|
||||||
|
if (outputParameter != null) {
|
||||||
|
if (!(op instanceof JitCallOtherDefOp defOp)) {
|
||||||
|
outMpType = null;
|
||||||
|
rv.visitInsn(ACONST_NULL);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TypeConversions.generate(gen, type, JitType.forJavaType(p.getType()), rv);
|
outMpType = MpIntJitType.forSize(defOp.out().size());
|
||||||
|
rv.visitLdcInsn(outMpType.legsAlloc());
|
||||||
|
rv.visitIntInsn(NEWARRAY, T_INT);
|
||||||
}
|
}
|
||||||
|
rv.visitVarInsn(ASTORE, out.idx(0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outMpType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int argIdx = 0;
|
||||||
|
for (int i = 0; i < parameters.length; i++) {
|
||||||
|
Parameter p = parameters[i];
|
||||||
|
|
||||||
|
if (p == outputParameter) {
|
||||||
|
rv.visitVarInsn(ALOAD, out.idx(0));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JitVal arg = op.args().get(argIdx++);
|
||||||
|
|
||||||
|
// TODO: Should this always be zero extension?
|
||||||
|
JitType type = gen.generateValReadCode(arg, JitTypeBehavior.ANY, Ext.ZERO);
|
||||||
|
if (p.getType() == boolean.class) {
|
||||||
|
TypeConversions.generateIntToBool(type, rv);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.getType() == int[].class) {
|
||||||
|
MpIntJitType mpType = MpIntJitType.forSize(type.size());
|
||||||
|
// NOTE: Would be nice to have annotation specify signedness
|
||||||
|
TypeConversions.generate(gen, type, mpType, Ext.ZERO, rv);
|
||||||
|
int legCount = mpType.legsAlloc();
|
||||||
|
try (JvmTempAlloc temp = am.allocateTemp(rv, "temp", legCount)) {
|
||||||
|
OpGen.generateMpLegsIntoTemp(temp, legCount, rv);
|
||||||
|
OpGen.generateMpLegsIntoArray(temp, legCount, legCount, rv);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some primitive/simple type
|
||||||
|
// TODO: Should this always be zero extension? Can annotation specify?
|
||||||
|
TypeConversions.generate(gen, type, JitType.forJavaType(p.getType()), Ext.ZERO,
|
||||||
|
rv);
|
||||||
}
|
}
|
||||||
// [library,params...]
|
// [library,params...]
|
||||||
rv.visitLabel(tryStart);
|
rv.visitLabel(tryStart);
|
||||||
|
@ -204,13 +288,41 @@ public enum CallOtherOpGen implements OpGen<JitCallOtherOpIf> {
|
||||||
Type.getMethodDescriptor(method), false);
|
Type.getMethodDescriptor(method), false);
|
||||||
// [return?]
|
// [return?]
|
||||||
rv.visitLabel(tryEnd);
|
rv.visitLabel(tryEnd);
|
||||||
if (op instanceof JitCallOtherDefOp defOp) {
|
if (outputParameter != null) {
|
||||||
gen.generateVarWriteCode(defOp.out(), JitType.forJavaType(method.getReturnType()));
|
if (outMpType != null && op instanceof JitCallOtherDefOp defOp) {
|
||||||
|
rv.visitVarInsn(ALOAD, out.idx(0));
|
||||||
|
OpGen.generateMpLegsFromArray(outMpType.legsAlloc(), rv);
|
||||||
|
// NOTE: Want annotation to specify signedness
|
||||||
|
gen.generateVarWriteCode(defOp.out(), outMpType, Ext.ZERO);
|
||||||
|
}
|
||||||
|
// Else there's either no @OpOutput or the output operand is absent
|
||||||
|
}
|
||||||
|
else if (op instanceof JitCallOtherDefOp defOp) {
|
||||||
|
// TODO: Can annotation specify signedness of return value?
|
||||||
|
gen.generateVarWriteCode(defOp.out(), JitType.forJavaType(method.getReturnType()),
|
||||||
|
Ext.ZERO);
|
||||||
}
|
}
|
||||||
else if (method.getReturnType() != void.class) {
|
else if (method.getReturnType() != void.class) {
|
||||||
TypeConversions.generatePop(JitType.forJavaType(method.getReturnType()), rv);
|
TypeConversions.generatePop(JitType.forJavaType(method.getReturnType()), rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ResourceGroup implements AutoCloseable {
|
||||||
|
private final List<AutoCloseable> resources = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
for (AutoCloseable r : resources) {
|
||||||
|
r.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends AutoCloseable> T add(T resource) {
|
||||||
|
resources.add(resource);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the Direct invocation strategy is applicable (see class documentation)
|
* Check if the Direct invocation strategy is applicable (see class documentation)
|
||||||
|
|
|
@ -31,7 +31,7 @@ import ghidra.pcode.emu.jit.op.JitFloatTestOp;
|
||||||
*
|
*
|
||||||
* @param <T> the class of p-code op node in the use-def graph
|
* @param <T> the class of p-code op node in the use-def graph
|
||||||
*/
|
*/
|
||||||
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends BinOpGen<T> {
|
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends FloatBinOpGen<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JVM opcode to perform the comparison with float operands on the stack.
|
* The JVM opcode to perform the comparison with float operands on the stack.
|
||||||
|
|
|
@ -20,7 +20,7 @@ import static ghidra.pcode.emu.jit.gen.GenConsts.*;
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import ghidra.lifecycle.Unfinished;
|
import ghidra.pcode.emu.jit.analysis.JitAllocationModel.JvmTempAlloc;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType;
|
import ghidra.pcode.emu.jit.analysis.JitType;
|
||||||
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
import ghidra.pcode.emu.jit.analysis.JitType.*;
|
||||||
|
@ -34,11 +34,10 @@ import ghidra.pcode.emu.jit.op.JitIntTestOp;
|
||||||
*
|
*
|
||||||
* @param <T> the class of p-code op node in the use-def graph
|
* @param <T> the class of p-code op node in the use-def graph
|
||||||
*/
|
*/
|
||||||
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T> {
|
public interface CompareIntBinOpGen<T extends JitIntTestOp> extends IntBinOpGen<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the comparison of p-code integers is signed
|
* {@inheritDoc}
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* If the comparison is unsigned, we will emit invocations of
|
* If the comparison is unsigned, we will emit invocations of
|
||||||
* {@link Integer#compareUnsigned(int, int)} or {@link Long#compareUnsigned(long, long)},
|
* {@link Integer#compareUnsigned(int, int)} or {@link Long#compareUnsigned(long, long)},
|
||||||
|
@ -49,6 +48,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
*
|
*
|
||||||
* @return true if signed, false if not
|
* @return true if signed, false if not
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
boolean isSigned();
|
boolean isSigned();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +58,11 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
*/
|
*/
|
||||||
int icmpOpcode();
|
int icmpOpcode();
|
||||||
|
|
||||||
|
default void generateIntCmp(String methodName, MethodVisitor rv) {
|
||||||
|
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, methodName, MDESC_INTEGER__COMPARE,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits bytecode for the JVM int case
|
* Emits bytecode for the JVM int case
|
||||||
*
|
*
|
||||||
|
@ -69,8 +74,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
rv.visitJumpInsn(icmpOpcode(), lblTrue);
|
rv.visitJumpInsn(icmpOpcode(), lblTrue);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rv.visitMethodInsn(INVOKESTATIC, NAME_INTEGER, "compareUnsigned",
|
generateIntCmp("compareUnsigned", rv);
|
||||||
MDESC_INTEGER__COMPARE_UNSIGNED, false);
|
|
||||||
rv.visitJumpInsn(ifOpcode(), lblTrue);
|
rv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +98,7 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The JVM opcode to perform the conditional jump for unsigned or long integers.
|
* The JVM opcode to perform the conditional jump for unsigned or long integers.
|
||||||
*
|
* <p>
|
||||||
* This is emitted <em>after</em> the application of {@link #LCMP} or the comparator method.
|
* This is emitted <em>after</em> the application of {@link #LCMP} or the comparator method.
|
||||||
*
|
*
|
||||||
* @return the opcode
|
* @return the opcode
|
||||||
|
@ -104,7 +108,34 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
@Override
|
@Override
|
||||||
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
default JitType afterLeft(JitCodeGenerator gen, T op, JitType lType, JitType rType,
|
||||||
MethodVisitor rv) {
|
MethodVisitor rv) {
|
||||||
return TypeConversions.forceUniformZExt(lType, rType, rv);
|
return TypeConversions.forceUniform(gen, lType, rType, ext(), rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
default JitType generateMpIntCmp(JitCodeGenerator gen, MpIntJitType type, Label lblTrue,
|
||||||
|
MethodVisitor mv) {
|
||||||
|
int legCount = type.legsAlloc();
|
||||||
|
Label lblDone = new Label();
|
||||||
|
// Need two temps, because comparison is from *most* to least-significant
|
||||||
|
try (
|
||||||
|
JvmTempAlloc tmpL = gen.getAllocationModel().allocateTemp(mv, "tmpL", legCount);
|
||||||
|
JvmTempAlloc tmpR = gen.getAllocationModel().allocateTemp(mv, "tmpR", legCount)) {
|
||||||
|
OpGen.generateMpLegsIntoTemp(tmpR, legCount, mv);
|
||||||
|
OpGen.generateMpLegsIntoTemp(tmpL, legCount, mv);
|
||||||
|
for (int i = 0; i < legCount; i++) {
|
||||||
|
mv.visitVarInsn(ILOAD, tmpL.idx(legCount - i - 1));
|
||||||
|
mv.visitVarInsn(ILOAD, tmpR.idx(legCount - i - 1));
|
||||||
|
//OpGen.generateSyserrInts(gen, 2, mv);
|
||||||
|
generateIntCmp(i == 0 ? "compare" : "compareUnsigned", mv);
|
||||||
|
if (i != legCount - 1) {
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
mv.visitJumpInsn(IFNE, lblDone);
|
||||||
|
mv.visitInsn(POP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mv.visitLabel(lblDone);
|
||||||
|
mv.visitJumpInsn(ifOpcode(), lblTrue);
|
||||||
|
return IntJitType.I4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,11 +154,11 @@ public interface CompareIntBinOpGen<T extends JitIntTestOp> extends BinOpGen<T>
|
||||||
Label lblTrue = new Label();
|
Label lblTrue = new Label();
|
||||||
Label lblDone = new Label();
|
Label lblDone = new Label();
|
||||||
|
|
||||||
rType = TypeConversions.forceUniformZExt(rType, lType, rv);
|
rType = TypeConversions.forceUniform(gen, rType, lType, ext(), rv);
|
||||||
switch (rType) {
|
switch (rType) {
|
||||||
case IntJitType t -> generateIntJump(lblTrue, rv);
|
case IntJitType t -> generateIntJump(lblTrue, rv);
|
||||||
case LongJitType t -> generateLongJump(lblTrue, rv);
|
case LongJitType t -> generateLongJump(lblTrue, rv);
|
||||||
case MpIntJitType t -> Unfinished.TODO("MpInt");
|
case MpIntJitType t -> generateMpIntCmp(gen, t, lblTrue, rv);
|
||||||
default -> throw new AssertionError();
|
default -> throw new AssertionError();
|
||||||
}
|
}
|
||||||
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
|
||||||
|
|
|
@ -34,6 +34,11 @@ public enum CopyOpGen implements UnOpGen<JitCopyOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSigned() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitCopyOp op, JitBlock block,
|
public JitType generateUnOpRunCode(JitCodeGenerator gen, JitCopyOp op, JitBlock block,
|
||||||
JitType uType, MethodVisitor rv) {
|
JitType uType, MethodVisitor rv) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.pcode.emu.jit.op.JitFloatAbsOp;
|
||||||
* This uses the unary operator generator and emits an invocation of {@link Math#abs(float)} or
|
* This uses the unary operator generator and emits an invocation of {@link Math#abs(float)} or
|
||||||
* {@link Math#abs(double)}, depending on the type.
|
* {@link Math#abs(double)}, depending on the type.
|
||||||
*/
|
*/
|
||||||
public enum FloatAbsOpGen implements UnOpGen<JitFloatAbsOp> {
|
public enum FloatAbsOpGen implements FloatUnOpGen<JitFloatAbsOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import ghidra.pcode.emu.jit.op.JitFloatAddOp;
|
||||||
* This uses the binary operator generator and simply emits {@link #FADD} or {@link #DADD} depending
|
* This uses the binary operator generator and simply emits {@link #FADD} or {@link #DADD} depending
|
||||||
* on the type.
|
* on the type.
|
||||||
*/
|
*/
|
||||||
public enum FloatAddOpGen implements BinOpGen<JitFloatAddOp> {
|
public enum FloatAddOpGen implements FloatBinOpGen<JitFloatAddOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* ###
|
||||||
|
* 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.pcode.emu.jit.gen.op;
|
||||||
|
|
||||||
|
import ghidra.pcode.emu.jit.op.JitFloatBinOp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension for floating-point binary operators
|
||||||
|
*
|
||||||
|
* @param <T> the class of p-code op node in the use-def graph
|
||||||
|
*/
|
||||||
|
public interface FloatBinOpGen<T extends JitFloatBinOp> extends BinOpGen<T> {
|
||||||
|
@Override
|
||||||
|
default boolean isSigned() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ import ghidra.pcode.emu.jit.op.JitFloatCeilOp;
|
||||||
* This uses the unary operator generator and emits an invocation of {@link Math#ceil(double)},
|
* This uses the unary operator generator and emits an invocation of {@link Math#ceil(double)},
|
||||||
* possibly surrounding it with conversions from and to float.
|
* possibly surrounding it with conversions from and to float.
|
||||||
*/
|
*/
|
||||||
public enum FloatCeilOpGen implements UnOpGen<JitFloatCeilOp> {
|
public enum FloatCeilOpGen implements FloatUnOpGen<JitFloatCeilOp> {
|
||||||
/** The generator singleton */
|
/** The generator singleton */
|
||||||
GEN;
|
GEN;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue