mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 15:36:33 +08:00
Merge remote-tracking branch 'origin/Ghidra_10.0'
This commit is contained in:
@@ -143,11 +143,13 @@
|
||||
<BLOCKQUOTE><UL>
|
||||
<P> NOTE: As this is a prototype script, the location, names, layout of data types, and default virtual function names created by this script are
|
||||
likely to change in the future once an official design for Object Oriented representation is determined.<P>
|
||||
<P>NOTE: Windows class recovery is fairly complete and tested, however GCC class recovery is still in early development.
|
||||
GCC class data types have not been recovered yet but if the program has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder.</P>
|
||||
<P>NOTE: Windows class recovery is fairly complete and tested, however GCC class recovery is still in early development.</P>
|
||||
<P>NOTE: For best results, run this script on freshly imported and analyzed programs. No testing has been done on programs previously imported with pre-existing user mark-up.</P>
|
||||
</UL></BLOCKQUOTE>
|
||||
|
||||
<P>Two related scripts have been added, ApplyClassFunctionSignatureUpdatesScript and ApplyClassFunctionDefinitionUpdatesScript, which are fix-up scripts that can be applied if a user
|
||||
makes changes to a virtual function recovered by the RecoverClassesFromRTTIScript. Both scripts identify differences between Function Signatures in the
|
||||
Listing and Function Definitions in the Data Type Manager, but the first script fixes all changes to match the signature and the second to match the definition. NOTE: These
|
||||
scripts are a temporary measure until an underlying connection between function signatures and their associated function definition can be implemented in the Ghidra API.</P>
|
||||
<BR />
|
||||
|
||||
<H2>PDB Symbol Server</H2
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
##VERSION: 2.0
|
||||
##MODULE IP: Jython License
|
||||
Module.manifest||GHIDRA||||END|
|
||||
data/scripts/define_info_proc_mappings||GHIDRA||||END|
|
||||
|
||||
@@ -364,8 +364,10 @@ public class GadpClientServerTest implements AsyncTestUtils {
|
||||
public TestGadpTargetMethod(TestTargetObject<?, ?> parent, String key) {
|
||||
super(parent.getModel(), parent, key, "Method");
|
||||
|
||||
setAttributes(Map.of(PARAMETERS_ATTRIBUTE_NAME, PARAMS, RETURN_TYPE_ATTRIBUTE_NAME,
|
||||
Integer.class), "Initialized");
|
||||
setAttributes(Map.of(
|
||||
PARAMETERS_ATTRIBUTE_NAME, PARAMS,
|
||||
RETURN_TYPE_ATTRIBUTE_NAME, Integer.class),
|
||||
"Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -381,6 +383,37 @@ public class GadpClientServerTest implements AsyncTestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static final TargetParameterMap ORDERED_PARAMS = TargetMethod.makeParameters(
|
||||
ParameterDescription.create(String.class, "H", true, "", "H", "H"),
|
||||
ParameterDescription.create(String.class, "e", true, "", "e", "e"),
|
||||
ParameterDescription.create(String.class, "l", true, "", "l", "l"),
|
||||
ParameterDescription.create(String.class, "m", true, "", "l", "l"),
|
||||
ParameterDescription.create(String.class, "o", true, "", "o", "o"),
|
||||
ParameterDescription.create(String.class, "W", true, "", "W", "W"),
|
||||
ParameterDescription.create(String.class, "p", true, "", "o", "o"),
|
||||
ParameterDescription.create(String.class, "r", true, "", "r", "r"),
|
||||
ParameterDescription.create(String.class, "n", true, "", "l", "l"),
|
||||
ParameterDescription.create(String.class, "d", true, "", "d", "d"));
|
||||
|
||||
public class TestGadpTargetMethodWithManyParameters
|
||||
extends TestTargetObject<TargetObject, TestTargetObject<?, ?>> implements TargetMethod {
|
||||
|
||||
public TestGadpTargetMethodWithManyParameters(TestTargetObject<?, ?> parent, String key) {
|
||||
super(parent.getModel(), parent, key, "MethodPlus");
|
||||
|
||||
setAttributes(Map.of(
|
||||
PARAMETERS_ATTRIBUTE_NAME, ORDERED_PARAMS,
|
||||
RETURN_TYPE_ATTRIBUTE_NAME, Integer.class),
|
||||
"Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> invoke(Map<String, ?> arguments) {
|
||||
fail("I wasn't expecting that");
|
||||
return AsyncUtils.nil();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetObjectSchemaInfo(name = "ProcessContainer")
|
||||
public class TestGadpTargetProcessContainer
|
||||
extends TestTargetObject<TestGadpTargetProcess, TestGadpTargetSession>
|
||||
@@ -473,8 +506,10 @@ public class GadpClientServerTest implements AsyncTestUtils {
|
||||
public TestGadpTargetAvailableContainer(TestGadpTargetSession session) {
|
||||
super(session.getModel(), session, "Available", "AvailableContainer");
|
||||
|
||||
setAttributes(List.of(new TestGadpTargetMethod(this, "greet")), Map.of(),
|
||||
"Initialized");
|
||||
setAttributes(List.of(
|
||||
new TestGadpTargetMethod(this, "greet"),
|
||||
new TestGadpTargetMethodWithManyParameters(this, "bigGreet")),
|
||||
Map.of(), "Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -815,6 +850,32 @@ public class GadpClientServerTest implements AsyncTestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodParametersOrderPreserved() throws Throwable {
|
||||
AsynchronousSocketChannel socket = socketChannel();
|
||||
try (ServerRunner runner = new ServerRunner()) {
|
||||
GadpClient client = new GadpClient("Test", socket);
|
||||
waitOn(AsyncUtils.completable(TypeSpec.VOID, socket::connect,
|
||||
runner.server.getLocalAddress()));
|
||||
waitOn(client.connect());
|
||||
TargetObject greet =
|
||||
waitOn(client.fetchModelObject(PathUtils.parse("Available.bigGreet")));
|
||||
assertTrue(greet.getInterfaceNames().contains("Method"));
|
||||
TargetMethod method = greet.as(TargetMethod.class);
|
||||
|
||||
TargetParameterMap params = method.getParameters();
|
||||
assertEquals(ORDERED_PARAMS, params);
|
||||
|
||||
assertEquals("HelmoWprnd",
|
||||
params.values().stream().map(p -> p.name).collect(Collectors.joining()));
|
||||
|
||||
waitOn(client.close());
|
||||
}
|
||||
finally {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListRoot() throws Throwable {
|
||||
AsynchronousSocketChannel socket = socketChannel();
|
||||
|
||||
+16
-4
@@ -19,6 +19,7 @@ import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -574,12 +575,23 @@ public class DebuggerListingProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||
ProgramLocation oneBack = new ProgramLocation(panel.getProgram(), addr.previous());
|
||||
runSwing(() -> panel.goTo(addr));
|
||||
runSwing(() -> panel.goTo(oneBack, false));
|
||||
Robot robot = new Robot();
|
||||
waitForPass(() -> {
|
||||
Rectangle r = panel.getBounds();
|
||||
// Capture off screen, so that focus/stacking doesn't matter
|
||||
BufferedImage image = new BufferedImage(r.width, r.height, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics g = image.getGraphics();
|
||||
try {
|
||||
runSwing(() -> panel.paint(g));
|
||||
}
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
Point locP = panel.getLocationOnScreen();
|
||||
Point locFP = panel.getLocationOnScreen();
|
||||
locFP.translate(-locP.x, -locP.y);
|
||||
Rectangle cursor = panel.getCursorBounds();
|
||||
Point panelLoc = panel.getFieldPanel().getLocationOnScreen();
|
||||
Color actual = robot.getPixelColor(panelLoc.x + cursor.x - 1,
|
||||
panelLoc.y + cursor.y + cursor.height * 3 / 2 + yAdjust);
|
||||
Color actual = new Color(image.getRGB(locFP.x + cursor.x - 1,
|
||||
locFP.y + cursor.y + cursor.height * 3 / 2 + yAdjust));
|
||||
assertEquals(expected, actual);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -201,7 +201,10 @@ public interface TargetMethod extends TargetObject {
|
||||
* @return a map of descriptions by name
|
||||
*/
|
||||
static TargetParameterMap makeParameters(Stream<ParameterDescription<?>> params) {
|
||||
return TargetParameterMap.copyOf(params.collect(Collectors.toMap(p -> p.name, p -> p)));
|
||||
return TargetParameterMap
|
||||
.copyOf(params.collect(Collectors.toMap(p -> p.name, p -> p, (a, b) -> {
|
||||
throw new IllegalArgumentException("duplicate parameters: " + a + " and " + b);
|
||||
}, LinkedHashMap::new)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -204,7 +204,7 @@ public enum CollectionUtils {
|
||||
protected final Map<K, V> wrapped;
|
||||
|
||||
public AbstractNMap(Map<K, V> map) {
|
||||
this.wrapped = Map.copyOf(map);
|
||||
this.wrapped = Collections.unmodifiableMap(new LinkedHashMap<>(map));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-3
@@ -201,9 +201,15 @@ public class DebuggerCallbackReordererTest implements DebuggerModelTestUtils {
|
||||
|
||||
assertEquals(toA1, waitOn(listener.get(PathUtils.parse("A[1]"))));
|
||||
assertEquals(toB2, waitOn(listener.get(PathUtils.parse("B[2]"))));
|
||||
// TODO: Not sure I can rely on add order where there's no dependency
|
||||
// E.g., will toA always precede toB, just because it was listed first?
|
||||
assertEquals(List.of(root, toA, toB, toA1, toB2), listener.getAdded());
|
||||
|
||||
// Note the order is not unique, but there are constraints.
|
||||
// It's similar to a topological sort.
|
||||
List<TargetObject> order = List.copyOf(listener.getAdded());
|
||||
assertEquals(0, order.indexOf(root));
|
||||
assertTrue(order.indexOf(root) < order.indexOf(toA));
|
||||
assertTrue(order.indexOf(root) < order.indexOf(toB));
|
||||
assertTrue(order.indexOf(toA) < order.indexOf(toA1));
|
||||
assertTrue(order.indexOf(toB) < order.indexOf(toB2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
//Script to graph class hierarchies given metadata found in class structure description that
|
||||
// was applied using the ExtractClassInfoFromRTTIScript.
|
||||
// was applied using the RecoverClassesFromRTTIScript.
|
||||
//@category C++
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@@ -1,191 +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.
|
||||
*/
|
||||
//Script to update the given class's virtual functions' function signature data types and
|
||||
// the given class's vfunction structure field name for any differing functions in
|
||||
// the class virtual function table(s). To run, put the cursor on any of the desired class's
|
||||
// virtual functions or at the top a class vftable. The script will not work if the <class>_vftable
|
||||
// structure is not applied to the vftable using the ExtractClassInfoFromRTTIScript.
|
||||
//@category C++
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class UpdateClassFunctionDataScript extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("There is no open program");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Function function = getFunctionContaining(currentAddress);
|
||||
if (function != null) {
|
||||
|
||||
Namespace parentNamespace = function.getParentNamespace();
|
||||
|
||||
Parameter thisParam = function.getParameter(0);
|
||||
if (thisParam.getName().equals("this")) {
|
||||
DataType dataType = thisParam.getDataType();
|
||||
if (dataType instanceof Pointer) {
|
||||
Pointer pointer = (Pointer) dataType;
|
||||
DataType baseDataType = pointer.getDataType();
|
||||
if (baseDataType.getName().equals(parentNamespace.getName())) {
|
||||
// call update
|
||||
println("updating class " + parentNamespace.getName());
|
||||
updateClassFunctionDataTypes(parentNamespace);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Symbol primarySymbol = currentProgram.getSymbolTable().getPrimarySymbol(currentAddress);
|
||||
if (primarySymbol.getName().equals("vftable") ||
|
||||
primarySymbol.getName().substring(1).startsWith("vftable")) {
|
||||
updateClassFunctionDataTypes(primarySymbol.getParentNamespace());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateClassFunctionDataTypes(Namespace classNamespace)
|
||||
throws CancelledException, DuplicateNameException, DataTypeDependencyException {
|
||||
|
||||
List<Symbol> classVftableSymbols = getClassVftableSymbols(classNamespace);
|
||||
|
||||
Iterator<Symbol> vftableIterator = classVftableSymbols.iterator();
|
||||
while (vftableIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol vftableSymbol = vftableIterator.next();
|
||||
Address vftableAddress = vftableSymbol.getAddress();
|
||||
Data data = getDataAt(vftableAddress);
|
||||
if (data == null) {
|
||||
continue;
|
||||
}
|
||||
DataType baseDataType = data.getBaseDataType();
|
||||
if (!(baseDataType instanceof Structure)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Structure vfunctionStructure = (Structure) baseDataType;
|
||||
|
||||
Category category = getDataTypeCategory(vfunctionStructure);
|
||||
|
||||
if (category == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check that the structure name starts with <classname>_vtable and that it is in
|
||||
// the dt folder with name <classname>
|
||||
if (category.getName().equals(classNamespace.getName()) &&
|
||||
vfunctionStructure.getName().startsWith(classNamespace.getName() + "_vftable")) {
|
||||
println(
|
||||
"Updating vfunction signature data types and (if necessary) vtable structure for vftable at address " +
|
||||
vftableAddress.toString());
|
||||
updateVfunctionDataTypes(data, vfunctionStructure, vftableAddress);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to find any function signatures in the given vfunction structure that have changed
|
||||
* and update the function signature data types
|
||||
* @throws DuplicateNameException
|
||||
* @throws DataTypeDependencyException
|
||||
*/
|
||||
private void updateVfunctionDataTypes(Data structureAtAddress, Structure vfunctionStructure,
|
||||
Address vftableAddress) throws DuplicateNameException, DataTypeDependencyException {
|
||||
|
||||
DataTypeManager dtMan = currentProgram.getDataTypeManager();
|
||||
|
||||
int numVfunctions = structureAtAddress.getNumComponents();
|
||||
|
||||
for (int i = 0; i < numVfunctions; i++) {
|
||||
Data dataComponent = structureAtAddress.getComponent(i);
|
||||
|
||||
Reference[] referencesFrom = dataComponent.getReferencesFrom();
|
||||
if (referencesFrom.length != 1) {
|
||||
continue;
|
||||
}
|
||||
Address functionAddress = referencesFrom[0].getToAddress();
|
||||
Function vfunction = getFunctionAt(functionAddress);
|
||||
if (vfunction == null) {
|
||||
continue;
|
||||
}
|
||||
FunctionDefinitionDataType functionSignatureDataType =
|
||||
(FunctionDefinitionDataType) vfunction.getSignature();
|
||||
|
||||
DataTypeComponent structureComponent = vfunctionStructure.getComponent(i);
|
||||
DataType componentDataType = structureComponent.getDataType();
|
||||
if (!(componentDataType instanceof Pointer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pointer pointer = (Pointer) componentDataType;
|
||||
DataType pointedToDataType = pointer.getDataType();
|
||||
if (functionSignatureDataType.equals(pointedToDataType)) {
|
||||
continue;
|
||||
}
|
||||
// update data type with new new signature
|
||||
dtMan.replaceDataType(pointedToDataType, functionSignatureDataType, true);
|
||||
if (!structureComponent.getFieldName().equals(vfunction.getName())) {
|
||||
structureComponent.setFieldName(vfunction.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Category getDataTypeCategory(DataType dataType) {
|
||||
|
||||
DataTypeManager dataTypeManager = currentProgram.getDataTypeManager();
|
||||
CategoryPath originalPath = dataType.getCategoryPath();
|
||||
Category category = dataTypeManager.getCategory(originalPath);
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
private List<Symbol> getClassVftableSymbols(Namespace classNamespace)
|
||||
throws CancelledException {
|
||||
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
List<Symbol> vftableSymbols = new ArrayList<Symbol>();
|
||||
|
||||
SymbolIterator symbols = symbolTable.getSymbols(classNamespace);
|
||||
while (symbols.hasNext()) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
Symbol symbol = symbols.next();
|
||||
if (symbol.getName().equals("vftable") ||
|
||||
symbol.getName().substring(1).startsWith("vftable")) {
|
||||
vftableSymbols.add(symbol);
|
||||
}
|
||||
|
||||
}
|
||||
return vftableSymbols;
|
||||
}
|
||||
}
|
||||
+92
@@ -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.app.plugin.core.codebrowser.hover;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JToolTip;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.plugin.core.hover.AbstractConfigurableHover;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.*;
|
||||
|
||||
/**
|
||||
* A Listing hover to show tool tips for function signatures
|
||||
*/
|
||||
public class FunctionSignatureListingHover extends AbstractConfigurableHover
|
||||
implements ListingHoverService {
|
||||
|
||||
private static final String NAME = "Function Signature Display";
|
||||
private static final String DESCRIPTION =
|
||||
"Toggle whether function signature is displayed in a tooltip " +
|
||||
"when the mouse hovers over a function signature.";
|
||||
|
||||
// note: guilty knowledge that the Truncated Text service has a priority of 10
|
||||
private static final int POPUP_PRIORITY = 20;
|
||||
|
||||
public FunctionSignatureListingHover(PluginTool tool) {
|
||||
super(tool, POPUP_PRIORITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getOptionsCategory() {
|
||||
return GhidraOptions.CATEGORY_BROWSER_POPUPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getHoverComponent(Program program, ProgramLocation programLocation,
|
||||
FieldLocation fieldLocation, Field field) {
|
||||
|
||||
if (!enabled || programLocation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends ProgramLocation> clazz = programLocation.getClass();
|
||||
if (clazz != FunctionSignatureFieldLocation.class &&
|
||||
clazz != FunctionNameFieldLocation.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// is the label local to the function
|
||||
FunctionSignatureFieldLocation functionLocation =
|
||||
(FunctionSignatureFieldLocation) programLocation;
|
||||
|
||||
Address entry = functionLocation.getFunctionAddress();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunctionAt(entry);
|
||||
|
||||
String toolTipText = ToolTipUtils.getToolTipText(function, true);
|
||||
JToolTip toolTip = new JToolTip();
|
||||
toolTip.setTipText(toolTipText);
|
||||
return toolTip;
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/* ###
|
||||
* 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.codebrowser.hover;
|
||||
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
|
||||
/**
|
||||
* A plugin to show tool tip text for a function signature
|
||||
*/
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.CODE_VIEWER,
|
||||
shortDescription = "Shows formatted tool tip text over function signatures",
|
||||
description = "This plugin extends the functionality of the code browser by adding a "
|
||||
+ "\tooltip\" over function signaturefields in Listing.",
|
||||
servicesProvided = { ListingHoverService.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class FunctionSignatureListingHoverPlugin extends Plugin {
|
||||
|
||||
private FunctionSignatureListingHover functionSignatureHover;
|
||||
|
||||
public FunctionSignatureListingHoverPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
functionSignatureHover = new FunctionSignatureListingHover(tool);
|
||||
registerServiceProvided(ListingHoverService.class, functionSignatureHover);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
functionSignatureHover.dispose();
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -123,9 +123,9 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
|
||||
return;
|
||||
}
|
||||
|
||||
String dataDescr = "Data Offset";
|
||||
String description = "Data Offset";
|
||||
if (data.getDataType() instanceof Structure) {
|
||||
dataDescr = "Structure Offset";
|
||||
description = "Structure Offset";
|
||||
}
|
||||
|
||||
String name = data.getLabel(); // prefer the label
|
||||
@@ -133,12 +133,14 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
|
||||
name = data.getDataType().getName();
|
||||
}
|
||||
|
||||
name = StringUtilities.trimMiddle(name, 60);
|
||||
|
||||
if (name == null) {
|
||||
// don't think we can get here
|
||||
name = italic("Unnamed");
|
||||
}
|
||||
|
||||
appendTableRow(sb, dataDescr, name, dataOffset);
|
||||
appendTableRow(sb, description, name, dataOffset);
|
||||
}
|
||||
|
||||
private void addByteSourceInfo(Program program, Address loc, StringBuilder sb) {
|
||||
@@ -150,7 +152,8 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
|
||||
if (addressSourceInfo.getFileName() == null) {
|
||||
return;
|
||||
}
|
||||
String filename = StringUtilities.trim(addressSourceInfo.getFileName(), MAX_FILENAME_SIZE);
|
||||
String filename =
|
||||
StringUtilities.trimMiddle(addressSourceInfo.getFileName(), MAX_FILENAME_SIZE);
|
||||
long fileOffset = addressSourceInfo.getFileOffset();
|
||||
String dataDescr = "Byte Source Offset";
|
||||
appendTableRow(sb, dataDescr, "File: " + filename, fileOffset);
|
||||
@@ -160,7 +163,10 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
|
||||
Function function = program.getFunctionManager().getFunctionContaining(loc);
|
||||
if (function != null) {
|
||||
long functionOffset = loc.subtract(function.getEntryPoint());
|
||||
appendTableRow(sb, "Function Offset", HTMLUtilities.escapeHTML(function.getName()),
|
||||
|
||||
String functionName = function.getName();
|
||||
functionName = StringUtilities.trimMiddle(functionName, 60);
|
||||
appendTableRow(sb, "Function Offset", HTMLUtilities.escapeHTML(functionName),
|
||||
functionOffset);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.Msg;
|
||||
@@ -37,7 +38,6 @@ public class TypeGraphTask extends Task {
|
||||
public static final String TYPE_ATTRIBUTE = "Type";
|
||||
public static final String EMBEDDED = "Composite";
|
||||
public static final String POINTER = "Reference";
|
||||
public static final String CONTENTS_ATTRIBUTE = "Contents";
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
@@ -87,7 +87,8 @@ public class TypeGraphTask extends Task {
|
||||
AttributedVertex lastVertex, String edgeType, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
AttributedVertex newVertex = new AttributedVertex(struct.getName());
|
||||
newVertex.setAttribute(CONTENTS_ATTRIBUTE, struct.toString());
|
||||
newVertex.setDescription(ToolTipUtils.getToolTipText(struct));
|
||||
|
||||
if (lastVertex == null) {
|
||||
graph.addVertex(newVertex);
|
||||
}
|
||||
|
||||
+14
-3
@@ -192,9 +192,20 @@ public abstract class AbstractHoverProvider implements HoverProvider {
|
||||
popupWindow.showPopup(event);
|
||||
}
|
||||
else {
|
||||
int xOffset = 50;// magic: trial and error
|
||||
Dimension size = fieldBounds.getSize();
|
||||
Dimension keepVisibleArea = new Dimension(xOffset, size.height);
|
||||
|
||||
//
|
||||
// Make an area over which to show the popup. The popup should not cover this area.
|
||||
// The field that is hovered may be too big to be this area, as a big field may cause
|
||||
// the popup to be too far away from the cursor.
|
||||
//
|
||||
// Use the mouse point and then create an area (based on trial-and-error) that should
|
||||
// not be occluded.
|
||||
//
|
||||
int horizontalPad = 100;
|
||||
int verticalPad = 50;
|
||||
Rectangle keepVisibleArea = new Rectangle(event.getPoint());
|
||||
keepVisibleArea.grow(horizontalPad, verticalPad);
|
||||
|
||||
popupWindow.showOffsetPopup(event, keepVisibleArea);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -91,14 +91,15 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
|
||||
|
||||
String hoverName = getName();
|
||||
options.getOptions(hoverName).setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption(hoverName, true, null, getDescription());
|
||||
enabled = options.getBoolean(hoverName, true);
|
||||
|
||||
options.registerOption(hoverName + Options.DELIMITER + "Dialog Height", 400, help,
|
||||
"Height of the popup window");
|
||||
options.registerOption(hoverName + Options.DELIMITER + "Dialog Width", 600, help,
|
||||
"Width of the popup window");
|
||||
|
||||
setOptions(options, hoverName);
|
||||
options.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
@@ -152,10 +153,18 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
|
||||
return;
|
||||
}
|
||||
|
||||
toolTip = new JToolTip();
|
||||
|
||||
panel = new ListingPanel(codeFormatService.getFormatManager());// share the manager from the code viewer
|
||||
panel.setTextBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
toolTip = new JToolTip();
|
||||
String name = getName();
|
||||
String widthOptionName = name + Options.DELIMITER + "Dialog Width";
|
||||
String heightOptionName = name + Options.DELIMITER + "Dialog Height";
|
||||
int dialogWidth = options.getInt(widthOptionName, 600);
|
||||
int dialogHeight = options.getInt(heightOptionName, 400);
|
||||
Dimension d = new Dimension(dialogWidth, dialogHeight);
|
||||
panel.setPreferredSize(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-2
@@ -526,11 +526,15 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
||||
.menuPath(ToolConstants.MENU_FILE, "&Open...")
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
.keyBinding("ctrl O")
|
||||
.withContext(ProgramActionContext.class)
|
||||
.inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
.onAction(c -> open())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
// .withContext(ProgramActionContext.class)
|
||||
// .inWindow(ActionBuilder.When.CONTEXT_MATCHES)
|
||||
// openAction doesn't really use a context, but we want it to be in windows that
|
||||
// have providers that use programs.
|
||||
openAction.addToWindowWhen(ProgramActionContext.class);
|
||||
|
||||
closeAction = new ActionBuilder("Close File", getName())
|
||||
.menuPath(ToolConstants.MENU_FILE, "&Close")
|
||||
.menuGroup(OPEN_GROUP, Integer.toString(subMenuGroupOrder++))
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
package ghidra.app.services;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* <code>HoverService</code> provides the ability to popup data Windows over a Field viewer
|
||||
@@ -30,7 +29,8 @@ import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
public interface HoverService {
|
||||
|
||||
/**
|
||||
* Returns the priority of this hover service.
|
||||
* Returns the priority of this hover service. A lower priority is more important.
|
||||
* @return the priority
|
||||
*/
|
||||
public int getPriority();
|
||||
|
||||
@@ -41,7 +41,8 @@ public interface HoverService {
|
||||
public void scroll(int amount);
|
||||
|
||||
/**
|
||||
* Return whether hover mode is "on."
|
||||
* Return whether hover mode is "on"
|
||||
* @return the priority
|
||||
*/
|
||||
public boolean hoverModeSelected();
|
||||
|
||||
|
||||
+43
@@ -22,6 +22,7 @@ import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
@@ -69,6 +70,48 @@ abstract public class ElfRelocationHandler implements ExtensionPoint {
|
||||
ElfRelocation relocation, Address relocationAddress)
|
||||
throws MemoryAccessException, NotFoundException;
|
||||
|
||||
/**
|
||||
* Determine if symbolAddr is contained within the EXTERNAL block. If so, relocationAddress will be marked
|
||||
* with a <code<Unsupported EXTERNAL Data Elf Relocation</code> error bookmark.
|
||||
* NOTE: This method should only be invoked when the symbol offset will be adjust with a non-zero
|
||||
* value (i.e., addend).
|
||||
* @param program
|
||||
* @param relocationAddress relocation address to be bookmarked if EXTERNAL block relocation
|
||||
* @param symbolAddr symbol address correspondng to relocation (may be null)
|
||||
* @param symbolName symbol name (may not be null if symbolAddr is not null)
|
||||
* @param adjustment relocation symbol offset adjustment/addend
|
||||
* @param log import log
|
||||
* @return true if symbolAddress contained within EXTERNAL block.
|
||||
*/
|
||||
public static boolean isUnsupportedExternalRelocation(Program program,
|
||||
Address relocationAddress, Address symbolAddr, String symbolName, long adjustment,
|
||||
MessageLog log) {
|
||||
|
||||
if (symbolAddr == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryBlock block = program.getMemory().getBlock(symbolAddr);
|
||||
if (block == null || !MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String sign = "+";
|
||||
if (adjustment < 0) {
|
||||
adjustment = -adjustment;
|
||||
sign = "-";
|
||||
}
|
||||
String adjStr = sign + "0x" + Long.toHexString(adjustment);
|
||||
|
||||
symbolName = symbolName == null ? "<no name>" : symbolName;
|
||||
log.appendMsg("Unsupported EXTERNAL Data Elf Relocation: at " + relocationAddress +
|
||||
" (External Location = " + symbolName + adjStr + ")");
|
||||
BookmarkManager bookmarkManager = program.getBookmarkManager();
|
||||
bookmarkManager.setBookmark(relocationAddress, BookmarkType.ERROR, "EXTERNAL Relocation",
|
||||
"Unsupported EXTERNAL Data Elf Relocation: External Location = " + symbolName + adjStr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate error log entry and bookmark at relocationAddress indicating
|
||||
* an unhandled relocation.
|
||||
|
||||
+3
-1
@@ -514,9 +514,11 @@ public class KeyBindingUtilsTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private void selectRowForAction(DockingActionIf action) throws Exception {
|
||||
String actionName = action.getName();
|
||||
String owner = action.getOwnerDescription();
|
||||
|
||||
for (int i = 0; i < model.getRowCount(); i++) {
|
||||
if (actionName.equals(model.getValueAt(i, 0))) {
|
||||
if (actionName.equals(model.getValueAt(i, 0)) &&
|
||||
owner.equals(model.getValueAt(i, 2))) {
|
||||
final int idx = i;
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
table.setRowSelectionInterval(idx, idx);
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Script to apply any changes the user has made to class virtual function definitions, ie ones they
|
||||
// have edited in the data type manager. To run the script, put the cursor on any member of the
|
||||
// desired class in the listing then run the script. For each function definition in the given class
|
||||
// that differs from the associated function signature in the listing, the script will update the
|
||||
// listing function signatures of any related virtual vunctions (ie parents and children) and update
|
||||
// related data types such as function definitions of the given class and related classes and also
|
||||
// field names in related vftable structures.
|
||||
// Note: The script will not work if the vftable structures were not originally applied to
|
||||
// the vftables using the RecoverClassesFromRTTIScript.
|
||||
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so.
|
||||
//@category C++
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import classrecovery.RecoveredClassUtils;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.data.FunctionDefinition;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
public class ApplyClassFunctionDefinitionUpdatesScript extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("There is no open program");
|
||||
return;
|
||||
}
|
||||
|
||||
RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation,
|
||||
state.getTool(), this, false, false, false, monitor);
|
||||
|
||||
Namespace classNamespace = classUtils.getClassNamespace(currentAddress);
|
||||
if (classNamespace == null) {
|
||||
println(
|
||||
"Either cannot retrieve class namespace or cursor is not in a member of a class namepace");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Symbol> classVftableSymbols = classUtils.getClassVftableSymbols(classNamespace);
|
||||
if (classVftableSymbols.isEmpty()) {
|
||||
println("There are no vftables in this class");
|
||||
return;
|
||||
}
|
||||
|
||||
println(
|
||||
"Applying differing function definitions for class " + classNamespace.getName(true));
|
||||
|
||||
List<Object> changedItems =
|
||||
classUtils.applyNewFunctionDefinitions(classNamespace, classVftableSymbols);
|
||||
|
||||
if (changedItems.isEmpty()) {
|
||||
println("No differences found for class " + classNamespace.getName(true) +
|
||||
" between the vftable listing function signatures and their associated data type manager function definition data types");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Structure> structuresOnList = classUtils.getStructuresOnList(changedItems);
|
||||
List<FunctionDefinition> functionDefinitionsOnList =
|
||||
classUtils.getFunctionDefinitionsOnList(changedItems);
|
||||
List<Function> functionsOnList = classUtils.getFunctionsOnList(changedItems);
|
||||
|
||||
println();
|
||||
println("Updated structures:");
|
||||
for (Structure structure : structuresOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(structure.getPathName());
|
||||
}
|
||||
|
||||
println();
|
||||
println("Updated function definitions:");
|
||||
for (FunctionDefinition functionDef : functionDefinitionsOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(functionDef.getPathName());
|
||||
}
|
||||
|
||||
println();
|
||||
println("Updated functions:");
|
||||
for (Function function : functionsOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(function.getEntryPoint().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Script to apply any changes the user has made to class virtual function signatures, ie ones they
|
||||
// have edited in the listing. To run the script, put the cursor on any member of the desired class in
|
||||
// the listing then run the script. For each function signature in the given class that differs from
|
||||
// the associated function definition in the data type manager, the script will update the listing
|
||||
// function signatures of any related virtual vunctions (ie parents and children) and update related
|
||||
// data types such as function definitions of the given class and related classes and also field names
|
||||
// in related vftable structures.
|
||||
// Note: The script will not work if the vftable structures were not originally applied to
|
||||
// the vftables using the RecoverClassesFromRTTIScript.
|
||||
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so.
|
||||
//@category C++
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import classrecovery.RecoveredClassUtils;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.data.FunctionDefinition;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
public class ApplyClassFunctionSignatureUpdatesScript extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("There is no open program");
|
||||
return;
|
||||
}
|
||||
|
||||
RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation,
|
||||
state.getTool(), this, false, false, false, monitor);
|
||||
|
||||
Namespace classNamespace = classUtils.getClassNamespace(currentAddress);
|
||||
if (classNamespace == null) {
|
||||
println(
|
||||
"Either cannot retrieve class namespace or cursor is not in a member of a class namepace");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Symbol> classVftableSymbols = classUtils.getClassVftableSymbols(classNamespace);
|
||||
if (classVftableSymbols.isEmpty()) {
|
||||
println("There are no vftables in this class");
|
||||
return;
|
||||
}
|
||||
|
||||
println("Applying differing virtual function signatures for class " +
|
||||
classNamespace.getName(true));
|
||||
|
||||
List<Object> changedItems =
|
||||
classUtils.applyNewFunctionSignatures(classNamespace, classVftableSymbols);
|
||||
|
||||
if (changedItems.isEmpty()) {
|
||||
println("No differences found for class " + classNamespace.getName(true) +
|
||||
" between the listing virtual function signatures and their associated data type manager function definition data types.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Structure> structuresOnList = classUtils.getStructuresOnList(changedItems);
|
||||
List<FunctionDefinition> functionDefinitionsOnList =
|
||||
classUtils.getFunctionDefinitionsOnList(changedItems);
|
||||
List<Function> functionsOnList = classUtils.getFunctionsOnList(changedItems);
|
||||
|
||||
println();
|
||||
println("Updated structures:");
|
||||
for (Structure structure : structuresOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(structure.getPathName());
|
||||
}
|
||||
|
||||
println();
|
||||
println("Updated function definitions:");
|
||||
for (FunctionDefinition functionDef : functionDefinitionsOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(functionDef.getPathName());
|
||||
}
|
||||
|
||||
println();
|
||||
println("Updated functions:");
|
||||
for (Function function : functionsOnList) {
|
||||
monitor.checkCanceled();
|
||||
println(function.getEntryPoint().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,6 +37,17 @@
|
||||
// NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still
|
||||
// in early stages of development. Gcc class data types have not been recovered yet but if the program
|
||||
// has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder.
|
||||
// NOTE: For likely the best results, run this script on freshly analyzed programs. No testing has been
|
||||
// done on user marked-up programs.
|
||||
// NOTE: After running this script if you edit function signatures in the listing for a particular
|
||||
// class and wish to update the corresponding class data (function definition data types, vftable
|
||||
// structure field names, ...) then you can run the ApplyClassFunctionSignatureUpdatesScript.java
|
||||
// to have it do so for you.
|
||||
// Conversely, if you update a particular class's function definitions in the data type manager and
|
||||
// wish to have related function signatures in the listing updated, as well as other data types that
|
||||
// are related, then run the ApplyClassFunctionDefinitionsUpdatesScript.java to do so. At some point,
|
||||
// the Ghidra API will be updated to do this all automatically instead of needing the scripts to do so.
|
||||
|
||||
//@category C++
|
||||
|
||||
import java.io.File;
|
||||
@@ -124,8 +135,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
||||
|
||||
int defaultPointerSize;
|
||||
|
||||
RecoveredClassUtils classUtils;
|
||||
|
||||
RTTIClassRecoverer recoverClassesFromRTTI;
|
||||
|
||||
ExtraScriptUtils extraUtils;
|
||||
@@ -143,25 +152,18 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
||||
|
||||
|
||||
if (isWindows()) {
|
||||
|
||||
// TODO: check for typeinfo using the other way i had then pull the hasRTTI in and if first
|
||||
// is true and second isn't then run the analyzer - move all this into a method
|
||||
isPDBLoaded = isPDBLoadedInProgram();
|
||||
nameVfunctions = !isPDBLoaded;
|
||||
recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram,
|
||||
currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
|
||||
USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor);
|
||||
|
||||
}
|
||||
else if (isGcc()) {
|
||||
|
||||
// for now assume gcc has named vfunctions
|
||||
// for now assume gcc has named vfunctions until a way to check is developed
|
||||
nameVfunctions = true;
|
||||
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation,
|
||||
state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
|
||||
USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, monitor);
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
println("This script will not work on this program type");
|
||||
@@ -185,9 +187,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
decompilerUtils = recoverClassesFromRTTI.getDecompilerUtils();
|
||||
DecompInterface decompInterface = decompilerUtils.getDecompilerInterface();
|
||||
|
||||
@@ -962,12 +961,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
|
||||
println("Total number of indetermined constructor/destructors: " +
|
||||
remainingIndeterminates.size());
|
||||
|
||||
//TODO: need to get from the new class
|
||||
// println("Total fixed incorrect FID functions: " + badFIDFunctions.size());
|
||||
// println("Total resolved functions that had multiple FID possiblities: " +
|
||||
// resolvedFIDFunctions.size());
|
||||
// println("Total fixed functions that had incorrect data types due to incorrect FID: " +
|
||||
// fixedFIDFunctions.size());
|
||||
println("Total fixed incorrect FID functions: " +
|
||||
recoverClassesFromRTTI.getBadFIDFunctions().size());
|
||||
println("Total resolved functions that had multiple FID possiblities: " +
|
||||
recoverClassesFromRTTI.getResolvedFIDFunctions().size());
|
||||
println("Total fixed functions that had incorrect data types due to incorrect FID: " +
|
||||
recoverClassesFromRTTI.getFixedFIDFunctions().size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ package classrecovery;
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
|
||||
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||
@@ -506,20 +506,19 @@ public class ExtraScriptUtils extends FlatProgramAPI {
|
||||
* Method to create a function in the given program at the given address
|
||||
* @param prog the given program
|
||||
* @param addr the given address
|
||||
* @param tMonitor a cancellable task monitor
|
||||
* @return true if the function was created, false otherwise
|
||||
*/
|
||||
public boolean createFunction(Program prog, Address addr, TaskMonitor tMonitor) {
|
||||
public boolean createFunction(Program prog, Address addr) {
|
||||
|
||||
try {
|
||||
AddressSet subroutineAddresses = getSubroutineAddresses(prog, addr, tMonitor);
|
||||
AddressSet subroutineAddresses = getSubroutineAddresses(prog, addr);
|
||||
if (subroutineAddresses.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CreateFunctionCmd cmd = new CreateFunctionCmd(null, subroutineAddresses.getMinAddress(),
|
||||
null, SourceType.DEFAULT);
|
||||
if (cmd.applyTo(prog, tMonitor)) {
|
||||
if (cmd.applyTo(prog, monitor)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -535,12 +534,11 @@ public class ExtraScriptUtils extends FlatProgramAPI {
|
||||
* Method to figure out a subroutine address set given an address contained in it
|
||||
* @param program the given program
|
||||
* @param address address in the potential subroutine
|
||||
* @param monitor allows canceling
|
||||
* @return address set of the subroutine to be created
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public static AddressSet getSubroutineAddresses(Program program, Address address,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
public AddressSet getSubroutineAddresses(Program program, Address address)
|
||||
throws CancelledException {
|
||||
|
||||
// Create a new address set to hold the entire selection.
|
||||
AddressSet subroutineAddresses = new AddressSet();
|
||||
|
||||
+1075
-80
File diff suppressed because it is too large
Load Diff
@@ -25,13 +25,19 @@ import java.util.List;
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
|
||||
import docking.widgets.shapes.*;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.util.bean.GGlassPane;
|
||||
import ghidra.util.bean.GGlassPanePainter;
|
||||
|
||||
/**
|
||||
* A generic window intended to be used as a temporary window to show information. This window is
|
||||
* designed to stay open as long as the user mouses over the window. Once the user mouses away,
|
||||
* the window will be closed.
|
||||
*/
|
||||
public class PopupWindow {
|
||||
private static final int X_PADDING = 20;
|
||||
private static final int Y_PADDING = 20;
|
||||
private static final int X_PADDING = 25;
|
||||
private static final int Y_PADDING = 25;
|
||||
private static final List<WeakReference<PopupWindow>> VISIBLE_POPUPS = new ArrayList<>();
|
||||
|
||||
public static void hideAllWindows() {
|
||||
@@ -43,11 +49,20 @@ public class PopupWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private JWindow popup;
|
||||
private static final PopupWindowPlacer DEFAULT_WINDOW_PLACER =
|
||||
new PopupWindowPlacerBuilder()
|
||||
.rightEdge(Location.BOTTOM)
|
||||
.leftEdge(Location.BOTTOM)
|
||||
.bottomEdge(Location.RIGHT)
|
||||
.topEdge(Location.CENTER)
|
||||
.leastOverlapCorner()
|
||||
.throwsAssertException()
|
||||
.build();
|
||||
|
||||
private Component sourceComponent;
|
||||
/** Area where user can mouse without hiding the window (in screen coordinates) */
|
||||
private Rectangle neutralMotionZone;
|
||||
private Rectangle mouseMovementArea;
|
||||
private JWindow popup;
|
||||
private Component sourceComponent;
|
||||
|
||||
private MouseMotionListener sourceMouseMotionListener;
|
||||
private MouseListener sourceMouseListener;
|
||||
@@ -87,7 +102,7 @@ public class PopupWindow {
|
||||
|
||||
popup = new JWindow(parentWindow);
|
||||
popup.setFocusableWindowState(false);
|
||||
// this is bad, as it keeps tooltips above all apps and they don't go away, as normal tooltips do
|
||||
// this is bad, as it keeps tooltips above all apps and they don't go away, as normal tooltips do
|
||||
// popup.setAlwaysOnTop( true );
|
||||
|
||||
popup.getContentPane().add(displayComponent);
|
||||
@@ -112,16 +127,16 @@ public class PopupWindow {
|
||||
sourceMouseMotionListener = new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
Point localPoint = e.getPoint();
|
||||
|
||||
Point localPoint = e.getPoint();
|
||||
SwingUtilities.convertPointToScreen(localPoint, e.getComponent());
|
||||
if (!neutralMotionZone.contains(localPoint)) {
|
||||
if (!mouseMovementArea.contains(localPoint)) {
|
||||
hide();
|
||||
}
|
||||
else {
|
||||
// If the user mouses around the neutral zone, then start the close timer. The
|
||||
// timer will be reset if the user enters the popup.
|
||||
closeTimer.start();
|
||||
closeTimer.restart();
|
||||
}
|
||||
e.consume(); // consume the event so that the source component doesn't processes it
|
||||
}
|
||||
@@ -183,8 +198,8 @@ public class PopupWindow {
|
||||
}
|
||||
|
||||
private void removeOldPopupReferences() {
|
||||
for (Iterator<WeakReference<PopupWindow>> iterator =
|
||||
VISIBLE_POPUPS.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<WeakReference<PopupWindow>> iterator = VISIBLE_POPUPS.iterator(); iterator
|
||||
.hasNext();) {
|
||||
WeakReference<PopupWindow> reference = iterator.next();
|
||||
PopupWindow window = reference.get();
|
||||
if (window == this) {
|
||||
@@ -202,7 +217,7 @@ public class PopupWindow {
|
||||
/**
|
||||
* Sets the amount of time that will pass before the popup window is closed <b>after</b> the
|
||||
* user moves away from the popup window and out of the neutral zone
|
||||
*
|
||||
*
|
||||
* @param delayInMillis the timer delay
|
||||
*/
|
||||
public void setCloseWindowDelay(int delayInMillis) {
|
||||
@@ -210,43 +225,32 @@ public class PopupWindow {
|
||||
closeTimer.setRepeats(false);
|
||||
}
|
||||
|
||||
public void showOffsetPopup(MouseEvent e, Dimension keepVisibleArea) {
|
||||
doShowPopup(e, keepVisibleArea);
|
||||
public void showOffsetPopup(MouseEvent e, Rectangle keepVisibleSize) {
|
||||
doShowPopup(e, keepVisibleSize, DEFAULT_WINDOW_PLACER);
|
||||
}
|
||||
|
||||
public void showPopup(MouseEvent e) {
|
||||
doShowPopup(e, null);
|
||||
doShowPopup(e, null, DEFAULT_WINDOW_PLACER);
|
||||
}
|
||||
|
||||
private void doShowPopup(MouseEvent e, Dimension keepVisibleArea) {
|
||||
private void doShowPopup(MouseEvent e, Rectangle keepVisibleSize, PopupWindowPlacer placer) {
|
||||
hideAllWindows();
|
||||
|
||||
sourceComponent = e.getComponent();
|
||||
sourceComponent.addMouseListener(sourceMouseListener);
|
||||
sourceComponent.addMouseMotionListener(sourceMouseMotionListener);
|
||||
|
||||
Point point = e.getPoint();
|
||||
SwingUtilities.convertPointToScreen(point, sourceComponent);
|
||||
if (keepVisibleArea == null) {
|
||||
keepVisibleArea = new Dimension(0, 0);
|
||||
}
|
||||
Dimension popupDimension = popup.getSize();
|
||||
ensureSize(popupDimension);
|
||||
|
||||
Rectangle popupBounds = popup.getBounds();
|
||||
|
||||
int x = point.x + keepVisibleArea.width + X_PADDING;
|
||||
int y = point.y + keepVisibleArea.height + Y_PADDING;
|
||||
popupBounds.setLocation(x, y);
|
||||
|
||||
WindowUtilities.ensureOnScreen(sourceComponent, popupBounds);
|
||||
|
||||
Rectangle hoverArea = new Rectangle(point, keepVisibleArea);
|
||||
adjustBoundsForCursorLocation(popupBounds, hoverArea);
|
||||
|
||||
neutralMotionZone = createNeutralMotionZone(popupBounds, hoverArea);
|
||||
Rectangle keepVisibleArea = createKeepVisibleArea(e, keepVisibleSize);
|
||||
Rectangle screenBounds = WindowUtilities.getVisibleScreenBounds().getBounds();
|
||||
Rectangle placement = placer.getPlacement(popupDimension, keepVisibleArea, screenBounds);
|
||||
mouseMovementArea = createMovementArea(placement, keepVisibleArea);
|
||||
|
||||
installDebugPainter(e);
|
||||
|
||||
popup.setBounds(popupBounds);
|
||||
popup.setBounds(placement);
|
||||
popup.setVisible(true);
|
||||
|
||||
removeOldPopupReferences();
|
||||
@@ -254,87 +258,62 @@ public class PopupWindow {
|
||||
VISIBLE_POPUPS.add(new WeakReference<>(this));
|
||||
}
|
||||
|
||||
private Rectangle createKeepVisibleArea(MouseEvent e, Rectangle keepVisibleAea) {
|
||||
|
||||
Rectangle newArea;
|
||||
if (keepVisibleAea == null) {
|
||||
Point point = new Point(e.getPoint());
|
||||
newArea = new Rectangle(point);
|
||||
newArea.grow(X_PADDING, Y_PADDING); // pad to avoid placing the popup too close
|
||||
}
|
||||
else {
|
||||
newArea = new Rectangle(keepVisibleAea);
|
||||
}
|
||||
|
||||
Point point = newArea.getLocation();
|
||||
SwingUtilities.convertPointToScreen(point, sourceComponent);
|
||||
newArea.setLocation(point);
|
||||
|
||||
return newArea;
|
||||
}
|
||||
|
||||
private void ensureSize(Dimension popupDimension) {
|
||||
Dimension screenDimension = WindowUtilities.getVisibleScreenBounds().getBounds().getSize();
|
||||
|
||||
if (screenDimension.width < popupDimension.width) {
|
||||
popupDimension.width = screenDimension.width / 2;
|
||||
}
|
||||
|
||||
if (screenDimension.height < popupDimension.height) {
|
||||
popupDimension.height = screenDimension.height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rectangle that contains both given rectangles entirely and includes padding.
|
||||
* The padding allows users to mouse over the edge of the hovered area without triggering the
|
||||
* popup to close.
|
||||
*/
|
||||
private Rectangle createMovementArea(Rectangle popupBounds, Rectangle hoverRectangle) {
|
||||
Rectangle result = popupBounds.union(hoverRectangle);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void installDebugPainter(MouseEvent e) {
|
||||
// GGlassPane glassPane = GGlassPane.getGlassPane(sourceComponent);
|
||||
// ShapeDebugPainter painter = new ShapeDebugPainter(e, neutralMotionZone);
|
||||
// glassPane.addPainter(painter);
|
||||
// GGlassPane glassPane = GGlassPane.getGlassPane(sourceComponent);
|
||||
// ShapeDebugPainter painter = new ShapeDebugPainter(e, null, neutralMotionZone);
|
||||
// painters.forEach(p -> glassPane.removePainter(p));
|
||||
//
|
||||
// glassPane.addPainter(painter);
|
||||
// painters.add(painter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the given bounds to make sure that they do not cover the given location.
|
||||
* <p>
|
||||
* When the <tt>hoverArea</tt> is obscured, this method will first attempt to move the
|
||||
* bounds up if possible. If moving up is not possible due to space constraints, then this
|
||||
* method will try to shift the bounds to the right of the hover area. If this is not
|
||||
* possible, then the bounds will not be changed.
|
||||
*
|
||||
* @param bounds The bounds to move as necessary.
|
||||
* @param hoverArea The area that should not be covered by the given bounds
|
||||
* @return the original bounds adjusted so that they do not cover the given <tt>hoverArea</tt>,
|
||||
* if possible.
|
||||
*/
|
||||
private Rectangle adjustBoundsForCursorLocation(Rectangle bounds, Rectangle hoverArea) {
|
||||
if (!bounds.intersects(hoverArea)) {
|
||||
return bounds;
|
||||
}
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
// first attempt to move the window--try to go up
|
||||
int movedY = hoverArea.y - bounds.height;
|
||||
boolean canMoveUp = movedY >= 0;
|
||||
if (canMoveUp) {
|
||||
// move the given bounds above the current point
|
||||
bounds.y = movedY;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// We couldn't move up, so we try to go left, since by default the popup is placed
|
||||
// to the right of the hover area.
|
||||
int movedX = hoverArea.x - bounds.width;
|
||||
boolean canMoveLeft = movedX >= 0;
|
||||
if (canMoveLeft) {
|
||||
bounds.x = movedX;
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rectangle that contains both given rectangles entirely.
|
||||
*/
|
||||
private Rectangle createNeutralMotionZone(Rectangle popupBounds, Rectangle hoverRectangle) {
|
||||
int newX = Math.min(hoverRectangle.x, popupBounds.x);
|
||||
int newY = Math.min(hoverRectangle.y, popupBounds.y);
|
||||
|
||||
double hoverLowestCornerX = hoverRectangle.x + hoverRectangle.getWidth();
|
||||
double popupLowestCornerX = popupBounds.x + popupBounds.getWidth();
|
||||
int lowestCornerX = (int) Math.max(hoverLowestCornerX, popupLowestCornerX);
|
||||
|
||||
double hoverLowestCornerY = hoverRectangle.y + hoverRectangle.getHeight();
|
||||
double popupLowestCornerY = popupBounds.y + popupBounds.getHeight();
|
||||
int lowestCornerY = (int) Math.max(hoverLowestCornerY, popupLowestCornerY);
|
||||
|
||||
int width = difference(newX, lowestCornerX);
|
||||
int height = difference(newY, lowestCornerY);
|
||||
|
||||
// add in some padding around the edges of the area, so that moving just over the edge
|
||||
// of the popup will not close it (this can happen when the user sloppy-scrolls)
|
||||
int padding = 25;
|
||||
newX -= padding;
|
||||
newY -= padding;
|
||||
width += (padding * 2); // * 2 to give the padding and to compensate for the shifted x
|
||||
height += (padding * 2); // * 2 to give the padding and to compensate for the shifted y
|
||||
|
||||
return new Rectangle(newX, newY, width, height);
|
||||
}
|
||||
|
||||
private int difference(int value1, int value2) {
|
||||
int abs1 = Math.abs(value1);
|
||||
int abs2 = Math.abs(value2);
|
||||
if (abs1 > abs2) {
|
||||
return abs1 - abs2;
|
||||
}
|
||||
return abs2 - abs1;
|
||||
}
|
||||
// for debug
|
||||
// private static List<GGlassPanePainter> painters = new ArrayList<>();
|
||||
|
||||
/** Paints shapes used by this class (useful for debugging) */
|
||||
@SuppressWarnings("unused")
|
||||
@@ -350,23 +329,27 @@ public class PopupWindow {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(GGlassPane glassPane, Graphics graphics) {
|
||||
public void paint(GGlassPane glassPane, Graphics g) {
|
||||
|
||||
// bounds of the popup and the mouse neutral zone
|
||||
Rectangle r = bounds;
|
||||
Point p = new Point(r.getLocation());
|
||||
SwingUtilities.convertPointFromScreen(p, glassPane);
|
||||
if (bounds != null) {
|
||||
Rectangle r = bounds;
|
||||
Point p = new Point(r.getLocation());
|
||||
SwingUtilities.convertPointFromScreen(p, glassPane);
|
||||
|
||||
Color c = new Color(50, 50, 200, 125);
|
||||
graphics.setColor(c);
|
||||
graphics.fillRect(p.x, p.y, r.width, r.height);
|
||||
Color c = new Color(50, 50, 200, 125);
|
||||
g.setColor(c);
|
||||
g.fillRect(p.x, p.y, r.width, r.height);
|
||||
}
|
||||
|
||||
// show where the user hovered
|
||||
p = sourceEvent.getPoint();
|
||||
p = SwingUtilities.convertPoint(sourceEvent.getComponent(), p.x, p.y, glassPane);
|
||||
graphics.setColor(Color.RED);
|
||||
int offset = 10;
|
||||
graphics.fillRect(p.x - offset, p.y - offset, (offset * 2), (offset * 2));
|
||||
if (sourceEvent != null) {
|
||||
Point p = sourceEvent.getPoint();
|
||||
p = SwingUtilities.convertPoint(sourceEvent.getComponent(), p.x, p.y, glassPane);
|
||||
g.setColor(Color.RED);
|
||||
int offset = 10;
|
||||
g.fillRect(p.x - offset, p.y - offset, (offset * 2), (offset * 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ class FileChooserActionManager {
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
renameAction = new DockingAction("Rename", OWNER) {
|
||||
renameAction = new DockingAction("Rename", OWNER, false) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@@ -87,7 +87,7 @@ class FileChooserActionManager {
|
||||
renameAction.markHelpUnnecessary();
|
||||
chooser.addAction(renameAction);
|
||||
|
||||
removeRecentAction = new DockingAction("Remove Recent", OWNER) {
|
||||
removeRecentAction = new DockingAction("Remove Recent", OWNER, false) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
@@ -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 docking.widgets.shapes;
|
||||
|
||||
/**
|
||||
* Specifies location and metrics for {@link PopupWindowPlacer}.
|
||||
*/
|
||||
public enum Location {
|
||||
LEFT, RIGHT, TOP, BOTTOM, CENTER;
|
||||
|
||||
static {
|
||||
LEFT.set(false, true, RIGHT, TOP);
|
||||
RIGHT.set(true, true, LEFT, BOTTOM);
|
||||
TOP.set(false, false, BOTTOM, RIGHT);
|
||||
BOTTOM.set(true, false, TOP, LEFT);
|
||||
CENTER.set(null, null, null, null);
|
||||
}
|
||||
|
||||
// Tri-valued value: false === Lesser, and null === Center
|
||||
private Boolean isGreater;
|
||||
// Tri-valued value: false === Vertical, and null === either (e.g., Center)
|
||||
// Means it is a measure of horizontal (e.g., left right) as opposed to a left edge, which
|
||||
// is a vertical line (the minor elements which describe location on this edge are vertical).
|
||||
private Boolean isHorizontal;
|
||||
private Location match;
|
||||
private Location clockwiseNext;
|
||||
|
||||
private void set(Boolean isGreater, Boolean isHorizontal, Location match,
|
||||
Location clockwiseNext) {
|
||||
this.isGreater = isGreater;
|
||||
this.isHorizontal = isHorizontal;
|
||||
this.match = match;
|
||||
this.clockwiseNext = clockwiseNext;
|
||||
}
|
||||
|
||||
public boolean isGreater() {
|
||||
return isGreater != null && isGreater;
|
||||
}
|
||||
|
||||
public boolean isLesser() {
|
||||
return isGreater != null && !isGreater;
|
||||
}
|
||||
|
||||
public boolean isCenter() {
|
||||
return isGreater == null;
|
||||
}
|
||||
|
||||
public Location match() {
|
||||
return match;
|
||||
}
|
||||
|
||||
public Location clockwise() {
|
||||
return clockwiseNext;
|
||||
}
|
||||
|
||||
public Location counterClockwise() {
|
||||
return clockwiseNext.match();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes "this" is a major axis, and tells whether the minor axis argument is valid for
|
||||
* the major value. Cannot have both major and minor be the same horizontal/vertical bearing.
|
||||
* Note that {@link #CENTER} can be horizontal or vertical, so this method should not count
|
||||
* this value as a bad minor value, as it also represents a good value.
|
||||
* @param minor the minor value to check
|
||||
* @return true if valid.
|
||||
*/
|
||||
public boolean validMinor(Location minor) {
|
||||
return isHorizontal() && minor.isVertical() || isVertical() && minor.isHorizontal();
|
||||
}
|
||||
|
||||
public boolean isHorizontal() {
|
||||
return isHorizontal == null || isHorizontal;
|
||||
}
|
||||
|
||||
public boolean isVertical() {
|
||||
return isHorizontal == null || !isHorizontal;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+429
@@ -0,0 +1,429 @@
|
||||
/* ###
|
||||
* 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 docking.widgets.shapes;
|
||||
|
||||
import docking.widgets.shapes.PopupWindowPlacer.*;
|
||||
|
||||
/**
|
||||
* This class builds a PopWindowPlacer that can have subsequent PopWindowPlacers.
|
||||
* <p>
|
||||
* General categories of placers available are <B>edge</B> placers, <B>overlapped-corner</B>
|
||||
* placers, and a clean-up <B>assert</B> placer. Additionally, there are <B>rotational</B> placers
|
||||
* that are composed of edge placers.
|
||||
* <p>
|
||||
* <BR>
|
||||
* <BR>
|
||||
*
|
||||
* <H1>Edge Placers</H1>
|
||||
*
|
||||
* <p>
|
||||
* The <B>edge</B> placers are the leftEdge, rightEdge, topEdge, and bottomEdge methods that take
|
||||
* Location arguments that one can think of as "cells" for optimal placement, but which have some
|
||||
* flexibility in making the placement. One such cell is the TOP Location of the rightEdge,
|
||||
* specified by <code>rightEdge(Location.TOP)</code>. If the placement does not quite fit this
|
||||
* cell because the optimal placement extend above the top of the screen, the placement may be
|
||||
* shifted down by a allowed amount so that it still fits. If more than the allowed amount is
|
||||
* needed, the placement fails.
|
||||
* <p>
|
||||
* Each edge placer takes a variable number of Location arguments. These arguments work in the
|
||||
* same way for each method, though some arguments are not valid for some edges; for instance,
|
||||
* <code>Location.TOP</code> is only valid for left and right edges.
|
||||
* <p>
|
||||
*
|
||||
* <H2>Two or More Location Arguments</H2>
|
||||
*
|
||||
* <p>
|
||||
* When two or more arguments are used, the first argument specifies the nominal placement cell
|
||||
* and the second argument specifies how far the solution is allowed to shift. If a solution is
|
||||
* not found and if there are more than two arguments, another placement attempt is made where
|
||||
* the second argument specifies the nominal placement cell and the third argument specifies how
|
||||
* far the solution is allowed to shift. To specify a "no-shift" solution, one specifies the same
|
||||
* placement cell twice (e.g., <code>rightEdge(Location.TOP, Location.TOP)</code>).
|
||||
* <p>
|
||||
*
|
||||
* <H2>One Location Argument</H2>
|
||||
*
|
||||
* <p>
|
||||
* When one argument is used, the solution is the same as when two arguments are specified except
|
||||
* that the second argument is automatically set to the nearest neighboring cell. Thus,
|
||||
* <code>rightEdge(Location.TOP)</code> is the same as
|
||||
* <code>rightEdge(Location.TOP, Location.CENTER)</code>. When the single argument is
|
||||
* <code>Location.CENTER</code>, two attempts are built, the first being the BOTTOM or RIGHT cell
|
||||
* and the second being the TOP or LEFT cell.
|
||||
* <p>
|
||||
*
|
||||
* <H2>No Arguments</H2>
|
||||
*
|
||||
* <p>
|
||||
* When no arguments are specified, two arguments to the underlying placer are automatically set
|
||||
* to BOTTOM or RIGHT for the first and TOP or LEFT for the second.
|
||||
* <p>
|
||||
*
|
||||
* <H2>Examples</H2>
|
||||
*
|
||||
* <p>
|
||||
* Builds a placer that first attempts a placement at the bottom of the right edge with no
|
||||
* shift, then tries the top of the right edge with no shift, then top center with no shift:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .rightEdge(Location.BOTTOM,Location.BOTTOM)
|
||||
* .rightEdge(Location.TOP, Location.TOP)
|
||||
* .topEdge(Location.CENTER, Location.CENTER)
|
||||
* .build();</pre>
|
||||
* Builds a placer that attempts a placement on the right edge from bottom to top, followed by
|
||||
* the top edge from center to right, then center to left:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .rightEdge()
|
||||
* .topEdge(Location.CENTER);
|
||||
* .build();</pre>
|
||||
* <p>
|
||||
* <BR>
|
||||
* <BR>
|
||||
*
|
||||
* <H1>Rotational Placers</H1>
|
||||
*
|
||||
* <p>
|
||||
* There are clockwise and counter-clockwise rotational placers that built up from edge placers.
|
||||
* These are:
|
||||
* <pre>
|
||||
* rotateClockwise(Location major, Location minor)
|
||||
* rotateCounterClockwise(Location major, Location minor)
|
||||
* thenRotateClockwise()
|
||||
* thenRotateCounterClockwise()</pre>
|
||||
* The first two of these take two Location arguments the specify the starting cell. For instance,
|
||||
* <code>rotateClockwise(Location.BOTTOM, Location.RIGHT)</code>. This specifies a set of edge
|
||||
* placers that attempt placement starting from the specified cell, and making attempt in a
|
||||
* clockwise fashion until the starting cell is revisited, at which time the attempt fails if a
|
||||
* viable placement has not been found. The <code>rotateCounterClockwise</code> placer works the
|
||||
* same, but in a counter-clockwise fashion. The <code>thenRotateClockwise</code> and
|
||||
* <code>thenRotateCounterClockwise</code> placers are the same as the previous two placers
|
||||
* except that they start at the "beginning" cell where the most previous placer had left off. If
|
||||
* there was not a previous placer, then the BOTTOM RIGHT cell is chosen as the starting cell.
|
||||
* <p>
|
||||
* <BR>
|
||||
* <BR>
|
||||
*
|
||||
* <H1>Overlapping Corner Placer</H1>
|
||||
*
|
||||
* <p>
|
||||
* There is one corner placer, <code>leastOverlapCorner()</code>. This placer tries to make a
|
||||
* placement at each of the corners of the context area and shifts into the context region as much
|
||||
* as necessary to fit the screen bounds. The corner that overlaps the context area the least is
|
||||
* chosen as the solution placement corner. In case of a tie (e.g., no overlap on some corners),
|
||||
* the placement order chosen in this preference order: bottom right, bottom left, top right, and
|
||||
* top left. Unless ill-constructed (sized of context area, screen, and pop-up dimension), this
|
||||
* placer should always find a solution.
|
||||
* <p>
|
||||
* <BR>
|
||||
* <BR>
|
||||
*
|
||||
* <H1>Assert Placer</H1>
|
||||
*
|
||||
* <p>
|
||||
* The <code>throwsAssertException()</code> placer is available, which automatically throws an
|
||||
* AssertException. This placer is only intended to be used by the client in such as case when
|
||||
* it is believed that a placement should have already been found, such as after the
|
||||
* <code>leastOverlapCorner()</code> placer. This just throws an exception instead of returning
|
||||
* the <code>null</code> return value that would be returned from previous placement attempts.
|
||||
* <p>
|
||||
* <BR>
|
||||
* <BR>
|
||||
*
|
||||
* <H1>Composite Placer</H1>
|
||||
*
|
||||
* <p>
|
||||
* Builds a placer that first attempts a placement at the right edge from bottom to top, then
|
||||
* left edge from bottom to top, then top edge from right to left, then bottom edge from right to
|
||||
* left, followed by a least-overlap-corner solution, followed by a failure assert:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .rightEdge()
|
||||
* .leftEdge()
|
||||
* .topEdge()
|
||||
* .bottomEdge()
|
||||
* .leastOverlapCorner()
|
||||
* .throwsAssertException()
|
||||
* .build();</pre>
|
||||
* <p>
|
||||
* Builds a placer that first attempts each of the four major corners in a specific order, with no
|
||||
* shifting, followed by an assertion failure:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .rightEdge(Location.BOTTOM, Location.BOTTOM)
|
||||
* .leftEdge(Location.TOP, Location.TOP)
|
||||
* .rightEdge(Location.TOP, Location.TOP)
|
||||
* .leftEdge(Location.BOTTOM, Location.BOTTOM)
|
||||
* .throwsAssertException()
|
||||
* .build();</pre>
|
||||
* <p>
|
||||
* Builds a placer that attempt to make a placement at the bottom right corner, first shifting up
|
||||
* to the center location then shifting left to the center location, then failing only with a
|
||||
* null return:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .rightEdge(Location.BOTTOM)
|
||||
* .bottomEdge(Location.RIGHT)
|
||||
* .build();</pre>
|
||||
* <p>
|
||||
* Builds a placer that attempts a placement at the top, left corner, the tries to make a placement
|
||||
* in a clockwise fashion, followed by a failure assert:
|
||||
* <pre>
|
||||
* PopupWindowPlacer placer =
|
||||
* new PopupWindowPlacerBuilder()
|
||||
* .topEdge(Location.LEFT, Location.LEFT)
|
||||
* .thenRotateClockwise()
|
||||
* .throwsAssertException()
|
||||
* .build();</pre>
|
||||
*
|
||||
* @see PopupWindowPlacer
|
||||
*/
|
||||
public class PopupWindowPlacerBuilder {
|
||||
|
||||
private PopupWindowPlacer head = null;
|
||||
private PopupWindowPlacer current = null;
|
||||
|
||||
/**
|
||||
* Builds the final PopupWindowPlacer.
|
||||
* @return the PopupWindowPlacer
|
||||
*/
|
||||
public PopupWindowPlacer build() {
|
||||
return head;
|
||||
}
|
||||
|
||||
private void add(PopupWindowPlacer next) {
|
||||
if (current == null) {
|
||||
current = next;
|
||||
head = current;
|
||||
}
|
||||
else {
|
||||
current.setNext(next);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement at the right
|
||||
* edge of the inner bounds (context) without exceeding outer bounds (screen), using
|
||||
* an ordered, preferred placements on that edge. Invalid values will error.
|
||||
* @param minors the ordered, preferred placements on the edge. If not specified, goes from
|
||||
* greater-valued end of the edge to the lesser-valued end of the edge.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder rightEdge(Location... minors) {
|
||||
return edge(Location.RIGHT, minors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement at the left
|
||||
* edge of the inner bounds (context) without exceeding outer bounds (screen), using
|
||||
* an ordered, preferred placements on that edge. Invalid values will error.
|
||||
* @param minors the ordered, preferred placements on the edge. If not specified, goes from
|
||||
* greater-valued end of the edge to the lesser-valued end of the edge.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder leftEdge(Location... minors) {
|
||||
return edge(Location.LEFT, minors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement at the bottom
|
||||
* edge of the inner bounds (context) without exceeding outer bounds (screen), using
|
||||
* an ordered, preferred placements on that edge. Invalid values will error.
|
||||
* @param minors the ordered, preferred placements on the edge. If not specified, goes from
|
||||
* greater-valued end of the edge to the lesser-valued end of the edge.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder bottomEdge(Location... minors) {
|
||||
return edge(Location.BOTTOM, minors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement at the top
|
||||
* edge of the inner bounds (context) without exceeding outer bounds (screen), using
|
||||
* an ordered, preferred placements on that edge. Invalid values will error.
|
||||
* @param minors the ordered, preferred placements on the edge. If not specified, goes from
|
||||
* greater-valued end of the edge to the lesser-valued end of the edge.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder topEdge(Location... minors) {
|
||||
return edge(Location.TOP, minors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement on the major
|
||||
* edge of the inner bounds (context) without exceeding outer bounds (screen), using
|
||||
* an ordered, preferred placements on that edge. Invalid values will error.
|
||||
* @param major the major edge of the context area
|
||||
* @param minors the ordered, preferred placements on the edge. If not specified, goes from
|
||||
* greater-valued end of the edge to the lesser-valued end of the edge.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder edge(Location major, Location... minors) {
|
||||
if (minors.length > 3) {
|
||||
throw new IllegalArgumentException("Too many preferred Locations: " + minors);
|
||||
}
|
||||
for (Location minor : minors) {
|
||||
if (!major.validMinor(minor)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Preferred Location " + minor + " is not valid for " + major + " edge.");
|
||||
}
|
||||
}
|
||||
|
||||
if (minors.length == 0) {
|
||||
// We are defaulting this as greater to lesser
|
||||
if (major.isHorizontal()) {
|
||||
add(new EdgePopupPlacer(major, Location.BOTTOM, Location.TOP));
|
||||
}
|
||||
else {
|
||||
add(new EdgePopupPlacer(major, Location.RIGHT, Location.LEFT));
|
||||
}
|
||||
}
|
||||
else if (minors.length == 1) {
|
||||
if (minors[0] == Location.CENTER) {
|
||||
// Trying center to greater and then center to lesser.
|
||||
if (major.isHorizontal()) {
|
||||
add(new EdgePopupPlacer(major, minors[0], Location.BOTTOM));
|
||||
add(new EdgePopupPlacer(major, minors[0], Location.TOP));
|
||||
}
|
||||
else {
|
||||
add(new EdgePopupPlacer(major, minors[0], Location.RIGHT));
|
||||
add(new EdgePopupPlacer(major, minors[0], Location.LEFT));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Only looking from greater/lesser to the the center.
|
||||
add(new EdgePopupPlacer(major, minors[0], Location.CENTER));
|
||||
}
|
||||
}
|
||||
else { // Since we tested minors.length > 3 above, then we know we must have 2 or 3
|
||||
for (int i = 0; i < minors.length - 1; i++) {
|
||||
add(new EdgePopupPlacer(major, minors[i], minors[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement by starting at
|
||||
* the last-used {@code majorBegin} and {@code minorBegin} and continues clockwise
|
||||
* to find a solution. If there was no last-used location set, then BOTTOM, RIGHT is used.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder thenRotateClockwise() {
|
||||
if (current == null) {
|
||||
return rotateClockwise(Location.BOTTOM, Location.RIGHT);
|
||||
}
|
||||
return rotateClockwise(current.major, current.minorBegin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement by starting at
|
||||
* a point specified by {@code majorBegin} and {@code minorBegin} and continues
|
||||
* clockwise to find a solution.
|
||||
* @param majorBegin the major coordinate location of the starting point
|
||||
* @param minorBegin the minor coordinate location of the starting point
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder rotateClockwise(Location majorBegin, Location minorBegin) {
|
||||
Location major = majorBegin;
|
||||
Location minor = minorBegin;
|
||||
do {
|
||||
add(new EdgePopupPlacer(major, minor, major.clockwise()));
|
||||
minor = major;
|
||||
major = major.clockwise();
|
||||
}
|
||||
while (major != majorBegin);
|
||||
if (minor != minorBegin) {
|
||||
// Does remaining portion of initial edge, but repeats first location.
|
||||
// So if starting at BOTTOM CENTER, will repeat that location in the last partial edge
|
||||
add(new EdgePopupPlacer(major, minor, minorBegin));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement by starting at
|
||||
* the last-used {@code majorBegin} and {@code minorBegin} and continues counter-clockwise
|
||||
* to find a solution. If there was no last-used location set, then RIGHT, BOTTOM is used.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder thenRotateCounterClockwise() {
|
||||
if (current == null) {
|
||||
return rotateCounterClockwise(Location.RIGHT, Location.BOTTOM);
|
||||
}
|
||||
return rotateCounterClockwise(current.major, current.minorBegin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement by starting at
|
||||
* a point specified by {@code majorBegin} and {@code minorBegin} and continues
|
||||
* counter-clockwise to find a solution.
|
||||
* @param majorBegin the major coordinate location of the starting point
|
||||
* @param minorBegin the minor coordinate location of the starting point
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder rotateCounterClockwise(Location majorBegin,
|
||||
Location minorBegin) {
|
||||
Location major = majorBegin;
|
||||
Location minor = minorBegin;
|
||||
do {
|
||||
add(new EdgePopupPlacer(major, minor, major.counterClockwise()));
|
||||
minor = major;
|
||||
major = major.counterClockwise();
|
||||
}
|
||||
while (major != majorBegin);
|
||||
if (minor != minorBegin) {
|
||||
// Does remaining portion of initial edge, but repeats first location.
|
||||
// So if starting at BOTTOM CENTER, will repeat that location in the last partial edge
|
||||
add(new EdgePopupPlacer(major, minor, minorBegin));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer to be one that tries to make the placement that is
|
||||
* allowed to overlap the inner bounds, but with the least overlap area. Tie-breaker
|
||||
* order is first in this order: Bottom Right, Bottom Left, Top Right, Top Left.
|
||||
* <p>
|
||||
* Should never return null, except if using impractical parameters, such as using
|
||||
* outer bounds that are smaller than inner bounds.
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder leastOverlapCorner() {
|
||||
add(new LeastOverlapCornerPopupWindowPlacer());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the next PopupWindowPlacer that throws an AssertException because no solution has
|
||||
* been found by the time this placer is tried. This is intended to be used when the coder
|
||||
* has already guaranteed that there is a solution (i.e., the {@link #leastOverlapCorner()}
|
||||
* placer has been used and the pop-up area will fit within the outer bounds).
|
||||
* @return this builder
|
||||
*/
|
||||
public PopupWindowPlacerBuilder throwsAssertException() {
|
||||
add(new ThrowsAssertExceptionPlacer());
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+610
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ public class Attributed {
|
||||
* cache of the html rendering of the vertex attributes
|
||||
*/
|
||||
private String htmlString;
|
||||
private static final String DESCRIPTION = "Description";
|
||||
|
||||
/**
|
||||
* the {@link HashMap} to contain attribute mappings
|
||||
@@ -145,6 +146,27 @@ public class Attributed {
|
||||
return attributes.entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a description for this Attributed object
|
||||
*
|
||||
* @param value text that provides a description for this Attributed object.
|
||||
* The text can be either a plain string or an HTML string.
|
||||
* @return the previously set description
|
||||
*/
|
||||
public String setDescription(String value) {
|
||||
htmlString = null;
|
||||
return attributes.put(DESCRIPTION, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the description of this Attributed object.
|
||||
*
|
||||
* @return the description of this Attributed object.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return getAttribute(DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse (one time) then cache the attributes to html
|
||||
* @return the html string
|
||||
@@ -155,22 +177,25 @@ public class Attributed {
|
||||
return htmlString;
|
||||
}
|
||||
|
||||
Set<Entry<String, String>> entries = entrySet();
|
||||
if (entries.isEmpty()) {
|
||||
return ""; // empty so tooltip clients can handle empty data
|
||||
htmlString = getDescription();
|
||||
if (htmlString == null) { // if no description is set, create a default one
|
||||
Set<Entry<String, String>> entries = entrySet();
|
||||
if (entries.isEmpty()) {
|
||||
return ""; // empty so tooltip clients can handle empty data
|
||||
}
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : entries) {
|
||||
buf.append(entry.getKey());
|
||||
buf.append(":");
|
||||
String value = entry.getValue();
|
||||
value = StringEscapeUtils.escapeHtml4(value);
|
||||
String split = String.join("<br>", Splitter.on('\n').split(value));
|
||||
split = split.replaceAll("\\s", " ");
|
||||
buf.append(split);
|
||||
buf.append("<br>");
|
||||
}
|
||||
htmlString = buf.toString();
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder("<html>");
|
||||
for (Map.Entry<String, String> entry : entries) {
|
||||
buf.append(entry.getKey());
|
||||
buf.append(":");
|
||||
String value = StringEscapeUtils.escapeHtml4(entry.getValue());
|
||||
String split = String.join("<br>", Splitter.on('\n').split(value));
|
||||
buf.append(split);
|
||||
buf.append("<br>");
|
||||
}
|
||||
htmlString = buf.toString();
|
||||
return htmlString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -64,12 +64,17 @@ public class AARCH64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
boolean isBigEndianInstructions =
|
||||
program.getLanguage().getLanguageDescription().getInstructionEndian().isBigEndian();
|
||||
|
||||
Address symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
long symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
long newValue = 0;
|
||||
|
||||
switch (type) {
|
||||
// .xword: (S+A)
|
||||
case AARCH64_ElfRelocationConstants.R_AARCH64_ABS64: {
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
newValue = (symbolValue + addend);
|
||||
memory.setLong(relocationAddress, newValue);
|
||||
break;
|
||||
|
||||
+7
-1
@@ -59,6 +59,7 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
long offset = (int) relocationAddress.getOffset();
|
||||
|
||||
Address symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
long symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
|
||||
int newValue = 0;
|
||||
@@ -85,6 +86,10 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
if (elfRelocationContext.extractAddend()) {
|
||||
addend = memory.getInt(relocationAddress);
|
||||
}
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
newValue = (int) (symbolValue + addend);
|
||||
if (isThumb) {
|
||||
newValue |= 1;
|
||||
@@ -354,8 +359,9 @@ public class ARM_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
oldValue = (oldValue ^ 0x8000) - 0x8000;
|
||||
|
||||
oldValue += symbolValue;
|
||||
if (type == ARM_ElfRelocationConstants.R_ARM_MOVT_ABS)
|
||||
if (type == ARM_ElfRelocationConstants.R_ARM_MOVT_ABS) {
|
||||
oldValue >>= 16;
|
||||
}
|
||||
|
||||
newValue &= 0xfff0f000;
|
||||
newValue |= ((oldValue & 0xf000) << 4) |
|
||||
|
||||
+10
-3
@@ -121,8 +121,8 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
ElfSymbol elfSymbol = mipsRelocationContext.getSymbol(symbolIndex);
|
||||
|
||||
Address symbolAddr = mipsRelocationContext.getSymbolAddress(elfSymbol);
|
||||
long symbolValue = mipsRelocationContext.getSymbolValue(elfSymbol);
|
||||
|
||||
String symbolName = elfSymbol.getNameAsString();
|
||||
|
||||
long addend = 0;
|
||||
@@ -152,7 +152,6 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
}
|
||||
}
|
||||
|
||||
mipsRelocationContext.useSavedAddend = saveValue;
|
||||
mipsRelocationContext.savedAddendHasError = false;
|
||||
mipsRelocationContext.savedAddend = 0;
|
||||
|
||||
@@ -407,7 +406,13 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
symbolValue = mipsRelocationContext.getImageBaseWordAdjustmentOffset();
|
||||
}
|
||||
value = (int) symbolValue;
|
||||
value += mipsRelocationContext.extractAddend() ? oldValue : addend;
|
||||
int a = (int) (mipsRelocationContext.extractAddend() ? oldValue : addend);
|
||||
// NOTE: this may not detect correctly for all combound relocations
|
||||
if (a != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, a, log)) {
|
||||
a = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
value += a;
|
||||
|
||||
newValue = value;
|
||||
writeNewValue = true;
|
||||
@@ -720,6 +725,8 @@ public class MIPS_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
}
|
||||
}
|
||||
|
||||
mipsRelocationContext.useSavedAddend = saveValue;
|
||||
|
||||
}
|
||||
|
||||
private boolean isMIPS16Reloc(int type) {
|
||||
|
||||
+8
-3
@@ -66,6 +66,8 @@ public class PowerPC64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
long offset = relocationAddress.getOffset();
|
||||
|
||||
ElfSymbol sym = elfRelocationContext.getSymbol(symbolIndex);
|
||||
String symbolName = sym.getNameAsString();
|
||||
Address symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
long symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
@@ -93,7 +95,7 @@ public class PowerPC64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
Symbol tocBaseSym = SymbolUtilities.getLabelOrFunctionSymbol(program,
|
||||
PowerPC64_ElfExtension.TOC_BASE, err -> log.error("PPC_ELF", err));
|
||||
if (tocBaseSym == null) {
|
||||
markAsError(program, relocationAddress, type, sym.getNameAsString(),
|
||||
markAsError(program, relocationAddress, type, symbolName,
|
||||
"TOC_BASE unknown", log);
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +106,7 @@ public class PowerPC64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
switch (type) {
|
||||
case PowerPC64_ElfRelocationConstants.R_PPC64_COPY:
|
||||
markAsWarning(program, relocationAddress, "R_PPC64_COPY", sym.getNameAsString(),
|
||||
markAsWarning(program, relocationAddress, "R_PPC64_COPY", symbolName,
|
||||
symbolIndex, "Runtime copy not supported", elfRelocationContext.getLog());
|
||||
break;
|
||||
case PowerPC64_ElfRelocationConstants.R_PPC64_ADDR32:
|
||||
@@ -220,6 +222,10 @@ public class PowerPC64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
case PowerPC64_ElfRelocationConstants.R_PPC64_UADDR64:
|
||||
case PowerPC64_ElfRelocationConstants.R_PPC64_ADDR64:
|
||||
case PowerPC64_ElfRelocationConstants.R_PPC64_GLOB_DAT:
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
value64 = symbolValue + addend;
|
||||
memory.setLong(relocationAddress, value64);
|
||||
break;
|
||||
@@ -227,7 +233,6 @@ public class PowerPC64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
memory.setLong(relocationAddress, toc);
|
||||
break;
|
||||
default:
|
||||
String symbolName = sym.getNameAsString();
|
||||
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
|
||||
elfRelocationContext.getLog());
|
||||
break;
|
||||
|
||||
+9
-5
@@ -63,7 +63,7 @@ public class PowerPC_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
int offset = (int) relocationAddress.getOffset();
|
||||
|
||||
ElfSymbol sym = elfRelocationContext.getSymbol(symbolIndex);
|
||||
int symbolValue;
|
||||
|
||||
// if (sym.isLocal() && sym.getSectionHeaderIndex() != ElfSectionHeaderConstants.SHN_UNDEF) {
|
||||
//
|
||||
// // see glibc - sysdeps/powerpc/powerpc32/dl-machine.h elf_machine_rela
|
||||
@@ -75,20 +75,26 @@ public class PowerPC_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
// symbolValue = (int) elfRelocationContext.getImageBaseWordAdjustmentOffset();
|
||||
// }
|
||||
// else {
|
||||
symbolValue = (int) elfRelocationContext.getSymbolValue(sym);
|
||||
Address symbolAddr = (elfRelocationContext.getSymbolAddress(sym));
|
||||
int symbolValue = (int) elfRelocationContext.getSymbolValue(sym);
|
||||
// }
|
||||
String symbolName = sym.getNameAsString();
|
||||
|
||||
int oldValue = memory.getInt(relocationAddress);
|
||||
int newValue = 0;
|
||||
|
||||
switch (type) {
|
||||
case PowerPC_ElfRelocationConstants.R_PPC_COPY:
|
||||
markAsWarning(program, relocationAddress, "R_PPC_COPY", sym.getNameAsString(),
|
||||
markAsWarning(program, relocationAddress, "R_PPC_COPY", symbolName,
|
||||
symbolIndex, "Runtime copy not supported", elfRelocationContext.getLog());
|
||||
break;
|
||||
case PowerPC_ElfRelocationConstants.R_PPC_ADDR32:
|
||||
case PowerPC_ElfRelocationConstants.R_PPC_UADDR32:
|
||||
case PowerPC_ElfRelocationConstants.R_PPC_GLOB_DAT:
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
newValue = symbolValue + addend;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
break;
|
||||
@@ -194,13 +200,11 @@ public class PowerPC_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
// TODO: Handle this case if needed - hopefully the EXTERNAL block is
|
||||
// not too far away since a fabricated GOT would be in the same block
|
||||
// and we may only have room in the plt for two instructions.
|
||||
String symbolName = sym.getNameAsString();
|
||||
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
|
||||
elfRelocationContext.getLog());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
String symbolName = sym.getNameAsString();
|
||||
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
|
||||
elfRelocationContext.getLog());
|
||||
break;
|
||||
|
||||
+6
@@ -51,6 +51,7 @@ public class RISCV_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
long base = elfRelocationContext.getImageBaseWordAdjustmentOffset();
|
||||
ElfSymbol sym = null;
|
||||
long symbolValue = 0;
|
||||
Address symbolAddr = null;
|
||||
String symbolName = null;
|
||||
|
||||
int symbolIndex = relocation.getSymbolIndex();
|
||||
@@ -59,6 +60,7 @@ public class RISCV_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
}
|
||||
|
||||
if (null != sym) {
|
||||
symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
symbolName = sym.getNameAsString();
|
||||
}
|
||||
@@ -94,6 +96,10 @@ public class RISCV_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
case RISCV_ElfRelocationConstants.R_RISCV_64:
|
||||
// Runtime relocation word64 = S + A
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
value64 = symbolValue + addend;
|
||||
memory.setLong(relocationAddress, value64);
|
||||
break;
|
||||
|
||||
@@ -1021,74 +1021,25 @@ MovMUReg2: MovMUReg2_15 is MovMUReg2_15 {
|
||||
local tmp1:1;
|
||||
local tmp2:4;
|
||||
local old_q:1;
|
||||
local old_q_eq_m:1;
|
||||
local m_eq_q:1;
|
||||
|
||||
old_q = $(Q_FLAG);
|
||||
$(Q_FLAG) = (0x80000000 & rn_08_11) != 0;
|
||||
tmp2 = rm_04_07;
|
||||
rn_08_11 = rn_08_11 << 1;
|
||||
rn_08_11 = rn_08_11 | zext($(T_FLAG));
|
||||
old_q_eq_m = old_q == $(M_FLAG);
|
||||
m_eq_q = $(M_FLAG) == $(Q_FLAG);
|
||||
|
||||
# FIXME: cleaner way to do this??
|
||||
tmp0 = rn_08_11;
|
||||
# rn_08_11 = old_q_eq_m ? rn_08_11 - tmp2 : rn_08_11 + tmp2;
|
||||
rn_08_11 = (zext(old_q_eq_m) * (rn_08_11 - tmp2)) + (zext(!old_q_eq_m) * (rn_08_11 + tmp2));
|
||||
# tmp1 = old_q_eq_m ? rn_08_11 > tmp0 : rn_08_11 < tmp0;
|
||||
tmp1 = (old_q_eq_m * (rn_08_11 > tmp0)) + (!old_q_eq_m * (rn_08_11 < tmp0));
|
||||
# $(Q_FLAG) = m_eq_q ? tmp1 : tmp1 == 0;
|
||||
$(Q_FLAG) = (m_eq_q * tmp1) + (!m_eq_q & (tmp1 == 0));
|
||||
|
||||
if(old_q == 0 && $(M_FLAG) == 0 && $(Q_FLAG) == 0) goto <OQ0_M0_Q0>;
|
||||
if(old_q == 0 && $(M_FLAG) == 0 && $(Q_FLAG) == 1) goto <OQ0_M0_Q1>;
|
||||
if(old_q == 0 && $(M_FLAG) == 1 && $(Q_FLAG) == 0) goto <OQ0_M1_Q0>;
|
||||
if(old_q == 0 && $(M_FLAG) == 1 && $(Q_FLAG) == 1) goto <OQ0_M1_Q1>;
|
||||
if(old_q == 1 && $(M_FLAG) == 0 && $(Q_FLAG) == 0) goto <OQ1_M0_Q0>;
|
||||
if(old_q == 1 && $(M_FLAG) == 0 && $(Q_FLAG) == 1) goto <OQ1_M0_Q1>;
|
||||
if(old_q == 1 && $(M_FLAG) == 1 && $(Q_FLAG) == 0) goto <OQ1_M1_Q0>;
|
||||
if(old_q == 1 && $(M_FLAG) == 1 && $(Q_FLAG) == 1) goto <OQ1_M1_Q1>;
|
||||
|
||||
<OQ0_M0_Q0>
|
||||
rn_08_11 = rn_08_11 - tmp2;
|
||||
tmp1 = rn_08_11 > tmp0;
|
||||
$(Q_FLAG) = tmp1;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ0_M0_Q1>
|
||||
rn_08_11 = rn_08_11 - tmp2;
|
||||
tmp1 = rn_08_11 > tmp0;
|
||||
$(Q_FLAG) = tmp1 == 0;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ0_M1_Q0>
|
||||
rn_08_11 = rn_08_11 + tmp2;
|
||||
tmp1 = rn_08_11 < tmp0;
|
||||
$(Q_FLAG) = tmp1 == 0;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ0_M1_Q1>
|
||||
rn_08_11 = rn_08_11 + tmp2;
|
||||
tmp1 = rn_08_11 < tmp0;
|
||||
$(Q_FLAG) = tmp1;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ1_M0_Q0>
|
||||
rn_08_11 = rn_08_11 + tmp2;
|
||||
tmp1 = rn_08_11 < tmp0;
|
||||
$(Q_FLAG) = tmp1;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ1_M0_Q1>
|
||||
rn_08_11 = rn_08_11 + tmp2;
|
||||
tmp1 = rn_08_11 < tmp0;
|
||||
$(Q_FLAG) = tmp1;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ1_M1_Q0>
|
||||
rn_08_11 = rn_08_11 - tmp2;
|
||||
tmp1 = rn_08_11 > tmp0;
|
||||
$(Q_FLAG) = tmp1 == 0;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<OQ1_M1_Q1>
|
||||
rn_08_11 = rn_08_11 - tmp2;
|
||||
tmp1 = rn_08_11 > tmp0;
|
||||
$(Q_FLAG) = tmp1;
|
||||
goto <SET_FLAG>;
|
||||
|
||||
<SET_FLAG>
|
||||
$(T_FLAG) = $(Q_FLAG) == $(M_FLAG);
|
||||
}
|
||||
|
||||
|
||||
@@ -2046,42 +2046,53 @@ Suffix3D: imm8 is imm8 [ suffix3D=imm8; ] { }
|
||||
:BTS rm64,imm8 is vexMode=0 & opsize=2 & byte=0xf; byte=0xba; (rm64 & reg_opcode=5 ...); imm8 { local bit=imm8&0x3f; local val=(rm64>>bit)&1; rm64=rm64 | (1<<bit); CF=(val!=0); }
|
||||
@endif
|
||||
|
||||
@ifndef IA64
|
||||
:CALL rel16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xe8; rel16 { push22(&:2 inst_next); call rel16; }
|
||||
:CALL rel16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xe8; rel16 { push42(&:2 inst_next); call rel16; }
|
||||
@ifdef IA64
|
||||
# 64-bit addressing mode does not support (N.S.) rel16
|
||||
#:CALL rel16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xe8; rel16 { push82(&:2 inst_next); call rel16; }
|
||||
@endif
|
||||
# When is a Call a Jump, when it jumps right after. Not always the case but...
|
||||
:CALL rel16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xe8; simm16=0 & rel16 { push22(&:2 inst_next); goto rel16; }
|
||||
:CALL rel16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xe8; simm16=0 & rel16 { push42(&:2 inst_next); goto rel16; }
|
||||
@ifdef IA64
|
||||
# 64-bit addressing mode does not support (N.S.) rel16
|
||||
#:CALL rel16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xe8; simm16=0 & rel16 { push82(&:2 inst_next); goto rel16; }
|
||||
@endif
|
||||
:CALL rel32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xe8; rel32 { push24(&:4 inst_next); call rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; rel32 { push44(&:4 inst_next); call rel32; }
|
||||
@ifdef IA64
|
||||
:CALL rel32 is vexMode=0 & addrsize=2 & (opsize=1 | opsize=2) & byte=0xe8; rel32 { push88(&:8 inst_next); call rel32; }
|
||||
@endif
|
||||
# When is a call a Jump, when it jumps right after. Not always the case but...
|
||||
:CALL rel32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xe8; simm32=0 & rel32 { push24(&:4 inst_next); goto rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; simm32=0 & rel32 { push44(&:4 inst_next); goto rel32; }
|
||||
@ifdef IA64
|
||||
:CALL rel32 is vexMode=0 & addrsize=2 & (opsize=1 | opsize=2) & byte=0xe8; simm32=0 & rel32 { push88(&:8 inst_next); goto rel32; }
|
||||
@endif
|
||||
:CALL rm16 is addrsize=0 & opsize=0 & byte=0xff & currentCS; rm16 & reg_opcode=2 ... { push22(&:2 inst_next); tmp:4 = segment(currentCS,rm16); call [tmp]; }
|
||||
:CALL rm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push42(&:2 inst_next); call [rm16]; }
|
||||
@ifdef IA64
|
||||
:CALL rm16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push82(&:2 inst_next); tmp:8 = inst_next + zext(rm16); call [tmp]; }
|
||||
@endif
|
||||
:CALL rm32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xff; rm32 & reg_opcode=2 ... { push24(&:4 inst_next); call [rm32]; }
|
||||
:CALL rm32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xff; rm32 & reg_opcode=2 ... { push44(&:4 inst_next); call [rm32]; }
|
||||
@ifdef IA64
|
||||
:CALL rm64 is vexMode=0 & addrsize=2 & opsize=1 & byte=0xff; rm64 & reg_opcode=2 ... { push88(&:8 inst_next); call [rm64]; }
|
||||
:CALL rm64 is vexMode=0 & addrsize=2 & opsize=2 & byte=0xff; rm64 & reg_opcode=2 ... { push88(&:8 inst_next); call [rm64]; }
|
||||
@else
|
||||
:CALL rel16 is vexMode=0 & (addrsize=1 | addrsize=2) & opsize=0 & byte=0xe8; rel16 { push88(&:8 inst_next); call rel16; }
|
||||
@endif
|
||||
|
||||
# When is a Call a Jump, when it jumps right after. Not always the case but...
|
||||
@ifndef IA64
|
||||
:CALL rel16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xe8; simm16=0 & rel16 { push22(&:2 inst_next); goto rel16; }
|
||||
:CALL rel16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xe8; simm16=0 & rel16 { push42(&:2 inst_next); goto rel16; }
|
||||
@else
|
||||
:CALL rel16 is vexMode=0 & (addrsize=1 | addrsize=2) & opsize=0 & byte=0xe8; simm16=0 & rel16 { push88(&:8 inst_next); goto rel16; }
|
||||
@endif
|
||||
|
||||
@ifndef IA64
|
||||
:CALL rel32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xe8; rel32 { push24(&:4 inst_next); call rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; rel32 { push44(&:4 inst_next); call rel32; }
|
||||
@else
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; rel32 { push88(&:8 inst_next); call rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=2 & (opsize=1 | opsize=2) & byte=0xe8; rel32 { push88(&:8 inst_next); call rel32; }
|
||||
@endif
|
||||
|
||||
# When is a call a Jump, when it jumps right after. Not always the case but...
|
||||
@ifndef IA64
|
||||
:CALL rel32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xe8; simm32=0 & rel32 { push24(&:4 inst_next); goto rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; simm32=0 & rel32 { push44(&:4 inst_next); goto rel32; }
|
||||
@else
|
||||
:CALL rel32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xe8; simm32=0 & rel32 { push88(&:8 inst_next); goto rel32; }
|
||||
:CALL rel32 is vexMode=0 & addrsize=2 & (opsize=1 | opsize=2) & byte=0xe8; simm32=0 & rel32 { push88(&:8 inst_next); goto rel32; }
|
||||
@endif
|
||||
|
||||
@ifndef IA64
|
||||
:CALL rm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xff & currentCS; rm16 & reg_opcode=2 ... { push22(&:2 inst_next); tmp:4 = segment(currentCS,rm16); call [tmp]; }
|
||||
:CALL rm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push42(&:2 inst_next); call [rm16]; }
|
||||
@else
|
||||
:CALL rm16 is vexMode=0 & (addrsize=1 | addrsize=2) & opsize=0 & byte=0xff; rm16 & reg_opcode=2 ... { push88(&:8 inst_next); tmp:8 = inst_next + zext(rm16); call [tmp]; }
|
||||
@endif
|
||||
|
||||
@ifndef IA64
|
||||
:CALL rm32 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xff; rm32 & reg_opcode=2 ... { push24(&:4 inst_next); call [rm32]; }
|
||||
:CALL rm32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xff; rm32 & reg_opcode=2 ... { push44(&:4 inst_next); call [rm32]; }
|
||||
@else
|
||||
:CALL rm64 is vexMode=0 & (addrsize=1 | addrsize=2) & (opsize=1 | opsize=2) & byte=0xff; rm64 & reg_opcode=2 ... { push88(&:8 inst_next); call [rm64]; }
|
||||
@endif
|
||||
|
||||
# direct far calls generate an opcode undefined exception in x86-64
|
||||
:CALLF ptr1616 is vexMode=0 & addrsize=0 & opsize=0 & byte=0x9a; ptr1616 { push22(CS); build ptr1616; push22(&:2 inst_next); call ptr1616; }
|
||||
:CALLF ptr1616 is vexMode=0 & addrsize=1 & opsize=0 & byte=0x9a; ptr1616 { push42(CS); build ptr1616; push42(&:2 inst_next); call ptr1616; }
|
||||
:CALLF ptr1632 is vexMode=0 & addrsize=0 & opsize=1 & byte=0x9a; ptr1632 { push22(CS); build ptr1632; push24(&:4 inst_next); call ptr1632; }
|
||||
@@ -2089,12 +2100,16 @@ Suffix3D: imm8 is imm8 [ suffix3D=imm8; ] { }
|
||||
:CALLF addr16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xff; addr16 & reg_opcode=3 ... { push22(CS); push22(&:2 inst_next); ptr:$(SIZE) = segment(DS,addr16); addrptr:$(SIZE) = segment(*:2 (ptr+2),*:2 ptr); call [addrptr]; }
|
||||
:CALLF addr32 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xff; addr32 & reg_opcode=3 ... { push42(CS); push42(&:2 inst_next); call [addr32]; }
|
||||
@ifdef IA64
|
||||
:CALLF addr32 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xff; addr32 & reg_opcode=3 ... { push82(CS); push82(&:2 inst_next); call [addr32]; }
|
||||
:CALLF addr64 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xff; addr64 & reg_opcode=3 ... { push82(CS); push82(&:2 inst_next); call [addr64]; }
|
||||
@endif
|
||||
|
||||
|
||||
:CALLF addr16 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xff; addr16 & reg_opcode=3 ... { push22(CS); push24(&:4 inst_next); call [addr16]; }
|
||||
:CALLF addr32 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xff; addr32 & reg_opcode=3 ... { push42(CS); push44(&:4 inst_next); call [addr32]; }
|
||||
@ifdef IA64
|
||||
:CALLF addr32 is vexMode=0 & addrsize=2 & opsize=1 & byte=0xff; addr32 & reg_opcode=3 ... { push82(CS); push84(&:4 inst_next); call [addr32]; }
|
||||
:CALLF addr32 is vexMode=0 & addrsize=1 & opsize=2 & byte=0xff; addr32 & reg_opcode=3 ... { push82(CS); push88(&:8 inst_next); call [addr32]; }
|
||||
:CALLF addr64 is vexMode=0 & addrsize=2 & opsize=1 & byte=0xff; addr64 & reg_opcode=3 ... { push82(CS); push84(&:4 inst_next); call [addr64]; }
|
||||
:CALLF addr64 is vexMode=0 & addrsize=2 & opsize=2 & byte=0xff; addr64 & reg_opcode=3 ... { push82(CS); push88(&:8 inst_next); call [addr64]; }
|
||||
@endif
|
||||
|
||||
:CBW is vexMode=0 & opsize=0 & byte=0x98 { AX = sext(AL); }
|
||||
@@ -3420,45 +3435,49 @@ define pcodeop rdpmc;
|
||||
define pcodeop rdtsc;
|
||||
:RDTSC is vexMode=0 & byte=0xf; byte=0x31 { tmp:8 = rdtsc(); EDX = tmp(4); EAX = tmp(0); }
|
||||
|
||||
@ifndef IA64
|
||||
:RET is vexMode=0 & addrsize=0 & opsize=0 & byte=0xc3 { pop22(IP); EIP=segment(CS,IP); return [EIP]; }
|
||||
:RET is vexMode=0 & addrsize=1 & opsize=0 & byte=0xc3 { pop42(IP); EIP=zext(IP); return [EIP]; }
|
||||
:RET is vexMode=0 & addrsize=0 & opsize=1 & byte=0xc3 { pop24(EIP); return [EIP]; }
|
||||
:RET is vexMode=0 & addrsize=1 & opsize=1 & byte=0xc3 { pop44(EIP); return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET is vexMode=0 & addrsize=2 & byte=0xc3 { pop88(RIP); return [RIP]; }
|
||||
@else
|
||||
:RET is vexMode=0 & byte=0xc3 { pop88(RIP); return [RIP]; }
|
||||
@endif
|
||||
|
||||
:RET is vexMode=0 & addrsize=0 & opsize=0 & byte=0xcb { pop22(IP); pop22(CS); EIP = segment(CS,IP); return [EIP]; }
|
||||
:RET is vexMode=0 & addrsize=1 & opsize=0 & byte=0xcb { pop42(IP); EIP=zext(IP); pop42(CS); return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET is vexMode=0 & addrsize=2 & opsize=0 & byte=0xcb { pop42(IP); RIP=zext(IP); pop42(CS); return [RIP]; }
|
||||
:RETF is vexMode=0 & addrsize=0 & opsize=0 & byte=0xcb { pop22(IP); pop22(CS); EIP = segment(CS,IP); return [EIP]; }
|
||||
@ifndef IA64
|
||||
:RETF is vexMode=0 & addrsize=1 & opsize=0 & byte=0xcb { pop42(IP); EIP=zext(IP); pop42(CS); return [EIP]; }
|
||||
@else
|
||||
:RETF is vexMode=0 & addrsize=1 & opsize=0 & byte=0xcb { pop82(IP); RIP=zext(IP); pop82(CS); return [RIP]; }
|
||||
:RETF is vexMode=0 & addrsize=2 & opsize=0 & byte=0xcb { pop82(IP); RIP=zext(IP); pop82(CS); return [RIP]; }
|
||||
@endif
|
||||
:RET is vexMode=0 & addrsize=0 & opsize=1 & byte=0xcb { pop24(EIP); tmp:4=0; pop24(tmp); CS=tmp(0); return [EIP]; }
|
||||
:RET is vexMode=0 & addrsize=1 & opsize=1 & byte=0xcb { pop44(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); return [EIP]; }
|
||||
:RETF is vexMode=0 & addrsize=0 & opsize=1 & byte=0xcb { pop24(EIP); tmp:4=0; pop24(tmp); CS=tmp(0); return [EIP]; }
|
||||
:RETF is vexMode=0 & addrsize=1 & opsize=1 & byte=0xcb { pop44(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET is vexMode=0 & addrsize=2 & opsize=1 & byte=0xcb { pop48(EIP); RIP=zext(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); return [RIP]; }
|
||||
:RET is vexMode=0 & addrsize=2 & opsize=2 & byte=0xcb { pop88(RIP); tmp:8=0; pop88(tmp); CS=tmp(0); return [RIP]; }
|
||||
:RETF is vexMode=0 & addrsize=2 & opsize=1 & byte=0xcb { pop48(EIP); RIP=zext(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); return [RIP]; }
|
||||
:RETF is vexMode=0 & addrsize=2 & opsize=2 & byte=0xcb { pop88(RIP); tmp:8=0; pop88(tmp); CS=tmp(0); return [RIP]; }
|
||||
@endif
|
||||
|
||||
@ifndef IA64
|
||||
:RET imm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xc2; imm16 { pop22(IP); EIP=zext(IP); SP=SP+imm16; return [EIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xc2; imm16 { pop42(IP); EIP=zext(IP); ESP=ESP+imm16; return [EIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xc2; imm16 { pop24(EIP); SP=SP+imm16; return [EIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xc2; imm16 { pop44(EIP); ESP=ESP+imm16; return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET imm16 is vexMode=0 & addrsize=2 & byte=0xc2; imm16 { pop88(RIP); RSP=RSP+imm16; return [RIP]; }
|
||||
@else
|
||||
:RET imm16 is vexMode=0 & byte=0xc2; imm16 { pop88(RIP); RSP=RSP+imm16; return [RIP]; }
|
||||
@endif
|
||||
|
||||
:RET imm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xca; imm16 { pop22(IP); EIP=zext(IP); pop22(CS); SP=SP+imm16; return [EIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xca; imm16 { pop42(IP); EIP=zext(IP); pop42(CS); ESP=ESP+imm16; return [EIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=0 & opsize=0 & byte=0xca; imm16 { pop22(IP); EIP=zext(IP); pop22(CS); SP=SP+imm16; return [EIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=1 & opsize=0 & byte=0xca; imm16 { pop42(IP); EIP=zext(IP); pop42(CS); ESP=ESP+imm16; return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET imm16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xca; imm16 { pop42(IP); RIP=zext(IP); pop42(CS); RSP=RSP+imm16; return [RIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=2 & opsize=0 & byte=0xca; imm16 { pop42(IP); RIP=zext(IP); pop42(CS); RSP=RSP+imm16; return [RIP]; }
|
||||
@endif
|
||||
|
||||
:RET imm16 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xca; imm16 { pop24(EIP); tmp:4=0; pop24(tmp); CS=tmp(0); SP=SP+imm16; return [EIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xca; imm16 { pop44(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); ESP=ESP+imm16; return [EIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=0 & opsize=1 & byte=0xca; imm16 { pop24(EIP); tmp:4=0; pop24(tmp); CS=tmp(0); SP=SP+imm16; return [EIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=1 & opsize=1 & byte=0xca; imm16 { pop44(EIP); tmp:4=0; pop44(tmp); CS=tmp(0); ESP=ESP+imm16; return [EIP]; }
|
||||
@ifdef IA64
|
||||
:RET imm16 is vexMode=0 & addrsize=2 & opsize=1 & byte=0xca; imm16 { pop44(EIP); tmp:4=0; pop44(tmp); RIP=zext(EIP); CS=tmp(0); RSP=RSP+imm16; return [RIP]; }
|
||||
:RET imm16 is vexMode=0 & addrsize=2 & opsize=2 & byte=0xca; imm16 { pop88(RIP); tmp:8=0; pop88(tmp); CS=tmp(0); RSP=RSP+imm16; return [RIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=2 & opsize=1 & byte=0xca; imm16 { pop44(EIP); tmp:4=0; pop44(tmp); RIP=zext(EIP); CS=tmp(0); RSP=RSP+imm16; return [RIP]; }
|
||||
:RETF imm16 is vexMode=0 & addrsize=2 & opsize=2 & byte=0xca; imm16 { pop88(RIP); tmp:8=0; pop88(tmp); CS=tmp(0); RSP=RSP+imm16; return [RIP]; }
|
||||
@endif
|
||||
|
||||
:ROL rm8,n1 is vexMode=0 & byte=0xD0; rm8 & n1 & reg_opcode=0 ... { CF = rm8 s< 0; rm8 = (rm8 << 1) | CF; OF = CF ^ (rm8 s< 0); }
|
||||
|
||||
+7
@@ -59,12 +59,15 @@ public class X86_32_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
ElfSymbol sym = null;
|
||||
long symbolValue = 0;
|
||||
Address symbolAddr = null;
|
||||
String symbolName = null;
|
||||
|
||||
if (symbolIndex != 0) {
|
||||
sym = elfRelocationContext.getSymbol(symbolIndex);
|
||||
}
|
||||
|
||||
if (sym != null) {
|
||||
symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
symbolName = sym.getNameAsString();
|
||||
}
|
||||
@@ -79,6 +82,10 @@ public class X86_32_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
switch (type) {
|
||||
case X86_32_ElfRelocationConstants.R_386_32:
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
value = (int) (symbolValue + addend);
|
||||
memory.setInt(relocationAddress, value);
|
||||
break;
|
||||
|
||||
+7
-2
@@ -59,16 +59,17 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
|
||||
ElfSymbol sym = null;
|
||||
long symbolValue = 0;
|
||||
long st_value = 0;
|
||||
Address symbolAddr = null;
|
||||
String symbolName = null;
|
||||
long symbolSize = 0;
|
||||
|
||||
if (symbolIndex != 0) {
|
||||
sym = elfRelocationContext.getSymbol(symbolIndex);
|
||||
}
|
||||
|
||||
if (sym != null) {
|
||||
symbolAddr = elfRelocationContext.getSymbolAddress(sym);
|
||||
symbolValue = elfRelocationContext.getSymbolValue(sym);
|
||||
st_value = sym.getValue();
|
||||
symbolName = sym.getNameAsString();
|
||||
symbolSize = sym.getSize();
|
||||
}
|
||||
@@ -86,6 +87,10 @@ public class X86_64_ElfRelocationHandler extends ElfRelocationHandler {
|
||||
"Runtime copy not supported", elfRelocationContext.getLog());
|
||||
break;
|
||||
case X86_64_ElfRelocationConstants.R_X86_64_64:
|
||||
if (addend != 0 && isUnsupportedExternalRelocation(program, relocationAddress,
|
||||
symbolAddr, symbolName, addend, elfRelocationContext.getLog())) {
|
||||
addend = 0; // prefer bad fixup for EXTERNAL over really-bad fixup
|
||||
}
|
||||
value = symbolValue + addend;
|
||||
memory.setLong(relocationAddress, value);
|
||||
break;
|
||||
|
||||
@@ -51,6 +51,8 @@ dependencies {
|
||||
api fileTree(dir: 'lib', include: "*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Framework', include: "**/*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Features', include: "**/*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Debug', include: "**/*.jar")
|
||||
api fileTree(dir: ghidraDir + '/Processors', include: "**/*.jar")
|
||||
helpPath fileTree(dir: ghidraDir + '/Features/Base', include: "**/Base.jar")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user