mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-27 21:10:26 +00:00
Merge remote-tracking branch 'origin/GT-2882-dragonmacher-decompiler-default-cursor'
This commit is contained in:
commit
d0ee2aa26b
|
@ -203,25 +203,77 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
|
||||
public void setLocation(ProgramLocation location, ViewerPosition viewerPosition) {
|
||||
repaint();
|
||||
Address address = location.getAddress();
|
||||
if (address == null) {
|
||||
if (location.getAddress() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewerPosition != null) {
|
||||
fieldPanel.setViewerPosition(viewerPosition.getIndex(), viewerPosition.getXOffset(),
|
||||
viewerPosition.getYOffset());
|
||||
}
|
||||
List<ClangToken> tokens =
|
||||
DecompilerUtils.getTokens(layoutMgr.getRoot(), translateAddress(address));
|
||||
|
||||
if (location instanceof DecompilerLocation) {
|
||||
DecompilerLocation decompilerLocation = (DecompilerLocation) location;
|
||||
fieldPanel.goTo(BigInteger.valueOf(decompilerLocation.getLineNumber()), 0, 0,
|
||||
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
|
||||
*/
|
||||
private void goToBeginningOfLine(List<ClangToken> tokens) {
|
||||
if (tokens.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int firstLineNumber = DecompilerUtils.findIndexOfFirstField(tokens, layoutMgr.getFields());
|
||||
if (firstLineNumber != -1) {
|
||||
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
|
||||
* @return the decompiler address
|
||||
*/
|
||||
private Address translateAddress(Address addr) {
|
||||
private Address translate(Address addr) {
|
||||
Function func = decompileData.getFunction();
|
||||
if (func == null) {
|
||||
return addr;
|
||||
|
@ -418,7 +474,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
ClangTextField textField = (ClangTextField) field;
|
||||
ClangToken token = textField.getToken(location);
|
||||
if (token instanceof ClangFuncNameToken) {
|
||||
tryGoToFunction(token, newWindow);
|
||||
tryGoToFunction((ClangFuncNameToken) token, newWindow);
|
||||
}
|
||||
else if (token instanceof ClangLabelToken) {
|
||||
tryGoToLabel((ClangLabelToken) token, newWindow);
|
||||
|
@ -451,16 +507,15 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
tryGoToScalar(word, newWindow);
|
||||
}
|
||||
|
||||
private void tryGoToFunction(ClangToken token, boolean newWindow) {
|
||||
Function function =
|
||||
DecompilerUtils.getFunction(controller.getProgram(), (ClangFuncNameToken) token);
|
||||
private void tryGoToFunction(ClangFuncNameToken functionToken, boolean newWindow) {
|
||||
Function function = DecompilerUtils.getFunction(controller.getProgram(), functionToken);
|
||||
if (function != null) {
|
||||
controller.goToFunction(function, newWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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")) {
|
||||
try {
|
||||
Address addr =
|
||||
|
|
|
@ -24,24 +24,29 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
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
|
||||
*/
|
||||
public static Varnode getVarnodeRef(ClangToken vartoken) {
|
||||
if (vartoken == null) {
|
||||
public static Varnode getVarnodeRef(ClangToken token) {
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
if (vartoken instanceof ClangVariableToken) {
|
||||
Varnode res = vartoken.getVarnode();
|
||||
|
||||
if (token instanceof ClangVariableToken) {
|
||||
Varnode res = token.getVarnode();
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
ClangNode parent = vartoken.Parent();
|
||||
|
||||
ClangNode parent = token.Parent();
|
||||
if (parent instanceof ClangVariableDecl) {
|
||||
HighVariable high = ((ClangVariableDecl) parent).getHighVariable();
|
||||
parent = parent.Parent();
|
||||
|
@ -63,8 +68,8 @@ public class DecompilerUtils {
|
|||
* @return set of Varnodes in the slice
|
||||
*/
|
||||
public static Set<Varnode> getForwardSlice(Varnode seed) {
|
||||
HashSet<Varnode> varnodes = new HashSet<>();
|
||||
ArrayList<Varnode> worklist = new ArrayList<>();
|
||||
Set<Varnode> varnodes = new HashSet<>();
|
||||
List<Varnode> worklist = new ArrayList<>();
|
||||
worklist.add(seed);
|
||||
|
||||
for (int i = 0; i < worklist.size(); i++) {
|
||||
|
@ -97,8 +102,8 @@ public class DecompilerUtils {
|
|||
}
|
||||
|
||||
public static Set<Varnode> getBackwardSlice(Varnode seed) {
|
||||
HashSet<Varnode> varnodes = new HashSet<>();
|
||||
ArrayList<Varnode> worklist = new ArrayList<>();
|
||||
Set<Varnode> varnodes = new HashSet<>();
|
||||
List<Varnode> worklist = new ArrayList<>();
|
||||
worklist.add(seed);
|
||||
|
||||
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) {
|
||||
|
||||
|
@ -221,6 +231,7 @@ public class DecompilerUtils {
|
|||
return clangFunction.getHighFunction().getFunction();
|
||||
}
|
||||
}
|
||||
|
||||
if (parent instanceof ClangStatement) {
|
||||
// sub-function call
|
||||
PcodeOp pcodeOp = token.getPcodeOp();
|
||||
|
@ -239,6 +250,10 @@ public class DecompilerUtils {
|
|||
* @return index of field, or -1
|
||||
*/
|
||||
public static int findIndexOfFirstField(List<ClangToken> queryTokens, Field[] fields) {
|
||||
if (queryTokens.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
ClangTextField f = (ClangTextField) fields[i];
|
||||
List<ClangToken> fieldTokens = f.getTokens();
|
||||
|
@ -252,6 +267,32 @@ public class DecompilerUtils {
|
|||
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
|
||||
* @param root the root of the token tree
|
||||
|
@ -265,26 +306,8 @@ public class DecompilerUtils {
|
|||
}
|
||||
|
||||
public static List<ClangToken> getTokens(ClangNode root, Address address) {
|
||||
List<ClangToken> tokenList = new ArrayList<>();
|
||||
collectTokens(tokenList, root, address);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddressSet set = new AddressSet(address);
|
||||
return getTokens(root, set);
|
||||
}
|
||||
|
||||
private static void collectTokens(List<ClangToken> tokenList, ClangNode parentNode,
|
||||
|
@ -292,6 +315,11 @@ public class DecompilerUtils {
|
|||
int nchild = parentNode.numChildren();
|
||||
for (int i = 0; i < nchild; i++) {
|
||||
ClangNode node = parentNode.Child(i);
|
||||
|
||||
if (node instanceof ClangFuncProto) {
|
||||
Msg.debug(null, "");
|
||||
}
|
||||
|
||||
if (node.numChildren() > 0) {
|
||||
collectTokens(tokenList, node, addressSet);
|
||||
}
|
||||
|
@ -314,18 +342,6 @@ public class DecompilerUtils {
|
|||
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) {
|
||||
Address address = token.getMinAddress();
|
||||
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.
|
||||
* Only adjacent tokens on the same line are examined.
|
||||
* @param token
|
||||
* @param token the query token
|
||||
* @return closest addressed token
|
||||
*/
|
||||
private static ClangToken findClosestAddressedToken(ClangToken token) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -97,6 +97,20 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||
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
|
||||
//==================================================================================================
|
||||
|
@ -141,6 +155,14 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||
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) {
|
||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||
ClangToken token = field.getToken(loc);
|
||||
|
|
Loading…
Reference in a new issue