Merge remote-tracking branch 'origin/GP-3437_Dan_emuStackInit--SQUASHED'

into patch (Closes #5331)
This commit is contained in:
ghidra1
2023-07-10 19:31:52 -04:00
4 changed files with 127 additions and 18 deletions
@@ -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
@@ -512,14 +512,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);
@@ -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;
}
@@ -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());
}
}