From 747e6bcc74b2d7cf22390b2f479f4961e5284168 Mon Sep 17 00:00:00 2001
From: dev747368 <48332326+dev747368@users.noreply.github.com>
Date: Mon, 29 Apr 2019 12:24:20 -0400
Subject: [PATCH 1/2] GT-2836 fix wrong hex/octal/char scalar operand preview
popup
---
.../hover/AbstractScalarOperandHover.java | 196 ++++++++++++------
1 file changed, 128 insertions(+), 68 deletions(-)
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
index f53647a722..b175f04db1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
@@ -21,12 +21,16 @@ import ghidra.docking.settings.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
+import ghidra.program.model.lang.Endian;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.*;
+import ghidra.program.model.mem.ByteMemBufferImpl;
+import ghidra.program.model.mem.Memory;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.HTMLUtilities;
+import ghidra.util.StringUtilities;
+import utilities.util.ArrayUtilities;
/**
* A hover service to show tool tip text for hovering over scalar values.
@@ -39,11 +43,10 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
//@formatter:off
private static final Settings[] INTEGER_SETTINGS = new Settings[] {
getSettingsForRadix("hex"),
- getSettingsForRadix("decimal"),
- getSettingsForRadix("char")
+ getSettingsForRadix("decimal")
};
- private static final AbstractIntegerDataType[] DISPLAY_TYPES =
+ private static final AbstractIntegerDataType[] INTEGER_DISPLAY_TYPES =
new AbstractIntegerDataType[] {
new ByteDataType(),
new WordDataType(),
@@ -65,22 +68,16 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
super(tool, priority);
}
- protected String formatScalar(Program program, Address addr, Scalar scalar) {
+ private void formatIntegerTypes(Program program, Address addr, Scalar scalar,
+ StringBuilder htmlText) {
+ ByteMemBufferImpl memBuffer = getScalarOperandAsMemBuffer(addr, scalar, 1, Endian.BIG);
- StringBuilder sb = new StringBuilder(HTMLUtilities.HTML);
-
- byte[] opBytes = getOperandBytes(scalar);
- int opSize = opBytes.length;
-
- Memory memory = program.getMemory();
- MemBuffer memBuffer = new ByteMemBufferImpl(addr, opBytes, !memory.isBigEndian());
-
- buildTableHeader(sb);
+ StringBuilder sb = new StringBuilder();
// For each data type, render different the bases/formats
- for (DataType type : DISPLAY_TYPES) {
+ for (DataType type : INTEGER_DISPLAY_TYPES) {
- if (type.getLength() != opSize) {
+ if (type.getLength() != memBuffer.getLength()) {
continue;
}
@@ -96,7 +93,75 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
addReprRow(sb, type.getDisplayName(), reprs);
}
- sb.append("");
+ if (sb.length() > 0) {
+ htmlText.append("
| ");
+ for (Settings setting : INTEGER_SETTINGS) {
+
+ String radixName = FORMAT.getDisplayChoice(setting);
+ radixName = Character.toTitleCase(radixName.charAt(0)) + radixName.substring(1);
+
+ htmlText.append("").append(radixName).append(" | ");
+ }
+ htmlText.append("
");
+ htmlText.append(sb);
+ htmlText.append("
");
+ }
+ }
+
+ private void formatCharTypes(Program program, Address addr, Scalar scalar,
+ StringBuilder htmlText) {
+
+ StringBuilder char_sb = new StringBuilder();
+
+ // The CharDataType can change depending on the DataOrg of the current program, so this
+ // can't be a static array like INTEGER_DISPLAY_TYPES
+ List charDataTypes = Arrays.asList(new CharDataType(program.getDataTypeManager()),
+ new WideChar16DataType(program.getDataTypeManager()),
+ new WideChar32DataType(program.getDataTypeManager()));
+
+ String prevCharVal = "";
+
+ Endian progEndian = program.getMemory().isBigEndian() ? Endian.BIG : Endian.LITTLE;
+ for (DataType charDt : charDataTypes) {
+ ByteMemBufferImpl charMemBuffer =
+ getScalarOperandAsMemBuffer(addr, scalar, charDt.getLength(), progEndian);
+
+ if (charMemBuffer.getLength() >= charDt.getLength()) {
+ StringDataInstance sdi = StringDataInstance.getStringDataInstance(charDt,
+ charMemBuffer, SettingsImpl.NO_SETTINGS, charMemBuffer.getLength());
+ boolean isArray = (charMemBuffer.getLength() >= charDt.getLength() * 2);
+ String charVal = sdi.getStringValue();
+ String charRep =
+ isArray ? sdi.getStringRepresentation() : sdi.getCharRepresentation();
+
+ // if the string-ified char data is the same as the previous instance, or if it
+ // doesn't have a quote mark in it (ie. all bytes sequences), skip it
+ boolean shouldSkip = prevCharVal.equals(charVal) //
+ || !charRep.contains(isArray ? "\"" : "'") //
+ || hasEncodingError(charVal);
+ if (!shouldSkip) {
+ char_sb.append("") //
+ .append(charDt.getName()) //
+ .append(isArray ? "[]" : "") //
+ .append(" " + progEndian.toShortString() + "") //
+ .append(" | ") //
+ .append(HTMLUtilities.friendlyEncodeHTML(charRep)) //
+ .append(" |
");
+ prevCharVal = charVal;
+ }
+ }
+ }
+
+ if (char_sb.length() > 0) {
+ htmlText.append("
");
+ htmlText.append("") //
+ .append(char_sb) //
+ .append("
");
+ }
+ }
+
+ private void formatAsAddressVal(Program program, Address addr, Scalar scalar,
+ StringBuilder htmlText) {
// maybe the scalar is an address..
long scalarLong = scalar.getValue();
@@ -109,76 +174,71 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
catch (AddressOutOfBoundsException ex) {
asAddress = null; // Constant doesn't make sense as an address
}
- if (asAddress != null && memory.contains(asAddress)) {
- sb.append("
");
- sb.append("");
- addReprRow(sb, "Address", asAddress.toString());
+ Memory memory = program.getMemory();
+ if (asAddress != null && memory.contains(asAddress)) {
+ htmlText.append("
");
+ htmlText.append("");
+
+ addReprRow(htmlText, "Address", asAddress.toString());
// .. and maybe it points to some data...
Data data = program.getListing().getDataContaining(asAddress);
if (data != null) {
Symbol primary = data.getPrimarySymbol();
if (primary != null) {
- addReprRow(sb, "Symbol", HTMLUtilities.italic(primary.getName()));
-
+ addReprRow(htmlText, "Symbol",
+ HTMLUtilities.italic(HTMLUtilities.friendlyEncodeHTML(primary.getName())));
}
}
- sb.append("
");
+ htmlText.append("
");
}
+ }
+
+ protected String formatScalar(Program program, Address addr, Scalar scalar) {
+
+ StringBuilder sb = new StringBuilder(HTMLUtilities.HTML);
+ formatIntegerTypes(program, addr, scalar, sb);
+ formatCharTypes(program, addr, scalar, sb);
+ formatAsAddressVal(program, addr, scalar, sb);
return sb.toString();
}
- private byte[] getOperandBytes(Scalar scalar) {
+ private boolean hasEncodingError(String s) {
+ return s.codePoints().anyMatch(
+ codePoint -> StringUtilities.isUnicodeReplacementCodePoint(codePoint));
+ }
+
+ private ByteMemBufferImpl getScalarOperandAsMemBuffer(Address addr, Scalar scalar,
+ int minTrimLen, Endian endian) {
byte[] operandBytes = scalar.byteArrayValue();
- byte[] trimmed = trimLeadingZeros(operandBytes);
- return trimmed;
+ if (minTrimLen > 0) {
+ operandBytes = trimLeadingZeros(operandBytes, minTrimLen);
+ }
+ if (endian == Endian.LITTLE) {
+ operandBytes = ArrayUtilities.reverse(operandBytes);
+ }
+ return new ByteMemBufferImpl(addr, operandBytes, endian == Endian.BIG);
}
- private static byte[] trimLeadingZeros(byte[] bytes) {
- int fullLength = bytes.length;
- for (int i = 0; i < fullLength; i++) {
- if (bytes[i] == 0) {
- continue;
- }
-
- if (i == 0) {
- // non-zero value in the first byte--nothing to trim
- break;
- }
-
- int toCopy = fullLength - i;
- int len = toCopy;
- if (len < 2) {
- len = 2;
- }
- else if (len < 4) {
- len = 4;
- }
- else if (len < 8) {
- len = 8;
- }
-
- byte[] newBytes = new byte[len];
- int offset = len - toCopy;
- System.arraycopy(bytes, i, newBytes, offset, toCopy);
- return newBytes;
+ private static byte[] trimLeadingZeros(byte[] bytes, int minTrimLen) {
+ int firstUsedByteIndex = 0;
+ for (; firstUsedByteIndex < bytes.length &&
+ bytes[firstUsedByteIndex] == 0; firstUsedByteIndex++) {
+ // no op
}
- return bytes;
- }
- private static void buildTableHeader(StringBuilder sb) {
- sb.append(" | ");
- for (Settings setting : INTEGER_SETTINGS) {
-
- String radixName = FORMAT.getDisplayChoice(setting);
- radixName = Character.toTitleCase(radixName.charAt(0)) + radixName.substring(1);
-
- sb.append("").append(radixName).append(" | ");
+ int bytesToCopy = bytes.length - firstUsedByteIndex;
+ int newLen = Math.max(bytesToCopy, minTrimLen);
+ if (newLen > 1) {
+ newLen += (newLen % 2);
}
- sb.append("
");
+ byte[] newBytes = new byte[newLen];
+ System.arraycopy(bytes, firstUsedByteIndex, newBytes, newLen - bytesToCopy, bytesToCopy);
+
+ return newBytes;
}
private static void addReprRow(StringBuilder sb, String typeName, String repr) {
@@ -186,9 +246,9 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
}
private static void addReprRow(StringBuilder sb, String typeName, Iterable reprs) {
- sb.append("").append(typeName).append(" | ");
+ sb.append("
").append(typeName).append(" | ");
for (String repr : reprs) {
- sb.append("").append(repr).append(" | ");
+ sb.append("").append(repr).append(" | ");
}
sb.append("
");
}
From e3b2dfe4921333f0c92d7742061ce88b6f08f0c5 Mon Sep 17 00:00:00 2001
From: dev747368 <48332326+dev747368@users.noreply.github.com>
Date: Fri, 3 May 2019 13:15:07 -0400
Subject: [PATCH 2/2] GT-2836 - simplify method
---
.../hover/AbstractScalarOperandHover.java | 71 +++++++++++--------
1 file changed, 42 insertions(+), 29 deletions(-)
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
index b175f04db1..d00fd570c3 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractScalarOperandHover.java
@@ -111,8 +111,6 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
private void formatCharTypes(Program program, Address addr, Scalar scalar,
StringBuilder htmlText) {
- StringBuilder char_sb = new StringBuilder();
-
// The CharDataType can change depending on the DataOrg of the current program, so this
// can't be a static array like INTEGER_DISPLAY_TYPES
List charDataTypes = Arrays.asList(new CharDataType(program.getDataTypeManager()),
@@ -120,46 +118,61 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
new WideChar32DataType(program.getDataTypeManager()));
String prevCharVal = "";
+ StringBuilder localHTMLText = new StringBuilder();
Endian progEndian = program.getMemory().isBigEndian() ? Endian.BIG : Endian.LITTLE;
for (DataType charDt : charDataTypes) {
+ // for each char data type, append its representation to the buffer, if it is
+ // a new way to display the scalar
ByteMemBufferImpl charMemBuffer =
getScalarOperandAsMemBuffer(addr, scalar, charDt.getLength(), progEndian);
-
- if (charMemBuffer.getLength() >= charDt.getLength()) {
- StringDataInstance sdi = StringDataInstance.getStringDataInstance(charDt,
- charMemBuffer, SettingsImpl.NO_SETTINGS, charMemBuffer.getLength());
- boolean isArray = (charMemBuffer.getLength() >= charDt.getLength() * 2);
- String charVal = sdi.getStringValue();
- String charRep =
- isArray ? sdi.getStringRepresentation() : sdi.getCharRepresentation();
-
- // if the string-ified char data is the same as the previous instance, or if it
- // doesn't have a quote mark in it (ie. all bytes sequences), skip it
- boolean shouldSkip = prevCharVal.equals(charVal) //
- || !charRep.contains(isArray ? "\"" : "'") //
- || hasEncodingError(charVal);
- if (!shouldSkip) {
- char_sb.append("") //
- .append(charDt.getName()) //
- .append(isArray ? "[]" : "") //
- .append(" " + progEndian.toShortString() + "") //
- .append(" | ") //
- .append(HTMLUtilities.friendlyEncodeHTML(charRep)) //
- .append(" |
");
- prevCharVal = charVal;
- }
- }
+ prevCharVal =
+ appendCharDataTypeFormattedHTML(prevCharVal, charDt, charMemBuffer, localHTMLText);
}
- if (char_sb.length() > 0) {
+ if (localHTMLText.length() > 0) {
htmlText.append("
");
htmlText.append("") //
- .append(char_sb) //
+ .append(localHTMLText) //
.append("
");
}
}
+ private String appendCharDataTypeFormattedHTML(String prevCharVal, DataType charDt,
+ ByteMemBufferImpl charMemBuffer, StringBuilder htmlText) {
+ // appends a HTML table row to the stringbuilder with the scalar displayed as the
+ // specified data type, only if its a value that hasn't already been added to the buffer.
+
+ if (charMemBuffer.getLength() >= charDt.getLength()) {
+ StringDataInstance sdi = StringDataInstance.getStringDataInstance(charDt, charMemBuffer,
+ SettingsImpl.NO_SETTINGS, charMemBuffer.getLength());
+ boolean isArray = (charMemBuffer.getLength() >= charDt.getLength() * 2);
+ String charVal = sdi.getStringValue();
+ String charRep = isArray ? sdi.getStringRepresentation() : sdi.getCharRepresentation();
+
+ // if the string-ified char data is the same as the previous instance, or if it
+ // doesn't have a quote mark in it (ie. all bytes sequences), skip it
+ boolean shouldSkip = prevCharVal.equals(charVal) //
+ || !charRep.contains(isArray ? "\"" : "'") //
+ || hasEncodingError(charVal);
+ if (!shouldSkip) {
+ htmlText.append("") //
+ .append(charDt.getName()) //
+ .append(isArray ? "[]" : "");
+ if (charMemBuffer.getLength() > 1) {
+ htmlText.append(" " +
+ (charMemBuffer.isBigEndian() ? Endian.BIG : Endian.LITTLE).toShortString() +
+ "");
+ }
+ htmlText.append(" | ") //
+ .append(HTMLUtilities.friendlyEncodeHTML(charRep)) //
+ .append(" |
");
+ prevCharVal = charVal;
+ }
+ }
+ return prevCharVal;
+ }
+
private void formatAsAddressVal(Program program, Address addr, Scalar scalar,
StringBuilder htmlText) {