mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-25 07:01:59 +08:00
Merge remote-tracking branch 'origin/GP-5583_emteere_DefaultCaseFlowAndLabels' into patch
This commit is contained in:
+177
-99
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -48,8 +48,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
protected DecompInterface decompiler;
|
||||
private boolean useArraysForSwitchTables = false;
|
||||
|
||||
public DecompilerSwitchAnalysisCmd(DecompileResults decopmileResults) {
|
||||
this.decompilerResults = decopmileResults;
|
||||
public DecompilerSwitchAnalysisCmd(DecompileResults decompileResults) {
|
||||
this.decompilerResults = decompileResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,20 +71,18 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
monitor.checkCancelled();
|
||||
|
||||
Function f = decompilerResults.getFunction();
|
||||
HighFunction hfunction = decompilerResults.getHighFunction();
|
||||
processBranchIND(f, hfunction, monitor);
|
||||
|
||||
monitor.checkCancelled();
|
||||
|
||||
|
||||
String errMsg = getStatusMsg();
|
||||
if (decompilerResults.getHighFunction() == null) {
|
||||
if (hfunction == null) {
|
||||
String msg = (errMsg != null && errMsg.length() != 0) ? (": " + errMsg) : "";
|
||||
Msg.debug(this, " Failed to decompile function: " + f.getName() + msg);
|
||||
}
|
||||
|
||||
processBranchIND(f, hfunction, monitor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (!monitor.isCancelled()) {
|
||||
@@ -114,34 +112,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
continue; // skip switch owned by a different defined function
|
||||
}
|
||||
|
||||
AddressSetView containingBody =
|
||||
containingFunction != null ? containingFunction.getBody() : null;
|
||||
|
||||
Reference[] referencesFrom = instr.getReferencesFrom();
|
||||
Address[] tableDest = table.getCases();
|
||||
|
||||
boolean foundNotThere = false;
|
||||
int tableIndx;
|
||||
for (tableIndx = 0; tableIndx < tableDest.length; tableIndx++) {
|
||||
monitor.checkCancelled();
|
||||
boolean foundit = false;
|
||||
if (containingBody != null && !containingBody.contains(tableDest[tableIndx])) {
|
||||
// switch case missing from owner function's body
|
||||
foundNotThere = true;
|
||||
break;
|
||||
}
|
||||
for (Reference element : referencesFrom) {
|
||||
if (element.getToAddress().equals(tableDest[tableIndx])) {
|
||||
foundit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundit) {
|
||||
foundNotThere = true;
|
||||
}
|
||||
}
|
||||
// references already there, ignore this table
|
||||
if (!foundNotThere) {
|
||||
if (hasAllReferences(monitor, table, instr, containingFunction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -158,64 +130,158 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
labelSwitch(table, monitor);
|
||||
|
||||
// disassemble the table
|
||||
// pull out the current context so we can flow anything that needs to flow
|
||||
ProgramContext programContext = program.getProgramContext();
|
||||
Register baseContextRegister = programContext.getBaseContextRegister();
|
||||
RegisterValue switchContext = null;
|
||||
if (baseContextRegister != null) {
|
||||
// Use disassembler context based upon context register value at switch address (i.e., computed jump)
|
||||
// Only use flowing context bits
|
||||
switchContext = programContext.getRegisterValue(baseContextRegister, switchAddr);
|
||||
switchContext = programContext.getFlowValue(switchContext);
|
||||
}
|
||||
|
||||
Listing listing = program.getListing();
|
||||
Address[] cases = table.getCases();
|
||||
AddressSet disSetList = new AddressSet();
|
||||
for (Address caseStart : cases) {
|
||||
monitor.checkCancelled();
|
||||
instr.addMnemonicReference(caseStart, flowType, SourceType.ANALYSIS);
|
||||
|
||||
// if conflict skip case
|
||||
if (listing.getUndefinedDataAt(caseStart) == null) {
|
||||
continue;
|
||||
}
|
||||
// already done
|
||||
if (disSetList.contains(caseStart)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
setSwitchTargetContext(programContext, caseStart, switchContext);
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// This can occur when two or more threads are working on the same function
|
||||
continue;
|
||||
}
|
||||
disSetList.add(caseStart);
|
||||
}
|
||||
|
||||
// do all cases at one time
|
||||
if (!disSetList.isEmpty()) {
|
||||
DisassembleCommand cmd = new DisassembleCommand(disSetList, null, true);
|
||||
cmd.applyTo(program);
|
||||
}
|
||||
disassembleTable(monitor, table, instr, flowType);
|
||||
|
||||
// fixup the function body
|
||||
// make sure this case isn't the result of an undefined function, that somehow one of the cases found a real function.
|
||||
Function fixupFunc = f;
|
||||
if (fixupFunc instanceof UndefinedFunction) {
|
||||
Function realFunc =
|
||||
program.getFunctionManager().getFunctionContaining(instr.getMinAddress());
|
||||
if (realFunc != null) {
|
||||
fixupFunc = realFunc;
|
||||
}
|
||||
}
|
||||
Instruction funcStartInstr =
|
||||
program.getListing().getInstructionAt(fixupFunc.getEntryPoint());
|
||||
CreateFunctionCmd.fixupFunctionBody(program, funcStartInstr, monitor);
|
||||
fixupFunction(f, monitor, instr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix the functions body with any newly reached code from the switch recovery
|
||||
*/
|
||||
private void fixupFunction(Function f, TaskMonitor monitor, Instruction instr)
|
||||
throws CancelledException {
|
||||
Function fixupFunc = f;
|
||||
|
||||
// Make sure this case isn't the result of an undefined function,
|
||||
// that somehow one of the cases found a real function.
|
||||
if (fixupFunc instanceof UndefinedFunction) {
|
||||
Function realFunc =
|
||||
program.getFunctionManager().getFunctionContaining(instr.getMinAddress());
|
||||
if (realFunc != null) {
|
||||
fixupFunc = realFunc;
|
||||
}
|
||||
}
|
||||
Instruction funcStartInstr =
|
||||
program.getListing().getInstructionAt(fixupFunc.getEntryPoint());
|
||||
CreateFunctionCmd.fixupFunctionBody(program, funcStartInstr, monitor);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disassemble all code reached from the table.
|
||||
* Also adds the case flow references to the switching instruction.
|
||||
*/
|
||||
private void disassembleTable(TaskMonitor monitor, JumpTable table,
|
||||
Instruction instr, FlowType flowType) throws CancelledException {
|
||||
|
||||
Address switchAddr = table.getSwitchAddress();
|
||||
|
||||
// pull out the current context so we can flow anything that needs to flow
|
||||
ProgramContext programContext = program.getProgramContext();
|
||||
Register baseContextRegister = programContext.getBaseContextRegister();
|
||||
RegisterValue switchContext = null;
|
||||
if (baseContextRegister != null) {
|
||||
// Use disassembler context based upon context register value at switch address (i.e., computed jump)
|
||||
// Only use flowing context bits
|
||||
switchContext = programContext.getRegisterValue(baseContextRegister, switchAddr);
|
||||
switchContext = programContext.getFlowValue(switchContext);
|
||||
}
|
||||
|
||||
Listing listing = program.getListing();
|
||||
Address[] cases = table.getCases();
|
||||
Integer[] caseValues = table.getLabelValues();
|
||||
AddressSet disSetList = new AddressSet();
|
||||
|
||||
for (int caseIndex = 0; caseIndex < cases.length; caseIndex++) {
|
||||
Address caseStart = cases[caseIndex];
|
||||
monitor.checkCancelled();
|
||||
|
||||
if (!isDefaultCase(caseValues, caseIndex)) {
|
||||
// only non-default cases should be added to the switching instruction
|
||||
instr.addMnemonicReference(caseStart, flowType, SourceType.ANALYSIS);
|
||||
}
|
||||
|
||||
// if conflict skip case
|
||||
if (listing.getUndefinedDataAt(caseStart) == null) {
|
||||
continue;
|
||||
}
|
||||
// already done
|
||||
if (disSetList.contains(caseStart)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
setSwitchTargetContext(programContext, caseStart, switchContext);
|
||||
}
|
||||
catch (ContextChangeException e) {
|
||||
// This can occur when two or more threads are working on the same function
|
||||
continue;
|
||||
}
|
||||
disSetList.add(caseStart);
|
||||
}
|
||||
|
||||
// do all cases at one time
|
||||
if (!disSetList.isEmpty()) {
|
||||
DisassembleCommand cmd = new DisassembleCommand(disSetList, null, true);
|
||||
cmd.applyTo(program);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if this case index is a default case.
|
||||
*
|
||||
* In general, each case target address should have an associated caseValue.
|
||||
* A case is default if it is first case to not have a case value, or has a magic case value.
|
||||
* It is possible that there could be more than one case without a value. The code shouldn't
|
||||
* blow up if this is the case.
|
||||
*
|
||||
* TODO: Should this check if the default case already has a reference to it
|
||||
* from a conditional jump?
|
||||
*/
|
||||
private boolean isDefaultCase(Integer[] caseValues, int caseIndex) {
|
||||
return (caseIndex == caseValues.length) ||
|
||||
(caseIndex < caseValues.length && caseValues[caseIndex] == DEFAULT_CASE_VALUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the switching instruction has all switch references already.
|
||||
* Extra check for default case target as part of the table, when it shouldn't be.
|
||||
*/
|
||||
public boolean hasAllReferences(TaskMonitor monitor, JumpTable table, Instruction instr,
|
||||
Function containingFunction) throws CancelledException {
|
||||
AddressSetView containingBody =
|
||||
containingFunction != null ? containingFunction.getBody() : null;
|
||||
|
||||
Reference[] referencesFrom = instr.getReferencesFrom();
|
||||
Address[] tableDest = table.getCases();
|
||||
Integer[] caseValues = table.getLabelValues();
|
||||
|
||||
// check that all cases are already a reference on the instruction, except default
|
||||
for (int caseIndex = 0; caseIndex < tableDest.length; caseIndex++) {
|
||||
monitor.checkCancelled();
|
||||
|
||||
// a case is default if it is first case to not have a value, or has a magic case value
|
||||
boolean isDefaultCase = isDefaultCase(caseValues, caseIndex);
|
||||
|
||||
if (containingBody != null && !containingBody.contains(tableDest[caseIndex])) {
|
||||
// switch case missing from owner function's body
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean foundit = false;
|
||||
for (Reference element : referencesFrom) {
|
||||
if (element.getToAddress().equals(tableDest[caseIndex])) {
|
||||
foundit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isDefaultCase) {
|
||||
// default case should not be on switching instruction
|
||||
if (foundit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!foundit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the context that should flow to the target so that target instruction will disassemble correctly
|
||||
*/
|
||||
private void setSwitchTargetContext(ProgramContext programContext, Address targetStart, RegisterValue switchContext) throws ContextChangeException {
|
||||
if (switchContext == null) {
|
||||
return;
|
||||
@@ -236,6 +302,9 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
program.getProgramContext().setRegisterValue(targetStart, targetStart, switchContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* Label switch table, cases, default with labels in namespace of the switch
|
||||
*/
|
||||
private void labelSwitch(JumpTable table, TaskMonitor monitor) throws CancelledException {
|
||||
AddLabelCmd tableNameLabel =
|
||||
new AddLabelCmd(table.getSwitchAddress(), "switchD", SourceType.ANALYSIS);
|
||||
@@ -266,18 +335,24 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
tableNameLabel.applyTo(program);
|
||||
|
||||
Address[] switchCases = table.getCases();
|
||||
Integer[] caseLabels = table.getLabelValues();
|
||||
Symbol[] caseSymbols = new Symbol[caseLabels.length];
|
||||
Integer[] caseValues = table.getLabelValues();
|
||||
Symbol[] caseSymbols = new Symbol[caseValues.length];
|
||||
SymbolTable symTable = program.getSymbolTable();
|
||||
for (int i = 0; i < switchCases.length; i++) {
|
||||
|
||||
for (int caseIndex = 0; caseIndex < switchCases.length; caseIndex++) {
|
||||
monitor.checkCancelled();
|
||||
int offset = (i >= caseLabels.length ? i : caseLabels[i]);
|
||||
String caseName = "caseD_" + Integer.toHexString(offset);
|
||||
if (offset == DEFAULT_CASE_VALUE) { // magic constant to indicate default case
|
||||
|
||||
// if there are more switchCases than switch values, just use the caseIndex
|
||||
int caseValue = (caseIndex < caseValues.length) ? caseValues[caseIndex] : caseIndex;
|
||||
|
||||
boolean isDefaultCase = isDefaultCase(caseValues, caseIndex);
|
||||
|
||||
String caseName = "caseD_" + Integer.toHexString(caseValue);
|
||||
if (isDefaultCase) {
|
||||
caseName = "default";
|
||||
}
|
||||
AddLabelCmd lcmd =
|
||||
new AddLabelCmd(switchCases[i], caseName, space, SourceType.ANALYSIS);
|
||||
new AddLabelCmd(switchCases[caseIndex], caseName, space, SourceType.ANALYSIS);
|
||||
|
||||
Symbol oldSym = symTable.getPrimarySymbol(lcmd.getLabelAddr());
|
||||
if (oldSym != null && oldSym.getSource() == SourceType.ANALYSIS &&
|
||||
@@ -285,8 +360,8 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
// cleanup AddressTableAnalyzer label
|
||||
oldSym.delete();
|
||||
}
|
||||
if (lcmd.applyTo(program) && i < caseSymbols.length) {
|
||||
caseSymbols[i] = symTable.getSymbol(caseName, switchCases[i], space);
|
||||
if (lcmd.applyTo(program) && caseIndex < caseSymbols.length) {
|
||||
caseSymbols[caseIndex] = symTable.getSymbol(caseName, switchCases[caseIndex], space);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,6 +413,9 @@ public class DecompilerSwitchAnalysisCmd extends BackgroundCommand<Program> {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/*
|
||||
* put labels on the switch table used to compute the target addresses of the switch.
|
||||
*/
|
||||
private void labelLoadTable(JumpTable.LoadTable loadtable, Address[] switchCases,
|
||||
Symbol[] caseSymbols, Namespace space, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
/* ###
|
||||
* 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.decompile;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.junit.*;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.plugin.core.analysis.*;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.SymbolicPropogator.Value;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.TaskUtilities;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
/**
|
||||
* Test of DecompilerSwitchAnalyzer
|
||||
*/
|
||||
public class DecompilerSwitchAnalyzerTest extends AbstractGenericTest {
|
||||
|
||||
private ProgramBuilder builder;
|
||||
private Program program;
|
||||
|
||||
private DecompilerSwitchAnalyzer analyzer;
|
||||
|
||||
public DecompilerSwitchAnalyzerTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected void setAnalysisOptions(String optionName) {
|
||||
int txId = program.startTransaction("Analyze");
|
||||
Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
|
||||
analysisOptions.setBoolean(optionName, false);
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultSwitchLabelAndFlow() throws Exception {
|
||||
|
||||
builder = new ProgramBuilder("SwitchDefaultTest", "x86:LE:64:default", "gcc", null);
|
||||
|
||||
//
|
||||
// void main(undefined4 param_1)
|
||||
//
|
||||
// {
|
||||
// switch(param_1) {
|
||||
// case 0:
|
||||
// puts("case 0");
|
||||
// break;
|
||||
// case 1:
|
||||
// puts("case 1");
|
||||
// break;
|
||||
// case 2:
|
||||
// puts("case 2");
|
||||
// break;
|
||||
// case 3:
|
||||
// puts("case 3");
|
||||
// break;
|
||||
// case 4:
|
||||
// puts("case 4");
|
||||
// break;
|
||||
// case 5:
|
||||
// puts("case 5");
|
||||
// break;
|
||||
// case 6:
|
||||
// puts("case 6");
|
||||
// break;
|
||||
// case 7:
|
||||
// puts("case 7");
|
||||
// break;
|
||||
// case 8:
|
||||
// puts("case 8");
|
||||
// break;
|
||||
// case 9:
|
||||
// puts("case 9");
|
||||
// break;
|
||||
// default:
|
||||
// puts("default");
|
||||
// }
|
||||
// /* WARNING: Subroutine does not return */
|
||||
// exit(0);
|
||||
// }
|
||||
|
||||
builder.setBytes("0x101169",
|
||||
"f30f1efa554889" +
|
||||
"e54883ec10897dfc488975f0837dfc09" +
|
||||
"0f87d70000008b45fc488d1485000000" +
|
||||
"00488d05bc0e00008b04024898488d15" +
|
||||
"b00e00004801d03effe0488d05530e00" +
|
||||
"004889c7e8a7feffffe9ae000000488d" +
|
||||
"05460e00004889c7e893feffffe99a00" +
|
||||
"0000488d05390e00004889c7e87ffeff" +
|
||||
"ffe986000000488d052c0e00004889c7" +
|
||||
"e86bfeffffeb75488d05220e00004889" +
|
||||
"c7e85afeffffeb64488d05180e000048" +
|
||||
"89c7e849feffffeb53488d050e0e0000" +
|
||||
"4889c7e838feffffeb42488d05040e00" +
|
||||
"004889c7e827feffffeb31488d05fa0d" +
|
||||
"00004889c7e816feffffeb20488d05f0" +
|
||||
"0d00004889c7e805feffffeb0f488d05" +
|
||||
"e60d00004889c7e8f4fdffffbf000000" +
|
||||
"00e8fafdffff");
|
||||
|
||||
// switch table
|
||||
builder.setBytes("0x102054",
|
||||
"56f1ffff6af1ffff7ef1ffff" +
|
||||
"92f1ffffa3f1ffffb4f1ffffc5f1ffff" +
|
||||
"d6f1ffffe7f1fffff8f1ffff011b033b");
|
||||
|
||||
builder.disassemble("0x101169", 64);
|
||||
|
||||
builder.createFunction("0x101169");
|
||||
|
||||
analyzer = new DecompilerSwitchAnalyzer();
|
||||
|
||||
program = builder.getProgram();
|
||||
program.startTransaction("Test");
|
||||
|
||||
Address codeStart = addr("0x101169");
|
||||
Listing listing = program.getListing();
|
||||
assertNotNull("Bad instruction disassembly", listing.getInstructionAt(codeStart));
|
||||
|
||||
AddressSet addressSet = new AddressSet(codeStart, codeStart.add(0x200));
|
||||
analyze(addressSet);
|
||||
|
||||
Symbol primarySymbol = program.getSymbolTable().getPrimarySymbol(addr("0x0010125d"));
|
||||
assertEquals("Default label set", primarySymbol.getName(), "default");
|
||||
assertEquals("Switch space set", primarySymbol.getParentNamespace().getName(), "switchD_001011a7");
|
||||
|
||||
primarySymbol = program.getSymbolTable().getPrimarySymbol(addr("0x0010124c"));
|
||||
assertEquals("Case label set", primarySymbol.getName(), "caseD_9");
|
||||
assertEquals("Switch space set", primarySymbol.getParentNamespace().getName(), "switchD_001011a7");
|
||||
|
||||
Instruction instr = listing.getInstructionAt(addr("0x001011a7"));
|
||||
assertNoOperandReference(0, instr);
|
||||
assertNoOperandReference(1, instr);
|
||||
assertNumMnemonicReferences(instr, 10);
|
||||
assertMnemonicReferenceTo(instr, addr("0x001011aa"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x001011be"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x001011d2"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x001011e6"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x001011f7"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x00101208"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x00101219"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x0010122a"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x0010123b"));
|
||||
assertMnemonicReferenceTo(instr, addr("0x0010124c"));
|
||||
|
||||
}
|
||||
|
||||
private void assertNoOperandReference(int opIndex, Instruction instr) {
|
||||
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||
assertEquals("No reference on operand " + opIndex, 0, refs.length);
|
||||
}
|
||||
|
||||
private void assertNumOperandReferences(int opIndex, Instruction instr, int num) {
|
||||
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||
assertEquals("Operand " + opIndex + " num refs", num, refs.length);
|
||||
}
|
||||
|
||||
private void assertNumMnemonicReferences(Instruction instr, int num) {
|
||||
Reference[] refs = instr.getMnemonicReferences();
|
||||
assertEquals("Mnemonic num refs", num, refs.length);
|
||||
}
|
||||
|
||||
private void assertMnemonicReferenceTo(Instruction instr, Address to) {
|
||||
Reference[] refs = instr.getMnemonicReferences();
|
||||
boolean found = false;
|
||||
for (Reference reference : refs) {
|
||||
if (reference.getToAddress().equals(to)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue("Missing Mnemonic Reference " + to + " on " + instr, found);
|
||||
}
|
||||
|
||||
private void assertOperandReferenceTo(int opIndex, Instruction instr, Address to) {
|
||||
Reference[] refs = instr.getOperandReferences(opIndex);
|
||||
boolean found = false;
|
||||
for (Reference reference : refs) {
|
||||
if (reference.getToAddress().equals(to)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue("Missing Reference " + to + " on " + instr, found);
|
||||
}
|
||||
|
||||
private void analyze(AddressSet addrs) throws Exception {
|
||||
analyzer.added(program, addrs, TaskMonitor.DUMMY, null);
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
return builder.addr(address);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user