diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/disassemble/DisassembleCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/disassemble/DisassembleCommand.java index 1c7925d011..9521ec3ece 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/disassemble/DisassembleCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/disassemble/DisassembleCommand.java @@ -22,10 +22,10 @@ import ghidra.program.disassemble.*; import ghidra.program.model.address.*; import ghidra.program.model.lang.Register; import ghidra.program.model.lang.RegisterValue; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** @@ -165,13 +165,18 @@ public class DisassembleCommand extends BackgroundCommand { int instructionAlignment) { exectuableSet = Disassembler.isRestrictToExecuteMemory(program) - ? exectuableSet = getExecutableSet(program) : null; + ? exectuableSet = getExecutableSet(program) + : null; this.alignment = instructionAlignment; disassemblyPerformed = false; unalignedStart = false; nonExecutableStart = false; + disassembledAddrs = new AddressSet(); + + Listing listing = program.getListing(); + Disassembler disassembler = Disassembler.getDisassembler(program, monitor, new MyListener(monitor)); disassembler.setSeedContext(seedContext); @@ -199,37 +204,136 @@ public class DisassembleCommand extends BackgroundCommand { if (startNumAddr > 1) { monitor.initialize(startNumAddr); } + long doneNumAddr = 0; - AddressSet allLocalDisAddrs = new AddressSet(); + AddressSet seedSet = new AddressSet(); - while (!set.isEmpty() && !monitor.isCancelled()) { - AddressSet seedSet = getNextSeedSet(program, set, monitor); - if (seedSet.isEmpty()) { - continue; + // For each range in the address set to disassemble + // small ranges get added to the seedSet + // If a large range is found, then disassemble the seedSet so far, and + // process the big range by disassembling flow through the range + // causing the analyzer to kick off between disassembly flows + AddressRangeIterator addressRanges = startSet.getAddressRanges(); + for (AddressRange addressRange : addressRanges) { + if (monitor.isCancelled()) { + break; } + // bigger address set + AddressSet subRangeSet = new AddressSet(addressRange); if (startNumAddr > 1) { - monitor.setProgress(set.getNumAddresses() - startNumAddr); + // report on done - number left in this subRange + monitor.setProgress(doneNumAddr); } + doneNumAddr += subRangeSet.getNumAddresses(); // addresses are only done when disassembled - AddressSet localDisAddrs = - disassembler.disassemble(seedSet, restrictedSet, initialContextValue, followFlow); + while (!subRangeSet.isEmpty() && !monitor.isCancelled()) { + Address nextAddr = subRangeSet.getMinAddress(); - allLocalDisAddrs.add(localDisAddrs); + // Check if location is already on disassembly list + if (disassembledAddrs.contains(nextAddr)) { + AddressRange doneRange = disassembledAddrs.getRangeContaining(nextAddr); + subRangeSet.delete(doneRange); + continue; + } - if (localDisAddrs != null && !localDisAddrs.isEmpty()) { - disassemblyPerformed = true; - analizeIfNeeded(mgr, set, localDisAddrs, monitor); + subRangeSet.delete(nextAddr, nextAddr); + + // detect disassembly started in non-executable initialized block + if (exectuableSet != null && !exectuableSet.contains(nextAddr) && + !program.getMemory().getLoadedAndInitializedAddressSet().contains(nextAddr)) { + nonExecutableStart = true; + } + + // only try disassembly on 2 byte boundaries + if ((nextAddr.getOffset() % alignment) != 0) { + // Align to the instruction alignment + // don't error on align problem here anymore + // unalignedStart = true; + nextAddr = nextAddr.subtract(nextAddr.getOffset() % alignment); + } + + // if range is small, just add it to the seedSet + long addrsLeft = subRangeSet.getNumAddresses(); + if (addrsLeft <= 4) { + seedSet.add(nextAddr); + continue; + } + + // location to disassemble is not undefined + Data data = listing.getUndefinedDataAt(nextAddr); + if (data == null) { + // set the subRangeSet to undefined data in the subRangeSet + try { + AddressSetView undefRanges; + undefRanges = + program.getListing().getUndefinedRanges(subRangeSet, true, monitor); + subRangeSet = new AddressSet(undefRanges); + } + catch (CancelledException e) { + // will get handled below + } + continue; + } + + // process the big range by disassembling flow through the range + // causing the analyzer to kick off between disassembly flows + // disassemble the seedSet first + doDisassemblySeeds(disassembler, seedSet, mgr); + seedSet = new AddressSet(); + + // do the current start of the subRangeSet + AddressSet localDisAddrs = + doDisassemblySeeds(disassembler, new AddressSet(nextAddr), mgr); + + // if anything disassembled, analyze the result set + analyzeIfNeeded(mgr, subRangeSet, localDisAddrs, monitor); + subRangeSet.delete(localDisAddrs); + + if (startNumAddr > 1) { + monitor.setMaximum(startNumAddr); + // report on done - number left in this subRange, in case large range + monitor.setProgress(doneNumAddr - subRangeSet.getNumAddresses()); + } } - - set = set.subtract(localDisAddrs); } - disassembledAddrs = allLocalDisAddrs; + // If there are any small seedSet ranges left, disassemble them + // Don't kick off analysis, that will be done later + if (!seedSet.isEmpty()) { + doDisassemblySeeds(disassembler, seedSet, mgr); + } return disassemblyPerformed || (!nonExecutableStart & !unalignedStart); } + /** + * Do disassembly of a seedSet of address locations + * + * @param disassembler disassembler to use + * @param seedSet set of addresses to be disassembled + * @param mgr + * + * @return addresses actually disassembled + */ + protected AddressSet doDisassemblySeeds(Disassembler disassembler, AddressSet seedSet, + AutoAnalysisManager mgr) { + AddressSet newDisassembledAddrs = + disassembler.disassemble(seedSet, restrictedSet, initialContextValue, followFlow); + + if (!newDisassembledAddrs.isEmpty()) { + disassemblyPerformed = true; + disassembledAddrs.add(newDisassembledAddrs); + + // notify analysis manager of new code + if (mgr != null) { + mgr.codeDefined(newDisassembledAddrs); + } + } + + return newDisassembledAddrs; + } + /** * Determine if intermediate analysis is required to reduce the risk of disassembling * data regions when performing static disassembly over a contiguous range if @@ -242,14 +346,15 @@ public class DisassembleCommand extends BackgroundCommand { * @param startSet disassembly seed points (prior to removing disassembledSet) * @param disassembledSet last set of disassembled addresses using startSet min-address as seed point */ - private static void analizeIfNeeded(AutoAnalysisManager mgr, AddressSetView startSet, + private static void analyzeIfNeeded(AutoAnalysisManager mgr, AddressSetView startSet, AddressSetView disassembledSet, TaskMonitor monitor) { - if (mgr == null || monitor.isCancelled()) { + if (disassembledSet == null || disassembledSet.isEmpty()) { return; } - // notify analysis manager of new code - mgr.codeDefined(disassembledSet); + if (mgr == null || monitor.isCancelled()) { + return; + } AddressRange firstRange = disassembledSet.getFirstRange(); Address rangeEnd = firstRange.getMaxAddress(); @@ -259,77 +364,6 @@ public class DisassembleCommand extends BackgroundCommand { } } - private AddressSet getNextSeedSet(Program program, AddressSet set, TaskMonitor monitor) { - AddressSet seedSet = new AddressSet(); - - boolean bigRangeFound = false; - - while (!monitor.isCancelled() && !set.isEmpty() && !bigRangeFound) { - Address firstaddr = set.getMinAddress(); - - // if the range is only 4 byte, assume these are seed disassembly points - // and don't cause analysis to happen until the end - // unless a range of addresses is found. - - AddressRange addressRange = set.iterator().next(); - if (addressRange.getLength() > 4) { - bigRangeFound = true; - } - set.deleteRange(firstaddr, firstaddr); - - if (!program.getListing().isUndefined(firstaddr, firstaddr)) { - Address end = firstaddr; - - // if nothing left, don't try to find the next undefined location - if (set.isEmpty()) { - return seedSet; - } - // if the address right after this one isn't in the set, just jump to the - // start of the next range in the set. start has already been deleted. - Address nextAddr = firstaddr.next(); - if (nextAddr == null || !set.contains(nextAddr)) { - continue; - } - - // find the next undefined after this, but restrict it to this set - Data next = program.getListing().getFirstUndefinedData(set, monitor); - - if (next != null) { - end = next.getMinAddress(); - set.deleteRange(firstaddr, end); - firstaddr = end; - } - else { - set.clear(); // no more undefined here - return seedSet; - } - } - - // detect case where disassembly was started in non-executable initialized block - if (exectuableSet != null && !exectuableSet.contains(firstaddr) && - !program.getMemory().getLoadedAndInitializedAddressSet().contains(firstaddr)) { - nonExecutableStart = true; - } - - // only try disassembly on 2 byte boundaries - if ((firstaddr.getOffset() % alignment) != 0) { - // Align to the instruction alignment - // don't error on align problem here anymore - // unalignedStart = true; - firstaddr = firstaddr.subtract(firstaddr.getOffset() % alignment); - } - - // if there is already stuff there, don't put any context - if (!program.getListing().isUndefined(firstaddr, firstaddr)) { - continue; - } - - seedSet.add(firstaddr); - } - - return seedSet; - } - /** * Returns an address set of all instructions that were disassembled. * @return an address set of all instructions that were disassembled diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java index d19e5e70a7..a9f2e3853d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java @@ -25,7 +25,6 @@ import ghidra.app.util.RepeatInstructionByteTracker; import ghidra.framework.options.Options; import ghidra.program.database.register.AddressRangeObjectMap; import ghidra.program.model.address.*; -import ghidra.program.model.data.DefaultDataType; import ghidra.program.model.lang.*; import ghidra.program.model.lang.InstructionError.InstructionErrorType; import ghidra.program.model.listing.*; @@ -127,6 +126,7 @@ public class Disassembler implements DisassemblerConflictHandler { * @param program the program to be disassembled. * @param monitor progress monitor * @param listener object to notify of disassembly messages. + * @return a disassembler ready to disassemble */ public static Disassembler getDisassembler(Program program, TaskMonitor monitor, DisassemblerMessageListener listener) { @@ -157,6 +157,7 @@ public class Disassembler implements DisassemblerConflictHandler { * @param addrFactory address factory * @param monitor progress monitor * @param listener object to notify of disassembly messages. + * @return a disassembler ready to disassemble */ public static Disassembler getDisassembler(Language language, AddressFactory addrFactory, TaskMonitor monitor, DisassemblerMessageListener listener) { @@ -186,6 +187,7 @@ public class Disassembler implements DisassemblerConflictHandler { * @param restrictToExecuteMemory if true disassembly will only be permitted with executable memory blocks * @param monitor progress monitor * @param listener object to notify of disassembly messages. + * @return a disassembler ready to disassemble */ public static Disassembler getDisassembler(Program program, boolean markBadInstructions, boolean markUnimplementedPcode, boolean restrictToExecuteMemory, TaskMonitor monitor, @@ -297,7 +299,7 @@ public class Disassembler implements DisassemblerConflictHandler { * Set seed context which will be used to establish initial context at starting points * which are not arrived at via a natural disassembly flow. A null value will disable * use of any previously set seed context - * @param seedContext + * @param seedContext initial context for disassembly */ public void setSeedContext(DisassemblerContextImpl seedContext) { if (seedContext != null && seedContext.getBaseContextRegister() != baseContextRegister) { @@ -341,7 +343,7 @@ public class Disassembler implements DisassemblerConflictHandler { } /** - * @param program + * @param program the program to check * @return true if program MARK_BAD_INSTRUCTION_PROPERTY has been enabled */ public static boolean isMarkBadDisassemblyOptionEnabled(Program program) { @@ -350,7 +352,7 @@ public class Disassembler implements DisassemblerConflictHandler { } /** - * @param program + * @param program the program to check * @return true if program MARK_UNIMPL_PCODE_PROPERTY has been enabled */ public static boolean isMarkUnimplementedPcodeOptionEnabled(Program program) { @@ -359,7 +361,7 @@ public class Disassembler implements DisassemblerConflictHandler { } /** - * @param program + * @param program the program to check * @return true if program RESTRICT_DISASSEMBLY_TO_EXECUTE_MEMORY_PROPERTY has been enabled */ public static boolean isRestrictToExecuteMemory(Program program) { @@ -436,50 +438,63 @@ public class Disassembler implements DisassemblerConflictHandler { disassembledAddrs = new AddressSet(); - AddressSet todo = new AddressSet(startSet); - int alignment = language.getInstructionAlignment(); - while (!todo.isEmpty() && !monitor.isCancelled()) { - Address addr = todo.getMinAddress(); - // must be aligned - if (addr.getOffset() % alignment != 0) { - todo.deleteRange(addr, addr); - continue; - } - Data data = listing.getUndefinedDataAt(addr); - if (data == null) { - Address endAddr = addr; - CodeUnitIterator it = listing.getCodeUnits(todo, true); - while (it.hasNext() && !monitor.isCancelled()) { - CodeUnit cu = it.next(); - if (cu instanceof Data && - ((Data) cu).getDataType() instanceof DefaultDataType) { - data = (Data) cu; - break; - } - endAddr = cu.getMaxAddress(); - } - if (data == null) { - todo.clear(); - } - else { - todo.deleteRange(addr, endAddr); - } - } - else { - AddressSet currentSet = - disassemble(addr, restrictedSet, initialContextValue, doFollowFlow); - if (currentSet.isEmpty()) { - todo.deleteRange(addr, addr); - } - else { - todo.delete(currentSet); - disassembledAddrs.add(currentSet); - } - } + + AddressRangeIterator addressRanges = startSet.getAddressRanges(); + for (AddressRange addressRange : addressRanges) { if (monitor.isCancelled()) { break; } + + if (disassembledAddrs.contains(addressRange.getMinAddress(), + addressRange.getMaxAddress())) { + continue; + } + + AddressSet todoSubset = new AddressSet(addressRange); + + while (!todoSubset.isEmpty() && !monitor.isCancelled()) { + Address nextAddr = todoSubset.getMinAddress(); + + // Check if location is already on disassembly list + if (disassembledAddrs.contains(nextAddr)) { + AddressRange doneRange = disassembledAddrs.getRangeContaining(nextAddr); + todoSubset.delete(doneRange); + continue; + } + + todoSubset.delete(nextAddr, nextAddr); + + // must be aligned + if (nextAddr.getOffset() % alignment != 0) { + continue; + } + + Data data = listing.getUndefinedDataAt(nextAddr); + if (data == null) { + AddressSetView undefinedRanges = null; + try { + undefinedRanges = + program.getListing().getUndefinedRanges(todoSubset, true, monitor); + todoSubset = new AddressSet(undefinedRanges); + } + catch (CancelledException e) { + break; + } + } + else { + AddressSet currentSet = + disassemble(nextAddr, restrictedSet, initialContextValue, doFollowFlow); + + if (!currentSet.isEmpty()) { // nothing disassembled + todoSubset.delete(currentSet); + disassembledAddrs.add(currentSet); + } + } + if (monitor.isCancelled()) { + break; + } + } } return disassembledAddrs; } @@ -905,8 +920,9 @@ public class Disassembler implements DisassemblerConflictHandler { disassemblerContext.flowToAddress(addr); - MemBuffer instrMemBuffer = new WrappedMemBuffer(blockMemBuffer, - (int) addr.subtract(blockMemBuffer.getAddress())); + // TODO: An overall better caching of bytes for this block could be done instead + // the previous buffering done here was not doing any buffering + MemBuffer instrMemBuffer = new DumbMemBufferImpl(blockMemBuffer.getMemory(), addr); adjustPreParseContext(instrMemBuffer); @@ -1100,7 +1116,7 @@ public class Disassembler implements DisassemblerConflictHandler { /** * Adjust disassembler context prior to disassembly of a new instruction. - * @param instrMemBuffer + * @param instrMemBuffer buffer for bytes from memory * @throws UnknownInstructionException if instruction is invalid */ protected void adjustPreParseContext(MemBuffer instrMemBuffer) @@ -1134,16 +1150,16 @@ public class Disassembler implements DisassemblerConflictHandler { * Process a new instruction which has just been parsed. This method is responsible for * adding the instruction to the current block as well as any delay-slotted instructions. * This method may be overridden and the instruction re-parsed if necessary. - * @param inst - * @param blockMemBuffer - * @param block - * @param instructionSet + * @param inst instruction to process + * @param blockMemBuffer buffer to get bytes + * @param block current block of instructions + * @param instructionSet address set of current instructions in block * @return instruction fallthrough address or null if no fallthrough * @throws InsufficientBytesException if a memory error occurs during instruction processing * @throws UnknownInstructionException if an error occurs during a modified re-parse of * the instruction. - * @throws AddressOverflowException - * @throws NestedDelaySlotException + * @throws AddressOverflowException if address goes out of address space + * @throws NestedDelaySlotException if delay slot found in a delay slot */ protected Address processInstruction(PseudoInstruction inst, MemBuffer blockMemBuffer, InstructionBlock block, InstructionSet instructionSet) @@ -1341,8 +1357,9 @@ public class Disassembler implements DisassemblerConflictHandler { PcodeInjectLibrary pcodeInjectLibrary = program.getCompilerSpec().getPcodeInjectLibrary(); InjectPayload callFixup = pcodeInjectLibrary.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixupStr, program, null); - if (callFixup == null) + if (callFixup == null) { return false; + } return !callFixup.isFallThru(); } @@ -1415,10 +1432,10 @@ public class Disassembler implements DisassemblerConflictHandler { /** * Mark all instructions with unimplemented pcode over the specified address set - * @param program + * @param program to mark unimplemented in * @param addressSet restricted address set or null for entire program - * @param monitor - * @throws CancelledException + * @param monitor allow canceling + * @throws CancelledException if monitor canceled */ public static void markUnimplementedPcode(Program program, AddressSetView addressSet, TaskMonitor monitor) throws CancelledException { @@ -1437,10 +1454,10 @@ public class Disassembler implements DisassemblerConflictHandler { /** * Clear all bookmarks which indicate unimplemented pcode within the specified address set. - * @param program + * @param program program to clear bookmarks * @param addressSet restricted address set or null for entire program - * @param monitor - * @throws CancelledException + * @param monitor allow canceling + * @throws CancelledException if monitor canceled */ public static void clearUnimplementedPcodeWarnings(Program program, AddressSetView addressSet, TaskMonitor monitor) throws CancelledException { @@ -1492,10 +1509,10 @@ public class Disassembler implements DisassemblerConflictHandler { * for minting a new instruction. If value is not null, the temporary context is expanded * to include the context-register value. The temporary context should be cleared after in-memory * instructions (i.e., InstructionSet) have been written to the program. - * @param value - * @param instrAddr - * @param instrLength - * @return + * @param value to add to instruction context if different + * @param instrAddr address of instruction that should have context + * @param instrLength length of instruction to set context + * @return instruction context with possible added value */ ProcessorContext getInstructionContext(RegisterValue value, Address instrAddr, int instrLength) { diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/disassemble/DisassemblerLargeSetTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/disassemble/DisassemblerLargeSetTest.java new file mode 100644 index 0000000000..6d986275dc --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/disassemble/DisassemblerLargeSetTest.java @@ -0,0 +1,394 @@ +/* ### + * 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 ghidra.program.disassemble; + +import static org.junit.Assert.*; + +import org.junit.*; + +import ghidra.app.cmd.disassemble.DisassembleCommand; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.test.AbstractGhidraHeadlessIntegrationTest; +import ghidra.test.ToyProgramBuilder; +import ghidra.util.task.TaskMonitorAdapter; + +public class DisassemblerLargeSetTest extends AbstractGhidraHeadlessIntegrationTest { + + private static final int MEMORY_SIZE = 1024 * 128; + + private static final int CASESIZE = 12; + + private static final long NUMCASES = MEMORY_SIZE / CASESIZE; + + private static byte disBlock[] = { (byte) 0xf5, 0x0c, 0x03, 0x04, (byte) 0xf4, 0x00, + (byte) 0xdf, 0x34, (byte) 0xf4, 0x00, (byte) 0xf4, 0x00 }; + + private ToyProgramBuilder programBuilder;// Instructions are 2-byte aligned + private Program program; + private Disassembler disassembler; + + private int txId; + + private long startTime = 0; + + @Before + public void setUp() throws Exception { + programBuilder = new ToyProgramBuilder("Test", true, true, null); + program = programBuilder.getProgram(); + txId = program.startTransaction("Add Memory");// leave open until tearDown + programBuilder.createMemory(".text", "0", MEMORY_SIZE).setExecute(true);// initialized + + // Fill memory + MemoryBlock block = programBuilder.getProgram().getMemory().getBlock(addr(0x0)); + long numCases = MEMORY_SIZE / CASESIZE; + for (long i = 0; i < numCases; i++) { + block.putBytes(addr(i * CASESIZE), disBlock); + } + + disassembler = new Disassembler(program, TaskMonitorAdapter.DUMMY_MONITOR, null); + + startTime = System.currentTimeMillis(); + } + + @After + public void tearDown() throws Exception { + if (program != null) { + program.endTransaction(txId, true); + } + if (programBuilder != null) { + programBuilder.dispose(); + } + + long endTime = System.currentTimeMillis(); + System.out.println("Time: " + ((double) endTime - (double) startTime) / 1000.0); + } + + private Address addr(long offset) { + return programBuilder.getAddress(offset); + } + + private void verifyBookmarks(int cnt) { + assertEquals("unexpected bookmarks exist", cnt, + program.getBookmarkManager().getBookmarkCount()); + } + + @Test + public void testLargeDisjointPointsNoPredisassembledPoints() throws Exception { + + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassemble1.contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE + 6)); + } + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2)); + + verifyBookmarks(1); + } + + @Test + public void testLargeDisjointPointsWithAlreadyDiassembledPoints() throws Exception { + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassemble1.contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6)); + } + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRange() throws Exception { + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassemble1.contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 10)); + } + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRangePartialOverlap() throws Exception { + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassemble1.contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 11)); + } + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRangeFullOverlap() throws Exception { + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE), addr(i * CASESIZE + 3)); + } + assertTrue(disassemble1.contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE), addr(i * CASESIZE + 3)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 11)); + } + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testSingleRange() throws Exception { + AddressSet disLocs2 = new AddressSet(addr(0x0), addr(CASESIZE * (NUMCASES))); + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + assertTrue(disassemble2.contains(disLocs2)); + } + + @Test + public void testSingleRangeDisjoint() throws Exception { + // disassemble the threaded flow + AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + + AddressSet disLocs2 = new AddressSet(addr(0x0), addr(CASESIZE * (NUMCASES))); + + AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(!disassemble2.contains(disLocs)); + } + + @Test + public void testLargeDisjointPointsNoPredisassembledPointsCmd() throws Exception { + + // disassemble the threaded flow + //AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE + 6)); + } + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs2)); + + verifyBookmarks(1); + } + + @Test + public void testLargeDisjointPointsWithAlreadyDiassembledPointsCmd() throws Exception { + // disassemble the threaded flow + //AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6)); + } + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue( + disassembleCommand.getDisassembledAddressSet().contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRangeCmd() throws Exception { + // disassemble the threaded flow + // AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 10)); + } + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue( + disassembleCommand.getDisassembledAddressSet().contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRangePartialOverlapCmd() throws Exception { + // disassemble the threaded flow + //AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 11)); + } + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue( + disassembleCommand.getDisassembledAddressSet().contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testLargeDisjointRangeFullOverlapCmd() throws Exception { + // disassemble the threaded flow + //AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE), addr(i * CASESIZE + 3)); + } + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + + AddressSet disLocs2 = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs2.add(addr(i * CASESIZE), addr(i * CASESIZE + 3)); + disLocs2.add(addr(i * CASESIZE + 6), addr(i * CASESIZE + 11)); + } + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue( + disassembleCommand.getDisassembledAddressSet().contains(disLocs2.subtract(disLocs))); + } + + @Test + public void testSingleRangeCmd() throws Exception { + AddressSet disLocs2 = new AddressSet(addr(0x0), addr(CASESIZE * (NUMCASES))); + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + assertTrue(disassembleCommand.getDisassembledAddressSet().contains(disLocs2)); + } + + @Test + public void testSingleRangeDisjointCmd() throws Exception { + // disassemble the threaded flow + //AddressSet disassemble1 = disassembler.disassemble(addr(0x0), null, true); + DisassembleCommand disassembleCommand = new DisassembleCommand(addr(0x0), null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs2 = new AddressSet(addr(0x0), addr(CASESIZE * (NUMCASES))); + + //AddressSet disassemble2 = disassembler.disassemble(disLocs2, null, true); + disassembleCommand = new DisassembleCommand(disLocs2, null, true); + disassembleCommand.applyTo(program); + + AddressSet disLocs = new AddressSet(); + for (long i = 0; i < NUMCASES; i++) { + disLocs.add(addr(i * CASESIZE)); + } + assertTrue(!disassembleCommand.getDisassembledAddressSet().contains(disLocs)); + } +}