diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java index ddeea7b792..2dc308def0 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointInfo.java @@ -24,10 +24,12 @@ import agent.gdb.manager.parsing.GdbParsingUtils; /** * Information about a GDB breakpoint * - * The contains the semantic processing for GDB breakpoint information. Mostly, it just stores the + *

+ * This contains the semantic processing for GDB breakpoint information. Mostly, it just stores the * information, but it also enumerates the locations of a breakpoint and generates the "effective" * breakpoints. * + *

* Note this is not a handle to the breakpoint. Rather, this is the captured information from some * event or request. If other commands have been executed since this information was gathered, the * information may be stale. @@ -48,8 +50,10 @@ public class GdbBreakpointInfo { /** * Process a parsed GDB breakpoint information block * + *

* The passed info should be the field list containing "{@code bkpt={...}}." * + *

* It may also contain {@code wpt}, {@code hw-awpt}, or {@code hw-rwpt}, in which case the * "parsed info" is synthesized to match what would be given by {@code -break-list} for that * watchpoint. @@ -65,15 +69,18 @@ public class GdbBreakpointInfo { } GdbMiFieldList wpt = info.getFieldList("wpt"); if (wpt != null) { - return parseWpt(wpt, GdbBreakpointType.HW_WATCHPOINT, curIid); + return parseWpt(wpt, GdbBreakpointType.HW_WATCHPOINT, + GdbBreakpointType.HW_WATCHPOINT.getName(), curIid); } GdbMiFieldList hwAWpt = info.getFieldList("hw-awpt"); if (hwAWpt != null) { - return parseWpt(hwAWpt, GdbBreakpointType.ACCESS_WATCHPOINT, curIid); + return parseWpt(hwAWpt, GdbBreakpointType.ACCESS_WATCHPOINT, + GdbBreakpointType.ACCESS_WATCHPOINT.getName(), curIid); } GdbMiFieldList hwRWpt = info.getFieldList("hw-rwpt"); if (hwRWpt != null) { - return parseWpt(hwRWpt, GdbBreakpointType.READ_WATCHPOINT, curIid); + return parseWpt(hwRWpt, GdbBreakpointType.READ_WATCHPOINT, + GdbBreakpointType.READ_WATCHPOINT.getName(), curIid); } throw new AssertionError("No breakpoint or watchpoint in info: " + info); } @@ -88,21 +95,27 @@ public class GdbBreakpointInfo { */ public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt, List allLocs, Integer curIid) { + // TODO: A more polymorphic approach to info types? long number = Long.parseLong(bkpt.getString("number")); - GdbBreakpointType type = GdbBreakpointType.fromStr(bkpt.getString("type")); + String typeName = bkpt.getString("type"); + GdbBreakpointType type = GdbBreakpointType.fromStr(typeName); GdbBreakpointDisp disp = GdbBreakpointDisp.fromStr(bkpt.getString("disp")); boolean enabled = "y".equals(bkpt.getString("enabled")); String addr = bkpt.getString("addr"); - String at = bkpt.getString("at"); - if (at == null) { - at = bkpt.getString("what"); + String what = bkpt.getString("at"); + if (what == null) { + what = bkpt.getString("what"); } + String catchType = bkpt.getString("catch-type"); String origLoc = bkpt.getString("original-location"); String pending = bkpt.getString("pending"); int times = Integer.parseInt(bkpt.getString("times")); List locations = new ArrayList<>(); - if ("".equals(addr)) { + if (type == GdbBreakpointType.CATCHPOINT) { + // no locations + } + else if ("".equals(addr)) { allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add); } else { @@ -112,8 +125,8 @@ public class GdbBreakpointInfo { } locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids)); } - return new GdbBreakpointInfo(number, type, disp, addr, at, origLoc, pending, enabled, times, - locations); + return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType, origLoc, + pending, enabled, times, locations); } /** @@ -125,7 +138,7 @@ public class GdbBreakpointInfo { * @return the info */ public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type, - int curIid) { + String typeName, int curIid) { int number = Integer.parseInt(wpt.getString("number")); String origLoc = wpt.getString("exp"); @@ -136,8 +149,8 @@ public class GdbBreakpointInfo { else { locs = List.of(); } - return new GdbBreakpointInfo(number, type, GdbBreakpointDisp.KEEP, null, origLoc, origLoc, - null, true, 0, locs); + return new GdbBreakpointInfo(number, type, typeName, GdbBreakpointDisp.KEEP, null, null, + origLoc, origLoc, null, true, 0, locs); } /** @@ -165,9 +178,11 @@ public class GdbBreakpointInfo { private final long number; private final GdbBreakpointType type; + private final String typeName; private final GdbBreakpointDisp disp; private final String addr; - private final String at; + private final String what; + private final String catchType; private final String originalLocation; private final String pending; private final boolean enabled; @@ -181,19 +196,22 @@ public class GdbBreakpointInfo { * @param type the type of breakpoint * @param disp the breakpoint disposition * @param addr the location of the breakpoint + * @param what the "what" of the breakpoint * @param pending if pending, the location that is not yet resolved * @param enabled true if the breakpoint is enabled, false otherwise * @param times the number of times the breakpoint has been hit * @param locations the resolved address(es) of this breakpoint */ - GdbBreakpointInfo(long number, GdbBreakpointType type, GdbBreakpointDisp disp, String addr, - String at, String origLoc, String pending, boolean enabled, int times, - List locations) { + GdbBreakpointInfo(long number, GdbBreakpointType type, String typeName, GdbBreakpointDisp disp, + String addr, String what, String catchType, String origLoc, String pending, + boolean enabled, int times, List locations) { this.number = number; this.type = type; + this.typeName = typeName; this.disp = disp; this.addr = addr; - this.at = at; + this.what = what; + this.catchType = catchType; this.originalLocation = origLoc; this.pending = pending; this.enabled = enabled; @@ -268,6 +286,19 @@ public class GdbBreakpointInfo { return type; } + /** + * Get the type of breakpoint as named by GDB + * + *

+ * In case of {@link GdbBreakpointType#OTHER}, this at least reports the string GDB uses to name + * the type. + * + * @return the type name + */ + public String getTypeName() { + return typeName; + } + /** * Get the breakpoint disposition, i.e., what happens to the breakpoint once it has been hit * @@ -292,7 +323,16 @@ public class GdbBreakpointInfo { * @return the location */ public String getWhat() { - return at; + return what; + } + + /** + * For a catchpoint, get the event being caught + * + * @return the catch-type + */ + public String getCatchType() { + return catchType; } /** @@ -357,7 +397,7 @@ public class GdbBreakpointInfo { if (isEnabled() == enabled) { return this; } - return new GdbBreakpointInfo(number, type, disp, addr, at, originalLocation, pending, - enabled, times, locations); + return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType, + originalLocation, pending, enabled, times, locations); } } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java index 514092471d..8d74c980ab 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/breakpoint/GdbBreakpointType.java @@ -47,6 +47,10 @@ public enum GdbBreakpointType { * An access (r/w) watchpoint, usually set via {@code awatch} */ ACCESS_WATCHPOINT("acc watchpoint", true), + /** + * A catchpoint to catch (break on) events, usual set via {@code catch} + */ + CATCHPOINT("catchpoint", false), /** * Some type not known to the manager */ diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java index f0a18b0a08..494c64dda0 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java @@ -649,6 +649,7 @@ public class GdbManagerImpl implements GdbManager { cliThread.setName("GDB Read CLI"); // Looks terrible, but we're already in this world cliThread.writer.print("set confirm off" + newLine); + cliThread.writer.print("set pagination off" + newLine); cliThread.writer .print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine); cliThread.writer.flush(); @@ -715,9 +716,11 @@ public class GdbManagerImpl implements GdbManager { */ protected CompletableFuture rc() { if (cliThread != null) { + // NB. confirm and pagination are already disabled here return AsyncUtils.NIL; } else { + // NB. Don't disable pagination here. MI2 is not paginated. return console("set confirm off", CompletesWithRunning.CANNOT); } } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java index 0d26511bd4..0db305f048 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointLocation.java @@ -90,11 +90,7 @@ public class GdbModelTargetBreakpointLocation */ protected CompletableFuture initWpt() { assert loc.getAddr() == null; - String what = parent.info.getWhat(); - if (!what.startsWith(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX)) { - throw new AssertionError("non-location location"); - } - String exp = what.substring(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX.length()); + String exp = parent.info.getWhat(); int iid = Unique.assertOne(loc.getInferiorIds()); GdbModelTargetInferior inf = impl.session.inferiors.getTargetInferior(iid); String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp); diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java index 6cb60b1d18..01c26444fa 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetBreakpointSpec.java @@ -19,8 +19,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -import agent.gdb.manager.breakpoint.GdbBreakpointInfo; -import agent.gdb.manager.breakpoint.GdbBreakpointLocation; +import agent.gdb.manager.breakpoint.*; import ghidra.async.AsyncUtils; import ghidra.dbg.agent.DefaultTargetObject; import ghidra.dbg.target.TargetBreakpointSpec; @@ -172,9 +171,11 @@ public class GdbModelTargetBreakpointSpec extends protected void updateAttributesFromInfo(String reason) { changeAttributes(List.of(), Map.of( ENABLED_ATTRIBUTE_NAME, enabled = info.isEnabled(), - EXPRESSION_ATTRIBUTE_NAME, expression = info.getOriginalLocation(), + EXPRESSION_ATTRIBUTE_NAME, + expression = info.getType() == GdbBreakpointType.CATCHPOINT ? info.getCatchType() + : info.getOriginalLocation(), KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info), - DISPLAY_ATTRIBUTE_NAME, updateDisplay()), + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay()), reason); } @@ -186,6 +187,7 @@ public class GdbModelTargetBreakpointSpec extends this.info = newInfo; List effectives = newInfo.getLocations() .stream() + .filter(l -> !"".equals(l.getAddr())) .map(this::getTargetBreakpointLocation) .collect(Collectors.toList()); breaksBySub.keySet() @@ -237,11 +239,46 @@ public class GdbModelTargetBreakpointSpec extends i -> new GdbModelTargetBreakpointLocation(this, loc)); } - protected String updateDisplay() { - display = String.format("%d breakpoint %s %s %s %s", - info.getNumber(), info.getDisp(), info.isEnabled() ? "y" : "n", info.getAddress(), - info.getWhat()); - return display; + protected String addressFromInfo() { + if (info.getAddress() != null) { + return info.getAddress(); + } + List locs = info.getLocations(); + if (locs.isEmpty()) { + return ""; + } + if (locs.size() == 1) { + String addr = locs.get(0).getAddr(); + if (addr == null) { + return ""; + } + return addr; + } + return ""; + } + + protected String computeDisplay() { + Object enb = info.isEnabled() ? "y" : "n"; + String addr = addressFromInfo(); + String what = info.getWhat() == null ? "" : info.getWhat(); + switch (info.getType()) { + case ACCESS_WATCHPOINT: + case HW_WATCHPOINT: + case READ_WATCHPOINT: + case BREAKPOINT: + case HW_BREAKPOINT: + case OTHER: + return String.format("%d %s %s %s %s %s", info.getNumber(), info.getTypeName(), + info.getDisp(), enb, addr, what).trim(); + case CATCHPOINT: + return String.format("%d %s %s %s %s", info.getNumber(), info.getTypeName(), + info.getDisp(), enb, what).trim(); + case DPRINTF: + // TODO: script? + return String.format("%d %s %s %s %s %s", info.getNumber(), info.getTypeName(), + info.getDisp(), enb, addr, what).trim(); + } + throw new AssertionError(); } @Override