diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py
index 21db9c8f19..07920515cc 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py
@@ -1224,6 +1224,7 @@ def put_regions():
regobj.set_value('_executable', r.perms == None or 'x' in r.perms)
regobj.set_value('_offset', r.offset)
regobj.set_value('_objfile', r.objfile)
+ regobj.set_value('_display', f'{r.objfile} (0x{r.start:x}-0x{r.end:x})')
regobj.insert()
STATE.trace.proxy_object_path(
MEMORY_PATTERN.format(infnum=inf.num)).retain_values(keys)
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
index 47d141936f..f883d39c54 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py
@@ -57,6 +57,7 @@ GNU_DEBUGDATA_PREFIX = ".gnu_debugdata for "
class Module(namedtuple('BaseModule', ['name', 'base', 'max', 'sections'])):
pass
+
class Index:
def __init__(self, regions):
self.regions = {}
@@ -64,6 +65,7 @@ class Index:
for r in regions:
self.regions[r.start] = r
self.bases.append(r.start)
+
def compute_base(self, address):
index = bisect.bisect_right(self.bases, address) - 1
if index == -1:
@@ -78,6 +80,7 @@ class Index:
else:
return region.start
+
class Section(namedtuple('BaseSection', ['name', 'start', 'end', 'offset', 'attrs'])):
def better(self, other):
start = self.start if self.start != 0 else other.start
@@ -103,8 +106,6 @@ class ModuleInfoReader(object):
if mat is None:
return None
n = mat['name']
- if n.startswith(GNU_DEBUGDATA_PREFIX):
- return None
return None if mat is None else mat['name']
def section_from_line(self, line):
@@ -135,7 +136,7 @@ class ModuleInfoReader(object):
for line in out.split('\n'):
n = self.name_from_line(line)
if n is not None:
- if name is not None:
+ if name is not None and not name.startswith(GNU_DEBUGDATA_PREFIX):
modules[name] = self.finish_module(name, sections, index)
name = n
sections = {}
@@ -148,7 +149,7 @@ class ModuleInfoReader(object):
if s.name in sections:
s = s.better(sections[s.name])
sections[s.name] = s
- if name is not None:
+ if name is not None and not name.startswith(GNU_DEBUGDATA_PREFIX):
modules[name] = self.finish_module(name, sections, index)
return modules
@@ -292,8 +293,9 @@ class BreakpointLocationInfoReaderV8(object):
inf = gdb.selected_inferior()
thread_groups = [inf.num]
if breakpoint.location is not None and breakpoint.location.startswith("*0x"):
- address = int(breakpoint.location[1:],16)
- loc = BreakpointLocation(address, breakpoint.enabled, thread_groups)
+ address = int(breakpoint.location[1:], 16)
+ loc = BreakpointLocation(
+ address, breakpoint.enabled, thread_groups)
return [loc]
return []
@@ -312,7 +314,8 @@ class BreakpointLocationInfoReaderV9(object):
return []
try:
address = gdb.parse_and_eval(breakpoint.location).address
- loc = BreakpointLocation(address, breakpoint.enabled, thread_groups)
+ loc = BreakpointLocation(
+ address, breakpoint.enabled, thread_groups)
return [loc]
except Exception as e:
print(f"Error parsing bpt location = {breakpoint.location}")
@@ -338,6 +341,7 @@ def _choose_breakpoint_location_info_reader():
BREAKPOINT_LOCATION_INFO_READER = _choose_breakpoint_location_info_reader()
+
def set_bool_param_by_api(name, value):
gdb.set_parameter(name, value)
@@ -353,6 +357,7 @@ def choose_set_parameter():
else:
return set_bool_param_by_cmd
+
set_bool_param = choose_set_parameter()
@@ -360,7 +365,7 @@ def get_level(frame):
if hasattr(frame, "level"):
return frame.level()
else:
- level = -1;
+ level = -1
f = frame
while f is not None:
level += 1
@@ -371,16 +376,16 @@ def get_level(frame):
class RegisterDesc(namedtuple('BaseRegisterDesc', ['name'])):
pass
+
def get_register_descs(arch, group='all'):
if hasattr(arch, "registers"):
return arch.registers(group)
else:
descs = []
- regset = gdb.execute(f"info registers {group}", to_string=True).strip().split('\n')
+ regset = gdb.execute(
+ f"info registers {group}", to_string=True).strip().split('\n')
for line in regset:
if not line.startswith(" "):
tokens = line.strip().split()
- descs.append(RegisterDesc(tokens[0]))
+ descs.append(RegisterDesc(tokens[0]))
return descs
-
-
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/LaunchAction.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/LaunchAction.java
index 8ec4b59839..4db42408e7 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/LaunchAction.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/LaunchAction.java
@@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
+import static ghidra.app.plugin.core.debug.gui.tracermi.launcher.TraceRmiLauncherServicePlugin.getProgramName;
+
import java.util.*;
import java.util.stream.Stream;
@@ -52,7 +54,7 @@ public class LaunchAction extends MultiActionDockingAction {
Program program = plugin.currentProgram;
String title = program == null
? "Configure and Launch ..."
- : "Configure and Launch %s using...".formatted(program.getName());
+ : "Configure and Launch %s using...".formatted(getProgramName(program));
return Stream.concat(Stream.of(title), menuPath.stream()).toArray(String[]::new);
}
@@ -77,12 +79,12 @@ public class LaunchAction extends MultiActionDockingAction {
Long last = saved.get(offer.getConfigName());
if (last == null) {
// NB. If program == null, this will always happen.
- // Thus, no worries about program.getName() below.
+ // Thus, no worries about getProgramName(program) below.
continue;
}
String title = program == null
? "Re-launch " + offer.getTitle()
- : "Re-launch %s using %s".formatted(program.getName(), offer.getTitle());
+ : "Re-launch %s using %s".formatted(getProgramName(program), offer.getTitle());
actions.add(new ActionBuilder(offer.getConfigName(), plugin.getName())
.popupMenuPath(title)
.popupMenuGroup("0", "%016x".formatted(Long.MAX_VALUE - last))
@@ -158,11 +160,11 @@ public class LaunchAction extends MultiActionDockingAction {
return "Configure and launch";
}
if (offer == null) {
- return "Configure and launch " + program.getName();
+ return "Configure and launch " + getProgramName(program);
}
if (program == null) {
return "Re-launch " + offer.getTitle();
}
- return "Re-launch %s using %s".formatted(program.getName(), offer.getTitle());
+ return "Re-launch %s using %s".formatted(getProgramName(program), offer.getTitle());
}
}
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
index 3f15ebe1cb..9a32f67f59 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/TraceRmiLauncherServicePlugin.java
@@ -38,6 +38,7 @@ import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchConfigurator;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.PromptMode;
import ghidra.debug.spi.tracermi.TraceRmiLaunchOpinion;
import ghidra.formats.gfilesystem.FSRL;
+import ghidra.framework.model.DomainFile;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@@ -236,10 +237,18 @@ public class TraceRmiLauncherServicePlugin extends Plugin
executeTask(new ConfigureAndLaunchTask(offer));
}
+ protected static String getProgramName(Program program) {
+ DomainFile df = program.getDomainFile();
+ if (df != null) {
+ return df.getName();
+ }
+ return program.getName();
+ }
+
protected String[] constructLaunchMenuPrefix() {
return new String[] {
DebuggerPluginPackage.NAME,
- "Configure and Launch " + currentProgram.getName() + " using..." };
+ "Configure and Launch " + getProgramName(currentProgram) + " using..." };
}
protected String[] prependConfigAndLaunch(List menuPath) {
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java
index 49bd096cd9..e1c1252a49 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java
@@ -810,6 +810,11 @@ public class TraceRmiHandler implements TraceRmiConnection {
}
DebuggerCoordinates finalCoords = object == null ? coords : coords.object(object);
Swing.runLater(() -> {
+ DebuggerTraceManagerService traceManager = this.traceManager;
+ if (traceManager == null) {
+ // Can happen during tear down.
+ return;
+ }
if (!traceManager.getOpenTraces().contains(open.trace)) {
traceManager.openTrace(open.trace);
traceManager.activate(finalCoords, ActivationCause.SYNC_MODEL);
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/AutoMapSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/AutoMapSpec.java
index b46c2526c6..466fe92371 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/AutoMapSpec.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/AutoMapSpec.java
@@ -26,7 +26,6 @@ import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.cmd.BackgroundCommand;
-import ghidra.framework.model.DomainObject;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
import ghidra.framework.plugintool.PluginTool;
@@ -104,9 +103,9 @@ public interface AutoMapSpec extends ExtensionPoint {
if (mappingService == null || programManager == null) {
return;
}
- BackgroundCommand cmd = new BackgroundCommand(getTaskTitle(), true, true, false) {
+ BackgroundCommand cmd = new BackgroundCommand<>(getTaskTitle(), true, true, false) {
@Override
- public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
+ public boolean applyTo(Trace trace, TaskMonitor monitor) {
try {
performMapping(mappingService, trace, programManager, monitor);
return true;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
index b08a143fd4..2b5f608ac6 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/copying/DebuggerCopyIntoProgramDialog.java
@@ -799,7 +799,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
protected void executeCapture(AddressRange range, Target target, TaskMonitor monitor)
throws Exception {
- target.readMemory(new AddressSet(range), monitor);
+ target.readMemoryAsync(new AddressSet(range), monitor).get();
}
protected void executePlan(TaskMonitor monitor) throws Exception {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DisplaysObjectValues.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DisplaysObjectValues.java
index 9502d5ae4a..008e216a1d 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DisplaysObjectValues.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DisplaysObjectValues.java
@@ -64,6 +64,11 @@ public interface DisplaysObjectValues {
.collect(Collectors.joining(":"));
}
+ default String getStringsDisplay(String[] strings) {
+ return Stream.of(strings)
+ .collect(Collectors.joining(":"));
+ }
+
default String getPrimitiveValueDisplay(Object value) {
assert !(value instanceof TraceObject);
assert !(value instanceof TraceObjectValue);
@@ -89,6 +94,9 @@ public interface DisplaysObjectValues {
if (value instanceof long[] longs) {
return getLongsDisplay(longs);
}
+ if (value instanceof String[] strings) {
+ return getStringsDisplay(strings);
+ }
return value.toString();
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java
index f86003460d..d39e427883 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModuleMapProposalDialog.java
@@ -42,13 +42,13 @@ public class DebuggerModuleMapProposalDialog
implements EnumeratedTableColumn {
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
- DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getModule().getBase()),
+ DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()),
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null
? e.getToProgram().getName()
: e.getToProgram().getDomainFile().getName())),
- STATIC_BASE("Static Base", Address.class, e -> e.getToProgram().getImageBase()),
- SIZE("Size", Long.class, e -> e.getModuleRange().getLength()),
+ STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()),
+ SIZE("Size", Long.class, e -> e.getFromRange().getLength()),
MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, ModuleMapEntry::setMemorize);
private final String header;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPlugin.java
index 224d361b88..a63bfa59b2 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesPlugin.java
@@ -32,6 +32,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.RELEASED,
eventsConsumed = {
+ ProgramOpenedPluginEvent.class,
ProgramActivatedPluginEvent.class,
ProgramLocationPluginEvent.class,
ProgramClosedPluginEvent.class,
@@ -65,7 +66,10 @@ public class DebuggerModulesPlugin extends AbstractDebuggerPlugin {
@Override
public void processEvent(PluginEvent event) {
super.processEvent(event);
- if (event instanceof ProgramActivatedPluginEvent ev) {
+ if (event instanceof ProgramOpenedPluginEvent ev) {
+ provider.programOpened(ev.getProgram());
+ }
+ else if (event instanceof ProgramActivatedPluginEvent ev) {
provider.setProgram(ev.getActiveProgram());
}
else if (event instanceof ProgramLocationPluginEvent ev) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
index 6a65bdea8b..4b20b4498d 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/modules/DebuggerModulesProvider.java
@@ -1078,6 +1078,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
actionMapSectionTo.getPopupMenuData().setMenuItemName(name);
}
+ public void programOpened(Program program) {
+ // TODO: Debounce this?
+ cueAutoMap = true;
+ doCuedAutoMap();
+ }
+
public void programClosed(Program program) {
if (currentProgram == program) {
currentProgram = null;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java
index 3dde790938..24795e3966 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/DebuggerPcodeStepperProvider.java
@@ -614,7 +614,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
pcodeTable.setTableHeader(null);
pcodeTable.setBackground(COLOR_BACKGROUND);
pcodeTable.setSelectionBackground(COLOR_BACKGROUND_CURSOR);
- pcodeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ pcodeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
pcodeTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/UniqueRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/UniqueRow.java
index d891dd909e..4f6c78ccd1 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/UniqueRow.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/pcode/UniqueRow.java
@@ -106,6 +106,9 @@ public class UniqueRow {
}
public RefType getRefType() {
+ if (provider.pcodeTable.getSelectedRowCount() != 1) {
+ return RefType.NONE;
+ }
int index = provider.pcodeTable.getSelectedRow();
if (index == -1) {
return RefType.NONE;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
index 808c307444..54bca75719 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java
@@ -42,8 +42,8 @@ import ghidra.trace.database.DBTrace;
import ghidra.trace.model.*;
import ghidra.trace.model.memory.*;
import ghidra.trace.model.modules.TraceConflictedMappingException;
-import ghidra.trace.model.target.TraceObject;
-import ghidra.trace.model.target.TraceObjectKeyPath;
+import ghidra.trace.model.target.*;
+import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.thread.*;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.*;
@@ -70,7 +70,8 @@ public class ProgramEmulationUtils {
-
+
@@ -494,6 +495,20 @@ public class ProgramEmulationUtils {
throw new EmulatorOutOfMemoryException();
}
+ protected static void createObjects(Trace trace) {
+ TraceObjectManager om = trace.getObjectManager();
+ om.createRootObject(EMU_SESSION_SCHEMA);
+
+ om.createObject(TraceObjectKeyPath.parse("Breakpoints"))
+ .insert(Lifespan.ALL, ConflictResolution.DENY);
+ om.createObject(TraceObjectKeyPath.parse("Memory"))
+ .insert(Lifespan.ALL, ConflictResolution.DENY);
+ om.createObject(TraceObjectKeyPath.parse("Modules"))
+ .insert(Lifespan.ALL, ConflictResolution.DENY);
+ om.createObject(TraceObjectKeyPath.parse("Threads"))
+ .insert(Lifespan.ALL, ConflictResolution.DENY);
+ }
+
/**
* Create a new trace with a single thread, ready for emulation of the given program
*
@@ -510,7 +525,8 @@ public class ProgramEmulationUtils {
try {
trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
try (Transaction tx = trace.openTransaction("Emulate")) {
- trace.getObjectManager().createRootObject(EMU_SESSION_SCHEMA);
+ createObjects(trace);
+
TraceSnapshot initial =
trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
long snap = initial.getKey();
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java
index 395e2df02d..c4d4739199 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java
@@ -34,7 +34,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
-import ghidra.trace.model.thread.TraceThread;
import ghidra.util.ComparatorMath;
import ghidra.util.Msg;
@@ -163,11 +162,17 @@ public enum DebuggerStaticMappingUtils {
private Address min = null;
private Address max = null;
+ public void consider(Address min, Address max) {
+ this.min = this.min == null ? min : ComparatorMath.cmin(this.min, min);
+ this.max = this.max == null ? max : ComparatorMath.cmax(this.max, max);
+ }
+
public void consider(AddressRange range) {
- min = min == null ? range.getMinAddress()
- : ComparatorMath.cmin(min, range.getMinAddress());
- max = max == null ? range.getMaxAddress()
- : ComparatorMath.cmax(max, range.getMaxAddress());
+ consider(range.getMinAddress(), range.getMaxAddress());
+ }
+
+ public void consider(Address address) {
+ consider(address, address);
}
public Address getMin() {
@@ -181,6 +186,10 @@ public enum DebuggerStaticMappingUtils {
public long getLength() {
return max.subtract(min) + 1;
}
+
+ public AddressRange getRange() {
+ return new AddressRangeImpl(getMin(), getMax());
+ }
}
public static boolean isReal(MemoryBlock block) {
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java
index 405bcf0d06..768849246f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DefaultModuleMapProposal.java
@@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.service.modules;
import java.util.*;
+import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.debug.api.modules.ModuleMapProposal;
import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
@@ -27,6 +28,7 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
import ghidra.trace.model.modules.TraceModule;
+import ghidra.util.MathUtilities;
public class DefaultModuleMapProposal
extends AbstractMapProposal
@@ -81,20 +83,24 @@ public class DefaultModuleMapProposal
* @param program the program image whose size to compute
* @return the size
*/
- public static long computeImageSize(Program program) {
- Address imageBase = program.getImageBase();
- long imageSize = 0;
+ public static AddressRange computeImageRange(Program program) {
+ Extrema extrema = new Extrema();
// TODO: How to handle Harvard architectures?
for (MemoryBlock block : program.getMemory().getBlocks()) {
if (!includeBlock(program, block)) {
continue;
}
- imageSize = Math.max(imageSize, block.getEnd().subtract(imageBase) + 1);
+ // includeBlock checks address space is same as image base
+ extrema.consider(block.getAddressRange());
}
- return imageSize;
+ if (program.getImageBase().getOffset() != 0) {
+ extrema.consider(program.getImageBase());
+ }
+ return extrema.getRange();
}
protected AddressRange moduleRange;
+ protected AddressRange imageRange;
protected boolean memorize = false;
/**
@@ -113,6 +119,7 @@ public class DefaultModuleMapProposal
AddressRange moduleRange) {
super(module.getTrace(), module, program, program);
this.moduleRange = moduleRange;
+ this.imageRange = quantize(computeImageRange(program));
}
@Override
@@ -125,9 +132,18 @@ public class DefaultModuleMapProposal
return getModule().getLifespan();
}
+ private long getLength() {
+ return MathUtilities.unsignedMin(moduleRange.getLength(), imageRange.getLength());
+ }
+
@Override
public AddressRange getFromRange() {
- return moduleRange;
+ try {
+ return new AddressRangeImpl(moduleRange.getMinAddress(), getLength());
+ }
+ catch (AddressOverflowException e) {
+ throw new AssertionError(e);
+ }
}
@Override
@@ -138,21 +154,13 @@ public class DefaultModuleMapProposal
@Override
public void setProgram(Program program) {
setToObject(program, program);
- try {
- this.moduleRange = quantize(
- new AddressRangeImpl(getModule().getBase(), computeImageSize(program)));
- }
- catch (AddressOverflowException e) {
- // This is terribly unlikely
- throw new IllegalArgumentException(
- "Specified program is too large for module's memory space");
- }
+ this.imageRange = quantize(computeImageRange(program));
}
@Override
public AddressRange getToRange() {
try {
- return new AddressRangeImpl(getToProgram().getImageBase(), moduleRange.getLength());
+ return new AddressRangeImpl(imageRange.getMinAddress(), getLength());
}
catch (AddressOverflowException e) {
throw new AssertionError(e);
@@ -174,10 +182,8 @@ public class DefaultModuleMapProposal
// indexed by region's offset from module base
protected final NavigableMap matchers = new TreeMap<>();
- protected Address imageBase;
- protected Address moduleBase;
- protected long imageSize;
- protected AddressRange moduleRange; // TODO: This is now in the trace schema. Use it.
+ protected AddressRange imageRange;
+ protected AddressRange moduleRange;
protected DefaultModuleMapProposal(TraceModule module, Program program) {
super(module.getTrace(), program);
@@ -196,8 +202,8 @@ public class DefaultModuleMapProposal
}
private void processProgram() {
- imageBase = program.getImageBase();
- imageSize = DefaultModuleMapEntry.computeImageSize(program);
+ imageRange = quantize(DefaultModuleMapEntry.computeImageRange(program));
+ Address imageBase = imageRange.getMinAddress(); // not precisely, but good enough
// TODO: How to handle Harvard architectures?
for (MemoryBlock block : program.getMemory().getBlocks()) {
if (!DefaultModuleMapEntry.includeBlock(program, block)) {
@@ -211,13 +217,8 @@ public class DefaultModuleMapProposal
* Must be called after processProgram, so that image size is known
*/
private void processModule() {
- moduleBase = module.getBase();
- try {
- moduleRange = quantize(new AddressRangeImpl(moduleBase, imageSize));
- }
- catch (AddressOverflowException e) {
- return; // Just score it as having no matches?
- }
+ moduleRange = quantize(module.getRange());
+ Address moduleBase = moduleRange.getMinAddress();
Lifespan lifespan = module.getLifespan();
for (TraceMemoryRegion region : module.getTrace()
.getMemoryManager()
diff --git a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathUtils.java b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathUtils.java
index f147a57c4e..8315c8dd82 100644
--- a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathUtils.java
+++ b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/util/PathUtils.java
@@ -499,6 +499,21 @@ public enum PathUtils {
return Objects.equals(ancestor, successor.subList(0, ancestor.size()));
}
+ /**
+ * Assuming the first path is an ancestor of the second, compute the relative path from the
+ * first to the second.
+ *
+ * @param ancestor the ancestor (from)
+ * @param successor the successor (to)
+ * @return the relative path
+ */
+ public static List relativize(List ancestor, List successor) {
+ if (!isAncestor(ancestor, successor)) {
+ throw new IllegalArgumentException("First must be an ancestor of the second");
+ }
+ return successor.subList(ancestor.size(), successor.size());
+ }
+
/**
* Check whether a given object-valued attribute is a link.
*
@@ -538,6 +553,7 @@ public enum PathUtils {
* Check whether a given attribute should be displayed.
*
* @param key the key of the given attribute
+ * @return true if hidden
*/
public static boolean isHidden(String key) {
return key.startsWith(TargetObject.PREFIX_INVISIBLE);
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointLocation.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointLocation.java
index a64c98b708..18152312b4 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointLocation.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointLocation.java
@@ -121,8 +121,19 @@ public class DBTraceObjectBreakpointLocation
@Override
public String getName() {
try (LockHold hold = object.getTrace().lockRead()) {
- return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
- TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
+ String display = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
+ TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, null);
+ if (display != null) {
+ return display;
+ }
+ TraceObject container =
+ object.queryCanonicalAncestorsTargetInterface(TargetBreakpointSpecContainer.class)
+ .findFirst()
+ .orElse(null);
+ if (container == null) {
+ return ""; // Should be impossible, but maybe not a sane schema
+ }
+ return container.getCanonicalPath().relativize(object.getCanonicalPath()).toString();
}
}
@@ -312,8 +323,16 @@ public class DBTraceObjectBreakpointLocation
@Override
public String getComment() {
try (LockHold hold = object.getTrace().lockRead()) {
- return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_COMMENT,
- String.class, "");
+ String comment = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
+ KEY_COMMENT, String.class, "");
+ if (!comment.isBlank()) {
+ return comment;
+ }
+ TraceObjectBreakpointSpec spec = getSpecification();
+ if (spec == null) {
+ return "";
+ }
+ return spec.getExpression();
}
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointSpec.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointSpec.java
index 570d5b5fd8..f762d0e017 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointSpec.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/breakpoint/DBTraceObjectBreakpointSpec.java
@@ -181,6 +181,12 @@ public class DBTraceObjectBreakpointSpec
}
}
+ @Override
+ public String getExpression() {
+ return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
+ TargetBreakpointSpec.EXPRESSION_ATTRIBUTE_NAME, String.class, null);
+ }
+
@Override
public Set getThreads() {
throw new UnsupportedOperationException("Ask a location instead");
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceSnapshot.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceSnapshot.java
index 479963e416..d37e4f5df6 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceSnapshot.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/time/DBTraceSnapshot.java
@@ -18,7 +18,12 @@ package ghidra.trace.database.time;
import java.io.IOException;
import db.DBRecord;
+import ghidra.dbg.target.TargetEventScope;
+import ghidra.trace.database.target.DBTraceObject;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.target.TraceObject;
+import ghidra.trace.model.target.TraceObjectValue;
+import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.trace.model.time.schedule.TraceSchedule;
@@ -143,7 +148,26 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
@Override
public TraceThread getEventThread() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
- return eventThread;
+ if (eventThread != null) {
+ return eventThread;
+ }
+ // TODO: Can it be something other than root?
+ DBTraceObject root = manager.trace.getObjectManager().getRootObject();
+ if (root == null) {
+ return null;
+ }
+ if (!root.getTargetSchema().getInterfaces().contains(TargetEventScope.class)) {
+ return null;
+ }
+ TraceObjectValue eventAttr =
+ root.getAttribute(getKey(), TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME);
+ if (eventAttr == null) {
+ return null;
+ }
+ if (!(eventAttr.getValue() instanceof TraceObject eventObj)) {
+ return null;
+ }
+ return eventObj.queryInterface(TraceObjectThread.class);
}
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceObjectBreakpointSpec.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceObjectBreakpointSpec.java
index 7ff66e1783..294ff11ba0 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceObjectBreakpointSpec.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/breakpoint/TraceObjectBreakpointSpec.java
@@ -43,5 +43,7 @@ public interface TraceObjectBreakpointSpec extends TraceBreakpoint, TraceObjectI
Collection extends TraceObjectBreakpointLocation> getLocations();
+ String getExpression();
+
void setKinds(Lifespan lifespan, Collection kinds);
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectKeyPath.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectKeyPath.java
index ece0f8b69c..1de27134a0 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectKeyPath.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/target/TraceObjectKeyPath.java
@@ -224,8 +224,8 @@ public final class TraceObjectKeyPath implements Comparable
/**
* Stream, starting with the longer paths, paths that match the given predicates
*
- * @param matcher
- * @return
+ * @param predicates the predicates to filter the ancestor paths
+ * @return the stream of matching paths, longest to shortest
*/
public Stream streamMatchingAncestry(PathPredicates predicates) {
if (!predicates.ancestorMatches(keyList, false)) {
@@ -253,4 +253,15 @@ public final class TraceObjectKeyPath implements Comparable
public boolean isAncestor(TraceObjectKeyPath that) {
return PathUtils.isAncestor(keyList, that.keyList);
}
+
+ /**
+ * Assuming this is an ancestor of the given successor, compute the relative path from here to
+ * there
+ *
+ * @param successor the successor
+ * @return the relative path
+ */
+ public TraceObjectKeyPath relativize(TraceObjectKeyPath successor) {
+ return TraceObjectKeyPath.of(PathUtils.relativize(keyList, successor.keyList));
+ }
}
diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java b/Ghidra/Test/DebuggerIntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java
index b7e62a3d40..168d6078de 100644
--- a/Ghidra/Test/DebuggerIntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java
+++ b/Ghidra/Test/DebuggerIntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertTrue;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.io.*;
+import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.util.*;
@@ -50,23 +51,24 @@ import ghidra.app.plugin.core.debug.gui.memory.DebuggerMemoryBytesProvider;
import ghidra.app.plugin.core.debug.gui.memory.DebuggerRegionsProvider;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProvider;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerStaticMappingProvider;
+import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerMethodInvocationDialog;
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperPlugin;
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperProvider;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider;
import ghidra.app.plugin.core.debug.gui.stack.DebuggerStackProvider;
import ghidra.app.plugin.core.debug.gui.stack.vars.VariableValueHoverPlugin;
-import ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsPlugin;
import ghidra.app.plugin.core.debug.gui.thread.DebuggerThreadsProvider;
import ghidra.app.plugin.core.debug.gui.time.DebuggerTimeProvider;
import ghidra.app.plugin.core.debug.gui.time.DebuggerTimeSelectionDialog;
+import ghidra.app.plugin.core.debug.gui.tracermi.connection.TraceRmiConnectionManagerPlugin;
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin.EmulateProgramAction;
-import ghidra.app.plugin.core.debug.service.model.DebuggerConnectDialog;
import ghidra.app.plugin.core.debug.stack.StackUnwinderTest;
import ghidra.app.plugin.core.debug.stack.StackUnwinderTest.HoverLocation;
import ghidra.app.plugin.core.debug.stack.UnwindStackCommand;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
+import ghidra.app.plugin.core.terminal.TerminalProvider;
import ghidra.app.script.GhidraState;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
@@ -74,19 +76,17 @@ import ghidra.app.util.importer.AutoImporter;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.LoadResults;
import ghidra.async.AsyncTestUtils;
-import ghidra.dbg.DebuggerModelFactory;
-import ghidra.dbg.target.TargetLauncher;
-import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
-import ghidra.dbg.testutil.DummyProc;
-import ghidra.dbg.util.ConfigurableFactory.Property;
-import ghidra.debug.api.model.DebuggerProgramLaunchOffer;
-import ghidra.debug.api.model.DebuggerProgramLaunchOffer.*;
+import ghidra.debug.api.modules.ModuleMapProposal;
+import ghidra.debug.api.tracermi.RemoteMethod;
+import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
+import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
import ghidra.debug.api.watch.WatchRow;
-import ghidra.debug.flatapi.FlatDebuggerAPI;
+import ghidra.debug.flatapi.FlatDebuggerRmiAPI;
import ghidra.framework.Application;
import ghidra.framework.TestApplicationUtils;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
+import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
@@ -96,11 +96,11 @@ import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.program.util.ProgramSelection;
+import ghidra.pty.*;
import ghidra.test.TestEnv;
-import ghidra.trace.model.breakpoint.TraceBreakpoint;
+import ghidra.trace.model.breakpoint.*;
import ghidra.trace.model.guest.TracePlatform;
-import ghidra.trace.model.modules.TraceModule;
-import ghidra.trace.model.modules.TraceSection;
+import ghidra.trace.model.modules.*;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.time.schedule.*;
import ghidra.util.InvalidNameException;
@@ -117,26 +117,36 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
protected static final String TERMMINES_PATH = "/tmp/termmines";
+ static class MyTestEnv extends TestEnv {
+ public MyTestEnv(String projectName) throws IOException {
+ super(projectName);
+ }
+
+ @Override
+ protected PluginTool launchDefaultToolByName(String toolName) {
+ return super.launchDefaultToolByName(toolName);
+ }
+ }
+
protected final ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
+ protected TerminalService terminalService;
protected ProgramManager programManager;
protected CodeViewerService staticListingService;
- protected DebuggerModelService modelService;
+ protected MyTestEnv env;
- protected DebuggerModelFactory gdbFactory;
-
- protected final FlatDebuggerAPI flatDbg = new FlatDebuggerAPI() {
+ protected final FlatDebuggerRmiAPI flatDbg = new FlatDebuggerRmiAPI() {
@Override
public GhidraState getState() {
Navigatable nav = staticListingService.getNavigatable();
- return new GhidraState(tool, env.getProject(),
- nav.getProgram(), nav.getLocation(), nav.getSelection(), nav.getHighlight());
+ return new GhidraState(tool, env.getProject(), nav.getProgram(), nav.getLocation(),
+ nav.getSelection(), nav.getHighlight());
}
};
@Override
protected TestEnv newTestEnv() throws Exception {
- return new TestEnv("DebuggerCourse");
+ return env = new MyTestEnv("DebuggerCourse");
}
// TODO: Propose this replace waitForProgram
@@ -167,7 +177,7 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
@Override
public void prepareTool() {
- tool = env.launchTool("Debugger");
+ tool = env.launchDefaultToolByName("Debugger");
}
@Override
@@ -214,20 +224,23 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
}
termmines.setExecutable(true);
+ terminalService = tool.getService(TerminalService.class);
programManager = tool.getService(ProgramManager.class);
staticListingService = getStaticListingService();
+ }
- modelService = tool.getService(DebuggerModelService.class);
- gdbFactory = modelService.getModelFactories()
- .stream()
- .filter(f -> "gdb".equals(f.getBrief()))
- .findAny()
- .get();
+ @Test
+ public void testGettingStarted_Termmines() throws Throwable {
+ Pty pty = PtyFactory.local().openpty();
+ pty.getChild().session(new String[] { TERMMINES_PATH }, Map.of("TERM", "xterm-256color"));
+ PtyParent parent = pty.getParent();
+ try (Terminal terminal =
+ terminalService.createWithStreams(Charset.forName("utf8"), parent.getInputStream(),
+ parent.getOutputStream())) {
- @SuppressWarnings("unchecked")
- Property gdbPathProp =
- (Property) gdbFactory.getOptions().get("GDB launch command");
- gdbPathProp.setValue(DummyProc.which("gdb"));
+ TerminalProvider provider = waitForComponentProvider(TerminalProvider.class);
+ captureIsolatedProvider(provider, 600, 600);
+ }
}
@Test
@@ -235,32 +248,46 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
captureToolWindow(1920, 1080);
}
- protected void launchProgramInGdb(String extraArgs) throws Throwable {
- DebuggerProgramLaunchOffer offer = modelService.getProgramLaunchOffers(program)
- .filter(o -> "IN-VM GDB".equals(o.getConfigName()))
- .findFirst()
- .get();
- LaunchConfigurator config = new LaunchConfigurator() {
+ protected void captureLaunchDialog(String title) {
+ TraceRmiLaunchOffer offer = flatDbg.getLaunchOffers(program)
+ .stream()
+ .filter(o -> title.equals(o.getTitle()))
+ .findAny()
+ .orElseThrow();
+
+ runSwingLater(() -> offer.launchProgram(monitor, new LaunchConfigurator() {
@Override
- public Map configureLauncher(TargetLauncher launcher,
- Map arguments, RelPrompt relPrompt) {
- if (extraArgs.length() == 0) {
- return arguments;
- }
- Map adjusted = new HashMap<>(arguments);
- TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.adjust(adjusted,
- c -> c + " " + extraArgs);
- return adjusted;
+ public PromptMode getPromptMode() {
+ return PromptMode.ALWAYS;
}
- };
- LaunchResult result = waitOn(offer.launchProgram(monitor, PromptMode.NEVER, config));
+ }));
+
+ captureDialog(DebuggerMethodInvocationDialog.class);
+ }
+
+ @Test
+ public void testGettingStarted_LaunchGDBDialog() {
+ captureLaunchDialog("gdb");
+ }
+
+ protected LaunchResult launchProgramInGdb(String extraArgs) throws Throwable {
+ TraceRmiLaunchOffer offer = flatDbg.getLaunchOffers(program)
+ .stream()
+ .filter(o -> "gdb".equals(o.getTitle()))
+ .findAny()
+ .orElseThrow();
+ LaunchResult result = flatDbg.launch(offer, Map.ofEntries(
+ Map.entry("env:OPT_START_CMD", "start"),
+ Map.entry("args", extraArgs)),
+ monitor);
if (result.exception() != null) {
throw result.exception();
}
+ return result;
}
- protected void launchProgramInGdb() throws Throwable {
- launchProgramInGdb("");
+ protected LaunchResult launchProgramInGdb() throws Throwable {
+ return launchProgramInGdb("");
}
@Test
@@ -278,15 +305,33 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
captureProvider(DebuggerBreakpointsProvider.class);
}
- protected void placeBreakpointsSRandRand() throws Throwable {
+ protected void waitBreakSpecExists(String expression) {
+ waitForCondition(() -> flatDbg.getAllBreakpoints()
+ .stream()
+ .flatMap(lb -> lb.getTraceBreakpoints().stream())
+ . mapMulti((loc, down) -> {
+ if (loc instanceof TraceObjectBreakpointLocation oloc) {
+ down.accept(oloc.getSpecification());
+ }
+ })
+ .distinct()
+ .filter(l -> expression.equals(l.getExpression()))
+ .count() == 1);
+ }
+
+ protected void placeBreakpointsSRand() throws Throwable {
assertTrue(flatDbg.execute("break srand"));
- assertTrue(flatDbg.execute("break rand"));
- waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 2);
+ waitBreakSpecExists("srand");
}
protected void placeBreakpointsRand() throws Throwable {
assertTrue(flatDbg.execute("break rand"));
- waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 1);
+ waitBreakSpecExists("rand");
+ }
+
+ protected void placeBreakpointsSRandRand() throws Throwable {
+ placeBreakpointsSRand();
+ placeBreakpointsRand();
}
@Test
@@ -298,11 +343,16 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
captureProvider(DebuggerBreakpointsProvider.class);
}
- protected Address navigateToBreakpoint(String comment) {
+ protected Address navigateToBreakpoint(String expression) {
TraceBreakpoint bp = flatDbg.getAllBreakpoints()
.stream()
.flatMap(l -> l.getTraceBreakpoints().stream())
- .filter(l -> comment.equals(l.getComment()))
+ . mapMulti((loc, down) -> {
+ if (loc instanceof TraceObjectBreakpointLocation oloc) {
+ down.accept(oloc);
+ }
+ })
+ .filter(l -> expression.equals(l.getSpecification().getExpression()))
.findAny()
.get();
Address dynAddr = bp.getMinAddress();
@@ -367,6 +417,14 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
Address dynAddr = navigateToBreakpoint("srand");
TraceModule modLibC = getModuleContaining(dynAddr);
Program progLibC = importModule(modLibC);
+
+ // This module might be symlinked, so module name and file name may not match.
+ DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
+ ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
+ try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
+ mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
+ }
+
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
disassembleSymbol(progLibC, "srand");
// Just to be sure.
@@ -385,9 +443,16 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
Address dynAddr = navigateToBreakpoint("srand");
TraceModule modLibC = getModuleContaining(dynAddr);
Program progLibC = importModule(modLibC);
- waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
+
+ // This module might be symlinked, so module name and file name may not match.
+ DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
+ ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
+ try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
+ mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
+ }
+
+ Address stAddr = waitForValue(() -> flatDbg.translateDynamicToStatic(dynAddr));
disassembleSymbol(progLibC, "srand");
- Address stAddr = flatDbg.translateDynamicToStatic(dynAddr);
// Just to be sure.
goTo(tool, progLibC, stAddr);
flatDbg.resume();
@@ -488,6 +553,18 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
public void testNavigation_ThreadsInCallRand() throws Throwable {
launchProgramInGdb();
placeBreakpointsRand();
+ Address dynAddr = navigateToBreakpoint("rand");
+ TraceModule modLibC = getModuleContaining(dynAddr);
+ Program progLibC = importModule(modLibC);
+
+ // This module might be symlinked, so module name and file name may not match.
+ DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
+ ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
+ try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
+ mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
+ }
+
+ waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
flatDbg.resume();
runSwing(() -> tool.setSize(1920, 1080));
@@ -500,7 +577,15 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
placeBreakpointsRand();
Address dynAddr = navigateToBreakpoint("rand");
TraceModule modLibC = getModuleContaining(dynAddr);
- importModule(modLibC);
+ Program progLibC = importModule(modLibC);
+
+ // This module might be symlinked, so module name and file name may not match.
+ DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
+ ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
+ try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
+ mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
+ }
+
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
flatDbg.resume();
@@ -513,8 +598,11 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
launchProgramInGdb();
placeBreakpointsSRandRand();
flatDbg.resume(); // srand
+ Thread.sleep(500);
flatDbg.resume(); // rand.1
+ Thread.sleep(500);
flatDbg.stepOut();
+ Thread.sleep(500);
runSwing(() -> tool.setSize(1920, 1080));
captureProvider(DebuggerTimeProvider.class);
@@ -522,18 +610,29 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
@Test
public void testNavigation_DialogCompareTimes() throws Throwable {
- launchProgramInGdb(); // main
+ LaunchResult result = launchProgramInGdb(); // main
placeBreakpointsRand();
Address pc = flatDbg.getProgramCounter();
long snapA = flatDbg.getCurrentSnap();
- TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
- .getModuleManager()
- .getModulesAt(snapA, pc));
+ try (Transaction tx = result.trace().openTransaction("Name snapshot")) {
+ result.trace()
+ .getTimeManager()
+ .getSnapshot(snapA, false)
+ .setDescription("Initial snapshot");
+ }
+ TraceObjectModule modTermmines =
+ (TraceObjectModule) Unique.assertOne(flatDbg.getCurrentTrace()
+ .getModuleManager()
+ .getModulesAt(snapA, pc));
+
+ RemoteMethod refreshSections = result.connection().getMethods().get("refresh_sections");
+ refreshSections.invoke(Map.of("node", modTermmines.getObject()));
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
flatDbg.readMemory(secTermminesData.getStart(),
(int) secTermminesData.getRange().getLength(), monitor);
flatDbg.resume(); // rand.1
+ Thread.sleep(500);
flatDbg.readMemory(secTermminesData.getStart(),
(int) secTermminesData.getRange().getLength(), monitor);
@@ -547,13 +646,23 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
@Test
public void testNavigation_CompareTimes() throws Throwable {
- launchProgramInGdb("-M 15"); // main
+ LaunchResult result = launchProgramInGdb("-M 15"); // main
placeBreakpointsRand();
Address pc = flatDbg.getProgramCounter();
long snapA = flatDbg.getCurrentSnap();
- TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
- .getModuleManager()
- .getModulesAt(snapA, pc));
+ try (Transaction tx = result.trace().openTransaction("Name snapshot")) {
+ result.trace()
+ .getTimeManager()
+ .getSnapshot(snapA, false)
+ .setDescription("Initial snapshot");
+ }
+ TraceObjectModule modTermmines =
+ (TraceObjectModule) Unique.assertOne(flatDbg.getCurrentTrace()
+ .getModuleManager()
+ .getModulesAt(snapA, pc));
+
+ RemoteMethod refreshSections = result.connection().getMethods().get("refresh_sections");
+ refreshSections.invoke(Map.of("node", modTermmines.getObject()));
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
flatDbg.readMemory(secTermminesData.getStart(),
(int) secTermminesData.getRange().getLength(), monitor);
@@ -582,6 +691,10 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
});
waitForCondition(() -> actionNextDiff.isEnabled());
flatDbg.goToDynamic(secTermminesData.getStart());
+ // Because auto-strack is a little broken right now
+ Thread.sleep(500);
+ flatDbg.goToDynamic(secTermminesData.getStart());
+
performAction(actionNextDiff);
runSwing(() -> tool.setSize(1920, 1080));
@@ -611,7 +724,15 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
showProvider(DebuggerStaticMappingProvider.class);
Address dynAddr = navigateToBreakpoint("srand");
TraceModule modLibC = getModuleContaining(dynAddr);
- importModule(modLibC);
+ Program progLibC = importModule(modLibC);
+
+ // This module might be symlinked, so module name and file name may not match.
+ DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
+ ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
+ try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
+ mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
+ }
+
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
runSwing(() -> tool.setSize(1920, 1080));
@@ -631,29 +752,30 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
DebuggerListingService listings = tool.getService(DebuggerListingService.class);
runSwing(() -> listings
.setCurrentSelection(new ProgramSelection(new AddressSet(modNcurses.getRange()))));
+ DebuggerListingProvider listingProvider =
+ waitForComponentProvider(DebuggerListingProvider.class);
performAction("Copy Into New Program",
- PluginUtils.getPluginNameFromClass(DebuggerCopyActionsPlugin.class), false);
+ PluginUtils.getPluginNameFromClass(DebuggerCopyActionsPlugin.class), listingProvider,
+ false);
captureDialog(DebuggerCopyIntoProgramDialog.class);
}
@Test
- public void testRemoteTargets_GdbOverSsh() throws Throwable {
- performAction("Connect", PluginUtils.getPluginNameFromClass(DebuggerTargetsPlugin.class),
- false);
- DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
- runSwing(() -> dialog.setFactoryByBrief("gdb via SSH"));
-
- captureDialog(dialog);
+ public void testRemoteTargets_GdbPlusGdbserverViaSsh() throws Throwable {
+ captureLaunchDialog("gdb + gdbserver via ssh");
}
@Test
- public void testRemoteTargets_Gadp() throws Throwable {
- performAction("Connect", PluginUtils.getPluginNameFromClass(DebuggerTargetsPlugin.class),
- false);
- DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
- runSwing(() -> dialog.setFactoryByBrief("Ghidra debug agent (GADP)"));
+ public void testRemoteTargets_GdbViaSsh() throws Throwable {
+ captureLaunchDialog("gdb via ssh");
+ }
- captureDialog(dialog);
+ @Test
+ public void testRemoteTargets_AcceptTraceRmi() throws Throwable {
+ performAction("Connect by Accept",
+ PluginUtils.getPluginNameFromClass(TraceRmiConnectionManagerPlugin.class),
+ false);
+ captureDialog(DebuggerMethodInvocationDialog.class);
}
protected Function findCommandLineParser() throws Throwable {
diff --git a/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html
index 19ddc8a8cd..77af0e12a4 100644
--- a/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html
+++ b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html
@@ -128,16 +128,14 @@ icon in my Tool Chest
There is no
Debug / Launch icon in the global toolbar
+There is no
+gdb option in the launch drop-down
There
-is no “Debug termmines in GDB locally IN-VM” option in the launch
-drop-down
-The
-launch hangs for several seconds and then prompt for a
-“recorder”
+href="#the-launch-hangs-for-several-seconds-and-then-i-get-prompted-with-a-wall-of-text"
+id="toc-the-launch-hangs-for-several-seconds-and-then-i-get-prompted-with-a-wall-of-text">The
+launch hangs for several seconds and then I get prompted with a wall of
+text
The Dynamic Listing is
empty
@@ -192,8 +190,16 @@ trust. For termmines
, the risk is negligible. Run it:
You should see a 9x9 grid and a cursor you can move with the arrow
-keys. Hit Ctrl-C to exit. Probe it for help. Most Linux
-programs accept a -h
argument for help:
+keys.
+
+
+Termmines running in a
+Terminal
+
+Hit CTRL
-C
to exit. Probe
+it for help. Most Linux programs accept a -h
argument for
+help:
You should now have all the information you need to understand how
@@ -223,7 +229,18 @@ open
In the Debugger tool, click the dropdown ▾ for the debug icon in the global tool
-bar, and select “Debug termmines in GDB locally IN-VM.”
+bar, and select Configure and Launch termmines using… →
+gdb .
+
+
+Launch GDB Dialog
+
+Change the Run Command to “start” (not
+“starti”). NOTE : In practice, this is rarely
+recommended, because most targets do not export their main
+function.
+Click the Launch button in the dialog.
Wait a bit then verify the Dynamic Listing window (top) is
displaying disassembly code.
@@ -236,20 +253,18 @@ termmines
Launching on Windows
-On Windows, we will use dbgeng to debug the specimen. This is the
-engine that backs WinDbg. You may choose an alternative Minesweeper,
-since terminal applications are less representative of Windows
-executables. Follow the same process as for Linux, except import
-termmines.exe
and select “Debug termmines.exe in dbgeng
-locally IN-VM.”
+On Windows, we will use the Windows Debugger dbgeng.dll to debug the
+specimen. This is the engine that backs WinDbg. You may choose an
+alternative Minesweeper, since terminal applications are less
+representative of Windows executables. Follow the same process as for
+Linux, except import termmines.exe
and select
+Configure and Launch termmines.exe using… → dbgeng .
Launching on macOS
-Unfortunately, things are not so simple on macOS. See the
-instructions for Building
-LLDB-Java Bindings . Once built, follow the same process as for
-Linux, except select “Debug termmines in LLDB locally IN-VM.”
+On macOS, we will use LLDB to debug the specimen. This is the
+debugger included with Xcode. Follow the same process as for Linux,
+except choose lldb in the last menu.
Troubleshooting
@@ -280,73 +295,71 @@ tool. If it is still not there, then you may need to re-import the
default Debugger tool as under the previous heading. If it is still not
there, your installation may be corrupt.
-
-There is no “Debug termmines in GDB locally IN-VM” option in the
-launch drop-down
-You may need to install GDB and/or configure Ghidra with its
-location. If you have a copy or custom build of GDB in a non-system
-path, note its full path. If you intend to use the system’s copy of GDB,
-then in a terminal:
-
-Note the path given. (If you get an error, then you need to install
-GDB.) In a terminal, type the full path of GDB to ensure it executes
-properly. Type q
to quit GDB.
-
-From the Debugger Targets window, click the Connect button.
-In the Connect dialog, select “gdb” from the dropdown at the
-top.
-Enter the full path, e.g., /usr/bin/gdb
, in the “GDB
-launch command” field.
-Click “Connect”
-If you get an Interpreter window, then things have gone well.
-Type echo test
into it to verify it’s responsive, then
-type q
to disconnect.
-Close the Debugger tool, then retry.
-
+There is no gdb option in the launch drop-down
+You may have an older Debugger tool still configured for
+Recorder-based targets. We are transitioning to TraceRmi-based targets.
+Delete your Debugger tool and re-import the default one using the
+instructions above. If it is still not there, it’s possible your
+installation is corrupt. Search for a file called
+local-gdb.sh
in your installation. Unlike the previous
+system, Trace RMI will not probe your system for dependencies nor hide
+incompatible launchers. All installed launchers should be present in the
+menus, even though some may not work on your configuration.
-The launch hangs for several seconds and then prompt for a
-“recorder”
-You probably have a stale GDB connection, so when you launched you
-now have multiple connections. For the prompt, select the option with
-the highest score. Examine the Targets window to confirm you have
-multiple GDB connections. If you know which is the stale connection, you
-can right-click it and choose Disconnect . Otherwise,
-use Disconnect All from the drop-down menu and
-re-launch.
+The launch hangs for several seconds and then I get prompted with a
+wall of text
+Read the wall of text. The first line should tell you the exception
+that it encountered. Often this is a timeout. Press the
+Keep button and then find the Terminal, usually in the
+bottom right. If you do not see it there, check the Window →
+Terminals menu. Once you have found the Terminal, check its
+output starting at the top for diagnostic messages. If you have
+something like bash: gdb: command not found
, it is because
+you are missing gdb
, or you need to tell Ghidra where to
+find it.
+If it is just missing, then install it and try again. If you need to
+tell Ghidra where it is, then in the launcher drop-down, select
+Configure and Launch termmines using… → gdb . DO NOT
+select Re-launch termmines using gdb , since this will
+not allow you to correct the configuration.
The Dynamic Listing is empty
Check for an actual connection. You should see an entry in the
-Debugger Targets window, a populated Object window, and there should be
-an Interpreter window. If not, then your GDB connector may not be
-configured properly. Try the steps under the previous heading.
-If you have an Interpreter window, there are several
+Connection Manager window, a populated
+Model window, and there should be a
+Terminal window. If not, then your GDB connector may
+not be configured properly. Try the steps under the previous
+heading.
+If you have a Terminal window, there are several
possibilities:
Ghidra or GDB failed to launch the target:
-Check that the original termmines
exists and is
-executable. It must be at the path from where it was originally
-imported. If you imported from a share, consider copying it locally,
-setting its permissions, then re-importing.
+If this is the case, you should see an error message in the Terminal,
+e.g.: termmines: no such file or directory
. Check that the
+original termmines
exists and is executable. You may also
+need to adjust the Image option when configuring the
+launch.
You hit an uncommon bug where the memory map is not applied
properly
-This is the case if the Dynamic Listing is completely blank but the
-Regions window is replete. The Dynamic Listing just needs to be kicked a
-little. The easiest way is to step once, using the Step Into button in the
-main toolbar. If this is not desirable, then you can toggle
-Force Full View back and forth. In the Regions window,
-use the drop-down menu to toggle it on, then toggle it off. The Dynamic
-Listing should now be populated. To go to the program counter,
-double-click the “pc = …” label in the top right.
+This is the case if the Dynamic Listing is
+completely blank but the Regions window is replete. The
+Dynamic Listing just needs to be kicked a little. The
+easiest way is to step once, using the Step Into button in the main
+toolbar. If this is not desirable, then you can toggle Force
+Full View back and forth. In the Regions
+window, use the drop-down menu to toggle it on, then toggle it off. The
+Dynamic Listing should now be populated. To go to the
+program counter, double-click the “pc = …” label in the top right.
Something else has gone wrong
Try typing info inferiors
and similar GDB diagnostic
-commands into the Interpreter.
+commands into the Terminal .
The listings are in sync, but the Dynamic Listing is grey 00s
-Check the Auto-Read drop-down near the top right of the Dynamic
-Listing. It should be set to Read Visible Memory, RO
-Once .
+Check the Auto-Read drop-down near the top right of
+the Dynamic Listing . It should be set to Read
+Visible Memory, RO Once .
@@ -395,63 +408,66 @@ Once.
termmines
and/or start a new Ghidra Project. Starting from
the beginning, import termmines
and launch it in the Ghidra
Debugger with GDB. When your tool looks like the screenshot with a
-populated Dynamic Listing, you have completed the exercise. Disconnect
-before proceeding to the next exercise.
+populated Dynamic Listing , you have completed the
+exercise. Disconnect before proceeding to the next exercise.
Customized Launching
For this specimen, you may occasionally need to provide custom
command-line parameters. By default, Ghidra attempts to launch the
-target without any parameters. In the menus, use Debugger →
-Debug termmmines → in GDB locally IN-VM to launch with
-customizations. Ghidra will remember these customizations the next time
-you launch using the drop-down button from the toolbar. The first dialog
-allows you to customize the connection to the back-end debugger. Unless
-you have a special copy of GDB, you should probably just click Connect.
-The second dialog allows you to customize how the back-end debugger
-launches the target. This is where you tweak the command line. You can
-also change the actual image, in case it has moved or you want to
-experiment with a patched version.
+target without any parameters. In the Debugger menu, or
+the Launch button’s drop-down menu, use
+Configure and Launch termmmines → gdb to adjust your
+configuration. This is where you can specify the image path and
+command-line parameters of your target. Ghidra will remember this
+configuration the next time you launch using the drop-down button from
+the toolbar. Launchers with memorized configurations are presented as
+Re-launch termmines using… options. Using one of those
+entries will re-launch with the saved configuration rather than
+prompting.
Exercise: Launch with Command-line Help
Launch the specimen so that it prints its usage. When successful, you
-will see the usage info in the Debugger’s Interpreter window.
-NOTE : The process will terminate after printing its
-usage, and as a result, the rest of the UI will be mostly empty.
+will see the usage info in the Debugger’s Terminal
+window. NOTE : The process will terminate after printing
+its usage, and as a result, the rest of the UI will be mostly empty.
Attaching
-Attaching is slightly more advanced, but because the target will need
-to read from stdin, and Ghidra does not properly attach the Interpreter
-to stdin, we will need to launch the target in a terminal and attach to
-it instead. Note this technique is only possible because the target
-waits for input. Depending on the task for future exercises, you may
-still need to launch from the Debugger instead of attaching.
+Attaching is slightly more advanced, but can be useful if the target
+is part of a larger system, and it needs to be running in situ .
+For this section, we will just run termmines
in a separate
+terminal and then attach to it from Ghidra. This used to be required,
+because the older Recorder-based system did not provide target I/O, but
+this limitation is overcome by the new Terminal window
+when using Trace RMI. Note this technique is only possible because the
+target waits for input.
-Run termmines
in a proper terminal with the desired
-command-line parameters.
-In the Ghidra Debugger, find the Targets window, and click the Connect button.
-Select “gdb” from the drop-down box.
-This dialog should look familiar from the Customized Launching
-section. Just click the Connect button.
-In the Objects window (below the Targets window), expand the node
-labeled “Available.”
+Run termmines
in a terminal outside of Ghidra with the
+desired command-line parameters.
+In the Ghidra Debugger, use the Launch button
+drop-down and select Configured and Launch termmines using… →
+raw gdb . The “raw” connector will give us a GDB session without
+a target.
+Ghidra needs to know the location of gdb and the architecture of the
+intended target. The defaults are correct for 64-bit x86 targets using
+the system’s copy of GDB. Probably, you can just click
+Launch .
+In the Model window (to the left), expand the
+Available node.
In the filter box, type termmines
.
-Right-click on the termmines process and select Attach. If this
-fails, select Available again, and click the
- Refresh
-button.
+Note the PID, e.g. 1234, then in the Terminal type,
+e.g., attach 1234
.
Exercise: Attach
Try attaching on your own, if you have not already. Check your work
-by typing bt
into the Interpreter. If you are in
-read
you have completed this exercise. Disconnect before
-proceeding to the next module: A Tour of the
-UI
+by typing bt
into the Terminal . If you are
+in read
you have completed this exercise. Quit GDB from the
+Terminal before proceeding to the next module: A Tour of the UI
Troubleshooting
@@ -464,14 +480,15 @@ be traced by any other process and then executes a shell command. Using
specimen in the permissive process, and thus you can attach to it as if
ptrace_scope=0
, but without reducing the security of the
rest of the system. For example:
-./anyptracer 'exec ./termmines'
+./anyptracer 'exec ./termmines'
Alternatively, if you have root access, you can rectify the issue
using the relevant documentation available online.
-Beware! You should not modify this setting on your
-daily driver, as this substantially reduces the security of your system.
-Any compromised process would be allowed to attach to and steal data,
-e.g., credentials, from any other process owned by the same user.
+Beware! You should not set ptrace_scope=0
+globally, except on a system set aside for debugging, as this
+substantially reduces the security of that system. Any compromised
+process would be allowed to attach to and steal data, e.g., credentials,
+from any other process owned by the same user.