Merge remote-tracking branch 'origin/patch'

This commit is contained in:
ghidra1 2023-07-10 19:54:55 -04:00
commit f8ec071906
6 changed files with 200 additions and 68 deletions

View File

@ -33,6 +33,12 @@
href="help/topics/DebuggerControlPlugin/DebuggerControlPlugin.html#emu_actions">emulator
controls</A> can then be used.</P>
<P>To control the initial stack allocation, create a <CODE>STACK</CODE> block in the target
program database before emulating. If the stack is already in the target image's memory map,
create an overlay block named <CODE>STACK</CODE>. This will initialize the stack pointer
without modifying the emulator's memory map. Note that customizing the stack initialization may
prevent you from adding a second thread.</P>
<H3><A name="add_emulated_thread"></A> Add Emulated Thread</H3>
<P>This action is available whenever a "pure emulation" trace is active. It spawns a new thread

View File

@ -510,14 +510,14 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
if (!block.isExecute()) {
return;
}*/
ProgramLocation progLoc =
staticMappings.getOpenMappedLocation(new DefaultTraceLocation(view.getTrace(), null,
Lifespan.at(view.getSnap()), tracePc));
Program program = progLoc == null ? null : progLoc.getProgram();
Address programPc = progLoc == null ? null : progLoc.getAddress();
long snap =
view.getViewport().getOrderedSnaps().stream().filter(s -> s >= 0).findFirst().get();
ProgramLocation progLoc = staticMappings.getOpenMappedLocation(
new DefaultTraceLocation(trace, null, Lifespan.at(snap), tracePc));
Program program = progLoc == null ? null : progLoc.getProgram();
Address programPc = progLoc == null ? null : progLoc.getAddress();
TraceThread thread = ProgramEmulationUtils.launchEmulationThread(trace, snap, program,
tracePc, programPc);
traceManager.activateThread(thread);

View File

@ -43,8 +43,7 @@ import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.thread.*;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.DifferenceAddressSetView;
import ghidra.util.NumericUtilities;
import ghidra.util.*;
import ghidra.util.exception.DuplicateNameException;
/**
@ -56,6 +55,7 @@ import ghidra.util.exception.DuplicateNameException;
*/
public enum ProgramEmulationUtils {
;
public static final String BLOCK_NAME_STACK = "STACK";
/**
* Conventional prefix for first snapshot to identify "pure emulation" traces.
@ -227,10 +227,10 @@ public enum ProgramEmulationUtils {
* @param program the program whose context to use
* @param tracePc the program counter in the trace's memory map
* @param programPc the program counter in the program's memory map
* @param stack optionally, the region representing the thread's stack
* @param stack optionally, the range for the thread's stack allocation
*/
public static void initializeRegisters(Trace trace, long snap, TraceThread thread,
Program program, Address tracePc, Address programPc, TraceMemoryRegion stack) {
Program program, Address tracePc, Address programPc, AddressRange stack) {
TraceMemoryManager memory = trace.getMemoryManager();
if (thread instanceof TraceObjectThread ot) {
TraceObject object = ot.getObject();
@ -283,28 +283,92 @@ public enum ProgramEmulationUtils {
}
}
public static AddressRange allocateStackCustom(Trace trace, long snap, TraceThread thread,
Program program) {
if (program == null) {
return null;
}
AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
MemoryBlock stackBlock = program.getMemory().getBlock(BLOCK_NAME_STACK);
if (stackBlock == null) {
return null;
}
if (space != stackBlock.getStart().getAddressSpace().getPhysicalSpace()) {
Msg.showError(ProgramEmulationUtils.class, null, "Invalid STACK block",
"The STACK block must be in the stack's base space. Ignoring.");
return null;
}
AddressRange alloc = new AddressRangeImpl(
stackBlock.getStart().getPhysicalAddress(),
stackBlock.getEnd().getPhysicalAddress());
if (stackBlock.isOverlay() || DebuggerStaticMappingUtils.isReal(stackBlock)) {
return alloc;
}
PathPattern patRegion = computePatternRegion(trace);
String path = PathUtils.toString(
patRegion.applyKeys(stackBlock.getStart() + "-STACK")
.getSingletonPath());
TraceMemoryManager mm = trace.getMemoryManager();
try {
return mm.createRegion(path, snap, alloc,
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange();
}
catch (TraceOverlappedRegionException e) {
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
("The STACK region %s conflicts with another: %s. " +
"You may need to initialize the stack pointer manually.").formatted(
alloc, e.getConflicts().iterator().next()));
return alloc;
}
catch (DuplicateNameException e) {
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
("A region already exists with the same name: %s. " +
"You may need to initialize the stack pointer manually.")
.formatted(path));
return alloc;
}
}
/**
* Attempt to allocate a new stack region for the given thread
*
* <p>
* If successful, this will create a dynamic memory region representing the stack. If the stack
* is specified by an override (STACK block) in the program, and that block overlays the image,
* then no region is created.
*
* @param trace the trace containing the stack and thread
* @param snap the creation snap for the new region
* @param thread the thread for which the stack is being allocated
* @param program the program being emulated (to check for stack allocation override)
* @param size the desired size of the region
* @return the new region representing the allocated stack
* @return the range allocated for the stack
*
* @throws EmulatorOutOfMemoryException if the stack cannot be allocated
*/
public static TraceMemoryRegion allocateStack(Trace trace, long snap, TraceThread thread,
long size) {
public static AddressRange allocateStack(Trace trace, long snap, TraceThread thread,
Program program, long size) {
AddressRange custom = allocateStackCustom(trace, snap, thread, program);
if (custom != null) {
return custom;
}
// Otherwise, just search for an un-allocated block of the given size.
AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
AddressSet except0 = new AddressSet(space.getAddress(0x1000), space.getMaxAddress());
Address max = space.getMaxAddress();
AddressSet eligible;
if (max.getOffsetAsBigInteger().compareTo(BigInteger.valueOf(0x1000)) < 0) {
eligible = new AddressSet(space.getMinAddress(), max);
}
else {
eligible = new AddressSet(space.getAddress(0x1000), max);
}
TraceMemoryManager mm = trace.getMemoryManager();
AddressSetView left =
new DifferenceAddressSetView(except0, mm.getRegionsAddressSet(snap));
new DifferenceAddressSetView(eligible, mm.getRegionsAddressSet(snap));
PathPattern patRegion = computePatternRegion(trace);
try {
for (AddressRange candidate : left) {
if (Long.compareUnsigned(candidate.getLength(), size) > 0) {
if (Long.compareUnsigned(candidate.getLength(), size) >= 0) {
AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size);
String threadName = PathUtils.isIndex(thread.getName())
? PathUtils.parseIndex(thread.getName())
@ -313,7 +377,7 @@ public enum ProgramEmulationUtils {
patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName)
.getSingletonPath());
return mm.createRegion(path, snap, alloc,
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange();
}
}
}
@ -373,7 +437,15 @@ public enum ProgramEmulationUtils {
public static TraceThread doLaunchEmulationThread(Trace trace, long snap, Program program,
Address tracePc, Address programPc) {
TraceThread thread = spawnThread(trace, snap);
TraceMemoryRegion stack = allocateStack(trace, snap, thread, 0x4000);
AddressRange stack;
try {
stack = allocateStack(trace, snap, thread, program, 0x4000);
}
catch (EmulatorOutOfMemoryException e) {
Msg.warn(ProgramEmulationUtils.class,
"Cannot allocate a stack. Please initialize manually.");
stack = null;
}
initializeRegisters(trace, snap, thread, program, tracePc, programPc, stack);
return thread;
}

View File

@ -381,7 +381,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
assertTrue(result.error() instanceof DecodePcodeExecutionException);
long scratch = result.snapshot();
assertEquals(new BigInteger("003ffffe", 16), regs.getViewValue(scratch, regPC).getUnsignedValue());
assertEquals(new BigInteger("003ffffe", 16),
regs.getViewValue(scratch, regPC).getUnsignedValue());
}
@Test
@ -715,4 +716,34 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
assertEquals(new BigInteger("5678", 16),
regs.getViewValue(scratch, regR2).getUnsignedValue());
}
@Test
public void testCustomStack() throws Exception {
createProgram();
intoProject(program);
Memory memory = program.getMemory();
Address addrText = addr(program, 0x00400000);
Register regSP = program.getRegister("sp");
try (Transaction tx = program.openTransaction("Initialize")) {
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
(byte) 0, TaskMonitor.DUMMY, false);
blockText.setExecute(true);
memory.createUninitializedBlock("STACK", addr(program, 0x00001234), 0x1000, false);
}
programManager.openProgram(program);
waitForSwing();
codeBrowser.goTo(new ProgramLocation(program, addrText));
waitForSwing();
assertTrue(emulationPlugin.actionEmulateProgram.isEnabled());
performAction(emulationPlugin.actionEmulateProgram);
Trace trace = traceManager.getCurrentTrace();
assertNotNull(trace);
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
assertEquals(new BigInteger("2234", 16), regs.getViewValue(0, regSP).getUnsignedValue());
}
}

View File

@ -36,8 +36,7 @@ import ghidra.app.script.GhidraScript;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
@ -56,6 +55,9 @@ public class WindowsResourceReference extends GhidraScript {
ArrayList<ArrayList<PcodeOp>> defUseLists = new ArrayList<>();
protected AddressSetPropertyMap alreadyDoneAddressSetPropertyMap;
// set of functions that decompilation failed on
protected AddressSet badDecompFunctions = new AddressSet();
public AddressSetPropertyMap getOrCreatePropertyMap(Program program, String mapName) {
if (alreadyDoneAddressSetPropertyMap != null) {
@ -367,6 +369,11 @@ public class WindowsResourceReference extends GhidraScript {
if (f == null) {
return;
}
// check if decompilation of this function failed previously
if (badDecompFunctions.contains(f.getEntryPoint())) {
return;
}
Instruction instr = prog.getListing().getInstructionAt(refAddr);
if (instr == null) {
@ -376,7 +383,9 @@ public class WindowsResourceReference extends GhidraScript {
decompileFunction(f, decompiler);
if (hfunction == null) {
return; // failed to decompile
// failed to decompile, add to bad list
badDecompFunctions.add(f.getEntryPoint());
return;
}
Iterator<PcodeOpAST> ops = hfunction.getPcodeOps(refAddr);

View File

@ -434,6 +434,15 @@ macro complement(op)
setNZFlags(op);
}
# Signed shift right.
# P-code INT_SRIGHT.
macro arithmeticShiftRight(op)
{
$(C) = op & 1;
op = (op s>> 1);
setNZFlags(op);
}
macro logicalShiftRight(op)
{
$(C) = op & 1;
@ -450,31 +459,25 @@ macro rotateRightWithCarry(op)
setNZFlags(op);
}
macro logicalShiftLeft(op)
{
local tmp = (op >> 7);
$(C) = tmp;
op = op << 1;
$(V) = tmp ^ (op >> 7);
setNZFlags(op);
}
macro rotateLeftWithCarry(op)
{
local carryIn = $(C);
$(C) = op >> 7;
local tmp = (op >> 7);
$(C) = tmp;
op = (op << 1) | carryIn;
$(V) = tmp ^ (op >> 7);
setNZFlags(op);
}
# Signed shift right.
# P-code INT_SRIGHT.
macro arithmeticShiftRight(op)
{
$(C) = op & 1;
op = (op s>> 1);
setNZFlags(op);
}
macro logicalShiftLeft(op)
{
$(C) = (op >> 7);
op = op << 1;
$(Z) = (op == 0);
$(N) = (op >> 7);
}
macro increment(op)
{
$(V) = (op == 0x7F);
@ -505,56 +508,69 @@ macro clear(op)
macro addition(reg, op)
{
$(C) = carry(reg, op);
$(V) = scarry(reg, op);
local tmp = reg;
local val = op;
$(C) = carry(tmp, val);
$(V) = scarry(tmp, val);
reg = reg + op;
tmp = tmp + val;
setNZFlags(reg);
setNZFlags(tmp);
reg = tmp;
}
macro additionWithCarry(reg, op)
{
local carryIn = zext($(C));
local tmp = reg;
local val = op;
local mask = 0x0F; # Low nibble mask
local tmpResult = reg + op;
local result = tmp + val;
$(H) = (((reg & mask) + (op & mask) + carryIn) >> 4) & 1;
$(C) = carry(reg, op) || carry(tmpResult, carryIn);
$(V) = scarry(reg, op) ^^ scarry(tmpResult, carryIn);
$(H) = (((tmp & mask) + (val & mask) + carryIn) >> 4) & 1;
$(C) = carry(tmp, val) || carry(result, carryIn);
$(V) = scarry(tmp, val) ^^ scarry(result, carryIn);
reg = tmpResult + carryIn;
tmp = result + carryIn;
setNZFlags(reg);
setNZFlags(tmp);
reg = tmp;
}
macro subtraction(reg, op)
{
$(V) = sborrow(reg, op);
reg = reg - op;
setNZFlags(reg);
$(C) = (reg < op);
local tmp = reg;
local val = op;
$(V) = sborrow(tmp, val);
$(C) = (tmp < val);
tmp = tmp - val;
setNZFlags(tmp);
reg = tmp;
}
macro subtractionWithCarry(reg, op)
{
local carryIn = zext($(C));
local tmpResult = reg - op;
local tmp = reg;
local val = op;
local tmpResult = tmp - val;
$(C) = (reg < op) || (tmpResult < carryIn);
$(V) = sborrow(reg, op) ^^ sborrow(tmpResult, carryIn);
reg = tmpResult - carryIn;
setNZFlags(reg);
$(C) = (tmp < val) || (tmpResult < carryIn);
$(V) = sborrow(tmp, val) ^^ sborrow(tmpResult, carryIn);
tmp = tmpResult - carryIn;
setNZFlags(tmp);
reg = tmp;
}
macro compare(reg, op)
{
$(V) = sborrow(reg, op);
local tmp = reg - op;
local tmp = reg;
local val = op;
$(V) = sborrow(tmp, val);
$(C) = (tmp < val);
tmp = tmp - val;
setNZFlags(tmp);
$(C) = (tmp < op);
reg = tmp;
}
macro logicalAnd(reg, op)
@ -609,9 +625,8 @@ macro Push1(reg, op)
# Push 2 byte operand op2
macro Push2(reg, op)
{
reg = reg - 1;
reg = reg - 2;
*:2 reg = op;
reg = reg - 1;
}
# Pull 1 byte operand op1
@ -624,9 +639,8 @@ macro Pull1(reg, op)
# Pull 2 byte operand op2
macro Pull2(reg, op)
{
reg = reg + 1;
op = *:2 reg;
reg = reg + 1;
reg = reg + 2;
}
macro PushUYXDpD()
@ -1204,7 +1218,7 @@ macro PushEntireState()
:ADDB OP1 is (op=0xCB | op=0xDB | op=0xEB | op=0xFB) ... & OP1
{
setHFlag(A, OP1);
setHFlag(B, OP1);
addition(B, OP1);
}