From f780012e8852d54eea85ebe225efdecf6ef94e1c Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Wed, 28 Sep 2022 01:21:38 -0400 Subject: [PATCH] GP-2619: Ported gdb connector to use JNA instead of piggybacking Jython's JNR --- Ghidra/Debug/Debugger-agent-gdb/build.gradle | 1 - .../gdb/manager/impl/GdbManagerImpl.java | 55 +++---------- .../agent/gdb/pty/linux/FdInputStream.java | 12 +-- .../agent/gdb/pty/linux/FdOutputStream.java | 13 +-- .../java/agent/gdb/pty/linux/LinuxPty.java | 31 +++----- .../gdb/pty/linux/LinuxPtySessionLeader.java | 79 ++++++++----------- .../main/java/agent/gdb/pty/linux/PosixC.java | 43 ++++++++++ .../main/java/agent/gdb/pty/linux/Util.java | 16 ++-- .../core/debug/DebuggerCoordinates.java | 2 +- 9 files changed, 113 insertions(+), 139 deletions(-) create mode 100644 Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/PosixC.java diff --git a/Ghidra/Debug/Debugger-agent-gdb/build.gradle b/Ghidra/Debug/Debugger-agent-gdb/build.gradle index b8d70ee893..c686052160 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/build.gradle +++ b/Ghidra/Debug/Debugger-agent-gdb/build.gradle @@ -26,7 +26,6 @@ dependencies { api project(':Framework-AsyncComm') api project(':Framework-Debugging') api project(':Debugger-gadp') - api project(':Python') api 'com.jcraft:jsch:0.1.55' testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') 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 39ec2b1fd9..9e4584f5d3 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 @@ -16,6 +16,7 @@ package agent.gdb.manager.impl; import java.io.*; +import java.text.MessageFormat; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -25,9 +26,6 @@ import javax.swing.JDialog; import javax.swing.JOptionPane; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.python.core.PyDictionary; -import org.python.icu.text.MessageFormat; -import org.python.util.InteractiveConsole; import agent.gdb.manager.*; import agent.gdb.manager.GdbCause.Causes; @@ -1564,28 +1562,6 @@ public class GdbManagerImpl implements GdbManager { } } - // Link lazily to Jython - private static class JythonConsole { - /** - * Launch a Jython interpreter - * - * The interpreter the variable "{@code mgr}" bound to the manager. This method does not - * return until the user exits the interpreter. - * - * @param manager the manager - */ - static void interact(GdbManagerImpl manager) { - PyDictionary dict = new PyDictionary(); - dict.put("mgr", manager); - try (InteractiveConsole jyConsole = new InteractiveConsole(dict);) { - jyConsole.interact(); - } - catch (Throwable e) { - e.printStackTrace(); - } - } - } - /** * An interface for taking lines of input */ @@ -1636,28 +1612,15 @@ public class GdbManagerImpl implements GdbManager { System.out.println("quit"); return; } - if (">>>".equals(cmd.trim())) { - try { - JythonConsole.interact(this); + console(cmd).exceptionally((e) -> { + Throwable realExc = AsyncUtils.unwrapThrowable(e); + if (realExc instanceof GdbCommandError) { + return null; // Gdb will have already printed it } - catch (NoClassDefFoundError e) { - Msg.error(this, "Jython is not in the classpath"); - } - catch (Throwable e) { - e.printStackTrace(); - } - } - else { - console(cmd).exceptionally((e) -> { - Throwable realExc = AsyncUtils.unwrapThrowable(e); - if (realExc instanceof GdbCommandError) { - return null; // Gdb will have already printed it - } - e.printStackTrace(); - //System.out.print(PROMPT_GDB + " "); - return null; - }); - } + e.printStackTrace(); + //System.out.print(PROMPT_GDB + " "); + return null; + }); } } finally { diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdInputStream.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdInputStream.java index ef1c5f5fcc..06d080d571 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdInputStream.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdInputStream.java @@ -17,10 +17,8 @@ package agent.gdb.pty.linux; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; -import jnr.posix.POSIX; -import jnr.posix.POSIXFactory; +import com.sun.jna.Memory; /** * An input stream that wraps a native POSIX file descriptor @@ -31,7 +29,7 @@ import jnr.posix.POSIXFactory; * behavior. */ public class FdInputStream extends InputStream { - private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + private static final PosixC LIB_POSIX = PosixC.INSTANCE; private final int fd; private boolean closed = false; @@ -67,11 +65,9 @@ public class FdInputStream extends InputStream { if (closed) { throw new IOException("Stream closed"); } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); + Memory buf = new Memory(len); int ret = LIB_POSIX.read(fd, buf, len); - if (ret < 0) { - throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); - } + buf.read(0, b, off, ret); return ret; } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdOutputStream.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdOutputStream.java index e458397a20..b3df529686 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdOutputStream.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/FdOutputStream.java @@ -17,10 +17,8 @@ package agent.gdb.pty.linux; import java.io.IOException; import java.io.OutputStream; -import java.nio.ByteBuffer; -import jnr.posix.POSIX; -import jnr.posix.POSIXFactory; +import com.sun.jna.Memory; /** * An output stream that wraps a native POSIX file descriptor @@ -31,7 +29,7 @@ import jnr.posix.POSIXFactory; * behavior. */ public class FdOutputStream extends OutputStream { - private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + private static final PosixC LIB_POSIX = PosixC.INSTANCE; private final int fd; private boolean closed = false; @@ -60,14 +58,11 @@ public class FdOutputStream extends OutputStream { if (closed) { throw new IOException("Stream closed"); } - ByteBuffer buf = ByteBuffer.wrap(b, off, len); - + Memory buf = new Memory(len); + buf.write(0, b, off, len); int total = 0; do { int ret = LIB_POSIX.write(fd, buf, len - total); - if (ret < 0) { - throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); - } total += ret; } while (total < len); diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPty.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPty.java index 785384b726..562c4eadaa 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPty.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPty.java @@ -16,17 +16,16 @@ package agent.gdb.pty.linux; import java.io.IOException; -import java.nio.ByteBuffer; + +import com.sun.jna.Memory; +import com.sun.jna.ptr.IntByReference; import agent.gdb.pty.Pty; import ghidra.util.Msg; -import jnr.ffi.Pointer; -import jnr.ffi.byref.IntByReference; -import jnr.posix.POSIX; -import jnr.posix.POSIXFactory; public class LinuxPty implements Pty { - static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + + static final PosixC LIB_POSIX = PosixC.INSTANCE; private final int aparent; private final int achild; @@ -40,12 +39,9 @@ public class LinuxPty implements Pty { // TODO: Support termp and winp? IntByReference p = new IntByReference(); IntByReference c = new IntByReference(); - Pointer n = Pointer.wrap(jnr.ffi.Runtime.getSystemRuntime(), ByteBuffer.allocate(1024)); - if (Util.INSTANCE.openpty(p, c, n, null, null) < 0) { - int errno = LIB_POSIX.errno(); - throw new IOException(errno + ": " + LIB_POSIX.strerror(errno)); - } - return new LinuxPty(p.intValue(), c.intValue(), n.getString(0)); + Memory n = new Memory(1024); + Util.INSTANCE.openpty(p, c, n, null, null); + return new LinuxPty(p.getValue(), c.getValue(), n.getString(0)); } LinuxPty(int aparent, int achild, String name) { @@ -73,15 +69,8 @@ public class LinuxPty implements Pty { if (closed) { return; } - int result; - result = LIB_POSIX.close(achild); - if (result < 0) { - throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); - } - result = LIB_POSIX.close(aparent); - if (result < 0) { - throw new IOException(LIB_POSIX.strerror(LIB_POSIX.errno())); - } + LIB_POSIX.close(achild); + LIB_POSIX.close(aparent); closed = true; } } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPtySessionLeader.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPtySessionLeader.java index be2a6c27b6..86222738e0 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPtySessionLeader.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/LinuxPtySessionLeader.java @@ -16,14 +16,9 @@ package agent.gdb.pty.linux; import java.util.List; -import java.util.concurrent.Callable; - -import ghidra.util.Msg; -import jnr.posix.POSIX; -import jnr.posix.POSIXFactory; public class LinuxPtySessionLeader { - private static final POSIX LIB_POSIX = POSIXFactory.getNativePOSIX(); + private static final PosixC LIB_POSIX = PosixC.INSTANCE; private static final int O_RDWR = 2; // TODO: Find this in libs public static void main(String[] args) throws Exception { @@ -40,54 +35,50 @@ public class LinuxPtySessionLeader { subArgs = List.of(args).subList(1, args.length); } - protected T checkErr(Callable r) throws Exception { - LIB_POSIX.errno(0); - T result = r.call(); - int errno = LIB_POSIX.errno(); - if (errno != 0) { - throw new RuntimeException("errno=" + errno + ": " + LIB_POSIX.strerror(errno)); - } - return result; - } - protected void run() throws Exception { /** This tells Linux to make this process the leader of a new session. */ - checkErr(() -> LIB_POSIX.setsid()); + LIB_POSIX.setsid(); /** * Open the TTY. On Linux, the first TTY opened since becoming a session leader becomes the * session's controlling TTY. Other platforms, e.g., BSD may require an explicit IOCTL. */ - int fd = checkErr(() -> LIB_POSIX.open(ptyPath, O_RDWR, 0)); - - /** Copy stderr to a backup descriptor, in case something goes wrong. */ - int bk = fd + 1; - checkErr(() -> LIB_POSIX.dup2(2, bk)); - - /** - * Copy the TTY fd over all standard streams. This effectively redirects the leader's - * standard streams to the TTY. - */ - checkErr(() -> LIB_POSIX.dup2(fd, 0)); - checkErr(() -> LIB_POSIX.dup2(fd, 1)); - checkErr(() -> LIB_POSIX.dup2(fd, 2)); - - /** - * At this point, we are the session leader and the named TTY is the controlling PTY. Now, - * exec the specified image with arguments as the session leader. Recall, this replaces the - * image of this process. - */ + int bk = -1; try { - checkErr(() -> LIB_POSIX.execv(subArgs.get(0), subArgs.toArray(new String[0]))); + int fd = LIB_POSIX.open(ptyPath, O_RDWR, 0); + + /** Copy stderr to a backup descriptor, in case something goes wrong. */ + int bkt = fd + 1; + LIB_POSIX.dup2(2, bkt); + bk = bkt; + + /** + * Copy the TTY fd over all standard streams. This effectively redirects the leader's + * standard streams to the TTY. + */ + LIB_POSIX.dup2(fd, 0); + LIB_POSIX.dup2(fd, 1); + LIB_POSIX.dup2(fd, 2); + + /** + * At this point, we are the session leader and the named TTY is the controlling PTY. + * Now, exec the specified image with arguments as the session leader. Recall, this + * replaces the image of this process. + */ + LIB_POSIX.execv(subArgs.get(0), subArgs.toArray(new String[0])); } catch (Throwable t) { - Msg.error(this, "Could not execv with args " + subArgs, t); - try { - checkErr(() -> LIB_POSIX.dup2(bk, 2)); - } - catch (Throwable t2) { - // Catastrophic - System.exit(-1); + System.err.println("Error with ptyPath = '" + ptyPath + "', and subArgs = " + subArgs); + t.printStackTrace(); + if (bk != -1) { + try { + int bkt = bk; + LIB_POSIX.dup2(bkt, 2); + } + catch (Throwable t2) { + // Catastrophic + System.exit(-1); + } } throw t; } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/PosixC.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/PosixC.java new file mode 100644 index 0000000000..c823029d5a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/PosixC.java @@ -0,0 +1,43 @@ +/* ### + * 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.pty.linux; + +import com.sun.jna.*; +import com.sun.jna.platform.linux.LibC; + +/** + * Interface for POSIX functions in libc + * + *

+ * The functions are not documented here. Instead see the POSIX manual pages. + */ +public interface PosixC extends LibC { + PosixC INSTANCE = Native.load("c", PosixC.class); + + int close(int fd) throws LastErrorException; + + int read(int fd, Pointer buf, int len) throws LastErrorException; + + int write(int fd, Pointer buf, int i) throws LastErrorException; + + int setsid() throws LastErrorException; + + int open(String path, int mode, int flags) throws LastErrorException; + + int dup2(int oldfd, int newfd) throws LastErrorException; + + int execv(String path, String[] argv) throws LastErrorException; +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/Util.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/Util.java index 1a27479c69..5ac94ac5f1 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/Util.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/pty/linux/Util.java @@ -15,16 +15,14 @@ */ package agent.gdb.pty.linux; -import jnr.ffi.LibraryLoader; -import jnr.ffi.Pointer; -import jnr.ffi.annotations.Out; -import jnr.ffi.byref.IntByReference; +import com.sun.jna.*; +import com.sun.jna.ptr.IntByReference; /** - * The interface for linking to {@code openpty} via jnr-ffi + * The interface for linking to {@code openpty} via jna */ -public interface Util { - Util INSTANCE = LibraryLoader.create(Util.class).load("util"); +public interface Util extends Library { + Util INSTANCE = Native.load("util", Util.class); /** * See the Linux manual pages @@ -36,6 +34,6 @@ public interface Util { * @param winp (purposefully undocumented here) * @return (purposefully undocumented here) */ - int openpty(@Out IntByReference amaster, @Out IntByReference aslave, @Out Pointer name, - @Out Pointer termp, @Out Pointer winp); + int openpty(IntByReference amaster, IntByReference aslave, Pointer name, + Pointer termp, Pointer winp) throws LastErrorException; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java index ac6e4a280d..f205a6c068 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/DebuggerCoordinates.java @@ -681,7 +681,7 @@ public class DebuggerCoordinates { catch (Exception e) { Msg.error(DebuggerCoordinates.class, "Could not restore invalid time specification: " + timeSpec); - time = null; + time = TraceSchedule.ZERO; } Integer frame = null; if (coordState.hasValue(KEY_FRAME)) {