GT-3079 Added checking to binary loader to check for memory conflicts

before loading in the "add to program" case.
This commit is contained in:
ghidravore 2019-08-12 12:44:59 -04:00
parent 6da0d9340d
commit ae5662a50f
19 changed files with 132 additions and 99 deletions

View file

@ -225,14 +225,13 @@ public class MemoryBlockUtils {
block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset, block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset,
length, isOverlay); length, isOverlay);
} }
catch (MemoryConflictException e) { catch (MemoryConflictException | DuplicateNameException e) {
block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset, block = createBlockNoDuplicateName(program, name, start, fileBytes, offset, length);
length, true);
log.appendMsg("Conflict attempting to create memory block: " + name + log.appendMsg("Conflict attempting to create memory block: " + name +
" at address " + start.toString() + " Created block in new overlay instead"); " at address " + start.toString() + " Created block in new overlay instead");
} }
} }
catch (LockException | DuplicateNameException | MemoryConflictException e) { catch (LockException | MemoryConflictException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -241,6 +240,22 @@ public class MemoryBlockUtils {
return block; return block;
} }
private static MemoryBlock createBlockNoDuplicateName(Program program, String blockName,
Address start, FileBytes fileBytes, long offset, long length)
throws LockException, MemoryConflictException, AddressOverflowException {
int count = 1;
String name = blockName;
while (true) {
try {
return program.getMemory().createInitializedBlock(name, start, fileBytes, offset,
length, true);
}
catch (DuplicateNameException e) {
name = blockName + "_" + count++;
}
}
}
/** /**
* Creates a new initialized block in memory using the bytes from the given input stream. * Creates a new initialized block in memory using the bytes from the given input stream.
* If there is a conflict when creating this block (some other block occupies at least some * If there is a conflict when creating this block (some other block occupies at least some

View file

@ -139,7 +139,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null) { if (options != null) {
for (Option option : options) { for (Option option : options) {
@ -151,7 +151,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
} }
} }
} }
return super.validateOptions(provider, loadSpec, options); return super.validateOptions(provider, loadSpec, options, program);
} }
@Override @Override

View file

@ -188,7 +188,7 @@ public abstract class AbstractProgramLoader implements Loader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null) { if (options != null) {
for (Option option : options) { for (Option option : options) {
String name = option.getName(); String name = option.getName();

View file

@ -23,16 +23,14 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainFolder; import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObject;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes; import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*; import ghidra.program.model.mem.Memory;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.NumericUtilities; import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class BinaryLoader extends AbstractProgramLoader { public class BinaryLoader extends AbstractProgramLoader {
@ -91,7 +89,8 @@ public class BinaryLoader extends AbstractProgramLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program) {
Address baseAddr = null; Address baseAddr = null;
long length = 0; long length = 0;
long fileOffset = 0; long fileOffset = 0;
@ -194,7 +193,12 @@ public class BinaryLoader extends AbstractProgramLoader {
if (length == -1) { if (length == -1) {
return "Invalid length specified"; return "Invalid length specified";
} }
return super.validateOptions(provider, loadSpec, options); if (program != null) {
if (program.getMemory().intersects(baseAddr, baseAddr.add(length - 1))) {
return "Memory Conflict: Use <Options...> to change the base address!";
}
}
return super.validateOptions(provider, loadSpec, options, program);
} }
private Address getBaseAddr(List<Option> options) { private Address getBaseAddr(List<Option> options) {
@ -322,27 +326,27 @@ public class BinaryLoader extends AbstractProgramLoader {
if (blockName == null || blockName.length() == 0) { if (blockName == null || blockName.length() == 0) {
blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace()); blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace());
} }
try { createBlock(prog, isOverlay, blockName, baseAddr, fileBytes, length, log);
MemoryBlock block = prog.getMemory().createInitializedBlock(blockName, baseAddr,
fileBytes, 0, length, isOverlay);
block.setRead(true);
block.setWrite(isOverlay ? false : true);
block.setExecute(isOverlay ? false : true);
block.setSourceName("Binary Loader");
MemoryBlockUtils.adjustFragment(prog, block.getStart(), blockName);
}
catch (LockException | MemoryConflictException e) {
Msg.error(this, "Unexpected exception creating memory block", e);
}
return true; return true;
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
throw new IllegalArgumentException("Invalid address range specified: start:" + throw new IllegalArgumentException("Invalid address range specified: start:" +
baseAddr + ", length:" + length + " - end address exceeds address space boundary!"); baseAddr + ", length:" + length + " - end address exceeds address space boundary!");
} }
catch (DuplicateNameException e) {
throw new IllegalArgumentException("Duplicate block name specified: " + blockName);
} }
private void createBlock(Program prog, boolean isOverlay, String blockName, Address baseAddr,
FileBytes fileBytes, long length, MessageLog log)
throws AddressOverflowException, IOException {
if (prog.getMemory().intersects(baseAddr, baseAddr.add(length - 1))) {
throw new IOException("Can't load " + length + " bytes at address " + baseAddr +
" since it conflicts with existing memory blocks!");
}
MemoryBlockUtils.createInitializedBlock(prog, isOverlay, blockName, baseAddr, fileBytes, 0,
length, null, "Binary Loader", true, !isOverlay, !isOverlay, log);
} }
private long clipToMemorySpace(long length, MessageLog log, Program program) { private long clipToMemorySpace(long length, MessageLog log, Program program) {

View file

@ -153,7 +153,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null) { if (options != null) {
for (Option option : options) { for (Option option : options) {
String name = option.getName(); String name = option.getName();
@ -164,7 +164,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
} }
} }
} }
return super.validateOptions(provider, loadSpec, options); return super.validateOptions(provider, loadSpec, options, program);
} }
private boolean performFakeLinking(List<Option> options) { private boolean performFakeLinking(List<Option> options) {

View file

@ -90,14 +90,14 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null) { if (options != null) {
String validationErrorStr = ElfLoaderOptionsFactory.validateOptions(loadSpec, options); String validationErrorStr = ElfLoaderOptionsFactory.validateOptions(loadSpec, options);
if (validationErrorStr != null) { if (validationErrorStr != null) {
return validationErrorStr; return validationErrorStr;
} }
} }
return super.validateOptions(provider, loadSpec, options); return super.validateOptions(provider, loadSpec, options, program);
} }
@Override @Override

View file

@ -89,7 +89,7 @@ public class GdtLoader implements Loader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null && options.size() > 0) { if (options != null && options.size() > 0) {
return "GDTLoader takes no options"; return "GDTLoader takes no options";
} }

View file

@ -54,7 +54,7 @@ public class GzfLoader implements Loader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null && options.size() > 0) { if (options != null && options.size() > 0) {
return "GzfLoader takes no options"; return "GzfLoader takes no options";
} }

View file

@ -75,7 +75,7 @@ public class IntelHexLoader extends AbstractProgramLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
Address baseAddr = null; Address baseAddr = null;
for (Option option : options) { for (Option option : options) {

View file

@ -23,7 +23,6 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
class IntelHexMemImage { class IntelHexMemImage {
@ -212,23 +211,9 @@ class IntelHexMemImage {
} }
String name = blockName == null ? base.getAddressSpace().getName() : blockName; String name = blockName == null ? base.getAddressSpace().getName() : blockName;
while (true) {
try {
MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, MemoryBlockUtils.createInitializedBlock(program, isOverlay, name,
blockRange.getMinAddress(), new ByteArrayInputStream(data), data.length, blockRange.getMinAddress(), new ByteArrayInputStream(data), data.length,
"Generated by " + creator, progFile, true, !isOverlay, !isOverlay, log, "Generated by " + creator, progFile, true, !isOverlay, !isOverlay, log, monitor);
monitor);
break;
}
catch (RuntimeException e) {
Throwable cause = e.getCause();
if (!(cause instanceof DuplicateNameException)) {
throw e;
}
++count;
name = blockName + "_" + count;
}
}
} }
return log.toString(); return log.toString();
} }

View file

@ -124,10 +124,13 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
* @param provider The bytes of the thing being loaded. * @param provider The bytes of the thing being loaded.
* @param loadSpec The proposed {@link LoadSpec}. * @param loadSpec The proposed {@link LoadSpec}.
* @param options The list of {@link Option}s to validate. * @param options The list of {@link Option}s to validate.
* @param program existing program if the loader is adding to an existing program. If it is
* a fresh import, then this will be null.
* @return null if all {@link Option}s are valid; otherwise, an error message describing the * @return null if all {@link Option}s are valid; otherwise, an error message describing the
* problem is returned. * problem is returned.
*/ */
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options); public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program);
/** /**
* Gets the {@link Loader}'s name, which is used both for display purposes, and to identify the * Gets the {@link Loader}'s name, which is used both for display purposes, and to identify the

View file

@ -92,7 +92,8 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
Program program) {
Address baseAddr = null; Address baseAddr = null;
for (Option option : options) { for (Option option : options) {
@ -335,23 +336,9 @@ public class MotorolaHexLoader extends AbstractProgramLoader {
String name = String name =
blockName == null ? baseAddr.getAddressSpace().getName() : blockName; blockName == null ? baseAddr.getAddressSpace().getName() : blockName;
int count = 0; MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start,
while (true) { new ByteArrayInputStream(data), data.length, "", provider.getName(),
try { true, isOverlay, isOverlay, log, monitor);
MemoryBlockUtils.createInitializedBlock(program, isOverlay, name,
start, new ByteArrayInputStream(data), data.length, "",
provider.getName(), true, isOverlay, isOverlay, log, monitor);
break;
}
catch (RuntimeException e) {
Throwable cause = e.getCause();
if (!(cause instanceof DuplicateNameException)) {
throw e;
}
++count;
name = blockName + "_" + count;
}
}
} }
offset = 0; offset = 0;
// set up new start address of new block we are starting // set up new start address of new block we are starting

View file

@ -177,7 +177,7 @@ public class PeLoader extends AbstractPeDebugLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
if (options != null) { if (options != null) {
for (Option option : options) { for (Option option : options) {
String name = option.getName(); String name = option.getName();
@ -188,7 +188,7 @@ public class PeLoader extends AbstractPeDebugLoader {
} }
} }
} }
return super.validateOptions(provider, loadSpec, options); return super.validateOptions(provider, loadSpec, options, program);
} }
@Override @Override

View file

@ -335,7 +335,7 @@ public class XmlLoader extends AbstractProgramLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
// XXX will this work? is there other state that xmlOptions needs to // XXX will this work? is there other state that xmlOptions needs to
// know? // know?
try { try {

View file

@ -60,10 +60,12 @@ public class AddToProgramDialog extends ImporterDialog {
folderButton.setEnabled(false); folderButton.setEnabled(false);
languageButton.setEnabled(false); languageButton.setEnabled(false);
filenameTextField.setEnabled(false); filenameTextField.setEnabled(false);
validateFormInput();
} }
@Override @Override
protected boolean validateFormInput() { protected boolean validateFormInput() {
setOkEnabled(false); setOkEnabled(false);
optionsButton.setEnabled(false); optionsButton.setEnabled(false);
Loader loader = getSelectedLoader(); Loader loader = getSelectedLoader();
@ -75,10 +77,20 @@ public class AddToProgramDialog extends ImporterDialog {
setStatusText(loader.getName() + " does not support add to program."); setStatusText(loader.getName() + " does not support add to program.");
return false; return false;
} }
optionsButton.setEnabled(true);
LoadSpec loadSpec = getSelectedLoadSpec(loader);
String result =
loader.validateOptions(byteProvider, loadSpec, getOptions(loadSpec), addToProgram);
if (result != null) {
setStatusText(result);
return false;
}
setStatusText(""); setStatusText("");
setOkEnabled(true); setOkEnabled(true);
optionsButton.setEnabled(true);
return true; return true;
} }
@ -102,19 +114,19 @@ public class AddToProgramDialog extends ImporterDialog {
options = selectedLoader.getDefaultOptions(byteProvider, selectedLoadSpec, null, true); options = selectedLoader.getDefaultOptions(byteProvider, selectedLoadSpec, null, true);
} }
TaskLauncher.launchNonModal("Import File", monitor -> { TaskLauncher.launchNonModal("Import File", monitor -> {
ImporterUtilities.addContentToProgram(tool, addToProgram, fsrl, selectedLoadSpec, options, ImporterUtilities.addContentToProgram(tool, addToProgram, fsrl, selectedLoadSpec,
monitor); options, monitor);
}); });
close(); close();
} }
@Override @Override
protected List<Option> getOptions(LoadSpec loadSpec) { protected List<Option> getOptions(LoadSpec loadSpec) {
if (options == null) { if (options != null) {
options = loadSpec.getLoader().getDefaultOptions(byteProvider, loadSpec, null, true);
}
return options; return options;
} }
return loadSpec.getLoader().getDefaultOptions(byteProvider, loadSpec, addToProgram, true);
}
/** /**
* Retrieves the current language/compiler spec from the program that will be added to. * Retrieves the current language/compiler spec from the program that will be added to.

View file

@ -346,10 +346,9 @@ public class ImporterDialog extends DialogComponentProvider {
String programPath = removeTrailingSlashes(getName()); String programPath = removeTrailingSlashes(getName());
DomainFolder importFolder = getOrCreateImportFolder(destinationFolder, programPath); DomainFolder importFolder = getOrCreateImportFolder(destinationFolder, programPath);
String programName = FilenameUtils.getName(programPath); String programName = FilenameUtils.getName(programPath);
List<Option> localOptions = getOptions(loadSpec);
TaskLauncher.launchNonModal("Import File", monitor -> { TaskLauncher.launchNonModal("Import File", monitor -> {
ImporterUtilities.importSingleFile(tool, programManager, fsrl, importFolder, ImporterUtilities.importSingleFile(tool, programManager, fsrl, importFolder,
loadSpec, programName, localOptions, monitor); loadSpec, programName, getOptions(loadSpec), monitor);
}); });
close(); close();
} }
@ -404,24 +403,24 @@ public class ImporterDialog extends DialogComponentProvider {
AddressFactory addressFactory = selectedLanguage.getLanguage().getAddressFactory(); AddressFactory addressFactory = selectedLanguage.getLanguage().getAddressFactory();
LoadSpec loadSpec = getSelectedLoadSpec(loader); LoadSpec loadSpec = getSelectedLoadSpec(loader);
OptionValidator validator = OptionValidator validator =
optionList -> loader.validateOptions(byteProvider, loadSpec, optionList); optionList -> loader.validateOptions(byteProvider, loadSpec, optionList, null);
AddressFactoryService service = () -> addressFactory; AddressFactoryService service = () -> addressFactory;
List<Option> defaultOptions = getOptions(loadSpec); List<Option> currentOptions = getOptions(loadSpec);
if (defaultOptions.isEmpty()) { if (currentOptions.isEmpty()) {
Msg.showInfo(this, null, "Options", "There are no options for this importer!"); Msg.showInfo(this, null, "Options", "There are no options for this importer!");
return; return;
} }
OptionsDialog optionsDialog = new OptionsDialog(defaultOptions, validator, service); OptionsDialog optionsDialog = new OptionsDialog(currentOptions, validator, service);
optionsDialog.setHelpLocation( optionsDialog.setHelpLocation(
new HelpLocation("ImporterPlugin", getAnchorForSelectedLoader(loader))); new HelpLocation("ImporterPlugin", getAnchorForSelectedLoader(loader)));
tool.showDialog(optionsDialog); tool.showDialog(optionsDialog);
if (!optionsDialog.wasCancelled()) { if (!optionsDialog.wasCancelled()) {
options = optionsDialog.getOptions(); options = optionsDialog.getOptions();
} }
validateFormInput();
} }
catch (LanguageNotFoundException e) { catch (LanguageNotFoundException e) {
Msg.showError(this, null, "Language Error", Msg.showError(this, null, "Language Error",

View file

@ -23,8 +23,12 @@ import java.util.Arrays;
import org.junit.*; import org.junit.*;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
@ -72,8 +76,7 @@ public class MemoryBlockUtilTest extends AbstractGhidraHeadedIntegrationTest {
Arrays.fill(data, (byte) 0xb); Arrays.fill(data, (byte) 0xb);
is = new ByteArrayInputStream(data); is = new ByteArrayInputStream(data);
MemoryBlockUtils.createInitializedBlock(prog, false, "b", space.getAddress(3500), is, 1000, MemoryBlockUtils.createInitializedBlock(prog, false, "b", space.getAddress(3500), is, 1000,
"bbbb", "b b b", false, "bbbb", "b b b", false, false, false, log, TaskMonitor.DUMMY);
false, false, log, TaskMonitor.DUMMY);
MemoryBlock[] blocks = prog.getMemory().getBlocks(); MemoryBlock[] blocks = prog.getMemory().getBlocks();
assertEquals(2, blocks.length); assertEquals(2, blocks.length);
@ -111,8 +114,8 @@ public class MemoryBlockUtilTest extends AbstractGhidraHeadedIntegrationTest {
byte[] cdata = new byte[1000]; byte[] cdata = new byte[1000];
Arrays.fill(cdata, (byte) 0xc); Arrays.fill(cdata, (byte) 0xc);
MemoryBlockUtils.createInitializedBlock(prog, false, "c", space.getAddress(4000), MemoryBlockUtils.createInitializedBlock(prog, false, "c", space.getAddress(4000),
new ByteArrayInputStream(cdata), new ByteArrayInputStream(cdata), 1000, "Ccomment", "Csource", false, false, false, log,
1000, "Ccomment", "Csource", false, false, false, log, TaskMonitor.DUMMY); TaskMonitor.DUMMY);
MemoryBlock[] blocks = prog.getMemory().getBlocks(); MemoryBlock[] blocks = prog.getMemory().getBlocks();
assertEquals(2, blocks.length); assertEquals(2, blocks.length);
@ -127,5 +130,30 @@ public class MemoryBlockUtilTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(blocks[1].isInitialized()); assertTrue(blocks[1].isInitialized());
} }
//TODO test bit blocks and code unit clearing... private Address addr(long offset) {
return space.getAddress(offset);
}
@Test
public void testDuplicateExceptionHandling() throws Exception {
ByteProvider byteProvider = new ByteArrayProvider(new byte[1000]);
FileBytes fileBytes =
MemoryBlockUtils.createFileBytes(prog, byteProvider, TaskMonitor.DUMMY);
MemoryBlockUtils.createInitializedBlock(prog, true, "test", addr(0), fileBytes, 0, 10, "",
"", true, true, true, new MessageLog());
MemoryBlockUtils.createInitializedBlock(prog, true, "test", addr(0), fileBytes, 0, 10, "",
"", true, true, true, new MessageLog());
MemoryBlockUtils.createInitializedBlock(prog, true, "test", addr(0), fileBytes, 0, 10, "",
"", true, true, true, new MessageLog());
MemoryBlock[] blocks = prog.getMemory().getBlocks();
assertEquals(3, blocks.length);
assertEquals("test", blocks[0].getName());
assertEquals("test_1", blocks[1].getName());
assertEquals("test_2", blocks[2].getName());
}
} }

View file

@ -15,7 +15,7 @@
*/ */
package generic.test; package generic.test;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
@ -661,10 +661,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
if (button == null) { if (button == null) {
throw new AssertionError("Couldn't find button " + buttonText + "."); throw new AssertionError("Couldn't find button " + buttonText + ".");
} }
if (!button.isShowing()) { if (!runSwing(() -> button.isShowing())) {
throw new AssertionError("Button " + buttonText + " is not showing."); throw new AssertionError("Button " + buttonText + " is not showing.");
} }
if (!button.isEnabled()) { if (!runSwing(() -> button.isEnabled())) {
throw new AssertionError("Button " + buttonText + " is not enabled."); throw new AssertionError("Button " + buttonText + " is not enabled.");
} }
pressButton(button, waitForCompletion); pressButton(button, waitForCompletion);
@ -700,10 +700,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
if (button == null) { if (button == null) {
throw new AssertionError("Couldn't find button " + buttonName + "."); throw new AssertionError("Couldn't find button " + buttonName + ".");
} }
if (!button.isVisible()) { if (!runSwing(() -> button.isShowing())) {
throw new AssertionError("Button " + buttonName + " is not showing."); throw new AssertionError("Button " + buttonName + " is not showing.");
} }
if (!button.isEnabled()) { if (!runSwing(() -> button.isEnabled())) {
throw new AssertionError("Button " + buttonName + " is not enabled."); throw new AssertionError("Button " + buttonName + " is not enabled.");
} }
pressButton(button, waitForCompletion); pressButton(button, waitForCompletion);

View file

@ -73,11 +73,11 @@ public class SkeletonLoader extends AbstractLibrarySupportLoader {
} }
@Override @Override
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options) { public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program) {
// TODO: If this loader has custom options, validate them here. Not all options require // TODO: If this loader has custom options, validate them here. Not all options require
// validation. // validation.
return super.validateOptions(provider, loadSpec, options); return super.validateOptions(provider, loadSpec, options, program);
} }
} }