mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-1364: Fixed parsing issues for several breakpoint types. Added CATCHPOINT
This commit is contained in:
parent
1a15d7893a
commit
5e324104a5
5 changed files with 116 additions and 36 deletions
|
@ -24,10 +24,12 @@ import agent.gdb.manager.parsing.GdbParsingUtils;
|
||||||
/**
|
/**
|
||||||
* Information about a GDB breakpoint
|
* Information about a GDB breakpoint
|
||||||
*
|
*
|
||||||
* The contains the semantic processing for GDB breakpoint information. Mostly, it just stores the
|
* <p>
|
||||||
|
* 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"
|
* information, but it also enumerates the locations of a breakpoint and generates the "effective"
|
||||||
* breakpoints.
|
* breakpoints.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Note this is not a handle to the breakpoint. Rather, this is the captured information from some
|
* 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
|
* event or request. If other commands have been executed since this information was gathered, the
|
||||||
* information may be stale.
|
* information may be stale.
|
||||||
|
@ -48,8 +50,10 @@ public class GdbBreakpointInfo {
|
||||||
/**
|
/**
|
||||||
* Process a parsed GDB breakpoint information block
|
* Process a parsed GDB breakpoint information block
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* The passed info should be the field list containing "{@code bkpt={...}}."
|
* The passed info should be the field list containing "{@code bkpt={...}}."
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* It may also contain {@code wpt}, {@code hw-awpt}, or {@code hw-rwpt}, in which case the
|
* 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
|
* "parsed info" is synthesized to match what would be given by {@code -break-list} for that
|
||||||
* watchpoint.
|
* watchpoint.
|
||||||
|
@ -65,15 +69,18 @@ public class GdbBreakpointInfo {
|
||||||
}
|
}
|
||||||
GdbMiFieldList wpt = info.getFieldList("wpt");
|
GdbMiFieldList wpt = info.getFieldList("wpt");
|
||||||
if (wpt != null) {
|
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");
|
GdbMiFieldList hwAWpt = info.getFieldList("hw-awpt");
|
||||||
if (hwAWpt != null) {
|
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");
|
GdbMiFieldList hwRWpt = info.getFieldList("hw-rwpt");
|
||||||
if (hwRWpt != null) {
|
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);
|
throw new AssertionError("No breakpoint or watchpoint in info: " + info);
|
||||||
}
|
}
|
||||||
|
@ -88,21 +95,27 @@ public class GdbBreakpointInfo {
|
||||||
*/
|
*/
|
||||||
public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt,
|
public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt,
|
||||||
List<GdbBreakpointLocation> allLocs, Integer curIid) {
|
List<GdbBreakpointLocation> allLocs, Integer curIid) {
|
||||||
|
// TODO: A more polymorphic approach to info types?
|
||||||
long number = Long.parseLong(bkpt.getString("number"));
|
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"));
|
GdbBreakpointDisp disp = GdbBreakpointDisp.fromStr(bkpt.getString("disp"));
|
||||||
boolean enabled = "y".equals(bkpt.getString("enabled"));
|
boolean enabled = "y".equals(bkpt.getString("enabled"));
|
||||||
String addr = bkpt.getString("addr");
|
String addr = bkpt.getString("addr");
|
||||||
String at = bkpt.getString("at");
|
String what = bkpt.getString("at");
|
||||||
if (at == null) {
|
if (what == null) {
|
||||||
at = bkpt.getString("what");
|
what = bkpt.getString("what");
|
||||||
}
|
}
|
||||||
|
String catchType = bkpt.getString("catch-type");
|
||||||
String origLoc = bkpt.getString("original-location");
|
String origLoc = bkpt.getString("original-location");
|
||||||
String pending = bkpt.getString("pending");
|
String pending = bkpt.getString("pending");
|
||||||
int times = Integer.parseInt(bkpt.getString("times"));
|
int times = Integer.parseInt(bkpt.getString("times"));
|
||||||
List<GdbBreakpointLocation> locations = new ArrayList<>();
|
List<GdbBreakpointLocation> locations = new ArrayList<>();
|
||||||
|
|
||||||
if ("<MULTIPLE>".equals(addr)) {
|
if (type == GdbBreakpointType.CATCHPOINT) {
|
||||||
|
// no locations
|
||||||
|
}
|
||||||
|
else if ("<MULTIPLE>".equals(addr)) {
|
||||||
allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add);
|
allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -112,8 +125,8 @@ public class GdbBreakpointInfo {
|
||||||
}
|
}
|
||||||
locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids));
|
locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids));
|
||||||
}
|
}
|
||||||
return new GdbBreakpointInfo(number, type, disp, addr, at, origLoc, pending, enabled, times,
|
return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType, origLoc,
|
||||||
locations);
|
pending, enabled, times, locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +138,7 @@ public class GdbBreakpointInfo {
|
||||||
* @return the info
|
* @return the info
|
||||||
*/
|
*/
|
||||||
public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type,
|
public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type,
|
||||||
int curIid) {
|
String typeName, int curIid) {
|
||||||
int number = Integer.parseInt(wpt.getString("number"));
|
int number = Integer.parseInt(wpt.getString("number"));
|
||||||
String origLoc = wpt.getString("exp");
|
String origLoc = wpt.getString("exp");
|
||||||
|
|
||||||
|
@ -136,8 +149,8 @@ public class GdbBreakpointInfo {
|
||||||
else {
|
else {
|
||||||
locs = List.of();
|
locs = List.of();
|
||||||
}
|
}
|
||||||
return new GdbBreakpointInfo(number, type, GdbBreakpointDisp.KEEP, null, origLoc, origLoc,
|
return new GdbBreakpointInfo(number, type, typeName, GdbBreakpointDisp.KEEP, null, null,
|
||||||
null, true, 0, locs);
|
origLoc, origLoc, null, true, 0, locs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,9 +178,11 @@ public class GdbBreakpointInfo {
|
||||||
|
|
||||||
private final long number;
|
private final long number;
|
||||||
private final GdbBreakpointType type;
|
private final GdbBreakpointType type;
|
||||||
|
private final String typeName;
|
||||||
private final GdbBreakpointDisp disp;
|
private final GdbBreakpointDisp disp;
|
||||||
private final String addr;
|
private final String addr;
|
||||||
private final String at;
|
private final String what;
|
||||||
|
private final String catchType;
|
||||||
private final String originalLocation;
|
private final String originalLocation;
|
||||||
private final String pending;
|
private final String pending;
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
|
@ -181,19 +196,22 @@ public class GdbBreakpointInfo {
|
||||||
* @param type the type of breakpoint
|
* @param type the type of breakpoint
|
||||||
* @param disp the breakpoint disposition
|
* @param disp the breakpoint disposition
|
||||||
* @param addr the location of the breakpoint
|
* @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 pending if pending, the location that is not yet resolved
|
||||||
* @param enabled true if the breakpoint is enabled, false otherwise
|
* @param enabled true if the breakpoint is enabled, false otherwise
|
||||||
* @param times the number of times the breakpoint has been hit
|
* @param times the number of times the breakpoint has been hit
|
||||||
* @param locations the resolved address(es) of this breakpoint
|
* @param locations the resolved address(es) of this breakpoint
|
||||||
*/
|
*/
|
||||||
GdbBreakpointInfo(long number, GdbBreakpointType type, GdbBreakpointDisp disp, String addr,
|
GdbBreakpointInfo(long number, GdbBreakpointType type, String typeName, GdbBreakpointDisp disp,
|
||||||
String at, String origLoc, String pending, boolean enabled, int times,
|
String addr, String what, String catchType, String origLoc, String pending,
|
||||||
List<GdbBreakpointLocation> locations) {
|
boolean enabled, int times, List<GdbBreakpointLocation> locations) {
|
||||||
this.number = number;
|
this.number = number;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.typeName = typeName;
|
||||||
this.disp = disp;
|
this.disp = disp;
|
||||||
this.addr = addr;
|
this.addr = addr;
|
||||||
this.at = at;
|
this.what = what;
|
||||||
|
this.catchType = catchType;
|
||||||
this.originalLocation = origLoc;
|
this.originalLocation = origLoc;
|
||||||
this.pending = pending;
|
this.pending = pending;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
@ -268,6 +286,19 @@ public class GdbBreakpointInfo {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of breakpoint as named by GDB
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 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
|
* 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
|
* @return the location
|
||||||
*/
|
*/
|
||||||
public String getWhat() {
|
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) {
|
if (isEnabled() == enabled) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new GdbBreakpointInfo(number, type, disp, addr, at, originalLocation, pending,
|
return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType,
|
||||||
enabled, times, locations);
|
originalLocation, pending, enabled, times, locations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,10 @@ public enum GdbBreakpointType {
|
||||||
* An access (r/w) watchpoint, usually set via {@code awatch}
|
* An access (r/w) watchpoint, usually set via {@code awatch}
|
||||||
*/
|
*/
|
||||||
ACCESS_WATCHPOINT("acc watchpoint", true),
|
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
|
* Some type not known to the manager
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -649,6 +649,7 @@ public class GdbManagerImpl implements GdbManager {
|
||||||
cliThread.setName("GDB Read CLI");
|
cliThread.setName("GDB Read CLI");
|
||||||
// Looks terrible, but we're already in this world
|
// Looks terrible, but we're already in this world
|
||||||
cliThread.writer.print("set confirm off" + newLine);
|
cliThread.writer.print("set confirm off" + newLine);
|
||||||
|
cliThread.writer.print("set pagination off" + newLine);
|
||||||
cliThread.writer
|
cliThread.writer
|
||||||
.print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine);
|
.print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine);
|
||||||
cliThread.writer.flush();
|
cliThread.writer.flush();
|
||||||
|
@ -715,9 +716,11 @@ public class GdbManagerImpl implements GdbManager {
|
||||||
*/
|
*/
|
||||||
protected CompletableFuture<Void> rc() {
|
protected CompletableFuture<Void> rc() {
|
||||||
if (cliThread != null) {
|
if (cliThread != null) {
|
||||||
|
// NB. confirm and pagination are already disabled here
|
||||||
return AsyncUtils.NIL;
|
return AsyncUtils.NIL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// NB. Don't disable pagination here. MI2 is not paginated.
|
||||||
return console("set confirm off", CompletesWithRunning.CANNOT);
|
return console("set confirm off", CompletesWithRunning.CANNOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,11 +90,7 @@ public class GdbModelTargetBreakpointLocation
|
||||||
*/
|
*/
|
||||||
protected CompletableFuture<Void> initWpt() {
|
protected CompletableFuture<Void> initWpt() {
|
||||||
assert loc.getAddr() == null;
|
assert loc.getAddr() == null;
|
||||||
String what = parent.info.getWhat();
|
String exp = 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());
|
|
||||||
int iid = Unique.assertOne(loc.getInferiorIds());
|
int iid = Unique.assertOne(loc.getInferiorIds());
|
||||||
GdbModelTargetInferior inf = impl.session.inferiors.getTargetInferior(iid);
|
GdbModelTargetInferior inf = impl.session.inferiors.getTargetInferior(iid);
|
||||||
String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp);
|
String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp);
|
||||||
|
|
|
@ -19,8 +19,7 @@ import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import agent.gdb.manager.breakpoint.GdbBreakpointInfo;
|
import agent.gdb.manager.breakpoint.*;
|
||||||
import agent.gdb.manager.breakpoint.GdbBreakpointLocation;
|
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
import ghidra.dbg.agent.DefaultTargetObject;
|
import ghidra.dbg.agent.DefaultTargetObject;
|
||||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||||
|
@ -172,9 +171,11 @@ public class GdbModelTargetBreakpointSpec extends
|
||||||
protected void updateAttributesFromInfo(String reason) {
|
protected void updateAttributesFromInfo(String reason) {
|
||||||
changeAttributes(List.of(), Map.of(
|
changeAttributes(List.of(), Map.of(
|
||||||
ENABLED_ATTRIBUTE_NAME, enabled = info.isEnabled(),
|
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),
|
KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info),
|
||||||
DISPLAY_ATTRIBUTE_NAME, updateDisplay()),
|
DISPLAY_ATTRIBUTE_NAME, display = computeDisplay()),
|
||||||
reason);
|
reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +187,7 @@ public class GdbModelTargetBreakpointSpec extends
|
||||||
this.info = newInfo;
|
this.info = newInfo;
|
||||||
List<GdbModelTargetBreakpointLocation> effectives = newInfo.getLocations()
|
List<GdbModelTargetBreakpointLocation> effectives = newInfo.getLocations()
|
||||||
.stream()
|
.stream()
|
||||||
|
.filter(l -> !"<PENDING>".equals(l.getAddr()))
|
||||||
.map(this::getTargetBreakpointLocation)
|
.map(this::getTargetBreakpointLocation)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
breaksBySub.keySet()
|
breaksBySub.keySet()
|
||||||
|
@ -237,11 +239,46 @@ public class GdbModelTargetBreakpointSpec extends
|
||||||
i -> new GdbModelTargetBreakpointLocation(this, loc));
|
i -> new GdbModelTargetBreakpointLocation(this, loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String updateDisplay() {
|
protected String addressFromInfo() {
|
||||||
display = String.format("%d breakpoint %s %s %s %s",
|
if (info.getAddress() != null) {
|
||||||
info.getNumber(), info.getDisp(), info.isEnabled() ? "y" : "n", info.getAddress(),
|
return info.getAddress();
|
||||||
info.getWhat());
|
}
|
||||||
return display;
|
List<GdbBreakpointLocation> locs = info.getLocations();
|
||||||
|
if (locs.isEmpty()) {
|
||||||
|
return "<PENDING>";
|
||||||
|
}
|
||||||
|
if (locs.size() == 1) {
|
||||||
|
String addr = locs.get(0).getAddr();
|
||||||
|
if (addr == null) {
|
||||||
|
return "<PENDING>";
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return "<MULTIPLE>";
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue