From 5e324104a5f95f78f8cc945d552c1d5f56b862e6 Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Wed, 6 Oct 2021 11:30:26 -0400
Subject: [PATCH] GP-1364: Fixed parsing issues for several breakpoint types.
Added CATCHPOINT
---
.../manager/breakpoint/GdbBreakpointInfo.java | 84 ++++++++++++++-----
.../manager/breakpoint/GdbBreakpointType.java | 4 +
.../gdb/manager/impl/GdbManagerImpl.java | 3 +
.../GdbModelTargetBreakpointLocation.java | 6 +-
.../impl/GdbModelTargetBreakpointSpec.java | 55 ++++++++++--
5 files changed, 116 insertions(+), 36 deletions(-)
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