mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-20 21:34:44 +08:00
Merge remote-tracking branch 'origin/GP-3437_Dan_emuStackInit--SQUASHED'
into patch (Closes #5331)
This commit is contained in:
+6
@@ -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
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
+84
-12
@@ -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;
|
||||
}
|
||||
|
||||
+32
-1
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user