Merge remote-tracking branch

'origin/GP-5678_Dan_fixWow64AutoReads--SQUASHED' into Ghidra_11.4
(Closes #8155)
This commit is contained in:
Ryan Kurtz 2025-05-15 12:46:50 -04:00
commit 284980b60b
20 changed files with 482 additions and 456 deletions

View file

@ -16,6 +16,7 @@
package ghidra.app.services;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.listing.MultiBlendedListingBackgroundColorModel;
import ghidra.framework.plugintool.ServiceInfo;
@ -50,12 +51,19 @@ public interface DebuggerListingService extends CodeViewerService {
void setTrackingSpec(LocationTrackingSpec spec);
/**
* Get the tracking specification of the listing.
* Get the tracking specification of the main listing.
*
* @return the current specification
*/
LocationTrackingSpec getTrackingSpec();
/**
* Get the auto-read memory specification of the main listing.
*
* @return the current specification
*/
AutoReadMemorySpec getAutoReadMemorySpec();
/**
* Add a listener for changes to the tracking specification.
*

View file

@ -13,58 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
package ghidra.debug.api.action;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import javax.swing.Icon;
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;
/**
* An interface for specifying how to automatically read target memory.
*/
public interface AutoReadMemorySpec extends ExtensionPoint {
class Private {
private final Map<String, AutoReadMemorySpec> specsByName = new TreeMap<>();
private final ChangeListener classListener = this::classesChanged;
private Private() {
ClassSearcher.addChangeListener(classListener);
classesChanged(null);
}
private synchronized void classesChanged(ChangeEvent evt) {
InstanceUtils.collectUniqueInstances(AutoReadMemorySpec.class, specsByName,
AutoReadMemorySpec::getConfigName);
}
}
Private PRIVATE = new Private();
public interface AutoReadMemorySpec {
/**
* Codec for saving/restoring the auto-read specification
*/
public static class AutoReadMemorySpecConfigFieldCodec
implements ConfigFieldCodec<AutoReadMemorySpec> {
@Override
public AutoReadMemorySpec read(SaveState state, String name,
AutoReadMemorySpec current) {
String specName = state.getString(name, null);
return fromConfigName(specName);
return AutoReadMemorySpecFactory.fromConfigName(specName);
}
@Override
@ -73,31 +48,43 @@ public interface AutoReadMemorySpec extends ExtensionPoint {
}
}
static AutoReadMemorySpec fromConfigName(String name) {
synchronized (PRIVATE) {
return PRIVATE.specsByName.get(name);
}
}
static Map<String, AutoReadMemorySpec> allSpecs() {
synchronized (PRIVATE) {
return new TreeMap<>(PRIVATE.specsByName);
}
}
/**
* Get the configuration name
*
* <p>
* This is the value stored in configuration files to identify this specification
*
* @return the configuration name
*/
String getConfigName();
/**
* A human-readable name for this specification
*
* <p>
* This is the text displayed in menus
*
* @return the menu name, or null to omit from menus
*/
String getMenuName();
/**
* Get the icon for this specification
*
* @return the icon
*/
Icon getMenuIcon();
default AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace != null && ProgramEmulationUtils.isEmulatedProgram(trace)) {
return LoadEmulatorAutoReadMemorySpec.INSTANCE;
}
return this;
}
/**
* Get the "effective" specification.
* <p>
* This allows a specification to defer to some other (possibly hidden) specification, depending
* on the coordinates.
*
* @param coordinates the current coordinates
* @return the specification
*/
AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates);
/**
* Perform the automatic read, if applicable
@ -120,18 +107,4 @@ public interface AutoReadMemorySpec extends ExtensionPoint {
*/
CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible);
/**
* A convenience for performing target memory reads with progress displayed
*
* @param tool the tool for displaying progress
* @param reader the method to perform the read, asynchronously
* @return a future which returns true if the read completes
*/
default CompletableFuture<Boolean> doRead(PluginTool tool,
Function<TaskMonitor, CompletableFuture<Void>> reader) {
return TargetActionTask
.executeTask(tool, getMenuName(), true, true, false, m -> reader.apply(m))
.thenApply(__ -> true);
}
}

View file

@ -0,0 +1,80 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.debug.api.action;
import java.util.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
/**
* A discoverable factory of auto-read memory specifications
*/
public interface AutoReadMemorySpecFactory extends ExtensionPoint {
/**
* Get the specification for the given configuration name
*
* @param name the name
* @return the spec, or null
*/
static AutoReadMemorySpec fromConfigName(String name) {
for (AutoReadMemorySpecFactory factory : ClassSearcher
.getInstances(AutoReadMemorySpecFactory.class)) {
AutoReadMemorySpec spec = factory.parseSpec(name);
if (spec != null) {
return spec;
}
}
return null;
}
/**
* Get a copy of all the known and visible specifications
*
* @param tool the plugin tool or context
* @return the specifications by configuration name
*/
static Map<String, AutoReadMemorySpec> allSuggested(PluginTool tool) {
Map<String, AutoReadMemorySpec> all = new TreeMap<>();
for (AutoReadMemorySpecFactory factory : ClassSearcher
.getInstances(AutoReadMemorySpecFactory.class)) {
for (AutoReadMemorySpec spec : factory.getSuggested(tool)) {
if (spec.getMenuName() != null) {
all.put(spec.getConfigName(), spec);
}
}
}
return all;
}
/**
* Get all the specifications currently suggested by this factory
*
* @param tool the plugin tool or context
* @return the list of suggested specifications
*/
List<AutoReadMemorySpec> getSuggested(PluginTool tool);
/**
* Attempt to parse the given configuration name as a specification
*
* @param name the configuration name, usually including a prefix unique to each factory
* @return the specification, or null if this factory cannot parse it
*/
AutoReadMemorySpec parseSpec(String name);
}

View file

@ -46,6 +46,7 @@ public interface LocationTrackingSpecFactory extends ExtensionPoint {
/**
* Get a copy of all the known specifications
*
* @param tool the plugin tool or context
* @return the specifications by configuration name
*/
static Map<String, LocationTrackingSpec> allSuggested(PluginTool tool) {

View file

@ -1,6 +1,5 @@
AutoMapSpec
AutoReadMemorySpec
DebuggerBot
AutoReadMemorySpecFactory
DebuggerMappingOpinion
DebuggerModelFactory
DebuggerPcodeEmulatorFactory

View file

@ -0,0 +1,242 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.nio.ByteBuffer;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import javax.swing.Icon;
import db.Transaction;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.trace.model.*;
import ghidra.trace.model.memory.*;
import ghidra.util.task.TaskMonitor;
public enum BasicAutoReadMemorySpec implements AutoReadMemorySpec {
/**
* Never automatically read memory
*/
NONE("0_READ_NONE", AutoReadMemoryAction.NAME_NONE, AutoReadMemoryAction.ICON_NONE) {
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool,
DebuggerCoordinates coordinates, AddressSetView visible) {
return CompletableFuture.completedFuture(false);
}
},
/**
* Automatically read all visible memory
*/
VISIBLE("1_READ_VISIBLE", AutoReadMemoryAction.NAME_VISIBLE, AutoReadMemoryAction.ICON_VISIBLE) {
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool,
DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndReadsPresent()) {
return CompletableFuture.completedFuture(false);
}
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
AddressSetView alreadyKnown = mm.getAddressesWithState(coordinates.getSnap(), visible,
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
return doRead(tool, monitor -> target.readMemoryAsync(toRead, monitor));
}
},
/**
* Automatically read all visible memory, unless it is read-only, in which case, only read it if
* it has not already been read.
*/
VIS_RO_ONCE("2_READ_VIS_RO_ONCE", AutoReadMemoryAction.NAME_VIS_RO_ONCE, AutoReadMemoryAction.ICON_VIS_RO_ONCE) {
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool,
DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndReadsPresent()) {
return CompletableFuture.completedFuture(false);
}
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
long snap = coordinates.getSnap();
AddressSetView alreadyKnown = mm.getAddressesWithState(snap, visible,
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
AddressSet everKnown = new AddressSet();
for (AddressRange range : visible) {
for (Entry<TraceAddressSnapRange, TraceMemoryState> ent : mm.getMostRecentStates(
snap,
range)) {
everKnown.add(ent.getKey().getRange());
}
}
AddressSet readOnly = new AddressSet();
for (AddressRange range : visible) {
for (TraceMemoryRegion region : mm.getRegionsIntersecting(Lifespan.at(snap),
range)) {
if (region.isWrite(snap)) {
continue;
}
readOnly.add(region.getRange(snap));
}
}
toRead.delete(everKnown.intersect(readOnly));
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
return doRead(tool, monitor -> target.readMemoryAsync(toRead, monitor));
}
},
/**
* Load memory from programs for "pure" emulation traces.
*/
LOAD_EMULATOR(null, null, null) {
protected AddressSetView quantize(int blockBits, AddressSetView set) {
if (blockBits == 1) {
return set;
}
long blockMask = -1L << blockBits;
AddressSet result = new AddressSet();
// Not terribly efficient, but this is one range most of the time
for (AddressRange range : set) {
AddressSpace space = range.getAddressSpace();
Address min = space.getAddress(range.getMinAddress().getOffset() & blockMask);
Address max = space.getAddress(range.getMaxAddress().getOffset() | ~blockMask);
result.add(new AddressRangeImpl(min, max));
}
return result;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool,
DebuggerCoordinates coordinates, AddressSetView visible) {
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return CompletableFuture.completedFuture(false);
}
Trace trace = coordinates.getTrace();
if (trace == null || coordinates.isAlive() ||
!ProgramEmulationUtils.isEmulatedProgram(trace)) {
// Never interfere with a live target
return CompletableFuture.completedFuture(false);
}
TraceMemoryManager mm = trace.getMemoryManager();
AddressSet toRead = new AddressSet(quantize(12, visible));
for (Lifespan span : coordinates.getView().getViewport().getOrderedSpans()) {
AddressSetView alreadyKnown =
mm.getAddressesWithState(span.lmin(), visible,
s -> s == TraceMemoryState.KNOWN);
toRead.delete(alreadyKnown);
if (span.lmax() != span.lmin() || toRead.isEmpty()) {
break;
}
}
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
long snap = coordinates.getSnap();
ByteBuffer buf = ByteBuffer.allocate(4096);
try (Transaction tx = trace.openTransaction("Load Visible")) {
new AbstractMappedMemoryBytesVisitor(mappingService, buf.array()) {
@Override
protected void visitData(Address hostAddr, byte[] data, int size) {
buf.position(0);
buf.limit(size);
mm.putBytes(snap, hostAddr, buf);
}
}.visit(trace, snap, toRead);
return CompletableFuture.completedFuture(true);
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
}
}
};
private final String configName;
private final String menuName;
private final Icon menuIcon;
private BasicAutoReadMemorySpec(String configName, String menuName, Icon menuIcon) {
this.configName = configName;
this.menuName = menuName;
this.menuIcon = menuIcon;
}
@Override
public String getConfigName() {
return configName;
}
@Override
public String getMenuName() {
return menuName;
}
@Override
public Icon getMenuIcon() {
return menuIcon;
}
@Override
public AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates) {
Trace trace = coordinates.getTrace();
if (trace != null && ProgramEmulationUtils.isEmulatedProgram(trace)) {
return LOAD_EMULATOR;
}
return this;
}
/**
* A convenience for performing target memory reads with progress displayed
*
* @param tool the tool for displaying progress
* @param reader the method to perform the read, asynchronously
* @return a future which returns true if the read completes
*/
protected CompletableFuture<Boolean> doRead(PluginTool tool,
Function<TaskMonitor, CompletableFuture<Void>> reader) {
return TargetActionTask
.executeTask(tool, getMenuName(), true, true, false, m -> reader.apply(m))
.thenApply(__ -> true);
}
}

View file

@ -0,0 +1,44 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.action.AutoReadMemorySpecFactory;
import ghidra.framework.plugintool.PluginTool;
public class BasicAutoReadMemorySpecFactory implements AutoReadMemorySpecFactory {
public static final List<AutoReadMemorySpec> ALL = List.of(BasicAutoReadMemorySpec.values());
public static final Map<String, AutoReadMemorySpec> BY_CONFIG_NAME = ALL.stream()
.filter(s -> s.getConfigName() != null)
.collect(Collectors.toUnmodifiableMap(
AutoReadMemorySpec::getConfigName,
Function.identity()));
@Override
public List<AutoReadMemorySpec> getSuggested(PluginTool tool) {
return ALL;
}
@Override
public AutoReadMemorySpec parseSpec(String name) {
return BY_CONFIG_NAME.get(name);
}
}

View file

@ -17,6 +17,8 @@ package ghidra.app.plugin.core.debug.gui.action;
import docking.action.builder.MultiStateActionBuilder;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.action.AutoReadMemorySpecFactory;
import ghidra.framework.plugintool.Plugin;
public interface DebuggerAutoReadMemoryAction extends AutoReadMemoryAction {
@ -24,7 +26,8 @@ public interface DebuggerAutoReadMemoryAction extends AutoReadMemoryAction {
static MultiStateActionBuilder<AutoReadMemorySpec> builder(Plugin owner) {
MultiStateActionBuilder<AutoReadMemorySpec> builder = AutoReadMemoryAction.builder(owner);
builder.toolBarGroup(NAME);
for (AutoReadMemorySpec spec : AutoReadMemorySpec.allSpecs().values()) {
for (AutoReadMemorySpec spec : AutoReadMemorySpecFactory.allSuggested(owner.getTool())
.values()) {
builder.addState(spec.getMenuName(), spec.getMenuIcon(), spec);
}
return builder;

View file

@ -29,9 +29,10 @@ import docking.menu.MultiStateDockingAction;
import docking.widgets.EventTrigger;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractRefreshSelectedMemoryAction;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
import ghidra.app.util.viewer.listingpanel.AddressSetDisplayListener;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectChangeRecord;
@ -149,8 +150,7 @@ public abstract class DebuggerReadsMemoryTrait {
protected MultiStateDockingAction<AutoReadMemorySpec> actionAutoRead;
protected RefreshSelectedMemoryAction actionRefreshSelected;
private final AutoReadMemorySpec defaultAutoSpec =
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
private final AutoReadMemorySpec defaultAutoSpec = BasicAutoReadMemorySpec.VIS_RO_ONCE;
@AutoConfigStateField(codec = AutoReadMemorySpecConfigFieldCodec.class)
protected AutoReadMemorySpec autoSpec = defaultAutoSpec;

View file

@ -1,116 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import javax.swing.Icon;
import db.Transaction;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.utils.AbstractMappedMemoryBytesVisitor;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryState;
enum LoadEmulatorAutoReadMemorySpec implements AutoReadMemorySpec {
INSTANCE;
@Override
public String getConfigName() {
return null;
}
@Override
public String getMenuName() {
return null;
}
@Override
public Icon getMenuIcon() {
return null;
}
protected AddressSetView quantize(int blockBits, AddressSetView set) {
if (blockBits == 1) {
return set;
}
long blockMask = -1L << blockBits;
AddressSet result = new AddressSet();
// Not terribly efficient, but this is one range most of the time
for (AddressRange range : set) {
AddressSpace space = range.getAddressSpace();
Address min = space.getAddress(range.getMinAddress().getOffset() & blockMask);
Address max = space.getAddress(range.getMaxAddress().getOffset() | ~blockMask);
result.add(new AddressRangeImpl(min, max));
}
return result;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible) {
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
if (mappingService == null) {
return CompletableFuture.completedFuture(false);
}
Trace trace = coordinates.getTrace();
if (trace == null || coordinates.isAlive() ||
!ProgramEmulationUtils.isEmulatedProgram(trace)) {
// Never interfere with a live target
return CompletableFuture.completedFuture(false);
}
TraceMemoryManager mm = trace.getMemoryManager();
AddressSet toRead = new AddressSet(quantize(12, visible));
for (Lifespan span : coordinates.getView().getViewport().getOrderedSpans()) {
AddressSetView alreadyKnown =
mm.getAddressesWithState(span.lmin(), visible, s -> s == TraceMemoryState.KNOWN);
toRead.delete(alreadyKnown);
if (span.lmax() != span.lmin() || toRead.isEmpty()) {
break;
}
}
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
long snap = coordinates.getSnap();
ByteBuffer buf = ByteBuffer.allocate(4096);
try (Transaction tx = trace.openTransaction("Load Visible")) {
new AbstractMappedMemoryBytesVisitor(mappingService, buf.array()) {
@Override
protected void visitData(Address hostAddr, byte[] data, int size) {
buf.position(0);
buf.limit(size);
mm.putBytes(snap, hostAddr, buf);
}
}.visit(trace, snap, toRead);
return CompletableFuture.completedFuture(true);
}
catch (MemoryAccessException e) {
throw new AssertionError(e);
}
}
}

View file

@ -1,60 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.util.concurrent.CompletableFuture;
import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
public class NoneAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "0_READ_NONE";
@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}
@Override
public String getConfigName() {
return CONFIG_NAME;
}
@Override
public String getMenuName() {
return AutoReadMemoryAction.NAME_NONE;
}
@Override
public Icon getMenuIcon() {
return AutoReadMemoryAction.ICON_NONE;
}
@Override
public AutoReadMemorySpec getEffective(DebuggerCoordinates coordinates) {
return this;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible) {
return CompletableFuture.completedFuture(false);
}
}

View file

@ -1,72 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.util.concurrent.CompletableFuture;
import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryState;
public class VisibleAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VISIBLE";
@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}
@Override
public String getConfigName() {
return CONFIG_NAME;
}
@Override
public String getMenuName() {
return AutoReadMemoryAction.NAME_VISIBLE;
}
@Override
public Icon getMenuIcon() {
return AutoReadMemoryAction.ICON_VISIBLE;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndReadsPresent()) {
return CompletableFuture.completedFuture(false);
}
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
AddressSetView alreadyKnown = mm.getAddressesWithState(coordinates.getSnap(), visible,
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
return doRead(tool, monitor -> target.readMemoryAsync(toRead, monitor));
}
}

View file

@ -1,96 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.action;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import javax.swing.Icon;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AutoReadMemoryAction;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.TraceAddressSnapRange;
import ghidra.trace.model.memory.*;
public class VisibleROOnceAutoReadMemorySpec implements AutoReadMemorySpec {
public static final String CONFIG_NAME = "1_READ_VIS_RO_ONCE";
@Override
public boolean equals(Object obj) {
return this.getClass() == obj.getClass();
}
@Override
public String getConfigName() {
return CONFIG_NAME;
}
@Override
public String getMenuName() {
return AutoReadMemoryAction.NAME_VIS_RO_ONCE;
}
@Override
public Icon getMenuIcon() {
return AutoReadMemoryAction.ICON_VIS_RO_ONCE;
}
@Override
public CompletableFuture<Boolean> readMemory(PluginTool tool, DebuggerCoordinates coordinates,
AddressSetView visible) {
if (!coordinates.isAliveAndReadsPresent()) {
return CompletableFuture.completedFuture(false);
}
Target target = coordinates.getTarget();
TraceMemoryManager mm = coordinates.getTrace().getMemoryManager();
long snap = coordinates.getSnap();
AddressSetView alreadyKnown = mm.getAddressesWithState(snap, visible,
s -> s == TraceMemoryState.KNOWN || s == TraceMemoryState.ERROR);
AddressSet toRead = visible.subtract(alreadyKnown);
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
AddressSet everKnown = new AddressSet();
for (AddressRange range : visible) {
for (Entry<TraceAddressSnapRange, TraceMemoryState> ent : mm.getMostRecentStates(snap,
range)) {
everKnown.add(ent.getKey().getRange());
}
}
AddressSet readOnly = new AddressSet();
for (AddressRange range : visible) {
for (TraceMemoryRegion region : mm.getRegionsIntersecting(Lifespan.at(snap), range)) {
if (region.isWrite(snap)) {
continue;
}
readOnly.add(region.getRange(snap));
}
}
toRead.delete(everKnown.intersect(readOnly));
if (toRead.isEmpty()) {
return CompletableFuture.completedFuture(false);
}
return doRead(tool, monitor -> target.readMemoryAsync(toRead, monitor));
}
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.debug.gui.listing;
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.GROUP_TRANSIENT_VIEWS;
import java.util.List;
import java.util.function.Consumer;
@ -32,11 +32,11 @@ import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractNewListingAction;
import ghidra.app.plugin.core.debug.gui.action.DebuggerProgramLocationActionContext;
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.listing.MultiBlendedListingBackgroundColorModel;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
@ -81,8 +81,7 @@ import ghidra.trace.model.program.TraceProgramView;
},
servicesProvided = {
DebuggerListingService.class,
}
)
})
public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerListingProvider>
implements DebuggerListingService {
private static final String KEY_CONNECTED_PROVIDER = "connectedProvider";
@ -332,6 +331,11 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin<DebuggerLis
connectedProvider.removeTrackingSpecChangeListener(listener);
}
@Override
public AutoReadMemorySpec getAutoReadMemorySpec() {
return connectedProvider.getAutoReadMemorySpec();
}
@Override
public void setCurrentSelection(ProgramSelection selection) {
connectedProvider.setSelection(selection);

View file

@ -66,8 +66,7 @@ import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer;
import ghidra.debug.api.action.GoToInput;
import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.action.*;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.listing.MultiBlendedListingBackgroundColorModel;
import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener;

View file

@ -43,12 +43,11 @@ import ghidra.app.plugin.core.debug.event.*;
import ghidra.app.plugin.core.debug.gui.*;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.FollowsCurrentThreadAction;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.app.plugin.core.format.*;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
import ghidra.debug.api.action.GoToInput;
import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.action.*;
import ghidra.debug.api.action.AutoReadMemorySpec.AutoReadMemorySpecConfigFieldCodec;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
import ghidra.features.base.memsearch.bytesource.EmptyByteSource;
@ -216,8 +215,7 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
}
}
private final AutoReadMemorySpec defaultReadMemorySpec =
AutoReadMemorySpec.fromConfigName(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
private final AutoReadMemorySpec defaultReadMemorySpec = BasicAutoReadMemorySpec.VIS_RO_ONCE;
private final DebuggerMemoryBytesPlugin myPlugin;

View file

@ -22,13 +22,15 @@ import java.util.concurrent.*;
import java.util.stream.Collectors;
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.gui.action.PCLocationTrackingSpec;
import ghidra.app.plugin.core.debug.mapping.*;
import ghidra.app.services.DebuggerTargetService;
import ghidra.app.services.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.MemBufferByteProvider;
import ghidra.app.util.bin.format.pe.*;
import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.platform.DebuggerPlatformMapper;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
@ -42,7 +44,6 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.target.TraceObject;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpinion {
protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default");
@ -55,34 +56,54 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini
X64, X86, UNK;
static Mode computeFor(PluginTool tool, Trace trace, Address address, long snap) {
DebuggerListingService listing = tool.getService(DebuggerListingService.class);
AutoReadMemorySpec readSpec = listing.getAutoReadMemorySpec();
DebuggerTargetService targetService = tool.getService(DebuggerTargetService.class);
Target target = targetService == null ? null : targetService.getTarget(trace);
DebuggerCoordinates coords = DebuggerCoordinates.NOWHERE
// force host platform, or else we'll recurse and blow the stack
.platform(trace.getPlatformManager().getHostPlatform())
.snap(snap)
.target(target);
Collection<? extends TraceModule> modules =
trace.getModuleManager().getModulesAt(snap, address);
Msg.debug(Mode.class, "Disassembling in modules: " +
Msg.debug(Mode.class, "Computing mode from modules: " +
modules.stream().map(m -> m.getName(snap)).collect(Collectors.joining(",")));
Set<Mode> modes = modules.stream()
.map(m -> modeForModule(target, trace, snap, m))
.map(m -> modeForModule(tool, readSpec, coords, m))
.filter(m -> m != UNK)
.collect(Collectors.toSet());
Msg.debug(Mode.class, "Disassembling in mode(s): " + modes);
Msg.debug(Mode.class, " Got mode(s): " + modes);
if (modes.size() != 1) {
return UNK;
}
return modes.iterator().next();
}
static Mode modeForModule(Target target, Trace trace, long snap, TraceModule module) {
if (target != null && target.getSnap() == snap) {
static Mode modeForModule(PluginTool tool, AutoReadMemorySpec readSpec,
DebuggerCoordinates coords, TraceModule module) {
AddressSet set = new AddressSet();
set.add(module.getBase(snap), module.getBase(snap)); // Recorder should read page
Trace trace = coords.getTrace();
long snap = coords.getSnap();
Address base = module.getBase(snap);
set.add(base, base); // Recorder should read page
try {
target.readMemoryAsync(set, TaskMonitor.DUMMY).get(1, TimeUnit.SECONDS);
readSpec.readMemory(tool, coords, set).get(1, TimeUnit.SECONDS);
trace.flushEvents();
}
catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new AssertionError(e);
DebuggerConsoleService console = tool.getService(DebuggerConsoleService.class);
String message = "Could not read PE header of %s to determine x86 vs x64 mode"
.formatted(module.getName(snap));
if (console != null) {
console.log(DebuggerResources.ICON_LOG_ERROR, message, e);
}
else {
Msg.error(Mode.class, message, e);
}
// Let it fall through in case stale memory is still accurate
}
MemBuffer bufferAt = trace.getMemoryManager().getBufferAt(snap, module.getBase(snap));
try (ByteProvider bp = new MemBufferByteProvider(bufferAt)) {
@ -111,7 +132,7 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini
CompilerSpec cSpec) {
super(tool, trace, cSpec);
}
// TODO: Map registers: efl,rfl,rflags->eflags
// LATER: Map registers: efl,rfl,rflags->eflags
@Override
protected TracePlatform getDisassemblyPlatform(TraceObject object, Address start,
@ -165,7 +186,7 @@ public class DbgengDebuggerPlatformOpinion extends AbstractDebuggerPlatformOpini
}
enum Offer implements DebuggerPlatformOffer {
// TODO: X86?
// LATER: X86, as in a 32-bit host? Not likely.
X64 {
@Override
public String getDescription() {

View file

@ -45,7 +45,7 @@ import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import ghidra.GhidraTestApplicationLayout;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.debug.gui.action.*;
import ghidra.app.plugin.core.debug.gui.action.BasicAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
import ghidra.app.plugin.core.debug.gui.model.columns.TraceValueObjectPropertyColumn;
import ghidra.app.plugin.core.debug.service.target.DebuggerTargetServicePlugin;
@ -53,8 +53,7 @@ import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerService
import ghidra.app.services.*;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.AsyncTestUtils;
import ghidra.debug.api.action.LocationTrackingSpec;
import ghidra.debug.api.action.LocationTrackingSpecFactory;
import ghidra.debug.api.action.*;
import ghidra.docking.settings.SettingsImpl;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
@ -600,15 +599,15 @@ public abstract class AbstractGhidraHeadedDebuggerTest
}
protected static AutoReadMemorySpec getAutoReadMemorySpec(String name) {
return AutoReadMemorySpec.fromConfigName(name);
return AutoReadMemorySpecFactory.fromConfigName(name);
}
protected final AutoReadMemorySpec readNone =
getAutoReadMemorySpec(NoneAutoReadMemorySpec.CONFIG_NAME);
getAutoReadMemorySpec(BasicAutoReadMemorySpec.NONE.getConfigName());
protected final AutoReadMemorySpec readVisible =
getAutoReadMemorySpec(VisibleAutoReadMemorySpec.CONFIG_NAME);
getAutoReadMemorySpec(BasicAutoReadMemorySpec.VISIBLE.getConfigName());
protected final AutoReadMemorySpec readVisROOnce =
getAutoReadMemorySpec(VisibleROOnceAutoReadMemorySpec.CONFIG_NAME);
getAutoReadMemorySpec(BasicAutoReadMemorySpec.VIS_RO_ONCE.getConfigName());
protected TestEnv env;
protected PluginTool tool;

View file

@ -28,8 +28,7 @@ import docking.action.DockingActionIf;
import generic.Unique;
import generic.test.category.NightlyCategory;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerIntegrationTest;
import ghidra.app.plugin.core.debug.gui.action.AutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.action.NoneAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.action.BasicAutoReadMemorySpec;
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyIntoProgramDialog.RangeEntry;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
@ -442,8 +441,7 @@ public class DebuggerCopyActionsPluginTest extends AbstractGhidraHeadedDebuggerI
TraceObject process = tb.obj("Processes[1]");
rmiCx.publishTarget(tool, tb.trace);
listingProvider.setAutoReadMemorySpec(
AutoReadMemorySpec.fromConfigName(NoneAutoReadMemorySpec.CONFIG_NAME));
listingProvider.setAutoReadMemorySpec(BasicAutoReadMemorySpec.NONE);
traceManager.activateTrace(tb.trace);
assertDisabled(copyActionsPlugin.actionCopyIntoNewProgram);

View file

@ -50,6 +50,7 @@ import ghidra.app.services.DebuggerControlService;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.async.SwingExecutorService;
import ghidra.debug.api.action.AutoReadMemorySpec;
import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.*;