mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-1666_Dan_gdbExecFile--SQUASHED'
This commit is contained in:
@@ -32,6 +32,7 @@ import agent.gdb.manager.GdbManager.StepCmd;
|
||||
import agent.gdb.manager.impl.cmd.*;
|
||||
import agent.gdb.manager.impl.cmd.GdbConsoleExecCommand.CompletesWithRunning;
|
||||
import ghidra.async.AsyncLazyValue;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@@ -90,7 +91,7 @@ public class GdbInferiorImpl implements GdbInferior {
|
||||
this.pid = g.getPid();
|
||||
this.exitCode = g.getExitCode();
|
||||
this.executable = g.getExecutable();
|
||||
|
||||
|
||||
// Because we're only called to resync, we should synth started, if needed
|
||||
if (oldPid == null && pid != null) {
|
||||
manager.fireInferiorStarted(this, Causes.UNCLAIMED, "resyncInferiorStarted");
|
||||
@@ -232,10 +233,17 @@ public class GdbInferiorImpl implements GdbInferior {
|
||||
@Override
|
||||
public CompletableFuture<Map<String, GdbModule>> listModules() {
|
||||
// "nosections" is an unlikely section name. Goal is to exclude section lines.
|
||||
// TODO: See how this behaves on other GDB versions.
|
||||
return consoleCapture("maintenance info sections ALLOBJ nosections",
|
||||
CompletesWithRunning.CANNOT)
|
||||
.thenApply(this::parseModuleNames);
|
||||
// TODO: Would be nice to save this switch, or better, choose at start based on version
|
||||
CompletableFuture<String> future =
|
||||
consoleCapture("maintenance info sections ALLOBJ nosections",
|
||||
CompletesWithRunning.CANNOT);
|
||||
return future.thenCompose(output -> {
|
||||
if (output.split("\n").length <= 1) {
|
||||
return consoleCapture("maintenance info sections -all-objects nosections")
|
||||
.thenApply(out2 -> parseModuleNames(out2, true));
|
||||
}
|
||||
return CompletableFuture.completedFuture(parseModuleNames(output, false));
|
||||
});
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> loadSections() {
|
||||
@@ -243,8 +251,16 @@ public class GdbInferiorImpl implements GdbInferior {
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> doLoadSections() {
|
||||
return consoleCapture("maintenance info sections ALLOBJ", CompletesWithRunning.CANNOT)
|
||||
.thenAccept(this::parseAndUpdateAllModuleSections);
|
||||
CompletableFuture<String> future =
|
||||
consoleCapture("maintenance info sections ALLOBJ", CompletesWithRunning.CANNOT);
|
||||
return future.thenCompose(output -> {
|
||||
if (output.split("\n").length <= 1) {
|
||||
return consoleCapture("maintenance info sections -all-objects")
|
||||
.thenAccept(out2 -> parseAndUpdateAllModuleSections(out2, true));
|
||||
}
|
||||
parseAndUpdateAllModuleSections(output, false);
|
||||
return AsyncUtils.NIL;
|
||||
});
|
||||
}
|
||||
|
||||
protected GdbModuleImpl resyncCreateModule(String name) {
|
||||
@@ -277,16 +293,36 @@ public class GdbInferiorImpl implements GdbInferior {
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseAndUpdateAllModuleSections(String out) {
|
||||
protected String nameFromLine(String line, boolean v11) {
|
||||
if (v11) {
|
||||
Matcher nameMatcher = GdbModuleImpl.V11_FILE_LINE_PATTERN.matcher(line);
|
||||
if (!nameMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
String name = nameMatcher.group("name");
|
||||
if (name.startsWith(GdbModuleImpl.GNU_DEBUGDATA_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
Matcher nameMatcher = GdbModuleImpl.OBJECT_FILE_LINE_PATTERN.matcher(line);
|
||||
if (!nameMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
return nameMatcher.group("name");
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseAndUpdateAllModuleSections(String out, boolean v11) {
|
||||
Set<String> namesSeen = new HashSet<>();
|
||||
GdbModuleImpl curModule = null;
|
||||
for (String line : out.split("\n")) {
|
||||
Matcher nameMatcher = GdbModuleImpl.OBJECT_FILE_LINE_PATTERN.matcher(line);
|
||||
if (nameMatcher.matches()) {
|
||||
String name = nameFromLine(line, v11);
|
||||
if (name != null) {
|
||||
if (curModule != null) {
|
||||
curModule.loadSections.provide().complete(null);
|
||||
}
|
||||
String name = nameMatcher.group("name");
|
||||
namesSeen.add(name);
|
||||
curModule = modules.computeIfAbsent(name, this::resyncCreateModule);
|
||||
// NOTE: This will usurp the module's lazy loader, but we're about to
|
||||
@@ -307,12 +343,11 @@ public class GdbInferiorImpl implements GdbInferior {
|
||||
resyncRetainModules(namesSeen);
|
||||
}
|
||||
|
||||
protected Map<String, GdbModule> parseModuleNames(String out) {
|
||||
protected Map<String, GdbModule> parseModuleNames(String out, boolean v11) {
|
||||
Set<String> namesSeen = new HashSet<>();
|
||||
for (String line : out.split("\n")) {
|
||||
Matcher nameMatcher = GdbModuleImpl.OBJECT_FILE_LINE_PATTERN.matcher(line);
|
||||
if (nameMatcher.matches()) {
|
||||
String name = nameMatcher.group("name");
|
||||
String name = nameFromLine(line, v11);
|
||||
if (name != null) {
|
||||
namesSeen.add(name);
|
||||
modules.computeIfAbsent(name, this::resyncCreateModule);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ import ghidra.util.Msg;
|
||||
public class GdbModuleImpl implements GdbModule {
|
||||
protected static final Pattern OBJECT_FILE_LINE_PATTERN =
|
||||
Pattern.compile("\\s*Object file: (?<name>.*)");
|
||||
protected static final Pattern V11_FILE_LINE_PATTERN =
|
||||
Pattern.compile("\\s*(Object)|(Exec) file: `(?<name>.*)', file type (?<type>.*)");
|
||||
protected static final String GNU_DEBUGDATA_PREFIX = ".gnu_debugdata for ";
|
||||
|
||||
// Pattern observed in GDB 8 (probably applies to previous, too)
|
||||
protected static final Pattern OBJECT_SECTION_LINE_PATTERN_V8 = Pattern.compile(
|
||||
|
||||
@@ -18,7 +18,9 @@ package agent.gdb.manager.impl;
|
||||
import static ghidra.dbg.testutil.DummyProc.run;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -36,6 +38,7 @@ import agent.gdb.manager.*;
|
||||
import agent.gdb.manager.GdbManager.StepCmd;
|
||||
import agent.gdb.manager.breakpoint.GdbBreakpointInfo;
|
||||
import agent.gdb.pty.PtyFactory;
|
||||
import agent.gdb.pty.linux.LinuxPtyFactory;
|
||||
import ghidra.async.AsyncReference;
|
||||
import ghidra.dbg.testutil.DummyProc;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
@@ -46,7 +49,22 @@ public abstract class AbstractGdbManagerTest extends AbstractGhidraHeadlessInteg
|
||||
protected static final long TIMEOUT_MILLISECONDS =
|
||||
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
|
||||
|
||||
protected abstract PtyFactory getPtyFactory();
|
||||
protected File gdbBin;
|
||||
|
||||
protected File findGdbBin() {
|
||||
return new File(GdbManager.DEFAULT_GDB_CMD);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void findAndCheckGdb() {
|
||||
gdbBin = findGdbBin();
|
||||
assumeTrue(gdbBin.exists());
|
||||
}
|
||||
|
||||
protected PtyFactory getPtyFactory() {
|
||||
// TODO: Choose by host OS
|
||||
return new LinuxPtyFactory();
|
||||
}
|
||||
|
||||
protected abstract CompletableFuture<Void> startManager(GdbManager manager);
|
||||
|
||||
@@ -143,6 +161,21 @@ public abstract class AbstractGdbManagerTest extends AbstractGhidraHeadlessInteg
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListModulesAndSections() throws Throwable {
|
||||
try (GdbManager mgr = GdbManager.newInstance(getPtyFactory())) {
|
||||
waitOn(startManager(mgr));
|
||||
GdbInferior inferior = mgr.currentInferior();
|
||||
waitOn(inferior.fileExecAndSymbols("/usr/bin/echo"));
|
||||
Map<String, GdbModule> modules = waitOn(inferior.listModules());
|
||||
GdbModule modEcho = modules.get("/usr/bin/echo");
|
||||
assertNotNull(modEcho);
|
||||
Map<String, GdbModuleSection> sectionsEcho = waitOn(modEcho.listSections());
|
||||
GdbModuleSection secEchoText = sectionsEcho.get(".text");
|
||||
assertNotNull(secEchoText);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInfoOs() throws Throwable {
|
||||
try (GdbManager mgr = GdbManager.newInstance(getPtyFactory())) {
|
||||
@@ -284,12 +317,16 @@ public abstract class AbstractGdbManagerTest extends AbstractGhidraHeadlessInteg
|
||||
}
|
||||
}
|
||||
|
||||
protected String getExpectedDefaultArgsVar() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVarGetVar() throws Throwable {
|
||||
try (GdbManager mgr = GdbManager.newInstance(getPtyFactory())) {
|
||||
waitOn(startManager(mgr));
|
||||
String val = waitOn(mgr.currentInferior().getVar("args"));
|
||||
assertEquals(null, val);
|
||||
assertEquals(getExpectedDefaultArgsVar(), val);
|
||||
waitOn(mgr.currentInferior().setVar("args", "test"));
|
||||
val = waitOn(mgr.currentInferior().getVar("args"));
|
||||
assertEquals("test", val);
|
||||
|
||||
@@ -22,10 +22,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
import agent.gdb.pty.PtyFactory;
|
||||
import agent.gdb.pty.PtySession;
|
||||
import agent.gdb.pty.linux.LinuxPty;
|
||||
import agent.gdb.pty.linux.LinuxPtyFactory;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@Ignore("Need compatible GDB version for CI")
|
||||
@@ -50,12 +48,6 @@ public class JoinedGdbManagerTest extends AbstractGdbManagerTest {
|
||||
protected LinuxPty ptyUserGdb;
|
||||
protected PtySession gdb;
|
||||
|
||||
@Override
|
||||
protected PtyFactory getPtyFactory() {
|
||||
// TODO: Choose by host OS
|
||||
return new LinuxPtyFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
@@ -64,7 +56,7 @@ public class JoinedGdbManagerTest extends AbstractGdbManagerTest {
|
||||
Msg.debug(this, "Starting GDB and invoking new-ui mi2 " + manager.getMi2PtyName());
|
||||
|
||||
gdb = ptyUserGdb.getChild()
|
||||
.session(new String[] { GdbManager.DEFAULT_GDB_CMD }, Map.of());
|
||||
.session(new String[] { gdbBin.getAbsolutePath() }, Map.of());
|
||||
new ReaderThread().start();
|
||||
PrintWriter gdbCmd = new PrintWriter(ptyUserGdb.getParent().getOutputStream());
|
||||
gdbCmd.println("new-ui mi2 " + manager.getMi2PtyName());
|
||||
|
||||
@@ -21,25 +21,17 @@ import java.util.concurrent.CompletableFuture;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
import agent.gdb.pty.PtyFactory;
|
||||
import agent.gdb.pty.linux.LinuxPtyFactory;
|
||||
|
||||
@Ignore("Need compatible GDB version for CI")
|
||||
public class SpawnedCliGdbManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start();
|
||||
manager.start(gdbBin.getAbsolutePath());
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PtyFactory getPtyFactory() {
|
||||
// TODO: Choose by host OS
|
||||
return new LinuxPtyFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,29 @@
|
||||
*/
|
||||
package agent.gdb.manager.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
import agent.gdb.pty.PtyFactory;
|
||||
import agent.gdb.pty.linux.LinuxPtyFactory;
|
||||
|
||||
@Ignore("Need to install GDB 7.6.1 to the expected directory on CI")
|
||||
public class SpawnedMi2Gdb7Dot6Dot1ManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected File findGdbBin() {
|
||||
return new File("/opt/gdb-7.6.1/bin/gdb");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start("/opt/gdb-7.6.1/bin/gdb", "-i", "mi2");
|
||||
manager.start(gdbBin.getAbsolutePath(), "-i", "mi2");
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PtyFactory getPtyFactory() {
|
||||
// TODO: Choose by host OS
|
||||
return new LinuxPtyFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* 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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
|
||||
@Ignore("Does not exist on CI")
|
||||
public class SpawnedMi2GdbHomeLocalManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected File findGdbBin() {
|
||||
String home = System.getProperty("user.home");
|
||||
return new File(home, "local/bin/gdb");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start(gdbBin.getAbsolutePath(), "-i", "mi2");
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExpectedDefaultArgsVar() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -21,25 +21,17 @@ import java.util.concurrent.CompletableFuture;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
import agent.gdb.pty.PtyFactory;
|
||||
import agent.gdb.pty.linux.LinuxPtyFactory;
|
||||
|
||||
@Ignore("Need compatible GDB version for CI")
|
||||
public class SpawnedMi2GdbManagerTest2 extends AbstractGdbManagerTest {
|
||||
@Ignore("Need compatible version install on CI")
|
||||
public class SpawnedMi2GdbSystemManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start(GdbManager.DEFAULT_GDB_CMD, "-i", "mi2");
|
||||
manager.start(gdbBin.getAbsolutePath(), "-i", "mi2");
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PtyFactory getPtyFactory() {
|
||||
// TODO: Choose by host OS
|
||||
return new LinuxPtyFactory();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user