From 3b0ae72f6a3c465fc80d5ddef8fa52d6dd037dc6 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Thu, 10 Nov 2022 13:44:27 -0500 Subject: [PATCH] GP-1459: Fixed parsing for GDB/MI-reported AArch64 vector registers. --- .../impl/cmd/GdbReadRegistersCommand.java | 73 ++++++++++++++----- .../impl/GdbReadRegistersCommandTest.java | 69 ++++++++++++++++++ 2 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbReadRegistersCommandTest.java diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java index a323fcf157..d21d4344dc 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/cmd/GdbReadRegistersCommand.java @@ -37,10 +37,54 @@ import ghidra.util.Msg; */ public class GdbReadRegistersCommand extends AbstractGdbCommandWithThreadAndFrameId> { - protected static final Set BYTE_ARRAY_KEYS = Set.of( - "v1_int8", "v2_int8", "v4_int8", "v8_int8", "v16_int8", "v32_int8", "v64_int8", // Observed on i386:x86-64 - "u8" // Observed on armv7 - ); + + protected static BigInteger tryStructPatterns(GdbCompositeValue cv, int byteCount, + ByteOrder endianness) { + for (Map.Entry ent : cv.entrySet()) { + switch (ent.getKey()) { + case "v1_int8", "v2_int8", "v4_int8", "v8_int8", "v16_int8", "v32_int8", "v64_int8": + // Observed on i386:x86-64 + case "u8": { + // Observed on armv7 + BigInteger result = (ent.getValue() instanceof GdbArrayValue arr) + ? packElements(arr, byteCount, 1, endianness) + : null; + if (result != null) { + return result; + } + } + case "b": { + // Observed on aarch64 + BigInteger result = (ent.getValue() instanceof GdbCompositeValue nested) + ? tryStructPatternsB(nested, byteCount, endianness) + : null; + if (result != null) { + return result; + } + } + } + } + return null; + } + + protected static BigInteger tryStructPatternsB(GdbCompositeValue cv, int byteCount, + ByteOrder endianness) { + for (Map.Entry ent : cv.entrySet()) { + switch (ent.getKey()) { + case "u": { + // Observed on aarch64, nested in "b" + BigInteger result = (ent.getValue() instanceof GdbArrayValue arr) + ? packElements(arr, byteCount, 1, endianness) + : null; + if (result != null) { + return result; + } + } + } + } + return null; + } + private final Set regs; private final GdbThreadImpl thread; @@ -68,7 +112,7 @@ public class GdbReadRegistersCommand return b.toString(); } - protected BigInteger packElements(GdbArrayValue av, int byteCount, int bytesPer, + public static BigInteger packElements(GdbArrayValue av, int byteCount, int bytesPer, ByteOrder endianness) { assert bytesPer * av.size() == byteCount; List elems = av.expectBigInts(); @@ -100,8 +144,8 @@ public class GdbReadRegistersCommand return new BigInteger(1, packed); } - protected BigInteger parseAndFindInteger(String val, int byteCount) throws GdbParseError { - ByteOrder endianness = thread.getInferior().getEndianness(); + public static BigInteger parseAndFindInteger(String val, int byteCount, ByteOrder endianness) + throws GdbParseError { if (val.contains("lbound")) { /** * TODO: This might be useful information, but I've only ever seen @@ -123,15 +167,9 @@ public class GdbReadRegistersCommand return iv.getValue(); } } - for (Map.Entry ent : cv.entrySet()) { - if (BYTE_ARRAY_KEYS.contains(ent.getKey())) { - GdbCValue int8v = ent.getValue(); - if (!(int8v instanceof GdbArrayValue)) { - throw new AssertionError("Expected an array of ints for " + ent); - } - GdbArrayValue int8a = (GdbArrayValue) int8v; - return packElements(int8a, byteCount, 1, endianness); - } + BigInteger result = tryStructPatterns(cv, byteCount, endianness); + if (result != null) { + return result; } } if (value instanceof GdbArrayValue) { @@ -157,6 +195,7 @@ public class GdbReadRegistersCommand regs.stream().collect(Collectors.toMap(GdbRegister::getNumber, r -> r)); List valueList = done.assumeRegisterValueList(); Map result = new LinkedHashMap<>(); + ByteOrder endianness = thread.getInferior().getEndianness(); for (GdbMiFieldList fields : valueList) { int number = Integer.parseInt(fields.getString("number")); String value = fields.getString("value"); @@ -166,7 +205,7 @@ public class GdbReadRegistersCommand continue; } try { - result.put(r, parseAndFindInteger(value, r.getSize())); + result.put(r, parseAndFindInteger(value, r.getSize(), endianness)); } catch (GdbParseError | AssertionError e) { Msg.warn(this, diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbReadRegistersCommandTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbReadRegistersCommandTest.java new file mode 100644 index 0000000000..414014f522 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/manager/impl/GdbReadRegistersCommandTest.java @@ -0,0 +1,69 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.gdb.manager.impl; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; +import java.nio.ByteOrder; + +import org.junit.Test; + +import agent.gdb.manager.impl.cmd.GdbReadRegistersCommand; + +public class GdbReadRegistersCommandTest { + @Test + public void testParseAndFindInteger_Integer() throws Exception { + String value = "0x1234"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 8, ByteOrder.LITTLE_ENDIAN)); + } + + @Test + public void testParseAndFindInteger_CompositeWithInteger() throws Exception { + String value = "{nope = {f = 0x0}, i = 0x1234}"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 8, ByteOrder.LITTLE_ENDIAN)); + } + + @Test + public void testParseAndFindInteger_Array() throws Exception { + String value = "{0x34, 0x12}"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 2, ByteOrder.LITTLE_ENDIAN)); + } + + @Test + public void testParseAndFindInteger_PatternX64() throws Exception { + String value = "{v64_int8 = {0x34, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 8, ByteOrder.LITTLE_ENDIAN)); + } + + @Test + public void testParseAndFindInteger_PatternARMv7() throws Exception { + String value = "{u8 = {0x34, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 8, ByteOrder.LITTLE_ENDIAN)); + } + + @Test + public void testParseAndFindInteger_PatternAArch64() throws Exception { + String value = "{b = {u = {0x34, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}"; + assertEquals(BigInteger.valueOf(0x1234), + GdbReadRegistersCommand.parseAndFindInteger(value, 8, ByteOrder.LITTLE_ENDIAN)); + } +}