From 59d4b56bdf6b934bfd1c2269c81d8cbbab312c1c Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Tue, 19 Oct 2021 11:53:16 -0400
Subject: [PATCH] GP-1356: Parceling GDB's listRegisters into smaller commands
(< 4096 chars each)
---
.../gdb/manager/impl/GdbManagerImpl.java | 1 +
.../agent/gdb/manager/impl/GdbThreadImpl.java | 69 ++++++++++++++-----
...bstractGdbCommandWithThreadAndFrameId.java | 3 +-
.../cmd/AbstractGdbCommandWithThreadId.java | 3 +-
.../manager/impl/cmd/GdbEvaluateCommand.java | 6 +-
.../impl/GdbModelTargetRegisterContainer.java | 10 +--
6 files changed, 66 insertions(+), 26 deletions(-)
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 494c64dda0..c90ebc2f7e 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
@@ -72,6 +72,7 @@ import sun.misc.SignalHandler;
*/
public class GdbManagerImpl implements GdbManager {
private static final String GDB_IS_TERMINATING = "GDB is terminating";
+ public static final int MAX_CMD_LEN = 4094; // Account for longest possible line end
private static final String PTY_DIALOG_MESSAGE_PATTERN =
"
Please enter:
" +
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java
index c194b8999f..472329578f 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbThreadImpl.java
@@ -19,9 +19,6 @@ import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
-
-import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.RangeSet;
@@ -34,8 +31,7 @@ import agent.gdb.manager.impl.cmd.GdbConsoleExecCommand.CompletesWithRunning;
import agent.gdb.manager.parsing.GdbCValueParser;
import agent.gdb.manager.parsing.GdbParsingUtils.GdbParseError;
import agent.gdb.manager.reason.GdbReason;
-import ghidra.async.AsyncLazyValue;
-import ghidra.async.AsyncReference;
+import ghidra.async.*;
/**
* The implementation of {@link GdbThread}
@@ -159,6 +155,46 @@ public class GdbThreadImpl implements GdbThread {
return registers.request();
}
+ private List generateEvaluateSizesParts(Collection names) {
+ List result = new ArrayList<>();
+ StringBuffer buf = new StringBuffer("{");
+ for (String n : names) {
+ String toAdd = "sizeof($" + n + "),";
+ if (buf.length() + toAdd.length() > GdbEvaluateCommand.MAX_EXPR_LEN) {
+ assert buf.length() > 0;
+ // Remove trailing comma
+ result.add(buf.substring(0, buf.length() - 1) + "}");
+ buf.delete(1, buf.length()); // Leave leading brace
+ }
+ buf.append(toAdd);
+ }
+ if (buf.length() > 0) {
+ result.add(buf.substring(0, buf.length() - 1) + "}");
+ }
+ return result;
+ }
+
+ private CompletableFuture> doEvaluateSizesInParts(Collection names) {
+ List parts = generateEvaluateSizesParts(names);
+ if (parts.isEmpty()) {
+ // I guess names was empty
+ return CompletableFuture.completedFuture(List.of());
+ }
+ if (parts.size() == 1) {
+ return evaluate(parts.get(0)).thenApply(List::of);
+ }
+ AsyncFence fence = new AsyncFence();
+ String[] result = new String[parts.size()];
+ for (int i = 0; i < result.length; i++) {
+ String p = parts.get(i);
+ final int j = i;
+ fence.include(evaluate(p).thenAccept(r -> {
+ result[j] = r;
+ }));
+ }
+ return fence.ready().thenApply(__ -> Arrays.asList(result));
+ }
+
private CompletableFuture doListRegisters() {
Map namesByNumber = new TreeMap<>();
return execute(new GdbListRegisterNamesCommand(manager, id)).thenCompose(names -> {
@@ -169,20 +205,17 @@ public class GdbThreadImpl implements GdbThread {
}
namesByNumber.put(i, n);
}
- List sizeofNames = namesByNumber.values()
- .stream()
- .map(n -> "sizeof($" + n + ")")
- .collect(Collectors.toList());
- String expr = "{" + StringUtils.join(sizeofNames, ",") + "}";
- return evaluate(expr);
- }).thenApply(value -> {
+ return doEvaluateSizesInParts(namesByNumber.values());
+ }).thenApply(values -> {
List regs = new ArrayList<>();
- List sizes;
- try {
- sizes = GdbCValueParser.parseArray(value).expectInts();
- }
- catch (GdbParseError e) {
- throw new AssertionError("GDB did not give an integer array!");
+ List sizes = new ArrayList<>();
+ for (String v : values) {
+ try {
+ sizes.addAll(GdbCValueParser.parseArray(v).expectInts());
+ }
+ catch (GdbParseError e) {
+ throw new AssertionError("GDB did not give an integer array!");
+ }
}
if (sizes.size() != namesByNumber.size()) {
throw new AssertionError("GDB did not give all the sizes!");
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java
index c40dd6c2ff..6124c9f39b 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadAndFrameId.java
@@ -25,6 +25,7 @@ import agent.gdb.manager.impl.GdbManagerImpl;
*/
public abstract class AbstractGdbCommandWithThreadAndFrameId
extends AbstractGdbCommandWithThreadId {
+ protected static final String MI2_FRAME_PREFIX = " --frame ";
protected final Integer frameId;
/**
@@ -41,7 +42,7 @@ public abstract class AbstractGdbCommandWithThreadAndFrameId
}
protected String makeFramePart() {
- return frameId == null ? "" : " --frame " + frameId;
+ return frameId == null ? "" : MI2_FRAME_PREFIX + frameId;
}
/**
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java
index 8575b4a021..6efdf3529b 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/AbstractGdbCommandWithThreadId.java
@@ -23,6 +23,7 @@ import agent.gdb.manager.impl.GdbManagerImpl;
* @param the type of object "returned" by the command
*/
public abstract class AbstractGdbCommandWithThreadId extends AbstractGdbCommand {
+ protected static final String MI2_THREAD_PREFIX = " --thread ";
protected final Integer threadId;
/**
@@ -37,7 +38,7 @@ public abstract class AbstractGdbCommandWithThreadId extends AbstractGdbComma
}
protected String makeThreadPart() {
- return threadId == null ? "" : " --thread " + threadId;
+ return threadId == null ? "" : MI2_THREAD_PREFIX + threadId;
}
@Override
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java
index 98f91e0ffc..a2e4b0e8b8 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbEvaluateCommand.java
@@ -26,6 +26,10 @@ import agent.gdb.manager.impl.GdbPendingCommand;
* Implementation of {@link GdbInferior#evaluate(String)}
*/
public class GdbEvaluateCommand extends AbstractGdbCommandWithThreadAndFrameId {
+ private static final String MI2_CMD = "-data-evaluate-expression";
+ // 6 accounts for digits in threadId and frameId. 999 each should be plenty....
+ public static final int MAX_EXPR_LEN = GdbManagerImpl.MAX_CMD_LEN - MI2_CMD.length() -
+ MI2_THREAD_PREFIX.length() - MI2_FRAME_PREFIX.length() - 6;
private final String expression;
public GdbEvaluateCommand(GdbManagerImpl manager, Integer threadId, Integer frameId,
@@ -36,7 +40,7 @@ public class GdbEvaluateCommand extends AbstractGdbCommandWithThreadAndFrameId
implements TargetRegisterContainer {
@@ -86,9 +89,6 @@ public class GdbModelTargetRegisterContainer
}
public CompletableFuture refreshInternal() {
- if (!isObserved()) {
- return AsyncUtils.NIL;
- }
return doRefresh().exceptionally(ex -> {
Msg.error(this, "Problem refreshing inferior's register descriptions", ex);
return null;