mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/Ghidra_9.1'
This commit is contained in:
commit
8e30848804
12 changed files with 450 additions and 62 deletions
|
@ -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>
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue