GP-5134: Don't require user to set 'Load Emulator'.

This commit is contained in:
Dan 2024-12-04 08:45:34 -05:00
parent 954ff4e124
commit cfc4adbef6
14 changed files with 45 additions and 74 deletions

View file

@ -273,10 +273,6 @@
<LI><IMG alt="" src="icon.debugger.autoread" width="16"> Read Visible Memory, RO Once -
(default) behaves like Read Visible Memory, except it will neglect read-only ranges that have
been read previously.</LI>
<LI><IMG alt="" src="icon.debugger.emulate"> Load Emulator from Programs - populates the
trace database using mapped program databases. This is often preferred during "pure
emulation," so that the dynamic listing's contents match those of the static listing.</LI>
</UL>
</BODY>
</HTML>

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.
@ -838,13 +838,11 @@ public interface DebuggerResources {
String NAME_VIS_RO_ONCE = "Read Visible Memory, RO Once";
String NAME_VISIBLE = "Read Visible Memory";
String NAME_LOAD_EMU = "Load Emulator from Programs";
String NAME_NONE = "Do Not Read Memory";
// TODO: Separate icon for each
Icon ICON_VIS_RO_ONCE = ICON_AUTOREAD;
Icon ICON_VISIBLE = ICON_AUTOREAD;
Icon ICON_LOAD_EMU = ICON_EMULATE;
Icon ICON_NONE = ICON_DELETE;
static <T> MultiStateActionBuilder<T> builder(Plugin owner) {

View file

@ -25,12 +25,14 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.debug.api.action.InstanceUtils;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.trace.model.Trace;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.task.TaskMonitor;
@ -89,6 +91,14 @@ public interface AutoReadMemorySpec extends ExtensionPoint {
Icon getMenuIcon();
default AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace != null && ProgramEmulationUtils.isEmulatedProgram(trace)) {
return LoadEmulatorAutoReadMemorySpec.INSTANCE;
}
return this;
}
/**
* Perform the automatic read, if applicable
*

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.
@ -228,7 +228,7 @@ public abstract class DebuggerReadsMemoryTrait {
return;
}
AddressSet visible = new AddressSet(this.visible);
autoSpec.readMemory(tool, current, visible).thenAccept(b -> {
autoSpec.getEffective(current).readMemory(tool, current, visible).thenAccept(b -> {
if (b) {
memoryWasRead(visible);
}
@ -284,6 +284,10 @@ public abstract class DebuggerReadsMemoryTrait {
public void setAutoSpec(AutoReadMemorySpec autoSpec) {
// TODO: What if action == null?
if (autoSpec == null || autoSpec.getConfigName() == null) {
throw new IllegalArgumentException("autoSpec " + autoSpec.getClass() +
"not allowed in menu. Cannot be set explicitly.");
}
actionAutoRead.setCurrentActionStateByUserData(autoSpec);
}

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.
@ -21,7 +21,6 @@ import java.util.concurrent.CompletableFuture;
import javax.swing.Icon;
import db.Transaction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.service.model.record.RecorderUtils;
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
@ -35,27 +34,22 @@ import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryState;
public class LoadEmulatorAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "2_LOAD_EMULATOR";
@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}
enum LoadEmulatorAutoReadMemorySpec implements AutoReadMemorySpec {
INSTANCE;
@Override
public String getConfigName() {
return CONFIG_NAME;
return null;
}
@Override
public String getMenuName() {
return AutoReadMemoryAction.NAME_LOAD_EMU;
return null;
}
@Override
public Icon getMenuIcon() {
return AutoReadMemoryAction.ICON_LOAD_EMU;
return null;
}
@Override

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.
@ -47,6 +47,11 @@ public class NoneAutoReadMemorySpec implements AutoReadMemorySpec {
return AutoReadMemoryAction.ICON_NONE;
}
@Override
public AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates) {
return this;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible) {

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.
@ -34,7 +34,6 @@ import generic.Unique;
import ghidra.app.context.ListingActionContext;
import ghidra.app.plugin.core.assembler.AssemblerPluginTestHelper;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.action.LoadEmulatorAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.listing.*;
import ghidra.app.plugin.core.debug.service.control.DebuggerControlServicePlugin;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin;
@ -155,10 +154,6 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
listingProvider.setAutoDisassemble(true);
}
protected void enableLoadEmulator() throws Throwable {
runSwing(() -> listingProvider.setAutoReadMemorySpec(new LoadEmulatorAutoReadMemorySpec()));
}
protected DebuggerListingActionContext createActionContext(Address start, int len) {
TraceProgramView view = tb.trace.getProgramView();
ProgramSelection sel = new ProgramSelection(start, start.addWrap(len - 1));
@ -369,7 +364,6 @@ public class DebuggerDisassemblyTest extends AbstractGhidraHeadedDebuggerTest {
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
enableLoadEmulator();
enableAutoDisassembly();
waitForPass(() -> {

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.
@ -40,7 +40,7 @@ import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.analysis.AutoAnalysisPlugin;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.SPLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider;
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyActionsPlugin;
@ -818,7 +818,7 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
}
@Test
public void testEmulation_LazyStaleListing() throws Throwable {
public void testEmulation_InitialListing() throws Throwable {
emulateCommandLineParser();
runSwing(() -> tool.setSize(1920, 1080));
@ -829,9 +829,6 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
public void testEmulation_ListingAfterResume() throws Throwable {
emulateCommandLineParser();
DebuggerListingProvider listing = getProvider(DebuggerListingProvider.class);
listing.setAutoReadMemorySpec(
AutoReadMemorySpec.fromConfigName(LoadEmulatorAutoReadMemorySpec.CONFIG_NAME));
EmulationResult result = flatDbg.getEmulationService()
.run(flatDbg.getCurrentPlatform(), flatDbg.getCurrentEmulationSchedule(), monitor,
Scheduler.oneThread(flatDbg.getCurrentThread()));

View file

@ -369,29 +369,13 @@ actually loaded into an emulator, yet, because Ghidra allocates and
caches emulators as needed. Instead, what you have is a single-snapshot
trace without a live target. The initial state is snapshot 0, and
emulation is started by navigating to a schedule, just like in
extrapolation. You might be unnerved by the apparently empty and stale
Dynamic listing:</p>
extrapolation.</p>
<figure>
<img src="images/Emulation_LazyStaleListing.png"
alt="Stale listing upon starting pure emulation" />
<figcaption aria-hidden="true">Stale listing upon starting pure
<img src="images/Emulation_InitialListing.png"
alt="Listing upon starting pure emulation" />
<figcaption aria-hidden="true">Listing upon starting pure
emulation</figcaption>
</figure>
<p>This is perhaps more a matter of preference, but by default, Ghidra
will only populate the Dynamic listing with state initialized by the
emulator itself. When the emulator reads, it will “read through”
uninitialized state by reading the mapped program image instead. This
spares the loader from having to copy a potentially large program image
into the emulator. In general, you should refer to the Static listing
when following the program counter. If you see contents in the Dynamic
listing following the program counter, then you are probably dealing
with self-modifying code.</p>
<p><strong>NOTE</strong>: If you prefer to see the Dynamic listing
initialized with the program image, you may select <strong>Load Emulator
from Program</strong> from the <strong>Auto-Read</strong> drop-down
button in the Dynamic Listing. The loading is still done lazily as each
page is viewed in the listing pane. You will want to change this back
when debugging a live target!</p>
<p>Because we can easily step back and forth as well as navigate to
arbitrary points in time, emulation should feel relatively free of risk;
however, the point about stubbing dependencies will become apparent. If

View file

@ -163,19 +163,8 @@ This will map the program into a new trace.
Technically, it is not actually loaded into an emulator, yet, because Ghidra allocates and caches emulators as needed.
Instead, what you have is a single-snapshot trace without a live target.
The initial state is snapshot 0, and emulation is started by navigating to a schedule, just like in extrapolation.
You might be unnerved by the apparently empty and stale Dynamic listing:
![Stale listing upon starting pure emulation](images/Emulation_LazyStaleListing.png)
This is perhaps more a matter of preference, but by default, Ghidra will only populate the Dynamic listing with state initialized by the emulator itself.
When the emulator reads, it will "read through" uninitialized state by reading the mapped program image instead.
This spares the loader from having to copy a potentially large program image into the emulator.
In general, you should refer to the Static listing when following the program counter.
If you see contents in the Dynamic listing following the program counter, then you are probably dealing with self-modifying code.
**NOTE**: If you prefer to see the Dynamic listing initialized with the program image, you may select **Load Emulator from Program** from the **Auto-Read** drop-down button in the Dynamic Listing.
The loading is still done lazily as each page is viewed in the listing pane.
You will want to change this back when debugging a live target!
![Listing upon starting pure emulation](images/Emulation_InitialListing.png)
Because we can easily step back and forth as well as navigate to arbitrary points in time, emulation should feel relatively free of risk; however, the point about stubbing dependencies will become apparent.
If you feel the need to start over, there are two methods:

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

View file

@ -83,7 +83,7 @@ GhidraClass/Debugger/images/Breakpoints_MissingModuleNote.png||GHIDRA||||END|
GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png||GHIDRA||||END|
GhidraClass/Debugger/images/Breakpoints_SeedValueAfterBreakSRand.png||GHIDRA||||END|
GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png||GHIDRA||||END|
GhidraClass/Debugger/images/Emulation_LazyStaleListing.png||GHIDRA||||END|
GhidraClass/Debugger/images/Emulation_InitialListing.png||GHIDRA||||END|
GhidraClass/Debugger/images/Emulation_ListingAfterResume.png||GHIDRA||||END|
GhidraClass/Debugger/images/Emulation_ListingForCmdlineSet.png||GHIDRA||||END|
GhidraClass/Debugger/images/Emulation_PcodeStepper.png||GHIDRA||||END|