Merge branch 'GP-6078_emteere_16bitStack' into Ghidra_12.0

This commit is contained in:
ghidra1
2025-11-07 09:31:11 -05:00
9 changed files with 285 additions and 44 deletions

View File

@@ -583,6 +583,9 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
if (local_offset == offset) {
return opIndex;
}
if ((local_offset & 0xffff) == offset) {
return opIndex;
}
}
}
return -1;

View File

@@ -104,13 +104,8 @@ class AnalyzeStackRefsAction extends ListingContextAction {
doParameterAnalysis = options.getBoolean("Create Param Variables", doParameterAnalysis);
BackgroundCommand<Program> cmd = null;
if (doNewStackAnalysis) {
cmd = new NewFunctionStackAnalysisCmd(funcSet, doParameterAnalysis, doLocalAnalysis,
cmd = new NewFunctionStackAnalysisCmd(funcSet, doParameterAnalysis, doLocalAnalysis,
true);
}
else {
cmd = new FunctionStackAnalysisCmd(funcSet, doParameterAnalysis, doLocalAnalysis, true);
}
funcPlugin.execute(program, cmd);
}

View File

@@ -17,14 +17,12 @@ package ghidra.app.plugin.core.function;
import java.util.*;
import ghidra.app.cmd.function.FunctionStackAnalysisCmd;
import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
@@ -41,7 +39,6 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
protected static final int MAX_THREAD_COUNT_OPTION_DEFAULT_VALUE = 2;
protected int maxThreadCount = MAX_THREAD_COUNT_OPTION_DEFAULT_VALUE;
private boolean doNewStackAnalysis = true;
private boolean doCreateLocalStackVars = true;
private boolean doCreateStackParams = false;
@@ -80,14 +77,8 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
final TaskMonitor monitor) throws CancelledException {
BackgroundCommand<Program> cmd;
if (doNewStackAnalysis) {
cmd = new NewFunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars,
cmd = new NewFunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars,
false);
}
else {
cmd = new FunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars,
false);
}
cmd.applyTo(program, monitor);
return EMPTY_ADDRESS_SET;
@@ -120,23 +111,8 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
}
}
// private boolean useOldStackAnalysisByDefault(Program program) {
// Language language = program.getLanguage();
// if (language.getProcessor().equals(Processor.findOrPossiblyCreateProcessor("x86"))) {
// if (language.getLanguageDescription().getSize() == 16) {
// // Prefer using old stack analysis for x86 16-bit with segmented addresses
// return true;
// }
// }
// return false;
// }
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS,
true, null,
"Use General Stack Reference Propogator (This works best on most processors)");
options.registerOption("Create Local Variables", doCreateLocalStackVars, null,
"Create Function Local stack variables and references");
@@ -149,10 +125,6 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
@Override
public void optionsChanged(Options options, Program program) {
doNewStackAnalysis =
options.getBoolean(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS,
true);
doCreateLocalStackVars =
options.getBoolean("Create Local Variables", doCreateLocalStackVars);

View File

@@ -228,7 +228,7 @@ class FunctionsXmlMgr {
parser.end(element);
FunctionPurgeAnalysisCmd purgeAnalysisCmd = new FunctionPurgeAnalysisCmd(functions);
purgeAnalysisCmd.applyTo(program, monitor);
FunctionStackAnalysisCmd stackAnalysisCmd = new FunctionStackAnalysisCmd(functions, true);
NewFunctionStackAnalysisCmd stackAnalysisCmd = new NewFunctionStackAnalysisCmd(functions, true);
stackAnalysisCmd.applyTo(program, monitor);
}

View File

@@ -886,6 +886,15 @@ public class SymbolicPropogator {
}
vContext.copy(out, in[0], mustClearAll, evaluator);
break;
case PcodeOp.SEGMENTOP:
// treat like a copy for now, and extend the size as if segment had been applied
Varnode vval = context.getValue(in[2], evaluator);
if (context.isSymbolicSpace(vval.getSpace())) {
vval = vContext.createVarnode(vval.getOffset(), vval.getSpace(), out.getSize());
}
vContext.putValue(out, vval, mustClearAll);
break;
case PcodeOp.LOAD:
Varnode memVal = null;
@@ -1843,7 +1852,7 @@ public class SymbolicPropogator {
for (int i = 1; i < ins.length; i++) {
Varnode vval = context.getValue(ins[i], evaluator);
if (vval == null || !context.isConstant(vval)) {
return null;
return checkSegmentCallOther(payload, instr, ins, out);
}
inputs.add(vval);
}
@@ -1867,6 +1876,29 @@ public class SymbolicPropogator {
return null;
}
private PcodeOp[] checkSegmentCallOther(InjectPayload payload, Instruction instr, Varnode[] ins, Varnode out) {
if (!payload.getName().equals("segment_pcode")) {
return null;
}
if (ins.length != 3) {
return null;
}
Varnode vval = context.getValue(ins[2], evaluator);
if (vval == null) {
return null;
}
if (!context.isSymbolicSpace(vval.getSpace())) {
return null;
}
if (!context.isRegister(ins[1])) {
return null;
}
// morph segment into COPY as long as still symbolic
PcodeOp[] newop = new PcodeOp[1];
newop[0] = new PcodeOp(instr.getAddress(), 1, PcodeOp.SEGMENTOP, ins, out);
return newop;
}
private InjectPayload findPcodeInjection(Program prog, PcodeInjectLibrary snippetLibrary,
long callOtherIndex) {
InjectPayload payload = injectPayloadCache.get(callOtherIndex);

View File

@@ -35,7 +35,7 @@ import docking.test.AbstractDockingTest;
import docking.widgets.dialogs.ReadTextDialog;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.cmd.function.FunctionStackAnalysisCmd;
import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd;
import ghidra.app.merge.AbstractMergeTest;
import ghidra.app.merge.ProgramMultiUserMergeManager;
import ghidra.app.merge.tool.ListingMergePanel;
@@ -364,7 +364,7 @@ public abstract class AbstractListingMergeManagerTest extends AbstractMergeTest
// TODO For thunk functions need to call thunk analyzer here before
// stack analysis occurs
}
FunctionStackAnalysisCmd analyzeCmd = new FunctionStackAnalysisCmd(addr, true);
NewFunctionStackAnalysisCmd analyzeCmd = new NewFunctionStackAnalysisCmd(addr, true);
assertTrue("Failed to analyze stack for " + name + " @ " + addr,
analyzeCmd.applyTo(program));
}

View File

@@ -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.
@@ -22,7 +22,7 @@ import org.junit.Test;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.cmd.function.FunctionStackAnalysisCmd;
import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.ProgramModifierListener;
import ghidra.program.model.address.Address;
@@ -154,7 +154,7 @@ public class SymbolMergeManager3Test extends AbstractListingMergeManagerTest {
functionCmd.applyTo(program));
Function newFunction = program.getFunctionManager().getFunctionAt(addr);
assertNotNull(newFunction);
FunctionStackAnalysisCmd analyzeCmd = new FunctionStackAnalysisCmd(addr, true);
NewFunctionStackAnalysisCmd analyzeCmd = new NewFunctionStackAnalysisCmd(addr, true);
assertTrue("Failed to analyze stack for " + name + " @ " + addr,
analyzeCmd.applyTo(program));
}

View File

@@ -0,0 +1,239 @@
/* ###
* 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.cmd.function;
import static org.junit.Assert.*;
import java.math.BigInteger;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.core.analysis.ConstantPropagationAnalyzer;
import ghidra.app.plugin.core.analysis.ConstantPropagationContextEvaluator;
import ghidra.app.plugin.core.function.StackVariableAnalyzer;
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.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.util.SymbolicPropogator.Value;
import ghidra.util.task.TaskMonitor;
/**
* quick and dirty test of the ProgramContextImpl just to see
* if the values are being set for specified address range.
* The ProgramContextPlugin will be a more complete test
* program, with a gui interface to specify the values and
* select/highlight an address range.
*/
public class FunctionStackAnalysisCmdTest extends AbstractGenericTest {
private ProgramBuilder builder;
private Program program;
private StackVariableAnalyzer analyzer;
public FunctionStackAnalysisCmdTest() {
super();
}
public void do16bitTest(String processor) throws Exception {
builder = new ProgramBuilder("ProtectedMode", processor);
/**
* undefined __cdecl16near FUN_1000_00cc()
1000:00cc 45 0 INC BP
1000:00cd 55 0 PUSH BP
1000:00ce 8b ec 002 MOV BP,SP
1000:00d0 56 002 PUSH SI
1000:00d1 57 004 PUSH DI
1000:00d2 8a 46 f0 006 MOV AL,byte ptr [BP + local_12]
1000:00d5 b4 4c 006 MOV AH,0x4c
1000:00d7 8b 56 08 006 MOV DX,word ptr [BP + Stack[0x6]]
1000:00da 8b 46 06 006 MOV AX,word ptr [BP + Stack[0x4]]
1000:00dd ff 76 08 006 PUSH word ptr [BP + Stack[0x6]]
1000:00e0 ff 76 ee 008 PUSH word ptr [BP + local_14]
1000:00e3 83 7e 06 00 00a CMP word ptr [BP + Stack[0x4]],0x0
1000:00e7 dd 46 04 00a FLD double ptr [BP + Stack[0x2]]
1000:00ea 07 00a POP ES
1000:00eb c3 008 RET
*/
builder.setBytes("0x1000:00cc",
"45 55 8b ec 56 57 8a 46 f0 b4 4c 8b 56 08 8b 46 06 ff 76 08 ff 76 ee 83 7e 06 00 dd 46 04 07 c3");
builder.disassemble("0x1000:0x00CC", 48);
analyzer = new StackVariableAnalyzer();
program = builder.getProgram();
program.startTransaction("Test");
Address codeStart = addr("0x1000:0x00CC");
Listing listing = program.getListing();
assertNotNull("Bad instruction disassembly", listing.getInstructionAt(codeStart));
builder.createFunction("0x1000:0x00cc");
Instruction instr = listing.getInstructionAt(addr("0x1000:0x00d2"));
assertNoOperandReference(0, instr);
assertNoOperandReference(1, instr);
AddressSet addressSet = new AddressSet(codeStart, codeStart.add(48));
analyze(addressSet);
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(0x12));
instr = listing.getInstructionAt(addr("0x1000:0x00d7"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x6"));
instr = listing.getInstructionAt(addr("0x1000:0x00da"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x4"));
instr = listing.getInstructionAt(addr("0x1000:0x00dd"));
assertNoOperandReference(1, instr);
assertOperandReferenceTo(0, instr, addr("stack:0x6"));
instr = listing.getInstructionAt(addr("0x1000:0x00e0"));
assertNoOperandReference(1, instr);
assertOperandReferenceTo(0, instr, addr("stack:0x0").subtract(0x14));
instr = listing.getInstructionAt(addr("0x1000:0x00e3"));
assertNoOperandReference(1, instr);
assertOperandReferenceTo(0, instr, addr("stack:0x4"));
instr = listing.getInstructionAt(addr("0x1000:0x00e7"));
assertOperandReferenceTo(0, instr, addr("stack:0x2"));
}
@Test
public void testOperandRef_x86ProtectedMode() throws Exception {
do16bitTest("x86:LE:16:Protected Mode");
}
@Test
public void testOperandRef_x86RealMode() throws Exception {
do16bitTest("x86:LE:16:Real Mode");
}
@Test
public void testMIPS64() throws Exception {
builder = new ProgramBuilder("MIPS", "MIPS:LE:32:default");
/**
* undefined FUN_9d010b2c()
9d010b2c c0 ff bd 27 0 addiu sp,sp,-0x40
9d010b30 24 00 b0 af 040 sw s0,local_1c(sp)
9d010b34 01 a0 10 3c 040 lui s0,0xa001
9d010b38 40 5c 02 26 040 addiu v0,s0,0x5c40
9d010b3c 14 00 42 8c 040 lw v0,0x14(v0)=>DAT_a0015c54
9d010b40 3c 00 bf af 040 sw ra,local_4(sp)
9d010b44 31 00 43 2c 040 sltiu v1,v0,0x31
9d010b48 38 00 b5 af 040 sw s5,local_8(sp)
9d010b4c 34 00 b4 af 040 sw s4,local_c(sp)
9d010b50 30 00 b3 af 040 sw s3,local_10(sp)
9d010b54 2c 00 b2 af 040 sw s2,local_14(sp)
9d010b58 b7 05 60 10 040 beq v1,zero,switchD_9d010b74
9d010b5c 28 00 b1 af _sw s1,local_18(sp)
9d010b60 01 9d 03 3c 040 lui v1,0x9d01
9d010b64 80 10 02 00 040 sll v0,v0,0x2
9d010b68 7c 0b 63 24 040 addiu v1,v1,0xb7c
9d010b6c 21 10 62 00 040 addu v0,v1,v0
9d010b70 00 00 42 8c 040 lw v0,0x0(v0)
9d010b74 08 00 40 00 040 jr v0
*/
String funcStartAddrString = "0x9d010b2c";
builder.setBytes(funcStartAddrString,
"c0 ff bd 27 24 00 b0 af 01 a0 10 3c 40 5c 02 26 14 00 42 8c 3c 00 bf af 31 00 43 2c 38 00 b5 af 34 "
+ "00 b4 af 30 00 b3 af 2c 00 b2 af b7 05 60 10 28 00 b1 af 01 9d 03 3c 80 10 02 00 7c 0b 63 24 21 "
+ "10 62 00 00 00 42 8c 08 00 40 00");
builder.disassemble(funcStartAddrString, 72);
analyzer = new StackVariableAnalyzer();
program = builder.getProgram();
program.startTransaction("Test");
Address codeStart = addr(funcStartAddrString);
Listing listing = program.getListing();
assertNotNull("Bad instruction disassembly", listing.getInstructionAt(codeStart));
builder.createFunction(funcStartAddrString);
Instruction instr = listing.getInstructionAt(addr("0x9d010b30"));
assertNoOperandReference(0, instr);
assertNoOperandReference(1, instr);
AddressSet addressSet = new AddressSet(codeStart, codeStart.add(72));
analyze(addressSet);
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(0x1c));
// should not create any memory refs, is stack analyzer
instr = listing.getInstructionAt(addr("0x9d010b3c"));
assertNoOperandReference(0, instr);
assertNoOperandReference(1, instr);
instr = listing.getInstructionAt(addr("0x9d010b40"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(4));
instr = listing.getInstructionAt(addr("0x9d010b48"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(8));
instr = listing.getInstructionAt(addr("0x9d010b4c"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(12));
// delay slot stack reference case
instr = listing.getInstructionAt(addr("0x9d010b5c"));
assertNoOperandReference(0, instr);
assertOperandReferenceTo(1, instr, addr("stack:0x0").subtract(0x18));
}
private void assertNoOperandReference(int opIndex, Instruction instr) {
Reference[] refs = instr.getOperandReferences(opIndex);
assertEquals("No reference on operand " + opIndex, 0, refs.length);
}
private void assertOperandReferenceTo(int opIndex, Instruction instr, Address to) {
Reference[] refs = instr.getOperandReferences(opIndex);
assertEquals("Operand " + opIndex + " num refs", 1, refs.length);
assertEquals("Operand " + opIndex + " Ref Check", to, refs[0].getToAddress());
}
private void analyze(AddressSet addrs) throws Exception {
analyzer.added(program, addrs, TaskMonitor.DUMMY, null);
}
private Address addr(String address) {
return builder.addr(address);
}
}

View File

@@ -433,8 +433,8 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer {
if (fixupFunc.hasNoReturn()) {
return fixupFunc;
}
return null;
}
return null;
}
}