mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GT-2882-dragonmacher-decompiler-default-cursor'
This commit is contained in:
@@ -203,25 +203,77 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
|
|
||||||
public void setLocation(ProgramLocation location, ViewerPosition viewerPosition) {
|
public void setLocation(ProgramLocation location, ViewerPosition viewerPosition) {
|
||||||
repaint();
|
repaint();
|
||||||
Address address = location.getAddress();
|
if (location.getAddress() == null) {
|
||||||
if (address == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewerPosition != null) {
|
if (viewerPosition != null) {
|
||||||
fieldPanel.setViewerPosition(viewerPosition.getIndex(), viewerPosition.getXOffset(),
|
fieldPanel.setViewerPosition(viewerPosition.getIndex(), viewerPosition.getXOffset(),
|
||||||
viewerPosition.getYOffset());
|
viewerPosition.getYOffset());
|
||||||
}
|
}
|
||||||
List<ClangToken> tokens =
|
|
||||||
DecompilerUtils.getTokens(layoutMgr.getRoot(), translateAddress(address));
|
|
||||||
|
|
||||||
if (location instanceof DecompilerLocation) {
|
if (location instanceof DecompilerLocation) {
|
||||||
DecompilerLocation decompilerLocation = (DecompilerLocation) location;
|
DecompilerLocation decompilerLocation = (DecompilerLocation) location;
|
||||||
fieldPanel.goTo(BigInteger.valueOf(decompilerLocation.getLineNumber()), 0, 0,
|
fieldPanel.goTo(BigInteger.valueOf(decompilerLocation.getLineNumber()), 0, 0,
|
||||||
decompilerLocation.getCharPos(), false);
|
decompilerLocation.getCharPos(), false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (!tokens.isEmpty()) {
|
|
||||||
goToBeginningOfLine(tokens);
|
//
|
||||||
|
// Try to figure out where the given location's address maps to. If we can find the
|
||||||
|
// line that contains the address, the go to the beginning of that line. (We do not try
|
||||||
|
// to go to an actual token, since multiple tokens can share an address, we woudln't know
|
||||||
|
// which token is best.)
|
||||||
|
//
|
||||||
|
// Note: at the time of this writing, not all fields have an address value. For
|
||||||
|
// example, the ClangFuncNameToken, does not have an address. (It seems that most
|
||||||
|
// of the tokens in the function signature do not have an address, which can
|
||||||
|
// probably be fixed.) So, to deal with this oddity, we will have some special
|
||||||
|
// case code below.
|
||||||
|
//
|
||||||
|
Address address = location.getAddress();
|
||||||
|
if (goToFunctionSignature(address)) {
|
||||||
|
// special case: the address is at the function entry, which means we just navigate
|
||||||
|
// to the signature
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address translated = translate(address);
|
||||||
|
List<ClangToken> tokens =
|
||||||
|
DecompilerUtils.getTokensFromView(layoutMgr.getFields(), translated);
|
||||||
|
goToBeginningOfLine(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean goToFunctionSignature(Address address) {
|
||||||
|
|
||||||
|
Address entry = decompileData.getFunction().getEntryPoint();
|
||||||
|
if (!entry.equals(address)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ClangLine> lines = layoutMgr.getLines();
|
||||||
|
ClangLine signatureLine = getFunctionSignatureLine(lines);
|
||||||
|
if (signatureLine == null) {
|
||||||
|
return false; // can happen when there is no function decompiled
|
||||||
|
}
|
||||||
|
|
||||||
|
// -1 since the FieldPanel is 0-based; we are 1-based
|
||||||
|
int lineNumber = signatureLine.getLineNumber() - 1;
|
||||||
|
fieldPanel.goTo(BigInteger.valueOf(lineNumber), 0, 0, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClangLine getFunctionSignatureLine(List<ClangLine> functionLines) {
|
||||||
|
for (ClangLine line : functionLines) {
|
||||||
|
List<ClangToken> tokens = line.getAllTokens();
|
||||||
|
for (ClangToken token : tokens) {
|
||||||
|
if (token.Parent() instanceof ClangFuncProto) {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,6 +281,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
* @param tokens the tokens to search for
|
* @param tokens the tokens to search for
|
||||||
*/
|
*/
|
||||||
private void goToBeginningOfLine(List<ClangToken> tokens) {
|
private void goToBeginningOfLine(List<ClangToken> tokens) {
|
||||||
|
if (tokens.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int firstLineNumber = DecompilerUtils.findIndexOfFirstField(tokens, layoutMgr.getFields());
|
int firstLineNumber = DecompilerUtils.findIndexOfFirstField(tokens, layoutMgr.getFields());
|
||||||
if (firstLineNumber != -1) {
|
if (firstLineNumber != -1) {
|
||||||
fieldPanel.goTo(BigInteger.valueOf(firstLineNumber), 0, 0, 0, false);
|
fieldPanel.goTo(BigInteger.valueOf(firstLineNumber), 0, 0, 0, false);
|
||||||
@@ -290,7 +346,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
* @param addr the Ghidra address
|
* @param addr the Ghidra address
|
||||||
* @return the decompiler address
|
* @return the decompiler address
|
||||||
*/
|
*/
|
||||||
private Address translateAddress(Address addr) {
|
private Address translate(Address addr) {
|
||||||
Function func = decompileData.getFunction();
|
Function func = decompileData.getFunction();
|
||||||
if (func == null) {
|
if (func == null) {
|
||||||
return addr;
|
return addr;
|
||||||
@@ -418,7 +474,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
ClangTextField textField = (ClangTextField) field;
|
ClangTextField textField = (ClangTextField) field;
|
||||||
ClangToken token = textField.getToken(location);
|
ClangToken token = textField.getToken(location);
|
||||||
if (token instanceof ClangFuncNameToken) {
|
if (token instanceof ClangFuncNameToken) {
|
||||||
tryGoToFunction(token, newWindow);
|
tryGoToFunction((ClangFuncNameToken) token, newWindow);
|
||||||
}
|
}
|
||||||
else if (token instanceof ClangLabelToken) {
|
else if (token instanceof ClangLabelToken) {
|
||||||
tryGoToLabel((ClangLabelToken) token, newWindow);
|
tryGoToLabel((ClangLabelToken) token, newWindow);
|
||||||
@@ -451,16 +507,15 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
tryGoToScalar(word, newWindow);
|
tryGoToScalar(word, newWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryGoToFunction(ClangToken token, boolean newWindow) {
|
private void tryGoToFunction(ClangFuncNameToken functionToken, boolean newWindow) {
|
||||||
Function function =
|
Function function = DecompilerUtils.getFunction(controller.getProgram(), functionToken);
|
||||||
DecompilerUtils.getFunction(controller.getProgram(), (ClangFuncNameToken) token);
|
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
controller.goToFunction(function, newWindow);
|
controller.goToFunction(function, newWindow);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO no idea what this is supposed to be handling...someone doc this please
|
// TODO no idea what this is supposed to be handling...someone doc this please
|
||||||
String labelName = token.getText();
|
String labelName = functionToken.getText();
|
||||||
if (labelName.startsWith("func_0x")) {
|
if (labelName.startsWith("func_0x")) {
|
||||||
try {
|
try {
|
||||||
Address addr =
|
Address addr =
|
||||||
|
|||||||
@@ -24,24 +24,29 @@ import ghidra.program.model.address.*;
|
|||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.*;
|
import ghidra.program.model.pcode.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class DecompilerUtils {
|
public class DecompilerUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the token refers to an individual Varnode, return it. Otherwise return null;
|
* If the token refers to an individual Varnode, return it. Otherwise return null
|
||||||
|
*
|
||||||
|
* @param token the token to check
|
||||||
* @return the Varnode or null otherwise
|
* @return the Varnode or null otherwise
|
||||||
*/
|
*/
|
||||||
public static Varnode getVarnodeRef(ClangToken vartoken) {
|
public static Varnode getVarnodeRef(ClangToken token) {
|
||||||
if (vartoken == null) {
|
if (token == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (vartoken instanceof ClangVariableToken) {
|
|
||||||
Varnode res = vartoken.getVarnode();
|
if (token instanceof ClangVariableToken) {
|
||||||
|
Varnode res = token.getVarnode();
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClangNode parent = vartoken.Parent();
|
|
||||||
|
ClangNode parent = token.Parent();
|
||||||
if (parent instanceof ClangVariableDecl) {
|
if (parent instanceof ClangVariableDecl) {
|
||||||
HighVariable high = ((ClangVariableDecl) parent).getHighVariable();
|
HighVariable high = ((ClangVariableDecl) parent).getHighVariable();
|
||||||
parent = parent.Parent();
|
parent = parent.Parent();
|
||||||
@@ -63,8 +68,8 @@ public class DecompilerUtils {
|
|||||||
* @return set of Varnodes in the slice
|
* @return set of Varnodes in the slice
|
||||||
*/
|
*/
|
||||||
public static Set<Varnode> getForwardSlice(Varnode seed) {
|
public static Set<Varnode> getForwardSlice(Varnode seed) {
|
||||||
HashSet<Varnode> varnodes = new HashSet<>();
|
Set<Varnode> varnodes = new HashSet<>();
|
||||||
ArrayList<Varnode> worklist = new ArrayList<>();
|
List<Varnode> worklist = new ArrayList<>();
|
||||||
worklist.add(seed);
|
worklist.add(seed);
|
||||||
|
|
||||||
for (int i = 0; i < worklist.size(); i++) {
|
for (int i = 0; i < worklist.size(); i++) {
|
||||||
@@ -97,8 +102,8 @@ public class DecompilerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Set<Varnode> getBackwardSlice(Varnode seed) {
|
public static Set<Varnode> getBackwardSlice(Varnode seed) {
|
||||||
HashSet<Varnode> varnodes = new HashSet<>();
|
Set<Varnode> varnodes = new HashSet<>();
|
||||||
ArrayList<Varnode> worklist = new ArrayList<>();
|
List<Varnode> worklist = new ArrayList<>();
|
||||||
worklist.add(seed);
|
worklist.add(seed);
|
||||||
|
|
||||||
for (int i = 0; i < worklist.size(); i++) {
|
for (int i = 0; i < worklist.size(); i++) {
|
||||||
@@ -209,7 +214,12 @@ public class DecompilerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the function referenced by the given token
|
* Returns the function represented by the given token. This will be either the
|
||||||
|
* decompiled function or a function referenced within the decompiled function.
|
||||||
|
*
|
||||||
|
* @param program the progam
|
||||||
|
* @param token the token
|
||||||
|
* @return the function
|
||||||
*/
|
*/
|
||||||
public static Function getFunction(Program program, ClangFuncNameToken token) {
|
public static Function getFunction(Program program, ClangFuncNameToken token) {
|
||||||
|
|
||||||
@@ -221,6 +231,7 @@ public class DecompilerUtils {
|
|||||||
return clangFunction.getHighFunction().getFunction();
|
return clangFunction.getHighFunction().getFunction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent instanceof ClangStatement) {
|
if (parent instanceof ClangStatement) {
|
||||||
// sub-function call
|
// sub-function call
|
||||||
PcodeOp pcodeOp = token.getPcodeOp();
|
PcodeOp pcodeOp = token.getPcodeOp();
|
||||||
@@ -239,6 +250,10 @@ public class DecompilerUtils {
|
|||||||
* @return index of field, or -1
|
* @return index of field, or -1
|
||||||
*/
|
*/
|
||||||
public static int findIndexOfFirstField(List<ClangToken> queryTokens, Field[] fields) {
|
public static int findIndexOfFirstField(List<ClangToken> queryTokens, Field[] fields) {
|
||||||
|
if (queryTokens.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < fields.length; i++) {
|
for (int i = 0; i < fields.length; i++) {
|
||||||
ClangTextField f = (ClangTextField) fields[i];
|
ClangTextField f = (ClangTextField) fields[i];
|
||||||
List<ClangToken> fieldTokens = f.getTokens();
|
List<ClangToken> fieldTokens = f.getTokens();
|
||||||
@@ -252,6 +267,32 @@ public class DecompilerUtils {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #getTokens(ClangNode, AddressSetView)}, but uses the tokens from
|
||||||
|
* the given view fields. Sometimes the tokens in the model (represented by the
|
||||||
|
* {@link ClangNode}) are different than the fields in the view (such as when a list of
|
||||||
|
* comment tokens are condensed into a single comment token).
|
||||||
|
*
|
||||||
|
* @param fields the fields to check
|
||||||
|
* @param address the address each returned token must match
|
||||||
|
* @return the matching tokens
|
||||||
|
*/
|
||||||
|
public static List<ClangToken> getTokensFromView(Field[] fields, Address address) {
|
||||||
|
|
||||||
|
AddressSetView set = new AddressSet(address);
|
||||||
|
List<ClangToken> result = new ArrayList<>();
|
||||||
|
for (Field f : fields) {
|
||||||
|
ClangTextField tf = (ClangTextField) f;
|
||||||
|
List<ClangToken> fieldTokens = tf.getTokens();
|
||||||
|
for (ClangToken token : fieldTokens) {
|
||||||
|
if (intersects(token, set)) {
|
||||||
|
result.add(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all ClangNodes that have a minimum address in the AddressSetView
|
* Find all ClangNodes that have a minimum address in the AddressSetView
|
||||||
* @param root the root of the token tree
|
* @param root the root of the token tree
|
||||||
@@ -265,26 +306,8 @@ public class DecompilerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<ClangToken> getTokens(ClangNode root, Address address) {
|
public static List<ClangToken> getTokens(ClangNode root, Address address) {
|
||||||
List<ClangToken> tokenList = new ArrayList<>();
|
AddressSet set = new AddressSet(address);
|
||||||
collectTokens(tokenList, root, address);
|
return getTokens(root, set);
|
||||||
return tokenList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void collectTokens(List<ClangToken> tokenList, ClangNode parentNode,
|
|
||||||
Address address) {
|
|
||||||
int nchild = parentNode.numChildren();
|
|
||||||
for (int i = 0; i < nchild; i++) {
|
|
||||||
ClangNode node = parentNode.Child(i);
|
|
||||||
if (node.numChildren() > 0) {
|
|
||||||
collectTokens(tokenList, node, address);
|
|
||||||
}
|
|
||||||
else if (node instanceof ClangToken) {
|
|
||||||
ClangToken token = (ClangToken) node;
|
|
||||||
if (intersects(token, address)) {
|
|
||||||
tokenList.add((ClangToken) node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void collectTokens(List<ClangToken> tokenList, ClangNode parentNode,
|
private static void collectTokens(List<ClangToken> tokenList, ClangNode parentNode,
|
||||||
@@ -292,6 +315,11 @@ public class DecompilerUtils {
|
|||||||
int nchild = parentNode.numChildren();
|
int nchild = parentNode.numChildren();
|
||||||
for (int i = 0; i < nchild; i++) {
|
for (int i = 0; i < nchild; i++) {
|
||||||
ClangNode node = parentNode.Child(i);
|
ClangNode node = parentNode.Child(i);
|
||||||
|
|
||||||
|
if (node instanceof ClangFuncProto) {
|
||||||
|
Msg.debug(null, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (node.numChildren() > 0) {
|
if (node.numChildren() > 0) {
|
||||||
collectTokens(tokenList, node, addressSet);
|
collectTokens(tokenList, node, addressSet);
|
||||||
}
|
}
|
||||||
@@ -314,18 +342,6 @@ public class DecompilerUtils {
|
|||||||
return addressSet.intersects(minAddress, maxAddress);
|
return addressSet.intersects(minAddress, maxAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean intersects(ClangToken token, Address address) {
|
|
||||||
Address minAddress = token.getMinAddress();
|
|
||||||
if (minAddress == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Address maxAddress = token.getMaxAddress();
|
|
||||||
if (maxAddress == null) {
|
|
||||||
return minAddress.equals(maxAddress);
|
|
||||||
}
|
|
||||||
return address.compareTo(minAddress) >= 0 && address.compareTo(maxAddress) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Address getClosestAddress(ClangToken token) {
|
public static Address getClosestAddress(ClangToken token) {
|
||||||
Address address = token.getMinAddress();
|
Address address = token.getMinAddress();
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
@@ -378,7 +394,7 @@ public class DecompilerUtils {
|
|||||||
/**
|
/**
|
||||||
* Find closest addressed token to a specified token or null if one is not found.
|
* Find closest addressed token to a specified token or null if one is not found.
|
||||||
* Only adjacent tokens on the same line are examined.
|
* Only adjacent tokens on the same line are examined.
|
||||||
* @param token
|
* @param token the query token
|
||||||
* @return closest addressed token
|
* @return closest addressed token
|
||||||
*/
|
*/
|
||||||
private static ClangToken findClosestAddressedToken(ClangToken token) {
|
private static ClangToken findClosestAddressedToken(ClangToken token) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.decompile;
|
package ghidra.app.plugin.core.decompile;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -97,6 +97,20 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||||||
assertCurrentAddress(addr(linkAddress));
|
assertCurrentAddress(addr(linkAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNewDecompileNavigatesToFunctionSignature() {
|
||||||
|
|
||||||
|
decompile("100000bf0"); // 'main'
|
||||||
|
int line = 5; // arbitrary value in view
|
||||||
|
int charPosition = 5; // arbitrary
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
|
||||||
|
decompile("100000d60"); // _call_structure_A()
|
||||||
|
line = 0; // function signature
|
||||||
|
charPosition = 0; // start of signature
|
||||||
|
assertCurrentLocation(line, charPosition);
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@@ -141,6 +155,14 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||||||
assertEquals("Line text not as expected at line " + line, expected, actual);
|
assertEquals("Line text not as expected at line " + line, expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertCurrentLocation(int line, int col) {
|
||||||
|
int oneBasedLine = line + 1;
|
||||||
|
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||||
|
FieldLocation actual = panel.getCursorPosition();
|
||||||
|
FieldLocation expected = loc(oneBasedLine, col);
|
||||||
|
assertEquals("Decompiler cursor is not at the expected location", expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
private String getTokenText(FieldLocation loc) {
|
private String getTokenText(FieldLocation loc) {
|
||||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||||
ClangToken token = field.getToken(loc);
|
ClangToken token = field.getToken(loc);
|
||||||
|
|||||||
Reference in New Issue
Block a user