GP-3630: Adding support for Mach-O/DyldCache ARM64_32 processor.

Also, made other usability improvements to the DyldCacheLoader (options,
entry point, program tree)
This commit is contained in:
Ryan Kurtz 2023-07-14 12:49:42 -04:00
parent befa5aa772
commit 1e9fcddaa3
10 changed files with 165 additions and 88 deletions

View file

@ -15,8 +15,12 @@
*/
package ghidra.app.util.bin.format.macho;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.Processor;
/**
*
* @see <a href="https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/mach/machine.h">osfmk/mach/machiine.h</a>
*/
public final class CpuTypes {
/** mask for architecture bits */
@ -25,6 +29,10 @@ public final class CpuTypes {
/** 64 bit ABI */
public final static int CPU_ARCH_ABI64 = 0x01000000;
/** ABI for 64-bit hardware with 32-bit types; LP32 */
public final static int CPU_ARCH_ABI64_32 = 0x02000000;
public final static int CPU_TYPE_ANY = -1;
public final static int CPU_TYPE_VAX = 0x1;
// UNUSED 0x2
@ -49,7 +57,7 @@ public final class CpuTypes {
public final static int CPU_TYPE_POWERPC64 = (CPU_TYPE_POWERPC | CPU_ARCH_ABI64);
public final static int CPU_TYPE_X86_64 = (CPU_TYPE_X86 | CPU_ARCH_ABI64);
public final static int CPU_TYPE_ARM_64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64);
public final static int CPU_TYPE_ARM64_32 = (CPU_TYPE_ARM | CPU_ARCH_ABI64_32);
/**
* Returns the processor name of the given CPU type value.
@ -75,31 +83,35 @@ public final class CpuTypes {
return Processor.findOrPossiblyCreateProcessor("ARM");
case CPU_TYPE_ARM_64:
return Processor.findOrPossiblyCreateProcessor("AARCH64");
case CPU_TYPE_ARM64_32:
return Processor.findOrPossiblyCreateProcessor("AARCH64");
}
throw new RuntimeException("Unrecognized CPU type: 0x"+Integer.toHexString(cpuType));
}
public final static int getProcessorBitSize(int cpuType) {
switch (cpuType) {
return switch (cpuType) {
case CPU_TYPE_ARM:
case CPU_TYPE_SPARC:
case CPU_TYPE_I860:
case CPU_TYPE_POWERPC:
case CPU_TYPE_X86: return 32;
case CPU_TYPE_X86:
case CPU_TYPE_ARM64_32:
yield 32;
case CPU_TYPE_ARM_64:
case CPU_TYPE_POWERPC64:
case CPU_TYPE_X86_64: return 64;
}
throw new RuntimeException("Unrecognized CPU type: 0x"+Integer.toHexString(cpuType));
case CPU_TYPE_X86_64:
yield 64;
default:
throw new RuntimeException("Unrecognized CPU type: 0x" + Integer.toHexString(cpuType));
};
}
public static String getMagicString(int cpuType, int cpuSubtype) {
switch (cpuType) {
case CPU_TYPE_ARM:
return ""+cpuType+"."+cpuSubtype;
}
return ""+cpuType;
return switch (cpuType) {
case CPU_TYPE_ARM -> cpuType + "." + cpuSubtype;
default -> "" + cpuType;
};
}
}

View file

@ -42,10 +42,11 @@ public final class DyldArchitecture {
public final static DyldArchitecture ARMV7K = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7K, "dyld_v1 armv7k", "arm7", Endian.LITTLE, false );
public final static DyldArchitecture ARMV8A = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64", "AARCH64", Endian.LITTLE, true );
public final static DyldArchitecture ARMV8Ae = new DyldArchitecture(CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64e", "AARCH64", Endian.LITTLE, true );
public final static DyldArchitecture ARM64_32 = new DyldArchitecture(CpuTypes.CPU_TYPE_ARM64_32, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1arm64_32", "ARM64_32", Endian.LITTLE, false );
// @formatter:on
public final static DyldArchitecture[] ARCHITECTURES = new DyldArchitecture[] { X86, X86_64,
X86_64h, POWERPC, ARMV6, ARMV7, ARMV7F, ARMV7S, ARMV7K, ARMV8A, ARMV8Ae };
X86_64h, POWERPC, ARMV6, ARMV7, ARMV7F, ARMV7S, ARMV7K, ARMV8A, ARMV8Ae, ARM64_32 };
/**
* Returns the architecture object with the given signature.

View file

@ -501,6 +501,18 @@ public class DyldCacheHeader implements StructConverter {
return NumericUtilities.convertBytesToString(uuid);
}
/**
* Gets the DYLD entry point address (if known)
*
* @return The DYLD entry point address, or null if it is not known
*/
public Long getEntryPoint() {
if (!hasAccelerateInfo()) {
return accelerateInfoSize_dyldInCacheEntry;
}
return null;
}
/**
* Gets the {@link List} of {@link DyldCacheMappingInfo}s. Requires header to have been parsed.
*

View file

@ -170,6 +170,7 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
* Fixes up the programs slide pointers
*
* @param program The {@link Program}
* @param markup True if the slide pointers should be marked up; otherwise, false
* @param addRelocations True if slide pointer locations should be added to the relocation
* table; otherwise, false
* @param log The log
@ -177,8 +178,8 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
* @throws MemoryAccessException If there was a problem accessing memory
* @throws CancelledException If the user cancelled the operation
*/
public void fixSlidePointers(Program program, boolean addRelocations, MessageLog log,
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
public void fixupSlidePointers(Program program, boolean markup, boolean addRelocations,
MessageLog log, TaskMonitor monitor) throws MemoryAccessException, CancelledException {
Memory memory = program.getMemory();
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
@ -202,6 +203,7 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
}
}
if (markup) {
monitor.initialize(fixups.size(), "Marking up DYLD Cache slide pointers...");
for (DyldCacheSlideFixup fixup : fixups) {
monitor.increment();
@ -219,6 +221,7 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
}
}
}
}
catch (IOException e) {
throw new MemoryAccessException(e.getMessage(), e);
}

View file

@ -61,7 +61,8 @@ public class ThreadCommand extends LoadCommand {
threadState = new ThreadStateARM(reader);
}
}
else if (header.getCpuType() == CpuTypes.CPU_TYPE_ARM_64) {
else if (header.getCpuType() == CpuTypes.CPU_TYPE_ARM_64 ||
header.getCpuType() == CpuTypes.CPU_TYPE_ARM64_32) {
if (threadStateHeader.getFlavor() == ThreadStateARM_64.ARM64_THREAD_STATE) {
threadState = new ThreadStateARM_64(reader);
}
@ -96,7 +97,9 @@ public class ThreadCommand extends LoadCommand {
struct.add(DWORD, "cmd", null);
struct.add(DWORD, "cmdsize", null);
struct.add(threadStateHeader.toDataType(), "threadStateHeader", null);
if (threadState != null) {
struct.add(threadState.toDataType(), "threadState", null);
}
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}

View file

@ -36,23 +36,29 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
public final static String DYLD_CACHE_NAME = "DYLD Cache";
/** Loader option to process chained fixups */
static final String PROCESS_CHAINED_FIXUPS_OPTION_NAME = "Process chained fixups";
/** Loader option to fixup slide pointers */
static final String FIXUP_SLIDE_POINTERS_OPTION_NAME = "Fixup slide pointers";
/** Default value for loader option to process chained fixups */
static final boolean PROCESS_CHAINED_FIXUPS_OPTION_DEFAULT = true;
/** Default value for loader option to fixup slide pointers */
static final boolean FIXUP_SLIDE_POINTERS_OPTION_DEFAULT = true;
/** Loader option to add chained fixups to relocation table */
static final String ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME =
"Add chained fixups to relocation table";
/** Loader option to mark up slide pointers */
static final String MARKUP_SLIDE_POINTERS_OPTION_NAME = "Markup slide pointers";
/** Default value for loader option to add chained fixups to relocation table */
static final boolean ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT = false;
/** Default value for loader option to mark up slide pointers */
static final boolean MARKUP_SLIDE_POINTERS_OPTION_DEFAULT = true;
/** Loader option to process symbols */
/** Loader option to add slide pointers to relocation table */
static final String ADD_SLIDE_POINTER_RELOCATIONS_OPTION_NAME =
"Add slide pointers to relocation table";
/** Default value for loader option to add slide pointers to relocation table */
static final boolean ADD_SLIDE_POINTERS_RELOCATIONS_OPTION_DEFAULT = false;
/** Loader option to process local symbols */
static final String PROCESS_LOCAL_SYMBOLS_OPTION_NAME = "Process local symbols";
/** Default value for loader option to process symbols */
/** Default value for loader option to process local symbols */
static final boolean PROCESS_LOCAL_SYMBOLS_OPTION_DEFAULT = true;
/** Loader option to mark up symbols */
@ -145,12 +151,15 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
List<Option> list =
super.getDefaultOptions(provider, loadSpec, domainObject, loadIntoProgram);
if (!loadIntoProgram) {
list.add(new Option(PROCESS_CHAINED_FIXUPS_OPTION_NAME,
PROCESS_CHAINED_FIXUPS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-processChainedFixups"));
list.add(new Option(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addChainedFixupsRelocations"));
list.add(new Option(FIXUP_SLIDE_POINTERS_OPTION_NAME,
FIXUP_SLIDE_POINTERS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-fixupSlidePointers"));
list.add(
new Option(MARKUP_SLIDE_POINTERS_OPTION_NAME, MARKUP_SLIDE_POINTERS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-markupSlidePointers"));
list.add(new Option(ADD_SLIDE_POINTER_RELOCATIONS_OPTION_NAME,
ADD_SLIDE_POINTERS_RELOCATIONS_OPTION_DEFAULT, Boolean.class,
Loader.COMMAND_LINE_ARG_PREFIX + "-addSlidePointerRelocations"));
list.add(
new Option(PROCESS_LOCAL_SYMBOLS_OPTION_NAME, PROCESS_LOCAL_SYMBOLS_OPTION_DEFAULT,
Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-processLocalSymbols"));
@ -177,11 +186,13 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
}
private DyldCacheOptions getDyldCacheOptions(List<Option> options) {
boolean processChainedFixups = OptionUtils.getOption(PROCESS_CHAINED_FIXUPS_OPTION_NAME,
options, PROCESS_CHAINED_FIXUPS_OPTION_DEFAULT);
boolean addChainedFixupsRelocations =
OptionUtils.getOption(ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_NAME, options,
ADD_CHAINED_FIXUPS_RELOCATIONS_OPTION_DEFAULT);
boolean fixupSlidePointers = OptionUtils.getOption(FIXUP_SLIDE_POINTERS_OPTION_NAME,
options, FIXUP_SLIDE_POINTERS_OPTION_DEFAULT);
boolean markupSlidePointers = OptionUtils.getOption(MARKUP_SLIDE_POINTERS_OPTION_NAME,
options, MARKUP_SLIDE_POINTERS_OPTION_DEFAULT);
boolean addSlidePointerRelocations =
OptionUtils.getOption(ADD_SLIDE_POINTER_RELOCATIONS_OPTION_NAME, options,
ADD_SLIDE_POINTERS_RELOCATIONS_OPTION_DEFAULT);
boolean processLocalSymbols = OptionUtils.getOption(PROCESS_LOCAL_SYMBOLS_OPTION_NAME,
options, PROCESS_LOCAL_SYMBOLS_OPTION_DEFAULT);
boolean markupLocalSymbols = OptionUtils.getOption(MARKUP_LOCAL_SYMBOLS_OPTION_NAME,
@ -196,9 +207,10 @@ public class DyldCacheLoader extends AbstractProgramWrapperLoader {
options, MARKUP_DYLIB_LC_DATA_OPTION_DEFAULT);
boolean processLibobjc = OptionUtils.getOption(PROCESS_DYLIB_LIBOBJC_OPTION_NAME,
options, PROCESS_DYLIB_LIBOBJC_OPTION_DEFAULT);
return new DyldCacheOptions(processChainedFixups, addChainedFixupsRelocations,
processLocalSymbols, markupLocalSymbols, processDylibMemory, processDylibSymbols,
processDylibExports, markupDylibLoadCommandData, processLibobjc);
return new DyldCacheOptions(fixupSlidePointers, markupSlidePointers,
addSlidePointerRelocations, processLocalSymbols, markupLocalSymbols,
processDylibMemory, processDylibSymbols, processDylibExports,
markupDylibLoadCommandData, processLibobjc);
}
@Override

View file

@ -18,8 +18,9 @@ package ghidra.app.util.opinion;
/**
* Options from the {@link DyldCacheLoader}
*
* @param processChainedFixups True if chained fixups should be processed; otherwise, false
* @param addChainedFixupsRelocations True if chained fixups should be added to the relocation
* @param fixupSlidePointers True if slide pointers should be fixed up; otherwise, false
* @param markupSlidePointers True if slide pointers should be marked up; otherwise, false
* @param addSlidePointerRelocations True if slide pointers should be added to the relocation
* table; otherwise false
* @param processLocalSymbols True if local symbols should be processes; otherwise, false
* @param markupLocalSymbols True if local symbols should be marked up; otherwise, false
@ -30,8 +31,8 @@ package ghidra.app.util.opinion;
* marked up; otherwise, false
* @param processLibobjc True if special libobjc should occur; otherwise, false
*/
public record DyldCacheOptions(boolean processChainedFixups, boolean addChainedFixupsRelocations,
boolean processLocalSymbols, boolean markupLocalSymbols, boolean processDylibMemory,
boolean processDylibSymbols, boolean processDylibExports,
public record DyldCacheOptions(boolean fixupSlidePointers, boolean markupSlidePointers,
boolean addSlidePointerRelocations, boolean processLocalSymbols, boolean markupLocalSymbols,
boolean processDylibMemory, boolean processDylibSymbols, boolean processDylibExports,
boolean markupDylibLoadCommandData, boolean processLibobjc) {
}

View file

@ -90,6 +90,9 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
// Set image base
setDyldCacheImageBase(splitDyldCache.getDyldCacheHeader(0));
// Set entry point
setDyldCacheEntryPoint(splitDyldCache.getDyldCacheHeader(0));
// Setup memory
// Check if local symbols are present
boolean localSymbolsPresent = false;
@ -110,7 +113,7 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
ByteProvider bp = splitDyldCache.getProvider(i);
fixSlidePointers(header);
fixupSlidePointers(header);
markupHeaders(header);
markupBranchIslands(header, bp);
createLocalSymbols(header);
@ -132,6 +135,26 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
monitor.incrementProgress(1);
}
/**
* Sets the program's entry point (if known).
*
* @param dyldCacheHeader The "base" DYLD Cache header
* @throws Exception if there was problem setting the program's entry point
*/
private void setDyldCacheEntryPoint(DyldCacheHeader dyldCacheHeader) throws Exception {
monitor.initialize(1, "Setting entry pointer base...");
Long entryPoint = dyldCacheHeader.getEntryPoint();
if (entryPoint != null) {
Address entryPointAddr = space.getAddress(entryPoint);
program.getSymbolTable().addExternalEntryPoint(entryPointAddr);
createOneByteFunction("entry", entryPointAddr);
}
else {
log.appendMsg("Unable to determine entry point.");
}
monitor.incrementProgress(1);
}
/**
* Processes the DYLD Cache's memory mappings and creates memory blocks for them.
*
@ -145,15 +168,16 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
List<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
monitor.setMessage("Processing DYLD mapped memory blocks...");
monitor.initialize(mappingInfos.size());
String extension = name.contains(".") ? name.substring(name.indexOf(".")) : "";
FileBytes fb = MemoryBlockUtils.createFileBytes(program, bp, monitor);
long endOfMappedOffset = 0;
boolean bookmarkSet = false;
for (DyldCacheMappingInfo mappingInfo : mappingInfos) {
long offset = mappingInfo.getFileOffset();
long size = mappingInfo.getSize();
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false, "DYLD",
space.getAddress(mappingInfo.getAddress()), fb, offset, size, "", "",
mappingInfo.isRead(), mappingInfo.isWrite(), mappingInfo.isExecute(), log);
MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false,
"DYLD" + extension, space.getAddress(mappingInfo.getAddress()), fb, offset, size,
"", "", mappingInfo.isRead(), mappingInfo.isWrite(), mappingInfo.isExecute(), log);
if (offset + size > endOfMappedOffset) {
endOfMappedOffset = offset + size;
@ -172,7 +196,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
if (endOfMappedOffset < bp.length()) {
monitor.setMessage("Processing DYLD unmapped memory block...");
MemoryBlock fileBlock = MemoryBlockUtils.createInitializedBlock(program, true, "FILE",
MemoryBlock fileBlock =
MemoryBlockUtils.createInitializedBlock(program, true, "FILE" + extension,
AddressSpace.OTHER_SPACE.getAddress(endOfMappedOffset), fb, endOfMappedOffset,
bp.length() - endOfMappedOffset, "Useful bytes that don't get mapped into memory",
"", false, false, false, log);
@ -260,9 +285,9 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* @throws MemoryAccessException if there was a problem reading/writing memory.
* @throws CancelledException if user cancels
*/
private void fixSlidePointers(DyldCacheHeader dyldCacheHeader)
private void fixupSlidePointers(DyldCacheHeader dyldCacheHeader)
throws MemoryAccessException, CancelledException {
if (!options.processChainedFixups()) {
if (!options.fixupSlidePointers()) {
return;
}
@ -272,7 +297,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
int version = info.getVersion();
log.appendMsg("Fixing slide pointers version: " + version);
info.fixSlidePointers(program, options.addChainedFixupsRelocations(), log, monitor);
info.fixupSlidePointers(program, options.markupSlidePointers(),
options.addSlidePointerRelocations(), log, monitor);
}
}

View file

@ -1192,6 +1192,11 @@ public class MachoProgramBuilder {
*/
private void performRelocations(LinkedHashMap<RelocationInfo, Address> relocationMap)
throws CancelledException {
if (relocationMap.isEmpty()) {
return;
}
MachoRelocationHandler handler = MachoRelocationHandlerFactory.getHandler(machoHeader);
if (handler == null) {
log.appendMsg(String.format("No relocation handler for machine type 0x%x",
@ -1491,7 +1496,7 @@ public class MachoProgramBuilder {
for (Address addr : chainedFixups) {
monitor.checkCancelled();
try {
listing.createData(addr, Pointer64DataType.dataType);
listing.createData(addr, PointerDataType.dataType);
}
catch (CodeUnitInsertionException e) {
// No worries, something presumably more important was there already

View file

@ -7,9 +7,11 @@
</constraint>
<constraint loader="Mac OS X Mach-O" compilerSpecID="default">
<constraint primary="16777228" processor="AARCH64" endian="little" size="64" variant="AppleSilicon" />
<constraint primary="33554444" processor="AARCH64" endian="little" size="32" variant="ilp32" />
</constraint>
<constraint loader="DYLD Cache" compilerSpecID="default">
<constraint primary="AARCH64" processor="AARCH64" endian="little" size="64" variant="AppleSilicon" />
<constraint primary="ARM64_32" processor="AARCH64" endian="little" size="32" variant="ilp32" />
</constraint>
<constraint loader="Portable Executable (PE)" compilerSpecID="windows">
<constraint primary="43620" processor="AARCH64" endian="little" size="64" variant="v8A" />