GP-4713: Launch mapping uses AutoMapSpec. Fix StaticMappingService.

This commit is contained in:
Dan
2024-08-14 15:44:42 -04:00
parent ebb4027d8b
commit d95200b166
17 changed files with 623 additions and 193 deletions

View File

@@ -0,0 +1,42 @@
/* ###
* 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.services;
import ghidra.debug.api.action.AutoMapSpec;
import ghidra.framework.plugintool.ServiceInfo;
import ghidra.trace.model.Trace;
/**
* The service to query auto-map settings
*/
@ServiceInfo(defaultProviderName = "ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesPlugin")
public interface DebuggerAutoMappingService {
/**
* Get the auto-map setting currently active in the Modules provider
*
* @return the current setting
*/
AutoMapSpec getAutoMapSpec();
/**
* Get the current auto-map setting for the given trace
*
* @param trace the trace
* @return the auto-map setting for the trace, or the setting in the Modules provider, if the
* trace does not have its own setting.
*/
AutoMapSpec getAutoMapSpec(Trace trace);
}

View File

@@ -0,0 +1,157 @@
/* ###
* 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 javax.swing.Icon;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.util.TraceEvent;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* An interface for specifying how to automatically map dynamic memory to static memory.
*/
public interface AutoMapSpec extends ExtensionPoint {
class Private {
private final Map<String, AutoMapSpec> specsByName = new TreeMap<>();
private final ChangeListener classListener = this::classesChanged;
private Private() {
ClassSearcher.addChangeListener(classListener);
classesChanged(null);
}
private synchronized void classesChanged(ChangeEvent evt) {
InstanceUtils.collectUniqueInstances(AutoMapSpec.class, specsByName,
AutoMapSpec::getConfigName);
}
}
Private PRIVATE = new Private();
public static class AutoMapSpecConfigFieldCodec implements ConfigFieldCodec<AutoMapSpec> {
@Override
public AutoMapSpec read(SaveState state, String name,
AutoMapSpec current) {
String specName = state.getString(name, null);
return fromConfigName(specName);
}
@Override
public void write(SaveState state, String name, AutoMapSpec value) {
state.putString(name, value.getConfigName());
}
}
static AutoMapSpec fromConfigName(String name) {
synchronized (PRIVATE) {
return PRIVATE.specsByName.get(name);
}
}
static Map<String, AutoMapSpec> allSpecs() {
synchronized (PRIVATE) {
return new TreeMap<>(PRIVATE.specsByName);
}
}
String getConfigName();
String getMenuName();
Icon getMenuIcon();
Collection<TraceEvent<?, ?>> getChangeTypes();
boolean objectHasType(TraceObjectValue value);
String getInfoForObjects(Trace trace);
default boolean hasTask() {
return true;
}
default String getTaskTitle() {
return getMenuName();
}
default void runTask(PluginTool tool, Trace trace) {
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
ProgramManager programManager = tool.getService(ProgramManager.class);
if (mappingService == null || programManager == null) {
return;
}
BackgroundCommand<Trace> cmd = new BackgroundCommand<>(getTaskTitle(), true, true, false) {
@Override
public boolean applyTo(Trace trace, TaskMonitor monitor) {
try {
performMapping(mappingService, trace, programManager, monitor);
return true;
}
catch (CancelledException e) {
return false;
}
}
};
tool.executeBackgroundCommand(cmd, trace);
}
List<Program> programs(ProgramManager programManager);
/**
* Perform the actual mapping
*
* @param mappingService the mapping service
* @param trace the trace
* @param programs the programs to consider
* @param monitor a task monitor
* @return true if any mappings were added
* @throws CancelledException if the task monitor cancelled the task
*/
boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
List<Program> programs, TaskMonitor monitor) throws CancelledException;
/**
* Perform the actual mapping
*
* @param mappingService the mapping service
* @param trace the trace
* @param programManager the program manager
* @param monitor a task monitor
* @return true if any mappings were added
* @throws CancelledException if the task monitor cancelled the task
*/
default boolean performMapping(DebuggerStaticMappingService mappingService, Trace trace,
ProgramManager programManager, TaskMonitor monitor) throws CancelledException {
return performMapping(mappingService, trace, programs(programManager), monitor);
}
}

View File

@@ -0,0 +1,41 @@
/* ###
* 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.Map;
import java.util.function.Function;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
public class InstanceUtils {
public static <T> void collectUniqueInstances(Class<T> cls, Map<String, T> map,
Function<T, String> keyFunc) {
// This is wasteful. Existing instances will be re-instantiated and thrown away
for (T t : ClassSearcher.getInstances(cls)) {
String key = keyFunc.apply(t);
T exists = map.get(key);
if (exists != null) {
if (exists.getClass().equals(t.getClass())) {
continue;
}
Msg.error(LocationTrackingSpec.class,
cls.getSimpleName() + " conflict over key: " + key);
}
map.put(key, t);
}
}
}