diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ExceptionDataDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ExceptionDataDirectory.java index a3da3ba80f..cec7800dd8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ExceptionDataDirectory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ExceptionDataDirectory.java @@ -15,6 +15,8 @@ */ package ghidra.app.util.bin.format.pe; +import static ghidra.app.util.bin.format.pe.FileHeader.*; + import java.io.IOException; import ghidra.app.util.bin.BinaryReader; @@ -23,15 +25,18 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; public class ExceptionDataDirectory extends DataDirectory { private final static String NAME = "IMAGE_DIRECTORY_ENTRY_EXCEPTION"; + private ImageRuntimeFunctionEntries functionEntries; + ExceptionDataDirectory(NTHeader ntHeader, BinaryReader reader) throws IOException { processDataDirectory(ntHeader, reader); - } + } @Override public String getDirectoryName() { @@ -39,11 +44,31 @@ public class ExceptionDataDirectory extends DataDirectory { } @Override - public boolean parse() { + public boolean parse() throws IOException { int ptr = getPointer(); if (ptr < 0) { return false; } + + long oldIndex = reader.getPointerIndex(); + reader.setPointerIndex(ptr); + + functionEntries = switch (ntHeader.getFileHeader().getMachine() & IMAGE_FILE_MACHINE_MASK) { + case IMAGE_FILE_MACHINE_I386: + case IMAGE_FILE_MACHINE_IA64: + case IMAGE_FILE_MACHINE_AMD64: + yield new ImageRuntimeFunctionEntries_X86(reader, size, ntHeader); + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_ARMNT: + yield new ImageRuntimeFunctionEntries_ARM(reader, size, ntHeader); + default: + Msg.error(this, String.format("Exception Data unsupported architecture: 0x%02x", + ntHeader.getFileHeader().getMachine())); + yield null; + }; + + reader.setPointerIndex(oldIndex); return true; } @@ -52,17 +77,16 @@ public class ExceptionDataDirectory extends DataDirectory { NTHeader ntHeader) throws DuplicateNameException, CodeUnitInsertionException, IOException { - monitor.setMessage(program.getName()+": exceptions..."); Address addr = PeUtils.getMarkupAddress(program, isBinary, ntHeader, virtualAddress); if (!program.getMemory().contains(addr)) { return; } createDirectoryBookmark(program, addr); + if (functionEntries != null) { + functionEntries.markup(program, addr); + } } - /** - * @see ghidra.app.util.bin.StructConverter#toDataType() - */ @Override public DataType toDataType() throws DuplicateNameException { StructureDataType struct = new StructureDataType(NAME, size); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java index 6713eef012..1828ddb5b1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/FileHeader.java @@ -22,7 +22,6 @@ import java.util.List; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; -import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbolAux; import ghidra.program.model.data.*; @@ -179,9 +178,6 @@ public class FileHeader implements StructConverter { private SectionHeader[] sectionHeaders; private List symbols = new ArrayList<>(); - // TODO: This is x86-64 architecture-specific and needs to be generalized. - private List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = new ArrayList<>(); - private BinaryReader reader; private int startIndex; private NTHeader ntHeader; @@ -238,15 +234,6 @@ public class FileHeader implements StructConverter { return symbols; } - /** - * Returns the array of RUNTIME_INFO entries, if any are present. - * @return An array of _IMAGE_RUNTIME_FUNCTION_ENTRY. The array can be empty. - * TODO: This is x86-64 architecture-specific and needs to be generalized. - */ - public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getImageRuntimeFunctionEntries() { - return irfes; - } - /** * Returns the section header that contains the specified virtual address. * @param virtualAddr the virtual address @@ -379,36 +366,6 @@ public class FileHeader implements StructConverter { reader.setPointerIndex(oldIndex); } - void processImageRuntimeFunctionEntries() throws IOException { - FileHeader fh = ntHeader.getFileHeader(); - SectionHeader[] sections = fh.getSectionHeaders(); - - // Look for an exception handler section for an array of - // RUNTIME_FUNCTION structures, bail if one isn't found - SectionHeader irfeHeader = null; - for (SectionHeader header : sections) { - if (header.getName().equals(".pdata")) { - irfeHeader = header; - break; - } - } - - if (irfeHeader == null) { - return; - } - - long oldIndex = reader.getPointerIndex(); - - int start = ntHeader.rvaToPointer(irfeHeader.getVirtualAddress()); - reader.setPointerIndex(start); - - ImageRuntimeFunctionEntries entries = - new ImageRuntimeFunctionEntries(reader, start, ntHeader); - irfes = entries.getRuntimeFunctionEntries(); - - reader.setPointerIndex(oldIndex); - } - void processSymbols() throws IOException { if (isLordPE()) { return; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java index 67b8ff0ec2..90211c7c1c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries.java @@ -16,160 +16,27 @@ package ghidra.app.util.bin.format.pe; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import ghidra.app.util.bin.BinaryReader; import ghidra.program.model.address.Address; -import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.util.exception.DuplicateNameException; /** - * typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY { - * DWORD BeginAddress; - * DWORD EndAddress; - * union { - * DWORD UnwindInfoAddress; - * DWORD UnwindData; - * } DUMMYUNIONNAME; - * } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY; - * - * #define UNW_FLAG_NHANDLER 0x0 - * #define UNW_FLAG_EHANDLER 0x1 - * #define UNW_FLAG_UHANDLER 0x2 - * #define UNW_FLAG_CHAININFO 0x4 - * - * typedef struct _UNWIND_INFO { - * UCHAR Version : 3; - * UCHAR Flags : 5; - * UCHAR SizeOfProlog; - * UCHAR CountOfUnwindCodes; - * UCHAR FrameRegister : 4; - * UCHAR FrameOffset : 4; - * UNWIND_CODE UnwindCode[1]; - * - * // - * // The unwind codes are followed by an optional DWORD aligned field that - * // contains the exception handler address or the address of chained unwind - * // information. If an exception handler address is specified, then it is - * // followed by the language specified exception handler data. - * // - * // union { - * // ULONG ExceptionHandler; - * // ULONG FunctionEntry; - * // }; - * // - * // ULONG ExceptionData[]; - * // - * } UNWIND_INFO, *PUNWIND_INFO; + * An interface for working with function table entries used for exception handling, which are found + * in the .pdata section. The actual implementations are architecture-specific. */ -public class ImageRuntimeFunctionEntries { - - private final static int UNWIND_INFO_SIZE = 0x0C; - - List<_IMAGE_RUNTIME_FUNCTION_ENTRY> functionEntries = new ArrayList<>(); - - ImageRuntimeFunctionEntries(BinaryReader reader, long index, NTHeader ntHeader) - throws IOException { - int entryCount = 0; - - // Find the exception handler data section. This is an unbounded array of - // RUNTIME_INFO structures one after another and there's no count field - // to tell us how many there are, so get the maximum number there could be - // based on the size of the section. - FileHeader fh = ntHeader.getFileHeader(); - for (SectionHeader section : fh.getSectionHeaders()) { - if (section.getName().contentEquals(".pdata")) { - entryCount = section.getSizeOfRawData() / UNWIND_INFO_SIZE; - break; - } - } - - if (entryCount == 0) { - return; - } - - long origIndex = reader.getPointerIndex(); - - reader.setPointerIndex(index); - - for (int i = 0; i < entryCount; i++) { - _IMAGE_RUNTIME_FUNCTION_ENTRY entry = new _IMAGE_RUNTIME_FUNCTION_ENTRY(); - entry.beginAddress = reader.readNextUnsignedInt(); - entry.endAddress = reader.readNextUnsignedInt(); - entry.unwindInfoAddressOrData = reader.readNextUnsignedInt(); - - // When the size of the section is bigger than the number of structures - // the structure data fields will all be null, signaling the end of the - // array of structures. Break out here. - if (entry.beginAddress == 0 && entry.endAddress == 0 && - entry.unwindInfoAddressOrData == 0) { - break; - } - - // Read and process the UNWIND_INFO structures the RUNTIME_INFO - // structures point to - entry.unwindInfo = - PEx64UnwindInfo.readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader); - - functionEntries.add(entry); - } - - reader.setPointerIndex(origIndex); - } - - public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getRuntimeFunctionEntries() { - return functionEntries; - } - - public static void createData(Program program, Address headerStart, - List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes) { - // TODO: This is x86-64 architecture-specific and needs to be generalized. - StructureDataType dt = new StructureDataType(".PDATA", 0); - dt.setCategoryPath(new CategoryPath("/PE")); - - // Lay an array of RUNTIME_INFO structure out over the data - StructureDataType irfeStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0); - irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "BeginAddress", null); - irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "EndAddress", null); - irfeStruct.add(ghidra.app.util.bin.StructConverter.IBO32, "UnwindInfoAddressOrData", null); - - ArrayDataType irfeArray = - new ArrayDataType(irfeStruct, irfes.size(), irfeStruct.getLength()); - - try { - DataUtilities.createData(program, headerStart, irfeArray, irfeArray.getLength(), true, - DataUtilities.ClearDataMode.CHECK_FOR_SPACE); - } - catch (CodeUnitInsertionException e) { - return; - } - } - - // FIXME: change name to conform to Java naming standards - // FIXME: If public visibility is required improved member protection is needed - public static class _IMAGE_RUNTIME_FUNCTION_ENTRY { - long beginAddress; - long endAddress; - long unwindInfoAddressOrData; - PEx64UnwindInfo unwindInfo; - - public void createData(Program program) { - if (unwindInfoAddressOrData > 0) { - try { - DataType dt = unwindInfo.toDataType(); - Address start = program.getImageBase().add(unwindInfoAddressOrData); - - DataUtilities.createData(program, start, dt, dt.getLength(), true, - DataUtilities.ClearDataMode.CHECK_FOR_SPACE); - } - catch (CodeUnitInsertionException | DuplicateNameException | IOException e) { - // ignore - } - } - } - } +public interface ImageRuntimeFunctionEntries { + /** + * Marks up an {@link ImageRuntimeFunctionEntries} + * + * @param program The {@link Program} + * @param start The start {@link Address} + * @throws IOException If there was an IO-related error creating the data + * @throws DuplicateNameException If a data type of the same name already exists + * @throws CodeUnitInsertionException If data creation failed + */ + public void markup(Program program, Address start) throws CodeUnitInsertionException, + IOException, DuplicateNameException; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_ARM.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_ARM.java new file mode 100644 index 0000000000..1d87684b0a --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_ARM.java @@ -0,0 +1,147 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.pe; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.exception.DuplicateNameException; + +/** + *
+ * typedef struct _IMAGE_ARM_RUNTIME_FUNCTION_ENTRY {
+ *   DWORD BeginAddress;
+ *   union {
+ *     DWORD UnwindData;
+ *     struct {
+ *       DWORD Flag : 2;
+ *       DWORD FunctionLength : 11;
+ *       DWORD Ret : 2;
+ *       DWORD H : 1;
+ *       DWORD Reg : 3;
+ *       DWORD R : 1;
+ *       DWORD L : 1;
+ *       DWORD C : 1;
+ *       DWORD StackAdjust : 10;
+ *     } DUMMYSTRUCTNAME;
+ *   } DUMMYUNIONNAME;
+ * } IMAGE_ARM_RUNTIME_FUNCTION_ENTRY, * PIMAGE_ARM_RUNTIME_FUNCTION_ENTRY;
+ * 
+ * @see arm-exception-handling.md + */ +public class ImageRuntimeFunctionEntries_ARM implements ImageRuntimeFunctionEntries { + + private final static int ENTRY_SIZE = 0x08; + + private List functionEntries = new ArrayList<>(); + + /** + * Creates a new {@link ImageRuntimeFunctionEntries_X86} + * + * @param reader A {@link BinaryReader} positioned at the start of the exception data directory + * @param size The size of the exception data directory + * @param ntHeader The {@link NTHeader} + * @throws IOException If there was an issue parsing the exception data directory + */ + ImageRuntimeFunctionEntries_ARM(BinaryReader reader, int size, NTHeader ntHeader) + throws IOException { + int numEntries = size / ENTRY_SIZE; + for (int i = 0; i < numEntries; i++) { + long beginAddress = reader.readNextUnsignedInt() & ~0x1L; // low bit is set when thumb + int data = reader.readNextInt(); + if (beginAddress == 0 && data == 0) { + break; + } + + functionEntries.add(new ImageRuntimeFunctionEntry_ARM(beginAddress, data)); + } + } + + @Override + public void markup(Program program, Address headerStart) throws CodeUnitInsertionException, + IOException, DuplicateNameException { + StructureDataType exceptionInfoStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0); + exceptionInfoStruct.setPackingEnabled(true); + exceptionInfoStruct.add(StructConverter.IBO32, "BeginAddress", null); + exceptionInfoStruct.add(StructConverter.IBO32, "ExceptionInfo", null); + + StructureDataType unwindDataStruct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY_2", 0); + unwindDataStruct.setPackingEnabled(true); + unwindDataStruct.add(StructConverter.IBO32, "BeginAddress", null); + try { + unwindDataStruct.addBitField(StructConverter.DWORD, 2, "Flag", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 11, "FunctionLength", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 2, "Ret", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 1, "H", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 3, "Reg", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 1, "R", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 1, "L", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 1, "C", null); + unwindDataStruct.addBitField(StructConverter.DWORD, 10, "StackAdjust", null); + } + catch (InvalidDataTypeException e) { + throw new IOException(e); + } + + Address addr = headerStart; + for (ImageRuntimeFunctionEntry_ARM entry : functionEntries) { + DataType struct = entry.isExceptionInfoRVA() ? exceptionInfoStruct : unwindDataStruct; + DataUtilities.createData(program, addr, struct, struct.getLength(), true, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + addr = addr.add(ENTRY_SIZE); + } + } + + /** + * Creates a new {@link ImageRuntimeFunctionEntries_ARM} + * + * @param beginAddress The RVA of the corresponding function + * @param data The exception info RVA or the packed unwind data, depending on lower 2 bit flag + */ + record ImageRuntimeFunctionEntry_ARM(long beginAddress, int data) { + + /** + * Checks whether or not this entry is an exception info RVA or packed unwind data + * + * @return True if this entry is an exception info RVA, or false if it's packed unwind data + */ + public boolean isExceptionInfoRVA() { + return (data & 0x3) == 0; + } + + /** + * Marks up this entry + * + * @param program The {@link Program} + * @throws IOException If there was an IO-related error creating the data + * @throws DuplicateNameException If a data type of the same name already exists + * @throws CodeUnitInsertionException If data creation failed + */ + public void markup(Program program) + throws DuplicateNameException, IOException, CodeUnitInsertionException { + if (isExceptionInfoRVA()) { + // TODO + } + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_X86.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_X86.java new file mode 100644 index 0000000000..0da7543385 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ImageRuntimeFunctionEntries_X86.java @@ -0,0 +1,155 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.pe; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.exception.DuplicateNameException; + +/** + *
+ * typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
+ *  DWORD BeginAddress;
+ *  DWORD EndAddress;
+ *  union {
+ *    DWORD UnwindInfoAddress;
+ *    DWORD UnwindData;
+ *  } DUMMYUNIONNAME;
+ * } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
+ *
+ * #define UNW_FLAG_NHANDLER 0x0
+ * #define UNW_FLAG_EHANDLER 0x1
+ * #define UNW_FLAG_UHANDLER 0x2
+ * #define UNW_FLAG_CHAININFO 0x4
+ *
+ * typedef struct _UNWIND_INFO {
+ *     UCHAR Version : 3;
+ *     UCHAR Flags : 5;
+ *     UCHAR SizeOfProlog;
+ *     UCHAR CountOfUnwindCodes;
+ *     UCHAR FrameRegister : 4;
+ *     UCHAR FrameOffset : 4;
+ *     UNWIND_CODE UnwindCode[1];
+ *
+ * //
+ * // The unwind codes are followed by an optional DWORD aligned field that
+ * // contains the exception handler address or the address of chained unwind
+ * // information. If an exception handler address is specified, then it is
+ * // followed by the language specified exception handler data.
+ * //
+ * //  union {
+ * //      ULONG ExceptionHandler;
+ * //      ULONG FunctionEntry;
+ * //  };
+ * //
+ * //  ULONG ExceptionData[];
+ * //
+ * } UNWIND_INFO, *PUNWIND_INFO;
+ * 
+ */ +public class ImageRuntimeFunctionEntries_X86 implements ImageRuntimeFunctionEntries { + + private final static int ENTRY_SIZE = 0x0C; + + private List functionEntries = new ArrayList<>(); + + /** + * Creates a new {@link ImageRuntimeFunctionEntries_X86} + * + * @param reader A {@link BinaryReader} positioned at the start of the exception data directory + * @param size The size of the exception data directory + * @param ntHeader The {@link NTHeader} + * @throws IOException If there was an issue parsing the exception data directory + */ + ImageRuntimeFunctionEntries_X86(BinaryReader reader, int size, NTHeader ntHeader) + throws IOException { + + int numEntries = size / ENTRY_SIZE; + for (int i = 0; i < numEntries; i++) { + long beginAddress = reader.readNextUnsignedInt(); + long endAddress = reader.readNextUnsignedInt(); + long unwindInfoAddressOrData = reader.readNextUnsignedInt(); + + if (beginAddress == 0 && endAddress == 0 && unwindInfoAddressOrData == 0) { + break; + } + + PEx64UnwindInfo unwindInfo = + PEx64UnwindInfo.readUnwindInfo(reader, unwindInfoAddressOrData, ntHeader); + + ImageRuntimeFunctionEntry_X86 entry = new ImageRuntimeFunctionEntry_X86(beginAddress, + endAddress, unwindInfoAddressOrData, unwindInfo); + functionEntries.add(entry); + } + } + + @Override + public void markup(Program program, Address headerStart) throws CodeUnitInsertionException, + IOException, DuplicateNameException { + StructureDataType struct = new StructureDataType("_IMAGE_RUNTIME_FUNCTION_ENTRY", 0); + struct.add(StructConverter.IBO32, "BeginAddress", null); + struct.add(StructConverter.IBO32, "EndAddress", null); + struct.add(StructConverter.IBO32, "UnwindInfoAddressOrData", null); + + ArrayDataType arr = new ArrayDataType(struct, functionEntries.size(), struct.getLength()); + + DataUtilities.createData(program, headerStart, arr, arr.getLength(), true, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + + for (ImageRuntimeFunctionEntry_X86 entry : functionEntries) { + entry.markup(program); + } + } + + /** + * Creates a new {@link ImageRuntimeFunctionEntries_X86} + * + * @param beginAddress The RVA of the corresponding function + * @param endAddress The RVA of the end of the function + * @param unwindInfoAddressOrData The RVA of the unwind information + * @param unwindInfo The parsed {@link PEx64UnwindInfo unwind information} + */ + record ImageRuntimeFunctionEntry_X86(long beginAddress, long endAddress, + long unwindInfoAddressOrData, PEx64UnwindInfo unwindInfo) { + + /** + * Marks up this entry + * + * @param program The {@link Program} + * @throws IOException If there was an IO-related error creating the data + * @throws DuplicateNameException If a data type of the same name already exists + * @throws CodeUnitInsertionException If data creation failed + */ + public void markup(Program program) + throws DuplicateNameException, IOException, CodeUnitInsertionException { + if (unwindInfoAddressOrData > 0) { + DataType dt = unwindInfo.toDataType(); + Address start = program.getImageBase().add(unwindInfoAddressOrData); + + DataUtilities.createData(program, start, dt, dt.getLength(), true, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java index 8b20fdfd2a..780b66d7cb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/NTHeader.java @@ -254,10 +254,6 @@ public class NTHeader implements StructConverter, OffsetValidator { fileHeader.processSections(optionalHeader); fileHeader.processSymbols(); - if ((fileHeader.getMachine() & - FileHeader.IMAGE_FILE_MACHINE_MASK) == FileHeader.IMAGE_FILE_MACHINE_AMD64) { - fileHeader.processImageRuntimeFunctionEntries(); - } if (advancedProcess) { optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfo.java index 6ecd231022..91d3fe5b08 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfo.java @@ -19,7 +19,7 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.StructConverter; -import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY; +import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries_X86.ImageRuntimeFunctionEntry_X86; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; @@ -48,7 +48,7 @@ class PEx64UnwindInfo implements StructConverter { byte frameOffset; UNWIND_CODE[] unwindCodes; int exceptionHandlerFunction; - _IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo; + ImageRuntimeFunctionEntry_X86 unwindHandlerChainInfo; long startOffset; @@ -199,16 +199,15 @@ class PEx64UnwindInfo implements StructConverter { unwindInfo.exceptionHandlerFunction = reader.readNextInt(); } else if (unwindInfo.hasChainedUnwindInfo()) { - unwindInfo.unwindHandlerChainInfo = - new _IMAGE_RUNTIME_FUNCTION_ENTRY(); - unwindInfo.unwindHandlerChainInfo.beginAddress = reader.readNextInt(); - unwindInfo.unwindHandlerChainInfo.endAddress = reader.readNextInt(); - unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData = reader.readNextInt(); + long beginAddress = reader.readNextUnsignedInt(); + long endAddress = reader.readNextUnsignedInt(); + long unwindInfoAddressOrData = reader.readNextUnsignedInt(); // Follow the chain to the referenced UNWIND_INFO structure until we // get to the end - unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader, - unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader); + PEx64UnwindInfo info = readUnwindInfo(reader, unwindInfoAddressOrData, ntHeader); + unwindInfo.unwindHandlerChainInfo = new ImageRuntimeFunctionEntry_X86(beginAddress, + endAddress, unwindInfoAddressOrData, info); } reader.setPointerIndex(origIndex); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index 828fa0a862..cd074fc7ed 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -26,7 +26,6 @@ import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.mz.DOSHeader; import ghidra.app.util.bin.format.pe.*; import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags; -import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY; import ghidra.app.util.bin.format.pe.PortableExecutable.SectionLayout; import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol; import ghidra.app.util.bin.format.pe.debug.DebugDirectoryParser; @@ -144,7 +143,6 @@ public class PeLoader extends AbstractPeDebugLoader { processProperties(optionalHeader, program, monitor); processComments(program.getListing(), monitor); processSymbols(ntHeader, sectionToAddress, program, monitor, log); - processImageRuntimeFunctionEntries(fileHeader, program, monitor, log); processEntryPoints(ntHeader, program, monitor); String compiler = CompilerOpinion.getOpinion(pe, provider).toString(); @@ -259,41 +257,6 @@ public class PeLoader extends AbstractPeDebugLoader { } } - private void processImageRuntimeFunctionEntries(FileHeader fileHeader, Program program, - TaskMonitor monitor, MessageLog log) { - - // Check to see that we have exception data to process - SectionHeader irfeHeader = null; - for (SectionHeader header : fileHeader.getSectionHeaders()) { - if (header.getName().contains(".pdata")) { - irfeHeader = header; - break; - } - } - - if (irfeHeader == null) { - return; - } - - Address start = program.getImageBase().add(irfeHeader.getVirtualAddress()); - - List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = fileHeader.getImageRuntimeFunctionEntries(); - - if (irfes.isEmpty()) { - return; - } - - // TODO: This is x86-64 architecture-specific and needs to be generalized. - ImageRuntimeFunctionEntries.createData(program, start, irfes); - - // Each RUNTIME_INFO contains an address to an UNWIND_INFO structure - // which also needs to be laid out. When they contain chaining data - // they're recursive but the toDataType() function handles that. - for (_IMAGE_RUNTIME_FUNCTION_ENTRY entry : irfes) { - entry.createData(program); - } - } - private void processSymbols(NTHeader ntHeader, Map sectionToAddress, Program program, TaskMonitor monitor, MessageLog log) { FileHeader fileHeader = ntHeader.getFileHeader();