Merge remote-tracking branch 'origin/Ghidra_9.1'

This commit is contained in:
Ryan Kurtz 2019-10-18 12:44:35 -04:00
commit 8e30848804
12 changed files with 450 additions and 62 deletions

View file

@ -5,6 +5,9 @@
<executable_format name="Mac OS X Mach-O"> <executable_format name="Mac OS X Mach-O">
<functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile> <functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile>
</executable_format> </executable_format>
<executable_format name="DYLD Cache">
<functionNamesFile>MachOFunctionsThatDoNotReturn</functionNamesFile>
</executable_format>
<executable_format name="Portable Executable (PE)"> <executable_format name="Portable Executable (PE)">
<functionNamesFile>PEFunctionsThatDoNotReturn</functionNamesFile> <functionNamesFile>PEFunctionsThatDoNotReturn</functionNamesFile>
</executable_format> </executable_format>

View file

@ -275,6 +275,29 @@ public class DyldCacheHeader implements StructConverter {
return localSymbolsInfo; return localSymbolsInfo;
} }
/**
* Gets the {@link DyldCacheSlideInfoCommon}.
*
* @return the {@link DyldCacheSlideInfoCommon}. Common, or particular version
*/
public DyldCacheSlideInfoCommon getSlideInfo() {
return slideInfo;
}
/**
* @return slideInfoOffset
*/
public long getSlideInfoOffset() {
return slideInfoOffset;
}
/**
* @return slideInfoSize
*/
public long getSlideInfoSize() {
return slideInfoSize;
}
/** /**
* Gets the {@link List} of branch pool address. Requires header to have been parsed. * Gets the {@link List} of branch pool address. Requires header to have been parsed.
* *

View file

@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
* *
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a> * @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused")
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon { public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
private int toc_offset; private int toc_offset;
@ -36,6 +35,26 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
private int entries_count; private int entries_count;
private int entries_size; private int entries_size;
public int getTocOffset() {
return toc_offset;
}
public int getTocCount() {
return toc_count;
}
public int getEntriesOffset() {
return entries_offset;
}
public int getEntriesCount() {
return entries_count;
}
public int getEntriesSize() {
return entries_size;
}
/** /**
* Create a new {@link DyldCacheSlideInfo1}. * Create a new {@link DyldCacheSlideInfo1}.
* *

View file

@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
* *
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a> * @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused")
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon { public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private int page_size; private int page_size;
@ -37,6 +36,44 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private int page_extras_count; private int page_extras_count;
private long delta_mask; private long delta_mask;
private long value_add; private long value_add;
private short page_starts_entries[];
private short page_extras_entries[];
public long getPageSize() {
return ((long)page_size) & 0xffffffff;
}
public long getPageStartsOffset() {
return ((long) page_starts_offset) & 0xffffffff;
}
public long getPageStartsCount() {
return ((long) page_starts_count) & 0xffffffff;
}
public long getPageExtrasOffset() {
return ((long) page_extras_offset) & 0xffffffff;
}
public long getPageExtrasCount() {
return ((long) page_extras_count) & 0xffffffff;
}
public long getDeltaMask() {
return delta_mask;
}
public long getValueAdd() {
return value_add;
}
public short[] getPageStartsEntries() {
return page_starts_entries;
}
public short[] getPageExtrasEntries() {
return page_extras_entries;
}
/** /**
* Create a new {@link DyldCacheSlideInfo2}. * Create a new {@link DyldCacheSlideInfo2}.
@ -53,6 +90,8 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
page_extras_count = reader.readNextInt(); page_extras_count = reader.readNextInt();
delta_mask = reader.readNextLong(); delta_mask = reader.readNextLong();
value_add = reader.readNextLong(); value_add = reader.readNextLong();
page_starts_entries = reader.readNextShortArray(page_starts_count);
page_extras_entries = reader.readNextShortArray(page_extras_count);
} }
@Override @Override

View file

@ -27,7 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
* *
* @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a> * @see <a href="https://opensource.apple.com/source/dyld/dyld-625.13/launch-cache/dyld_cache_format.h.auto.html">launch-cache/dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused")
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon { public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
private int page_size; private int page_size;
@ -35,6 +34,22 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
private long auth_value_add; private long auth_value_add;
private short page_starts[]; private short page_starts[];
public int getPageSize() {
return page_size;
}
public int getPageStartsCount() {
return page_starts_count;
}
public long getAuthValueAdd() {
return auth_value_add;
}
public short[] getPageStarts() {
return page_starts;
}
/** /**
* Create a new {@link DyldCacheSlideInfo3}. * Create a new {@link DyldCacheSlideInfo3}.
* *

View file

@ -20,6 +20,7 @@ import java.util.*;
import ghidra.app.util.MemoryBlockUtils; import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option; import ghidra.app.util.Option;
import ghidra.app.util.OptionUtils;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture; import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture;
@ -47,7 +48,13 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
static final String CREATE_DYLIB_SECTIONS_OPTION_NAME = "Create DYLIB section memory blocks"; static final String CREATE_DYLIB_SECTIONS_OPTION_NAME = "Create DYLIB section memory blocks";
/** Default value for loader option to create memory blocks for DYLIB sections */ /** Default value for loader option to create memory blocks for DYLIB sections */
static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = true; static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false;
/** Loader option to add relocation entries for each fixed chain pointer */
static final String ADD_RELOCATION_ENTRIES_OPTION_NAME = "Add relocation entries for fixed chain pointers";
/** Default value for loader option add relocation entries */
static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = false;
@Override @Override
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException { public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
@ -84,7 +91,8 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
try { try {
DyldCacheProgramBuilder.buildProgram(program, provider, DyldCacheProgramBuilder.buildProgram(program, provider,
MemoryBlockUtils.createFileBytes(program, provider, monitor), MemoryBlockUtils.createFileBytes(program, provider, monitor),
shouldProcessSymbols(options), shouldCreateDylibSections(options), log, monitor); shouldProcessSymbols(options), shouldCreateDylibSections(options),
shouldAddRelocationEntries(options), log, monitor);
} }
catch (CancelledException e) { catch (CancelledException e) {
return; return;
@ -105,32 +113,23 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader {
list.add( list.add(
new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT, new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections")); Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections"));
list.add(
new Option(ADD_RELOCATION_ENTRIES_OPTION_NAME, ADD_RELOCATION_ENTRIES_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-addRelocationEntries"));
} }
return list; return list;
} }
private boolean shouldProcessSymbols(List<Option> options) { private boolean shouldProcessSymbols(List<Option> options) {
if (options != null) { return OptionUtils.getOption(PROCESS_SYMBOLS_OPTION_NAME, options, PROCESS_SYMBOLS_OPTION_DEFAULT);
for (Option option : options) {
String optName = option.getName();
if (optName.equals(PROCESS_SYMBOLS_OPTION_NAME)) {
return (Boolean) option.getValue();
}
}
}
return PROCESS_SYMBOLS_OPTION_DEFAULT;
} }
private boolean shouldCreateDylibSections(List<Option> options) { private boolean shouldCreateDylibSections(List<Option> options) {
if (options != null) { return OptionUtils.getOption(CREATE_DYLIB_SECTIONS_OPTION_NAME, options, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT);
for (Option option : options) { }
String optName = option.getName();
if (optName.equals(CREATE_DYLIB_SECTIONS_OPTION_NAME)) { private boolean shouldAddRelocationEntries(List<Option> options) {
return (Boolean) option.getValue(); return OptionUtils.getOption(ADD_RELOCATION_ENTRIES_OPTION_NAME, options, ADD_RELOCATION_ENTRIES_OPTION_DEFAULT);
}
}
}
return CREATE_DYLIB_SECTIONS_OPTION_DEFAULT;
} }
@Override @Override

View file

@ -17,7 +17,10 @@ package ghidra.app.util.opinion;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import ghidra.app.util.MemoryBlockUtils; import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
@ -25,15 +28,28 @@ import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.MachException; import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader; import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.commands.NList; import ghidra.app.util.bin.format.macho.commands.NList;
import ghidra.app.util.bin.format.macho.dyld.*; import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheImageInfo;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheLocalSymbolsInfo;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheSlideInfo2;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheSlideInfoCommon;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.importer.MessageLogContinuesFactory; import ghidra.app.util.importer.MessageLogContinuesFactory;
import ghidra.program.database.mem.FileBytes; import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.*; import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.data.Pointer64DataType;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramFragment;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
@ -41,9 +57,16 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DyldCacheProgramBuilder extends MachoProgramBuilder { public class DyldCacheProgramBuilder extends MachoProgramBuilder {
private static final int DATA_PAGE_MAP_ENTRY = 1;
private static final int BYTES_PER_CHAIN_OFFSET = 4;
private static final int CHAIN_OFFSET_MASK = 0x3fff;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
protected DyldCacheHeader dyldCacheHeader; protected DyldCacheHeader dyldCacheHeader;
private boolean shouldProcessSymbols; private boolean shouldProcessSymbols;
private boolean shouldCreateDylibSections; private boolean shouldCreateDylibSections;
private boolean shouldAddRelocationEntries;
/** /**
* Creates a new {@link DyldCacheProgramBuilder} based on the given information. * Creates a new {@link DyldCacheProgramBuilder} based on the given information.
@ -54,15 +77,18 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false * @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections; * @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false * otherwise, false
* @param shouldAddRelocationEntries True to create a relocation entry for each fixed up pointer in pointer chain
* @param log The log * @param log The log
* @param monitor A cancelable task monitor * @param monitor A cancelable task monitor
*/ */
protected DyldCacheProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes, protected DyldCacheProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log, boolean shouldProcessSymbols, boolean shouldCreateDylibSections,
boolean shouldAddRelocationEntries, MessageLog log,
TaskMonitor monitor) { TaskMonitor monitor) {
super(program, provider, fileBytes, log, monitor); super(program, provider, fileBytes, log, monitor);
this.shouldProcessSymbols = shouldProcessSymbols; this.shouldProcessSymbols = shouldProcessSymbols;
this.shouldCreateDylibSections = shouldCreateDylibSections; this.shouldCreateDylibSections = shouldCreateDylibSections;
this.shouldAddRelocationEntries = shouldAddRelocationEntries;
} }
/** /**
@ -74,15 +100,17 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @param shouldProcessSymbols True if symbols should be processed; otherwise, false * @param shouldProcessSymbols True if symbols should be processed; otherwise, false
* @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections; * @param shouldCreateDylibSections True if memory blocks should be created for DYLIB sections;
* otherwise, false * otherwise, false
* @param addRelocationEntries True to create a relocation entry for each fixed up pointer in pointer chain
* @param log The log * @param log The log
* @param monitor A cancelable task monitor * @param monitor A cancelable task monitor
* @throws Exception if a problem occurs * @throws Exception if a problem occurs
*/ */
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes, public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
boolean shouldProcessSymbols, boolean shouldCreateDylibSections, MessageLog log, boolean shouldProcessSymbols, boolean shouldCreateDylibSections, boolean addRelocationEntries,
TaskMonitor monitor) throws Exception { MessageLog log, TaskMonitor monitor) throws Exception {
DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program, DyldCacheProgramBuilder dyldCacheProgramBuilder = new DyldCacheProgramBuilder(program,
provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections, log, monitor); provider, fileBytes, shouldProcessSymbols, shouldCreateDylibSections,
addRelocationEntries, log, monitor);
dyldCacheProgramBuilder.build(); dyldCacheProgramBuilder.build();
} }
@ -97,9 +125,11 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
setDyldCacheImageBase(); setDyldCacheImageBase();
processDyldCacheMemoryBlocks(); processDyldCacheMemoryBlocks();
fixPageChains();
markupHeaders(); markupHeaders();
markupBranchIslands(); markupBranchIslands();
createSymbols(); createSymbols();
processDylibs(); processDylibs();
} }
@ -212,6 +242,138 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
} }
} }
/**
* Fixes any chained pointers within each of the data pages.
*
* @throws MemoryAccessException if there was a problem reading/writing memory.
* @throws CancelledException
*/
private void fixPageChains() throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
// locate slide Info
DyldCacheSlideInfoCommon slideInfo = dyldCacheHeader.getSlideInfo();
if (slideInfo == null || !(slideInfo instanceof DyldCacheSlideInfo2)) {
log.appendMsg("Can't handle version " +slideInfo.getVersion() + " slide info, only version 2");
return;
}
DyldCacheSlideInfo2 slideInfo2 = (DyldCacheSlideInfo2) slideInfo;
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
long pageSize = slideInfo2.getPageSize();
long pageStartsCount = slideInfo2.getPageStartsCount();
long deltaMask = slideInfo2.getDeltaMask();
long deltaShift = Long.numberOfTrailingZeros(deltaMask);
long valueAdd = slideInfo2.getValueAdd();
short[] pageEntries = slideInfo2.getPageStartsEntries();
short[] extraEntries = slideInfo2.getPageExtrasEntries();
monitor.setMessage("Fixing chained data page pointers...");
monitor.setMaximum(pageStartsCount);
for (int index=0; index < pageStartsCount; index++) {
monitor.checkCanceled();
long page = dataPageStart + (pageSize * index);
monitor.setProgress(index);
int pageEntry = ((int)pageEntries[index]) & 0xffff;
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
continue;
}
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
do {
pageEntry = ((int) extraEntries[extraIndex]) & 0xffff;
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
fixedAddressCount += processPointerChain(page, pageOffset, deltaMask, deltaShift, valueAdd);
extraIndex++;
} while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
fixedAddressCount += processPointerChain(page, pageOffset, deltaMask, deltaShift, valueAdd);
}
}
log.appendMsg("Fixed " + fixedAddressCount + " chained pointers. Creating Pointers");
monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
}
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param page within data pages that has pointers to be unchained
* @param nextOff offset within the page that is the chain start
* @param deltaMask delta offset mask for each value
* @param deltaShift shift needed for the deltaMask to extract the next offset
* @param valueAdd value to be added to each chain pointer
*
* @return count of number of addresses fixed
* @throws MemoryAccessException
* @throws CancelledException
*/
private long processPointerChain(long page, long nextOff, long deltaMask, long deltaShift, long valueAdd)
throws MemoryAccessException, CancelledException {
// TODO: should the image base be used to perform the ASLR slide on the pointers.
// currently image is kept at it's initial location with no ASLR.
Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
long fixedAddressCount = 0;
byte origBytes[] = new byte[8];
long valueMask = ~deltaMask;
long delta = -1;
while (delta != 0) {
monitor.checkCanceled();
Address chainLoc = chainStart.add(nextOff);
long chainValue = memory.getLong(chainLoc);
delta = (chainValue & deltaMask) >> deltaShift;
chainValue = chainValue & valueMask;
if (chainValue != 0) {
chainValue += valueAdd;
}
if (shouldAddRelocationEntries) {
// Add entry to relocation table for the pointer fixup
memory.getBytes(chainLoc, origBytes);
program.getRelocationTable().add(chainLoc, (int) 1,
new long[] { chainValue }, origBytes, null);
}
memory.setLong(chainLoc, chainValue);
// create a pointer at the fixed up chain pointer location
try {
// don't use data utilities. does too much extra checking work
listing.createData(chainLoc, Pointer64DataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already
}
fixedAddressCount++;
nextOff += (delta * 4);
}
return fixedAddressCount;
}
/** /**
* Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the * Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the
* program tree, and make memory blocks for them. * program tree, and make memory blocks for them.

View file

@ -15,20 +15,51 @@
*/ */
package ghidra.program.database.mem; package ghidra.program.database.mem;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Iterator; import java.util.Iterator;
import org.junit.*; import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import ghidra.app.plugin.core.memory.UninitializedBlockCmd; import ghidra.app.plugin.core.memory.UninitializedBlockCmd;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.listing.*; import ghidra.program.model.address.AddressRange;
import ghidra.program.model.mem.*; import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.symbol.*; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.ProgramFragment;
import ghidra.program.model.listing.ProgramModule;
import ghidra.program.model.mem.LiveMemoryHandler;
import ghidra.program.model.mem.LiveMemoryListener;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockException;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import ghidra.program.model.mem.MemoryBlockStub;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.program.model.mem.MemoryConflictException;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceManager;
import ghidra.program.model.symbol.SourceType;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.ToyProgramBuilder; import ghidra.test.ToyProgramBuilder;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -316,6 +347,57 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(newBlock, block); assertEquals(newBlock, block);
} }
@Test
public void testGetBlockByName() throws Exception {
MemoryBlock block1 = createBlock("Test1", addr(100), 100);
MemoryBlock block2 = createBlock("Test2", addr(300), 100);
MemoryBlock block = mem.getBlock("Test1");
assertEquals("Test1", block.getName());
assertEquals("get same block", block, block1);
mem.split(block, addr(150));
block = mem.getBlock("Test1");
assertEquals("Test1", block.getName());
assertEquals(50, block.getSize());
// non-existent block
block = mem.getBlock("NoExist");
assertNull(block);
program.endTransaction(transactionID, true);
transactionID = program.startTransaction("Test");
// now exists
mem.getBlock("Test1").setName("NoExist");
// Test1 no longer exists
assertNull("block deleted", mem.getBlock("Test1"));
block = mem.getBlock("NoExist");
assertEquals("NoExist", block.getName());
mem.removeBlock(block, new TaskMonitorAdapter());
block = mem.getBlock("NoExist");
assertNull("block should be deleted", block);
// Test1 still doesn't exist
block = mem.getBlock("Test1");
assertNull("block deleted", block);
block = mem.getBlock("Test2");
assertEquals("Test2", block.getName());
program.endTransaction(transactionID, true);
program.undo();
// Test1 still doesn't exist
block = mem.getBlock("Test1");
assertNotNull("Undo, Test1 exists again", block);
transactionID = program.startTransaction("Test");
}
@Test @Test
public void testSave() throws Exception { public void testSave() throws Exception {
MemoryBlock block1 = createBlock("Test1", addr(0), 100); MemoryBlock block1 = createBlock("Test1", addr(0), 100);

View file

@ -19,15 +19,39 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressIterator;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType; import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.InstructionPrototype;
import ghidra.program.model.listing.*; import ghidra.program.model.lang.InsufficientBytesException;
import ghidra.program.model.mem.*; import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.lang.UnknownContextException;
import ghidra.program.model.lang.UnknownInstructionException;
import ghidra.program.model.listing.ContextChangeException;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramContext;
import ghidra.program.model.mem.ByteMemBufferImpl;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.FlowType;
import ghidra.program.util.ProgramContextImpl; import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
/** /**
* PseudoDisassembler.java * PseudoDisassembler.java
@ -71,6 +95,8 @@ public class PseudoDisassembler {
private boolean respectExecuteFlag = false; private boolean respectExecuteFlag = false;
private AddressSetView executeSet;
/** /**
* Create a pseudo disassembler for the given program. * Create a pseudo disassembler for the given program.
*/ */
@ -86,16 +112,16 @@ public class PseudoDisassembler {
this.programContext = program.getProgramContext(); this.programContext = program.getProgramContext();
} }
public PseudoDisassembler(Language lang, Memory mem) { /**
program = null; * @return cached addressSet of executable memory blocks
*/
private AddressSetView getExecuteSet() {
if (executeSet != null) {
return executeSet;
}
this.language = lang; executeSet = memory.getExecuteSet();
return executeSet;
this.memory = mem;
pointerSize = language.getDefaultSpace().getPointerSize();
programContext = new ProgramContextImpl(language.getRegisters());
} }
/** /**
@ -615,8 +641,6 @@ public class PseudoDisassembler {
return false; return false;
} }
AddressSetView executeSet = memory.getExecuteSet();
RepeatInstructionByteTracker repeatInstructionByteTracker = RepeatInstructionByteTracker repeatInstructionByteTracker =
new RepeatInstructionByteTracker(MAX_REPEAT_BYTES_LIMIT, null); new RepeatInstructionByteTracker(MAX_REPEAT_BYTES_LIMIT, null);
@ -777,8 +801,8 @@ public class PseudoDisassembler {
} }
} }
// if respecting execute flag on memory, test to make sure we did flow into non-execute memory // if respecting execute flag on memory, test to make sure we did flow into non-execute memory
if (respectExecuteFlag && !executeSet.isEmpty() && AddressSetView execSet = getExecuteSet();
!executeSet.contains(flows[j])) { if (respectExecuteFlag && !execSet.isEmpty() && !execSet.contains(flows[j])) {
if (!flows[j].isExternalAddress()) { if (!flows[j].isExternalAddress()) {
MemoryBlock block = memory.getBlock(flows[j]); MemoryBlock block = memory.getBlock(flows[j]);
// flowing into non-executable, but readable memory is bad // flowing into non-executable, but readable memory is bad
@ -878,8 +902,8 @@ public class PseudoDisassembler {
} }
// check that body does not wander into non-executable memory // check that body does not wander into non-executable memory
AddressSetView executeSet = program.getMemory().getExecuteSet(); AddressSetView execSet = getExecuteSet();
if (respectExecuteFlag && !executeSet.isEmpty() && !body.subtract(executeSet).isEmpty()) { if (respectExecuteFlag && !execSet.isEmpty() && !body.subtract(execSet).isEmpty()) {
return false; return false;
} }

View file

@ -60,6 +60,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
private MemoryBlock lastBlock;// the last accessed block private MemoryBlock lastBlock;// the last accessed block
private LiveMemoryHandler liveMemory; private LiveMemoryHandler liveMemory;
// lazy hashmap of block names to blocks, must be reloaded if blocks are removed or added
private HashMap<String,MemoryBlock> nameBlockMap = new HashMap<String, MemoryBlock>();
private final static MemoryBlock NoBlock = new MemoryBlockStub(); // placeholder for no block, not given out
Lock lock; Lock lock;
private Set<MemoryBlock> potentialOverlappingBlocks; private Set<MemoryBlock> potentialOverlappingBlocks;
@ -182,6 +186,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
lastBlock = null; lastBlock = null;
blocks = newBlocks; blocks = newBlocks;
addrMap.memoryMapChanged(this); addrMap.memoryMapChanged(this);
nameBlockMap = new HashMap<>();
} }
public void setLanguage(Language newLanguage) { public void setLanguage(Language newLanguage) {
@ -302,11 +307,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
*/ */
@Override @Override
public synchronized MemoryBlock getBlock(String blockName) { public synchronized MemoryBlock getBlock(String blockName) {
// find block that might have been cached from previous call
MemoryBlock memoryBlock = nameBlockMap.get(blockName);
if (memoryBlock != null) {
if (memoryBlock == NoBlock) {
// found placeholder, have searched and found nothing before
return null;
}
return memoryBlock;
}
for (MemoryBlock block : blocks) { for (MemoryBlock block : blocks) {
if (block.getName().equals(blockName)) { if (block.getName().equals(blockName)) {
nameBlockMap.put(blockName, block);
return block; return block;
} }
} }
// store placeholder there is no memory block with that name
nameBlockMap.put(blockName, NoBlock);
return null; return null;
} }
@ -374,6 +393,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
if (program != null) { if (program != null) {
program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null); program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null);
} }
// name could have changed
nameBlockMap = new HashMap<>();
} }
void fireBytesChanged(Address addr, int count) { void fireBytesChanged(Address addr, int count) {

View file

@ -390,7 +390,7 @@ class BigRefListV0 extends RefList {
if (maxLevel < level) { if (maxLevel < level) {
maxLevel = level; maxLevel = level;
} }
if (level > currentRefLevel) { if (level >= currentRefLevel) {
return level; return level;
} }
} }

View file

@ -342,7 +342,7 @@ class RefListV0 extends RefList {
if (maxLevel < level) { if (maxLevel < level) {
maxLevel = level; maxLevel = level;
} }
if (level > currentRefLevel) { if (level >= currentRefLevel) {
return level; return level;
} }
} }