Merge remote-tracking branch 'origin/GP-4834_Dan_initEmuSpFromContext'

(Closes #6427)
This commit is contained in:
Ryan Kurtz 2024-08-13 09:48:06 -04:00
commit 4a90c53c0e
2 changed files with 158 additions and 15 deletions

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -390,8 +390,108 @@ public class ProgramEmulationUtils {
} }
} }
public static AddressRange allocateStackCustom(Trace trace, long snap, TraceThread thread, /**
Program program) { * Attempt allocation of the stack using the program context and the initial PC.
*
* <p>
* This examines the program context for a stack pointer value at the thread's initial program
* counter. If it has a value, this computes a range, based on the expected stack growth
* direction, of the specified size. If the range would wrap, it is truncated toe the space's
* bounds. This then attempts to create a region at the computed range to allocate the stack. If
* it already exists, an error dialog is presented, but the SP is still initialized as
* specified.
*
* @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
* @param programPc the program counter in the program's memory map, in case SP is given by the
* program context
* @return the range allocated for the stack, or null if no SP value is set
*/
public static AddressRange allocateStackCustomByContext(Trace trace, long snap,
TraceThread thread, Program program, long size, Address programPc) {
if (program == null) {
return null;
}
ProgramContext ctx = program.getProgramContext();
CompilerSpec cSpec = trace.getBaseCompilerSpec();
Register sp = cSpec.getStackPointer();
RegisterValue spVal = ctx.getRegisterValue(sp, programPc);
if (spVal == null || !spVal.hasValue()) {
return null;
}
Address spAddr = cSpec.getStackBaseSpace().getAddress(spVal.getUnsignedValue().longValue());
final AddressRange alloc;
if (cSpec.stackGrowsNegative()) {
Address max = spAddr.subtractWrap(1);
Address min = spAddr.subtractWrapSpace(size);
if (min.compareTo(max) > 0) {
alloc = new AddressRangeImpl(max.getAddressSpace().getMinAddress(), max);
}
else {
alloc = new AddressRangeImpl(min, max);
}
}
else {
Address min = spAddr;
Address max = spAddr.addWrap(size - 1);
if (min.compareTo(max) > 0) {
alloc = new AddressRangeImpl(min, min.getAddressSpace().getMaxAddress());
}
else {
alloc = new AddressRangeImpl(min, max);
}
}
PathPattern patRegion = computePatternRegion(trace);
String threadName = PathUtils.isIndex(thread.getName())
? PathUtils.parseIndex(thread.getName())
: thread.getName();
String path = PathUtils.toString(
patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName)
.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 allocation of the stack using the program's STACK block.
*
* <p>
* This tries to find a block named STACK in the emulated program. If it finds one, it will
* attempt to create a region in the trace at the mapped dynamic location. It's possible (likely
* even, for a multi-threaded emulation session) that the region already exists. In that case,
* an error dialog is displayed, but the stack pointer is still initialized to the block.
*
* @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)
* @return the range allocated for the stack, or null if no STACK block exists
*/
public static AddressRange allocateStackCustomByBlock(Trace trace, long snap,
TraceThread thread, Program program) {
if (program == null) { if (program == null) {
return null; return null;
} }
@ -441,23 +541,30 @@ public class ProgramEmulationUtils {
* *
* <p> * <p>
* If successful, this will create a dynamic memory region representing the stack. If the stack * 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, * is specified by an override (SP register context or STACK block) in the program, and that
* then no region is created. * block overlays the image, then no region is created, but the range is still returned.
* *
* @param trace the trace containing the stack and thread * @param trace the trace containing the stack and thread
* @param snap the creation snap for the new region * @param snap the creation snap for the new region
* @param thread the thread for which the stack is being allocated * @param thread the thread for which the stack is being allocated
* @param program the program being emulated (to check for stack allocation override) * @param program the program being emulated (to check for stack allocation override)
* @param size the desired size of the region * @param size the desired size of the region
* @param programPc the program counter in the program's memory map, in case SP is given by the
* program context
* @return the range allocated for the stack * @return the range allocated for the stack
* *
* @throws EmulatorOutOfMemoryException if the stack cannot be allocated * @throws EmulatorOutOfMemoryException if the stack cannot be allocated
*/ */
public static AddressRange allocateStack(Trace trace, long snap, TraceThread thread, public static AddressRange allocateStack(Trace trace, long snap, TraceThread thread,
Program program, long size) { Program program, long size, Address programPc) {
AddressRange custom = allocateStackCustom(trace, snap, thread, program); AddressRange customByContext =
if (custom != null) { allocateStackCustomByContext(trace, snap, thread, program, size, programPc);
return custom; if (customByContext != null) {
return customByContext;
}
AddressRange customByBlock = allocateStackCustomByBlock(trace, snap, thread, program);
if (customByBlock != null) {
return customByBlock;
} }
// Otherwise, just search for an un-allocated block of the given size. // Otherwise, just search for an un-allocated block of the given size.
AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace(); AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
@ -565,7 +672,7 @@ public class ProgramEmulationUtils {
TraceThread thread = spawnThread(trace, snap); TraceThread thread = spawnThread(trace, snap);
AddressRange stack; AddressRange stack;
try { try {
stack = allocateStack(trace, snap, thread, program, 0x4000); stack = allocateStack(trace, snap, thread, program, 0x4000, programPc);
} }
catch (EmulatorOutOfMemoryException e) { catch (EmulatorOutOfMemoryException e) {
Msg.warn(ProgramEmulationUtils.class, Msg.warn(ProgramEmulationUtils.class,

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -723,7 +723,42 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
} }
@Test @Test
public void testCustomStack() throws Exception { public void testCustomStackByContext() 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);
program.getProgramContext()
.setRegisterValue(addrText, addrText,
new RegisterValue(regSP, BigInteger.valueOf(0x00200000)));
}
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("00200000", 16),
regs.getViewValue(0, regSP).getUnsignedValue());
}
@Test
public void testCustomStackByBlock() throws Exception {
createProgram(); createProgram();
intoProject(program); intoProject(program);
Memory memory = program.getMemory(); Memory memory = program.getMemory();
@ -733,7 +768,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000, MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
(byte) 0, TaskMonitor.DUMMY, false); (byte) 0, TaskMonitor.DUMMY, false);
blockText.setExecute(true); blockText.setExecute(true);
memory.createUninitializedBlock("STACK", addr(program, 0x00001234), 0x1000, false); memory.createUninitializedBlock(ProgramEmulationUtils.BLOCK_NAME_STACK,
addr(program, 0x00001234), 0x1000, false);
} }
programManager.openProgram(program); programManager.openProgram(program);