From 3b4fefe3ba18e6b4d1b3fdcc60975f1350048600 Mon Sep 17 00:00:00 2001
From: d-millar <33498836+d-millar@users.noreply.github.com>
Date: Fri, 1 Nov 2024 14:36:53 -0400
Subject: [PATCH] GP-5034: post-review GP-5034: oops GP-5034: mods for android
etc GP-5034: running android debugger GP-5034: running android debugger
GP-5034: start of dalvik changes GP-5034: start of Dalvik changes GP-5034:
android-native launcher GP-5034: added mips:3000 GP-5034: fix for arch error
---
.../src/main/py/src/ghidragdb/arch.py | 27 +++---
.../data/debugger-launchers/android-lldb.sh | 82 +++++++++++++++++
.../src/main/py/src/ghidralldb/arch.py | 35 ++++---
.../java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java | 9 +-
.../ghidra/dbg/jdi/rmi/jpda/JdiCommands.java | 92 ++++++++++++++-----
.../ghidra/dbg/jdi/rmi/jpda/JdiManager.java | 8 +-
.../ghidra/dbg/jdi/rmi/jpda/JdiMethods.java | 26 ++++++
7 files changed, 221 insertions(+), 58 deletions(-)
create mode 100755 Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh
diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py
index 3c115c99d7..0769aff7fb 100644
--- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py
+++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py
@@ -1,17 +1,17 @@
## ###
-# 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.
+# 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.
##
from ghidratrace.client import Address, RegVal
@@ -61,6 +61,7 @@ language_map = {
'm68k:68020': ['68000:BE:32:MC68020'],
'm68k:68030': ['68000:BE:32:MC68030'],
'm9s12x': ['HCS-12:BE:24:default', 'HCS-12X:BE:24:default'],
+ 'mips:3000': ['MIPS:BE:32:default', 'MIPS:LE:32:default'],
'mips:4000': ['MIPS:BE:32:default', 'MIPS:LE:32:default'],
'mips:5000': ['MIPS:BE:64:64-32addr', 'MIPS:BE:64:default', 'MIPS:LE:64:64-32addr', 'MIPS:LE:64:default'],
'mips:micromips': ['MIPS:BE:32:micro'],
diff --git a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh
new file mode 100755
index 0000000000..679cf4ca0e
--- /dev/null
+++ b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+## ###
+# 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.
+##
+#@title android lldb
+#@image-opt arg:1
+#@desc
+#@desc Launch with local lldb and connect to a stub (e.g., gdbserver)
+#@desc
+#@desc This will start lldb on the local system and then use it to connect to the remote system.
+#@desc For setup instructions, press F1.
+#@desc
+#@desc
+#@menu-group remote
+#@icon icon.debugger
+#@help TraceRmiLauncherServicePlugin#lldb_remote
+#@enum StartCmd:str "process launch" "process launch --stop-at-entry"
+#@arg :file "Image" "The target binary executable image"
+#@args "Arguments" "Command-line arguments to pass to the target"
+#@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
+#@env OPT_PORT:str="9999" "Port" "The host's listening port"
+#@env OPT_ARCH:str="" "Architecture" "Target architecture override"
+#@env OPT_LLDB_PATH:file="lldb" "lldb command" "The path to lldb on the local system. Omit the full path to resolve using the system PATH."
+#@env OPT_START_CMD:StartCmd="process launch" "Run command" "The lldb command to actually run the target."
+
+if [ -d ${GHIDRA_HOME}/ghidra/.git ]
+then
+ export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH
+ export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH
+elif [ -d ${GHIDRA_HOME}/.git ]
+then
+ export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH
+ export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH
+else
+ export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/pypkg/src:$PYTHONPATH
+ export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/pypkg/src:$PYTHONPATH
+fi
+
+target_image="$1"
+shift
+target_args="$@"
+
+if [ -z "$target_args" ]
+then
+ argspart=
+else
+ argspart=-o "settings set target.run-args $target_args"
+fi
+
+if [ -z "$OPT_ARCH" ]
+then
+ archcmd=
+else
+ archcmd=-o "settings set target.default-arch $OPT_ARCH"
+fi
+
+"$OPT_LLDB_PATH" \
+ -o "version" \
+ -o "script import ghidralldb" \
+ -o "platform select remote-android" \
+ -o "platform connect connect://$OPT_HOST:$OPT_PORT" \
+ $archcmd \
+ -o "target create \"$target_image\"" \
+ $argspart \
+ -o "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
+ -o "ghidra trace start" \
+ -o "ghidra trace sync-enable" \
+ -o "ghidra trace sync-synth-stopped" \
+ -o "$OPT_START_CMD"
+
diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py
index eca5ae3f04..e0fb5a9f07 100644
--- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py
+++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py
@@ -1,17 +1,17 @@
## ###
-# 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.
+# 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.
##
from ghidratrace.client import Address, RegVal
import lldb
@@ -226,14 +226,11 @@ def compute_ghidra_compiler(lang):
)
if len(matched_lang) == 0:
return 'default'
+
comp_map = compiler_map[matched_lang[0]]
osabi = get_osabi()
- matched_osabi = sorted(
- (l for l in comp_map if l in osabi),
- key=lambda l: comp_map[l]
- )
- if len(matched_osabi) > 0:
- return comp_map[matched_osabi[0]]
+ if osabi in comp_map:
+ return comp_map[osabi]
if None in comp_map:
return comp_map[None]
return 'default'
diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java
index 27d444eb98..c69e2f2d65 100644
--- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java
+++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java
@@ -25,13 +25,18 @@ import ghidra.program.util.DefaultLanguageService;
public class JdiArch {
+ private JdiManager manager;
private LanguageID langID;
private Language language;
private final LanguageService languageService = DefaultLanguageService.getLanguageService();
+ public JdiArch(JdiManager manager) {
+ this.manager = manager;
+ }
+
public String getArch() {
- Map env = new HashMap<>(System.getenv());
+ Map env = new HashMap<>(manager.getEnv());
String arch = "JVM";
if (env.containsKey("OPT_ARCH")) {
arch = env.get("OPT_ARCH");
@@ -44,7 +49,7 @@ public class JdiArch {
}
public String getOSABI() {
- Map env = new HashMap<>(System.getenv());
+ Map env = new HashMap<>(manager.getEnv());
String arch = "JVM";
if (env.containsKey("OPT_ARCH")) {
arch = env.get("OPT_ARCH");
diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java
index 75fff119cb..e635a76a3d 100644
--- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java
+++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java
@@ -588,6 +588,9 @@ public class JdiCommands {
int ireg = 0;
String r = regNames[0];
Register register = lang.getRegister(r);
+ if (register == null) {
+ register = fabricatePcRegister(lang, r);
+ }
keys.add(manager.key(r));
Location loc = frame.location();
Address addr = putRegister(ppath, r, loc);
@@ -596,6 +599,9 @@ public class JdiCommands {
r = regNames[1];
register = lang.getRegister(r);
+ if (register == null) {
+ register = fabricatePcRegister(lang, r);
+ }
keys.add(manager.key(r));
ThreadReference thread = frame.thread();
Location ploc = null;
@@ -648,6 +654,9 @@ public class JdiCommands {
JdiArch arch = manager.getArch();
Language lang = arch.getLanguage();
Register register = lang.getRegister(name);
+ if (register == null) {
+ register = fabricatePcRegister(lang, name);
+ }
RegisterValue rv = new RegisterValue(register, addr.getOffsetAsBigInteger());
RegisterValue mapped = mapper.mapValue(name, rv);
Address regAddr = addr.getNewAddress(mapped.getUnsignedValue().longValue());
@@ -660,6 +669,11 @@ public class JdiCommands {
return addr;
}
+ private Register fabricatePcRegister(Language lang, String name) {
+ int size = lang.getAddressFactory().getDefaultAddressSpace().getSize();
+ return new Register(name, name, null, size, lang.isBigEndian(), Register.TYPE_PC);
+ }
+
public void putMem(Address address, long length, boolean create) {
MemoryMapper mapper = state.trace.memoryMapper;
Address mappedAddress = mapper.map(address);
@@ -754,16 +768,22 @@ public class JdiCommands {
String rpath = createObject(path + ".Relations");
insertObject(rpath);
- ModuleReference module = reftype.module();
- String moduleName = module.name();
- if (moduleName == null) {
- moduleName = "";
+ try {
+ ModuleReference module = reftype.module();
+ String moduleName = module.name();
+ if (moduleName == null) {
+ moduleName = "";
+ }
+ if (moduleName.contains(".")) {
+ moduleName = "\"" + moduleName + "\"";
+ }
+ String mrpath = createObject(module, moduleName, rpath + ".ModuleRef");
+ insertObject(mrpath);
}
- if (moduleName.contains(".")) {
- moduleName = "\"" + moduleName + "\"";
+ catch (UnsupportedOperationException e) {
+ //Msg.info(this, e.getMessage());
}
- String mrpath = createObject(module, moduleName, rpath + ".ModuleRef");
- insertObject(mrpath);
+
if (reftype instanceof ArrayType at) {
putArrayTypeDetails(rpath, at);
}
@@ -823,9 +843,14 @@ public class JdiCommands {
AddressRange range = manager.putAddressRange(reftype, bounds);
setValue(path, ATTR_RANGE, range);
- setValue(path, ATTR_COUNT, reftype.constantPoolCount());
- range = manager.getPoolAddressRange(reftype, getSize(reftype) - 1);
- setValue(path, ATTR_RANGE_CP, range);
+ try {
+ setValue(path, ATTR_COUNT, reftype.constantPoolCount());
+ range = manager.getPoolAddressRange(reftype, getSize(reftype) - 1);
+ setValue(path, ATTR_RANGE_CP, range);
+ }
+ catch (UnsupportedOperationException e) {
+ // Ignore
+ }
try {
putMem(range.getMinAddress(), range.getLength(), true);
}
@@ -1157,11 +1182,16 @@ public class JdiCommands {
VirtualMachine vm = manager.getJdi().getCurrentVM();
String ppath = getPath(vm) + ".ModuleRefs";
Set keys = new HashSet<>();
- List modules = vm.allModules();
- for (ModuleReference ref : modules) {
- keys.add(manager.key(ref.name()));
- String mpath = createObject(ref, ref.name(), ppath);
- insertObject(mpath);
+ try {
+ List modules = vm.allModules();
+ for (ModuleReference ref : modules) {
+ keys.add(manager.key(ref.name()));
+ String mpath = createObject(ref, ref.name(), ppath);
+ insertObject(mpath);
+ }
+ }
+ catch (UnsupportedOperationException e) {
+ // Msg.info(this, e.getMessage());
}
retainKeys(ppath, keys);
}
@@ -1380,11 +1410,16 @@ public class JdiCommands {
public void putMethodContainer(String path, ReferenceType reftype) {
boolean scope = manager.getScope(reftype);
- List methods = scope ? reftype.allMethods() : reftype.methods();
Set keys = new HashSet<>();
- for (Method m : methods) {
- keys.add(manager.key(m.name()));
- putMethod(path, m);
+ try {
+ List methods = scope ? reftype.allMethods() : reftype.methods();
+ for (Method m : methods) {
+ keys.add(manager.key(m.name()));
+ putMethod(path, m);
+ }
+ }
+ catch (Exception e) {
+ Msg.info(this, e.getMessage());
}
retainKeys(path, keys);
}
@@ -1477,7 +1512,10 @@ public class JdiCommands {
String tgpath = createObject(path + ".ThreadGroups");
String tpath = createObject(path + ".Threads");
Event currentEvent = jdi.getCurrentEvent();
- String shortName = vm.name().substring(0, vm.name().indexOf(" "));
+ String shortName = vm.name();
+ if (shortName.contains(" ")) {
+ shortName = vm.name().substring(0, vm.name().indexOf(" "));
+ }
String display = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]";
setValue(path, ATTR_DISPLAY, display);
setValue(path, ATTR_ARCH, vm.name());
@@ -1640,7 +1678,12 @@ public class JdiCommands {
}
createLink(location, "Method", method);
createLink(location, "DeclaringType", location.declaringType());
- createLink(location, "ModuleRef", location.declaringType().module());
+ try {
+ createLink(location, "ModuleRef", location.declaringType().module());
+ }
+ catch (UnsupportedOperationException e) {
+ // IGNORE
+ }
}
private boolean isLoaded(Location location) {
@@ -2249,7 +2292,10 @@ public class JdiCommands {
}
if (obj instanceof VirtualMachine vm) {
Event currentEvent = jdi.getCurrentEvent();
- String shortName = vm.name().substring(0, vm.name().indexOf(" "));
+ String shortName = vm.name();
+ if (shortName.contains(" ")) {
+ shortName = vm.name().substring(0, vm.name().indexOf(" "));
+ }
name = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]";
}
setValue(path, ATTR_ACCESSIBLE, suspended);
diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java
index ce7ba8a173..76dfcd958f 100644
--- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java
+++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java
@@ -93,9 +93,11 @@ public class JdiManager {
private final Map returnStatusMap = new HashMap<>();
final TargetObjectSchema rootSchema;
+ private Map env;
public JdiManager(JdiManagerImpl manager, Map env) {
this(manager);
+ this.env = env;
commands.ghidraTraceConnect(env.get("GHIDRA_TRACE_RMI_ADDR"));
commands.ghidraTraceStart(env.get("OPT_TARGET_CLASS"));
}
@@ -106,7 +108,7 @@ public class JdiManager {
defaultRange = new AddressRangeImpl(start, start.add(BLOCK_SIZE - 1));
rootSchema = RmiClient.loadSchema("jdi_schema.xml", "Debugger");
- arch = new JdiArch();
+ arch = new JdiArch(this);
commands = new JdiCommands(this); // Must precede methods/hooks
methods = new JdiMethods(this, commands);
hooks = new JdiHooks(this, commands);
@@ -137,6 +139,10 @@ public class JdiManager {
return commands.state.client;
}
+ public Map getEnv() {
+ return env;
+ }
+
public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) {
String action = name;
String display = name;
diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java
index 1ed7dabdfc..e226cd61bd 100644
--- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java
+++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java
@@ -330,6 +330,20 @@ public class JdiMethods implements RmiMethods {
}
}
+ @TraceMethod(display = "Load class")
+ public boolean find_canonical_class(
+ @Param(
+ schema = "CanonicalReferenceTypeContainer",
+ description = "Container",
+ display = "Container",
+ name = "container") RmiTraceObject obj,
+ @Param(
+ description = "Class to open",
+ display = "Class",
+ name = "find") String targetClass) {
+ return find_class(obj, targetClass);
+ }
+
@TraceMethod(display = "Load class")
public boolean find_class(
@Param(
@@ -1428,6 +1442,12 @@ public class JdiMethods implements RmiMethods {
cmds.putEvents();
}
+ @TraceMethod(action = "toggle", display = "Toggle scope")
+ public void toggle_scope_canonical_methods(
+ @Param(schema = "CanonicalMethodContainer", name = "container") RmiTraceObject obj) {
+ toggle_scope_methods(obj);
+ }
+
@TraceMethod(action = "toggle", display = "Toggle scope")
public void toggle_scope_methods(
@Param(schema = "MethodContainer", name = "container") RmiTraceObject obj) {
@@ -1437,6 +1457,12 @@ public class JdiMethods implements RmiMethods {
refresh_methods(obj);
}
+ @TraceMethod(action = "toggle", display = "Toggle scope")
+ public void toggle_scope_canonical_fields(
+ @Param(schema = "CanonicalFieldContainer", name = "container") RmiTraceObject obj) {
+ toggle_scope_fields(obj);
+ }
+
@TraceMethod(action = "toggle", display = "Toggle scope")
public void toggle_scope_fields(
@Param(schema = "FieldContainer", name = "container") RmiTraceObject obj) {