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;