mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 21:51:47 +00:00
Merge remote-tracking branch 'origin/GT-2836_dev747368_tooltip_wrong_octal_char_preview'
This commit is contained in:
commit
0be4cf06f3
@ -21,12 +21,16 @@ import ghidra.docking.settings.*;
|
|||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.lang.Endian;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
import ghidra.program.model.listing.Program;
|
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.scalar.Scalar;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.util.HTMLUtilities;
|
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.
|
* 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
|
//@formatter:off
|
||||||
private static final Settings[] INTEGER_SETTINGS = new Settings[] {
|
private static final Settings[] INTEGER_SETTINGS = new Settings[] {
|
||||||
getSettingsForRadix("hex"),
|
getSettingsForRadix("hex"),
|
||||||
getSettingsForRadix("decimal"),
|
getSettingsForRadix("decimal")
|
||||||
getSettingsForRadix("char")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final AbstractIntegerDataType[] DISPLAY_TYPES =
|
private static final AbstractIntegerDataType[] INTEGER_DISPLAY_TYPES =
|
||||||
new AbstractIntegerDataType[] {
|
new AbstractIntegerDataType[] {
|
||||||
new ByteDataType(),
|
new ByteDataType(),
|
||||||
new WordDataType(),
|
new WordDataType(),
|
||||||
@ -65,22 +68,16 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
|
|||||||
super(tool, priority);
|
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);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
byte[] opBytes = getOperandBytes(scalar);
|
|
||||||
int opSize = opBytes.length;
|
|
||||||
|
|
||||||
Memory memory = program.getMemory();
|
|
||||||
MemBuffer memBuffer = new ByteMemBufferImpl(addr, opBytes, !memory.isBigEndian());
|
|
||||||
|
|
||||||
buildTableHeader(sb);
|
|
||||||
|
|
||||||
// For each data type, render different the bases/formats
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +93,88 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
|
|||||||
addReprRow(sb, type.getDisplayName(), reprs);
|
addReprRow(sb, type.getDisplayName(), reprs);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("</table>");
|
if (sb.length() > 0) {
|
||||||
|
htmlText.append("<table><tr><th nowrap> </th>");
|
||||||
|
for (Settings setting : INTEGER_SETTINGS) {
|
||||||
|
|
||||||
|
String radixName = FORMAT.getDisplayChoice(setting);
|
||||||
|
radixName = Character.toTitleCase(radixName.charAt(0)) + radixName.substring(1);
|
||||||
|
|
||||||
|
htmlText.append("<th nowrap>").append(radixName).append("</th>");
|
||||||
|
}
|
||||||
|
htmlText.append("</tr>");
|
||||||
|
htmlText.append(sb);
|
||||||
|
htmlText.append("</table>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatCharTypes(Program program, Address addr, Scalar scalar,
|
||||||
|
StringBuilder htmlText) {
|
||||||
|
|
||||||
|
// 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<DataType> charDataTypes = Arrays.asList(new CharDataType(program.getDataTypeManager()),
|
||||||
|
new WideChar16DataType(program.getDataTypeManager()),
|
||||||
|
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);
|
||||||
|
prevCharVal =
|
||||||
|
appendCharDataTypeFormattedHTML(prevCharVal, charDt, charMemBuffer, localHTMLText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localHTMLText.length() > 0) {
|
||||||
|
htmlText.append("<hr>");
|
||||||
|
htmlText.append("<table width=\"100%\">") //
|
||||||
|
.append(localHTMLText) //
|
||||||
|
.append("</table>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("<tr><td>") //
|
||||||
|
.append(charDt.getName()) //
|
||||||
|
.append(isArray ? "[]" : "");
|
||||||
|
if (charMemBuffer.getLength() > 1) {
|
||||||
|
htmlText.append(" <b>" +
|
||||||
|
(charMemBuffer.isBigEndian() ? Endian.BIG : Endian.LITTLE).toShortString() +
|
||||||
|
"</b>");
|
||||||
|
}
|
||||||
|
htmlText.append("</td><td>") //
|
||||||
|
.append(HTMLUtilities.friendlyEncodeHTML(charRep)) //
|
||||||
|
.append("</td></tr>");
|
||||||
|
prevCharVal = charVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prevCharVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatAsAddressVal(Program program, Address addr, Scalar scalar,
|
||||||
|
StringBuilder htmlText) {
|
||||||
|
|
||||||
// maybe the scalar is an address..
|
// maybe the scalar is an address..
|
||||||
long scalarLong = scalar.getValue();
|
long scalarLong = scalar.getValue();
|
||||||
@ -109,76 +187,71 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
|
|||||||
catch (AddressOutOfBoundsException ex) {
|
catch (AddressOutOfBoundsException ex) {
|
||||||
asAddress = null; // Constant doesn't make sense as an address
|
asAddress = null; // Constant doesn't make sense as an address
|
||||||
}
|
}
|
||||||
if (asAddress != null && memory.contains(asAddress)) {
|
|
||||||
sb.append("<hr>");
|
|
||||||
sb.append("<table>");
|
|
||||||
|
|
||||||
addReprRow(sb, "Address", asAddress.toString());
|
Memory memory = program.getMemory();
|
||||||
|
if (asAddress != null && memory.contains(asAddress)) {
|
||||||
|
htmlText.append("<hr>");
|
||||||
|
htmlText.append("<table>");
|
||||||
|
|
||||||
|
addReprRow(htmlText, "Address", asAddress.toString());
|
||||||
|
|
||||||
// .. and maybe it points to some data...
|
// .. and maybe it points to some data...
|
||||||
Data data = program.getListing().getDataContaining(asAddress);
|
Data data = program.getListing().getDataContaining(asAddress);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
Symbol primary = data.getPrimarySymbol();
|
Symbol primary = data.getPrimarySymbol();
|
||||||
if (primary != null) {
|
if (primary != null) {
|
||||||
addReprRow(sb, "Symbol", HTMLUtilities.italic(primary.getName()));
|
addReprRow(htmlText, "Symbol",
|
||||||
|
HTMLUtilities.italic(HTMLUtilities.friendlyEncodeHTML(primary.getName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("</table>");
|
htmlText.append("</table>");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
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[] operandBytes = scalar.byteArrayValue();
|
||||||
byte[] trimmed = trimLeadingZeros(operandBytes);
|
if (minTrimLen > 0) {
|
||||||
return trimmed;
|
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) {
|
private static byte[] trimLeadingZeros(byte[] bytes, int minTrimLen) {
|
||||||
int fullLength = bytes.length;
|
int firstUsedByteIndex = 0;
|
||||||
for (int i = 0; i < fullLength; i++) {
|
for (; firstUsedByteIndex < bytes.length &&
|
||||||
if (bytes[i] == 0) {
|
bytes[firstUsedByteIndex] == 0; firstUsedByteIndex++) {
|
||||||
continue;
|
// no op
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void buildTableHeader(StringBuilder sb) {
|
int bytesToCopy = bytes.length - firstUsedByteIndex;
|
||||||
sb.append("<table><tr><th></th>");
|
int newLen = Math.max(bytesToCopy, minTrimLen);
|
||||||
for (Settings setting : INTEGER_SETTINGS) {
|
if (newLen > 1) {
|
||||||
|
newLen += (newLen % 2);
|
||||||
String radixName = FORMAT.getDisplayChoice(setting);
|
|
||||||
radixName = Character.toTitleCase(radixName.charAt(0)) + radixName.substring(1);
|
|
||||||
|
|
||||||
sb.append("<th>").append(radixName).append("</th>");
|
|
||||||
}
|
}
|
||||||
sb.append("</tr>");
|
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) {
|
private static void addReprRow(StringBuilder sb, String typeName, String repr) {
|
||||||
@ -186,9 +259,9 @@ public abstract class AbstractScalarOperandHover extends AbstractConfigurableHov
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void addReprRow(StringBuilder sb, String typeName, Iterable<String> reprs) {
|
private static void addReprRow(StringBuilder sb, String typeName, Iterable<String> reprs) {
|
||||||
sb.append("<tr><td style=\"text-align: left;\">").append(typeName).append("</td>");
|
sb.append("<tr><td nowrap style=\"text-align: left;\">").append(typeName).append("</td>");
|
||||||
for (String repr : reprs) {
|
for (String repr : reprs) {
|
||||||
sb.append("<td style=\"text-align: right;\">").append(repr).append("</td>");
|
sb.append("<td nowrap style=\"text-align: right;\">").append(repr).append("</td>");
|
||||||
}
|
}
|
||||||
sb.append("</tr>");
|
sb.append("</tr>");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user