Merge remote-tracking branch 'origin/GP-1666_Dan_gdbExecFile--SQUASHED'

This commit is contained in:
ghidra1
2022-01-21 11:37:12 -05:00
8 changed files with 153 additions and 55 deletions

View File

@@ -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);
}

View File

@@ -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(

View File

@@ -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);

View File

@@ -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());

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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 "";
}
}

View File

@@ -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();
}
}