diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc1MessageAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc1MessageAnalyzer.java deleted file mode 100644 index 18e8f1009a..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc1MessageAnalyzer.java +++ /dev/null @@ -1,324 +0,0 @@ -/* ### - * 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.plugin.core.analysis.objc; - -import java.util.List; - -import ghidra.app.services.*; -import ghidra.app.util.bin.format.macho.SectionNames; -import ghidra.app.util.bin.format.objc.ObjcUtils; -import ghidra.app.util.bin.format.objc.objc1.Objc1Constants; -import ghidra.app.util.importer.MessageLog; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.symbol.*; -import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; - -public class Objc1MessageAnalyzer extends AbstractAnalyzer { - private static final String DESCRIPTION = - "An analyzer for extracting _objc_msgSend information."; - - private static final String NAME = "Objective-C Message"; - - public Objc1MessageAnalyzer() { - super(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER); - setDefaultEnablement(true); - setPriority(new AnalysisPriority(10000000)); - } - - /* ************************************************************************** */ - /* ************************************************************************** */ - - @Override - public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) - throws CancelledException { - CurrentState state = new CurrentState(program); - - monitor.initialize(set.getNumAddresses()); - int progress = 0; - - AddressIterator iterator = set.getAddresses(true); - while (iterator.hasNext()) { - if (monitor.isCancelled()) { - break; - } - - monitor.setProgress(++progress); - Address address = iterator.next(); - - Function function = program.getListing().getFunctionAt(address); - - try { - inspectFunction(program, function, state, monitor); - } - catch (Exception e) { - // do nothing - } - } - - return true; - } - - @Override - public boolean canAnalyze(Program program) { - return Objc1Constants.isObjectiveC(program); - } - - /* ************************************************************************** */ - /* ************************************************************************** */ - - private void inspectFunction(Program program, Function function, CurrentState state, - TaskMonitor monitor) { - if (function == null) { - return; - } - InstructionIterator instructionIterator = - program.getListing().getInstructions(function.getBody(), true); - while (instructionIterator.hasNext()) { - if (monitor.isCancelled()) { - break; - } - - Instruction instruction = instructionIterator.next(); - - if (isCallingObjcMsgSend(instruction)) { - String eolComment = instruction.getComment(CommentType.EOL); - - if (eolComment != null) {//if a comment already exists, ignore... - continue; - } - - markupInstruction(instruction, state, monitor); - } - } - } - - private boolean isCallingObjcMsgSend(Instruction instruction) { - if (instruction.getNumOperands() != 1) { - return false; - } - Reference reference = instruction.getPrimaryReference(0); - if (reference == null) { - return false; - } - if (!reference.getReferenceType().isCall() && !reference.getReferenceType().isJump()) { - return false; - } - SymbolTable symbolTable = instruction.getProgram().getSymbolTable(); - Symbol symbol = symbolTable.getPrimarySymbol(reference.getToAddress()); - return isObjcNameMatch(symbol); - } - - private boolean isObjcNameMatch(Symbol symbol) { - String name = symbol.getName(); - return name.startsWith(Objc1Constants.OBJC_MSG_SEND) || - name.equals(Objc1Constants.READ_UNIX2003) || - name.startsWith("thunk" + Objc1Constants.OBJC_MSG_SEND); - } - - private class CurrentState { - Program program; - Namespace globalNamespace; - Namespace selectorNamespace; - Namespace idNamespace; - - String currentClassName = null; - String currentMethodName = null; - - //Function currentFunction = null; - - CurrentState(Program program) { - this.program = program; - globalNamespace = program.getGlobalNamespace(); - SymbolTable symbolTable = program.getSymbolTable(); - selectorNamespace = findMatchingChildNamespace("@sel", globalNamespace, symbolTable); - idNamespace = findMatchingChildNamespace("@id", globalNamespace, symbolTable); - } - - boolean isValid() { - return currentMethodName != null && currentClassName != null; - } - - void reset() { - currentClassName = null; - currentMethodName = null; - } - - @Override - public String toString() { - return "[" + currentClassName + " " + currentMethodName + "]"; - } - - private Namespace findMatchingChildNamespace(String namespaceName, - Namespace parentNamespace, SymbolTable symbolTable) { - SymbolIterator it = symbolTable.getSymbols(parentNamespace); - while (it.hasNext()) { - Symbol s = it.next(); - if (s.getSymbolType() == SymbolType.NAMESPACE) { - if (namespaceName.equals(s.getName())) { - return (Namespace) s.getObject(); - } - } - } - try { - return symbolTable.createNameSpace(parentNamespace, namespaceName, - SourceType.ANALYSIS); - } - catch (InvalidInputException | DuplicateNameException e) { - return null; - } - } - } - - private void markupInstruction(Instruction instruction, CurrentState state, - TaskMonitor monitor) { - Address fromAddress = instruction.getMinAddress(); - Function function = state.program.getListing().getFunctionContaining(fromAddress); - if (function == null) { - return; - } - - state.reset(); - InstructionIterator iter = state.program.getListing().getInstructions(fromAddress, false); - while (iter.hasNext()) { - if (monitor.isCancelled()) { - break; - } - - Instruction instructionBefore = iter.next(); - - if (!function.getBody().contains(instructionBefore.getMinAddress())) { - break;//don't look outside of the function - } - if (!isValidInstruction(instructionBefore)) { - continue; - } - - Reference[] opRefs = instructionBefore.getOperandReferences(1); - if (opRefs.length != 1) { - continue; - } - Address toAddress = opRefs[0].getToAddress(); - - MemoryBlock block = state.program.getMemory().getBlock(toAddress); - if (block == null) { - continue; - } - - pullNameThrough(state, toAddress, null); - - if (state.isValid()) { - instruction.setComment(CommentType.EOL, state.toString()); - setReference(fromAddress, state); - break; - } - } - } - - // Tries to lay down a reference to the function that is actually being called - private void setReference(Address fromAddress, CurrentState state) { - SymbolTable symbolTable = state.program.getSymbolTable(); - Symbol classSymbol = symbolTable.getClassSymbol(state.currentClassName, (Namespace) null); - if (classSymbol == null) { - return; - } - Namespace namespace = (Namespace) classSymbol.getObject(); - List functionSymbols = symbolTable.getSymbols(state.currentMethodName, namespace); - if (functionSymbols.size() >= 1) { - Address toAddress = functionSymbols.get(0).getAddress(); - ReferenceManager referenceManager = state.program.getReferenceManager(); - Reference reference = referenceManager.addMemoryReference(fromAddress, toAddress, - RefType.UNCONDITIONAL_CALL, SourceType.ANALYSIS, 0); - referenceManager.setPrimary(reference, true); - } - } - - /** - * Objective-C class and method names are stored in the - * "__cstring" memory block. The strings are referenced - * by either the "class" block or the "message" block. - * The references are through n-levels of pointer indirection - * based on the specific target (x86 vs ppc vs arm). - * This method will pull the string through the pointer indirection - * and set the appropriate value in the current state. - */ - String pullNameThrough(CurrentState state, Address address, Namespace space) { - MemoryBlock block = state.program.getMemory().getBlock(address); - if (block == null) { - return null; - } - if (block.getName().equals(SectionNames.TEXT_CSTRING)) { - return ObjcUtils.createString(state.program, address); - } - Data data = state.program.getListing().getDataAt(address); - if (data == null) { - data = state.program.getListing().getDataContaining(address); - if (data == null) { - return null; - } - data = data.getComponentContaining((int) address.subtract(data.getAddress())); - if (data == null) { - return null; - } - } - Reference[] references = data.getValueReferences(); - if (references.length == 0) { - return null; - } - if (address.equals(references[0].getToAddress())) { - return null;//self reference - } - if (isClassBlock(block)) { - space = state.idNamespace; - } - else if (isMessageBlock(block)) { - space = state.selectorNamespace; - } - String name = pullNameThrough(state, references[0].getToAddress(), space); - if (isClassBlock(block)) { - if (state.currentClassName == null) { - state.currentClassName = name; - } - } - else if (isMessageBlock(block)) { - if (state.currentMethodName == null) { - state.currentMethodName = name; - } - } - return name; - } - - private boolean isMessageBlock(MemoryBlock block) { - return block.getName().equals(Objc1Constants.OBJC_SECTION_MESSAGE_REFS); - } - - private boolean isClassBlock(MemoryBlock block) { - return block.getName().equals(Objc1Constants.OBJC_SECTION_CLASS_REFS) || - block.getName().equals(Objc1Constants.OBJC_SECTION_CLASS); - } - - private boolean isValidInstruction(Instruction instruction) { - if (instruction.getNumOperands() != 2) { - return false; - } - boolean isMOV = instruction.getMnemonicString().equals("MOV");//intel - boolean isLWZ = instruction.getMnemonicString().equals("lwz");//powerpc - boolean isLDR = instruction.getMnemonicString().equals("ldr");//arm - return isMOV || isLWZ || isLDR; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc2MessageAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc2MessageAnalyzer.java deleted file mode 100644 index 9131db4ebf..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/objc/Objc2MessageAnalyzer.java +++ /dev/null @@ -1,371 +0,0 @@ -/* ### - * 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.plugin.core.analysis.objc; - -import java.math.BigInteger; - -import ghidra.app.services.*; -import ghidra.app.util.bin.format.macho.SectionNames; -import ghidra.app.util.bin.format.objc.objc1.Objc1Constants; -import ghidra.app.util.bin.format.objc.objc2.Objc2Constants; -import ghidra.app.util.importer.MessageLog; -import ghidra.program.model.address.*; -import ghidra.program.model.lang.Register; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.scalar.Scalar; -import ghidra.program.model.symbol.*; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; - -public class Objc2MessageAnalyzer extends AbstractAnalyzer { - private static final String NAME = "Objective-C 2 Message"; - private static final String DESCRIPTION = - "An analyzer for extracting Objective-C 2.0 message information."; - - /* ************************************************************************** */ - /* ************************************************************************** */ - - public Objc2MessageAnalyzer() { - super(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER); - setPrototype(); - //The Objective-C 2.0 analyzer should always run after the class analyzer. - //It knows the deal! - setPriority(AnalysisPriority.FORMAT_ANALYSIS.after()); - } - - @Override - public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) - throws CancelledException { - AddressIterator iterator = set.getAddresses(true); - while (iterator.hasNext()) { - Address address = iterator.next(); - - Function function = program.getListing().getFunctionAt(address); - - try { - inspectFunction(program, function, monitor); - } - catch (Exception e) { - // ignore - } - } - - return true; - } - - @Override - public boolean canAnalyze(Program program) { - return Objc2Constants.isObjectiveC2(program); - } - - /* ************************************************************************** */ - /* ************************************************************************** */ - - private void inspectFunction(Program program, Function function, TaskMonitor monitor) { - if (function == null) { - return; - } - - InstructionIterator instructionIterator = - program.getListing().getInstructions(function.getBody(), true); - while (instructionIterator.hasNext()) { - Instruction instruction = instructionIterator.next(); - - if (isCallingObjcMsgSend(instruction)) { - String eolComment = instruction.getComment(CommentType.EOL); - - if (eolComment != null) {//if a comment already exists, ignore... - continue; - } - - markupInstruction(program, instruction, monitor); - } - } - } - - private boolean isCallingObjcMsgSend(Instruction instruction) { - if (instruction.getNumOperands() != 1) { - return false; - } - Reference reference = instruction.getPrimaryReference(0); - if (reference == null) { - return false; - } -// if (!reference.getReferenceType().isCall() && !reference.getReferenceType().isJump()) { -// return false; -// } - SymbolTable symbolTable = instruction.getProgram().getSymbolTable(); - Symbol symbol = symbolTable.getPrimarySymbol(reference.getToAddress()); - return isObjcNameMatch(symbol); - } - - private boolean isObjcNameMatch(Symbol symbol) { - String name = symbol.getName(); - return name.startsWith(Objc1Constants.OBJC_MSG_SEND) || - name.equals(Objc1Constants.READ_UNIX2003); - } - - private void markupInstruction(Program program, Instruction instruction, TaskMonitor monitor) { - Address fromAddress = instruction.getMinAddress(); - Function function = program.getListing().getFunctionContaining(fromAddress); - if (function == null) { - return; - } - - String currentClass = null; - String currentMethod = null; - - InstructionIterator iter = program.getListing().getInstructions(fromAddress, false); - while (iter.hasNext()) { - if (monitor.isCancelled()) { - break; - } - Instruction instructionBefore = iter.next(); - - if (!function.getBody().contains(instructionBefore.getMinAddress())) { - break;//don't look outside of the function - } - - boolean is64bit = program.getDefaultPointerSize() == 8; - boolean isX86 = program.getLanguageID().getIdAsString().equals("x86"); - final String CLASS_REGISTER = is64bit ? "x0" : "r0"; - final String METHOD_REGISTER = is64bit ? "x1" : "r1"; - - boolean isRegisterModified = false; - - if (isRegisterModified(instructionBefore, CLASS_REGISTER)) { - currentClass = null; - isRegisterModified = true; - } - - if (isRegisterModified(instructionBefore, METHOD_REGISTER)) { - currentClass = null; - isRegisterModified = true; - } - - if (!isValidInstruction(instructionBefore)) { - if (isRegisterModified) { - break; - } - continue; - } - - Object[] firstOperandObjects = instructionBefore.getOpObjects(0); - if (firstOperandObjects.length != 1) { - continue; - } - if (!(firstOperandObjects[0] instanceof Register)) { - continue; - } - Register register = (Register) firstOperandObjects[0]; - - if (!register.getName().equals(CLASS_REGISTER) && - !register.getName().equals(METHOD_REGISTER)) { - continue; - } - - Object[] secondOperandObjects = instructionBefore.getOpObjects(1); - if (secondOperandObjects.length < 1) { - continue; - } - - Address toAddress = null; - if (secondOperandObjects.length == 1 && - secondOperandObjects[0] instanceof Address addr) { - toAddress = addr; - } - else if (secondOperandObjects.length == 2 && - secondOperandObjects[0] instanceof Register reg && - secondOperandObjects[1] instanceof Scalar scalar) { - Address instrAddr = instructionBefore.getAddress(); - ProgramContext programContext = program.getProgramContext(); - BigInteger registerValue = programContext.getValue(reg, instrAddr, false); - toAddress = instrAddr.getNewAddress(registerValue.longValue() + scalar.getValue()); - } - - if (toAddress == null) { - continue; - } - - MemoryBlock block = program.getMemory().getBlock(toAddress); - if (block == null) { - continue; - } - - if (register.getName().equals(CLASS_REGISTER)) { - currentClass = getClassName(program, toAddress); - } - else if (register.getName().equals(METHOD_REGISTER)) { - currentMethod = getMethodName(program, toAddress); - } - - if (currentClass != null && currentMethod != null) { - instruction.setComment(CommentType.EOL, - "[" + currentClass + " " + currentMethod + "]"); - break; - } - } - } - - private boolean isRegisterModified(Instruction instruction, String registerName) { - Object[] destinationOperandObjects = instruction.getOpObjects(0); - if (destinationOperandObjects.length != 1) { - return false; - } - if (!(destinationOperandObjects[0] instanceof Register)) { - return false; - } - Register register = (Register) destinationOperandObjects[0]; - if (register.getName().equals(registerName)) { - return true; - } - return false; - } - - private String getClassName(Program program, Address toAddress) { - try { - int classPointerValue = program.getMemory().getInt(toAddress); - Address classPointerAddress = toAddress.getNewAddress(classPointerValue); - - if (!isObjcClassRefBlock(program, classPointerAddress)) { - return null; - } - - Data classPointerData = program.getListing().getDefinedDataAt(classPointerAddress); - - Address classAddress = (Address) classPointerData.getValue(); - - if (!isObjcDataBlock(program, classAddress)) { - return null; - } - - Data classData = program.getListing().getDefinedDataAt(classAddress); - - Data classRwPointerData = classData.getComponent(4); - Address classRwPointerAddress = (Address) classRwPointerData.getValue(); - - if (!isObjcConstBlock(program, classRwPointerAddress)) { - return null; - } - - Data classRwData = program.getListing().getDefinedDataAt(classRwPointerAddress); - Data classNamePointerData = classRwData.getComponent(4); - - Address classNameAddress = (Address) classNamePointerData.getValue(); - - if (!isCStringBlock(program, classNameAddress)) { - return null; - } - - Data classNameData = program.getListing().getDefinedDataAt(classNameAddress); - String className = (String) classNameData.getValue(); - return className; - } - catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private String getMethodName(Program program, Address toAddress) { - try { - int methodNamePointerValue = program.getMemory().getInt(toAddress); - Address methodNamePointerAddress = toAddress.getNewAddress(methodNamePointerValue); - - if (!isObjcSelectorRefBlock(program, methodNamePointerAddress)) { - return null; - } - - Data methodNamePointerData = - program.getListing().getDefinedDataAt(methodNamePointerAddress); - - Address methodNameAddress = (Address) methodNamePointerData.getValue(); - - if (!isCStringBlock(program, methodNameAddress)) { - return null; - } - - Data methodNameData = program.getListing().getDefinedDataAt(methodNameAddress); - String methodName = (String) methodNameData.getValue(); - return methodName; - } - catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private boolean isValidInstruction(Instruction instruction) { - if (instruction.getNumOperands() != 2) { - return false; - } - boolean isMOV = instruction.getMnemonicString().equals("MOV");//intel - boolean isLWZ = instruction.getMnemonicString().equals("lwz");//powerpc - boolean isLDR = instruction.getMnemonicString().equals("ldr");//arm - return isMOV || isLWZ || isLDR; - } - - private boolean isCStringBlock(Program program, Address address) { - MemoryBlock block = program.getMemory().getBlock(address); - if (block != null) { - if (block.getName().equals(SectionNames.TEXT_CSTRING)) { - return true; - } - } - return false; - } - - private boolean isObjcSelectorRefBlock(Program program, Address address) { - MemoryBlock block = program.getMemory().getBlock(address); - if (block != null) { - if (block.getName().equals(Objc2Constants.OBJC2_SELECTOR_REFS)) { - return true; - } - } - return false; - } - - private boolean isObjcClassRefBlock(Program program, Address address) { - MemoryBlock block = program.getMemory().getBlock(address); - if (block != null) { - if (block.getName().equals(Objc2Constants.OBJC2_CLASS_REFS)) { - return true; - } - } - return false; - } - - private boolean isObjcConstBlock(Program program, Address address) { - MemoryBlock block = program.getMemory().getBlock(address); - if (block != null) { - if (block.getName().equals(Objc2Constants.OBJC2_CONST)) { - return true; - } - } - return false; - } - - private boolean isObjcDataBlock(Program program, Address address) { - MemoryBlock block = program.getMemory().getBlock(address); - if (block != null) { - if (block.getName().equals(Objc2Constants.OBJC2_DATA)) { - return true; - } - } - return false; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/ObjcUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/ObjcUtils.java index c8d059ebb5..15c736f2e0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/ObjcUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/ObjcUtils.java @@ -15,24 +15,32 @@ */ package ghidra.app.util.bin.format.objc; -import java.io.IOException; +import java.io.*; import java.math.BigInteger; import java.util.*; +import org.xml.sax.SAXException; + +import generic.jar.ResourceFile; import ghidra.app.cmd.data.CreateDataCmd; import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.register.SetRegisterCmd; +import ghidra.app.plugin.processors.sleigh.SleighException; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.objc.objc2.Objc2Constants; import ghidra.app.util.importer.MessageLog; +import ghidra.framework.Application; import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.cmd.Command; +import ghidra.framework.store.LockException; +import ghidra.program.database.SpecExtension; +import ghidra.program.database.SpecExtension.DocInfo; import ghidra.program.database.symbol.ClassSymbol; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.data.DataUtilities.ClearDataMode; -import ghidra.program.model.lang.Processor; -import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; @@ -43,9 +51,34 @@ import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; +import ghidra.xml.XmlParseException; +/** + * Objective-C utilities + */ public final class ObjcUtils { + /** + * The Objective-C compiler name, used by {@link Program#setCompiler(String)} + */ + public static final String OBJC_COMPILER = "objc"; + + /** + * The Objective-C {@code _objc_msgSend} stub calling convention name (added with processor + * extension) + */ + public static final String OBJC_MSGSEND_STUBS_CC = "__objc_msgSend_stub"; + + /** + * String that prefixes Objective-C class symbols + */ + public static final String OBJC_CLASS_SYMBOL_PREFIX = "_OBJC_CLASS_$_"; + + /** + * String that prefixes Objective-C meta-class symbols + */ + public static final String OBJC_META_CLASS_SYMBOL_PREFIX = "_OBJC_METACLASS_$_"; + /** * {@return the next read index value} *

@@ -370,4 +403,99 @@ public final class ObjcUtils { .filter(b -> b.getName().equals(section)) .toList(); } + + /** + * {@return true if the given {@link Program} is an Objective-C program; otherwise, false} + *

+ * NOTE: This method only identifies Mach-O Objective-C programs. ELF Objective-C programs + * produced with GCC use different section names. + * + * @param program The {@link Program} to check + */ + public static boolean isObjc(Program program) { + return isObjc( + Arrays.stream(program.getMemory().getBlocks()).map(MemoryBlock::getName).toList()); + } + + /** + * {@return true if the given {@link List} of section names contains an Objective-C section + * name; otherwise, false} + *

+ * NOTE: This method only identifies Mach-O Objective-C programs. ELF Objective-C programs + * produced with GCC use different section names. + * + * @param sectionNames The {@link List} of section names to check + */ + public static boolean isObjc(List sectionNames) { + return sectionNames.stream().anyMatch(n -> n.startsWith(Objc2Constants.OBJC2_PREFIX)); + } + + /** + * {@return the given name with any Objective-C class prefixes stripped off} + * + * @param name The name to strip + * @see #OBJC_CLASS_SYMBOL_PREFIX + * @see #OBJC_META_CLASS_SYMBOL_PREFIX + */ + public static String stripClassPrefix(String name) { + if (name.startsWith(ObjcUtils.OBJC_CLASS_SYMBOL_PREFIX)) { + return name.substring(ObjcUtils.OBJC_CLASS_SYMBOL_PREFIX.length()); + } + if (name.startsWith(ObjcUtils.OBJC_META_CLASS_SYMBOL_PREFIX)) { + return name.substring(ObjcUtils.OBJC_META_CLASS_SYMBOL_PREFIX.length()); + } + return name; + } + + /** + * Adds Objective-C processor extensions to the {@link Program}, which include: + *

+ * + * @param program The {@link Program} to add the extensions to + * @param monitor A cancelable task monitor + * @return The number of extensions successfully added + * @throws IOException if an IO-related error occurred + * @see Heros in Action: Analyzing Objective-C Binaries through Decompilation and IFDS + * @see RE//verse 2025: Langs Beyond The C + */ + public static int addExtensions(Program program, TaskMonitor monitor) throws IOException { + Language language = program.getLanguageCompilerSpecPair().getLanguage(); + Processor processor = language.getProcessor(); + String spath = "extensions/" + OBJC_COMPILER; + + int extensionCount = 0; + + try { + ResourceFile module = + Application.getModuleDataSubDirectory(processor.toString(), spath); + ResourceFile[] files = module.listFiles(); + if (files != null) { + for (ResourceFile file : files) { + InputStream stream = file.getInputStream(); + byte[] bytes = stream.readAllBytes(); + String xml = new String(bytes); + try { + SpecExtension extension = new SpecExtension(program); + DocInfo docInfo = extension.testExtensionDocument(xml); + if (SpecExtension.getCompilerSpecExtension(program, docInfo) == null) { + extension.addReplaceCompilerSpecExtension(xml, monitor); + extensionCount++; + } + } + catch (SleighException | SAXException | XmlParseException | LockException e) { + Msg.error(ObjcUtils.class, + "Failed to load Objective-C cspec extension: " + file, e); + } + } + } + } + catch (FileNotFoundException e) { + // fall thru + } + + return extensionCount; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc1/Objc1TypeEncodings.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc1/Objc1TypeEncodings.java index 11734f861e..1ccdc5ba07 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc1/Objc1TypeEncodings.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc1/Objc1TypeEncodings.java @@ -248,7 +248,8 @@ public final class Objc1TypeEncodings { } case _C_SEL: { buffer.deleteCharAt(0); - return createTypeDef("SEL"); + return new TypedefDataType("SEL", + PointerDataType.getPointer(new CharDataType(), pointerSize)); } case _C_CHR: { buffer.deleteCharAt(0); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc2/Objc2MessageReference.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc2/Objc2MessageReference.java index b083c35c15..9d9186d381 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc2/Objc2MessageReference.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/objc/objc2/Objc2MessageReference.java @@ -82,6 +82,7 @@ public class Objc2MessageReference extends ObjcTypeMetadataStructure { Structure struct = new StructureDataType(NAME, 0); struct.add(new PointerDataType(VOID), pointerSize, "imp", null); struct.add(new PointerDataType(ASCII), pointerSize, "sel", null); + struct.setCategoryPath(Objc2Constants.CATEGORY_PATH); return struct; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index 06fb8e573f..2916eaeda8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -40,6 +40,7 @@ import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType; import ghidra.app.util.bin.format.macho.dyld.DyldFixup; import ghidra.app.util.bin.format.macho.relocation.*; import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand; +import ghidra.app.util.bin.format.objc.ObjcUtils; import ghidra.app.util.bin.format.objc.objc1.Objc1Constants; import ghidra.app.util.importer.MessageLog; import ghidra.framework.options.Options; @@ -68,6 +69,13 @@ public class MachoProgramBuilder { public static final String HEADER_SYMBOL = "MACH_HEADER"; + /** + * The spacing between symbols that get created in the + * {@link MemoryBlock#EXTERNAL_BLOCK_NAME EXTERNAL block}. This will allow some room for + * the recreation of external method thunks as they are discovered during analysis. + */ + public static final int UNDEFINED_SYMBOL_SPACING = 0x100; + protected MachHeader machoHeader; protected Program program; protected ByteProvider provider; @@ -168,7 +176,7 @@ public class MachoProgramBuilder { // Perform additional actions renameObjMsgSendRtpSymbol(); fixupProgramTree(null); // should be done last to account for new memory blocks - setCompiler(); + setCompiler(provider.getName()); } /** @@ -708,37 +716,37 @@ public class MachoProgramBuilder { if (monitor.isCancelled()) { return; } - if (!(command instanceof SymbolTableCommand)) { + if (!(command instanceof SymbolTableCommand symbolTableCommand)) { continue; } - SymbolTableCommand symbolTableCommand = (SymbolTableCommand) command; List symbols = symbolTableCommand.getSymbols(); - monitor.initialize(symbols.size(), "Collectiing undefined symbols..."); + monitor.initialize(symbols.size(), "Collecting undefined symbols..."); for (NList symbol : symbols) { monitor.increment(); if (symbol.isSymbolicDebugging()) { continue; } if (symbol.isTypeUndefined()) { - List globalSymbols = program.getSymbolTable() - .getLabelOrFunctionSymbols(symbol.getString(), null); - if (globalSymbols.isEmpty()) {//IF IT DOES NOT ALREADY EXIST... + String name = symbol.getString(); + List globalSymbols = + program.getSymbolTable().getLabelOrFunctionSymbols(name, null); + if (globalSymbols.isEmpty()) { //IF IT DOES NOT ALREADY EXIST... undefinedSymbols.add(symbol); } } } } - if (undefinedSymbols.size() == 0) { + if (undefinedSymbols.isEmpty()) { return; } try { - Address addr = MemoryBlockUtils.addExternalBlock(program, - undefinedSymbols.size() * machoHeader.getAddressSize(), log); + long blockSize = undefinedSymbols.size() * UNDEFINED_SYMBOL_SPACING; + Address addr = MemoryBlockUtils.addExternalBlock(program, blockSize, log); monitor.initialize(undefinedSymbols.size(), "Processing undefined symbols..."); for (NList symbol : undefinedSymbols) { monitor.increment(); + String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true); try { - String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true); if (name != null && name.length() > 0) { program.getSymbolTable().createLabel(addr, name, SourceType.IMPORTED); program.getExternalManager() @@ -748,7 +756,7 @@ public class MachoProgramBuilder { catch (Exception e) { log.appendMsg("Unable to create undefined symbol: " + e.getMessage()); } - addr = addr.add(machoHeader.getAddressSize()); + addr = addr.add(UNDEFINED_SYMBOL_SPACING); } } catch (Exception e) { @@ -761,10 +769,9 @@ public class MachoProgramBuilder { List commands = machoHeader.getLoadCommands(); for (LoadCommand command : commands) { monitor.checkCancelled(); - if (!(command instanceof SymbolTableCommand)) { + if (!(command instanceof SymbolTableCommand symbolTableCommand)) { continue; } - SymbolTableCommand symbolTableCommand = (SymbolTableCommand) command; List symbols = symbolTableCommand.getSymbols(); monitor.initialize(symbols.size(), "Collecting absolute symbols..."); for (NList symbol : symbols) { @@ -995,8 +1002,7 @@ public class MachoProgramBuilder { listing.setComment(loadCommandAddr, CommentType.PRE, LoadCommandTypes.getLoadCommandName(loadCommand.getCommandType())); - if (loadCommand instanceof SegmentCommand) { - SegmentCommand segmentCommand = (SegmentCommand) loadCommand; + if (loadCommand instanceof SegmentCommand segmentCommand) { listing.setComment(loadCommandAddr, CommentType.EOL, segmentCommand.getSegmentName()); @@ -1011,51 +1017,43 @@ public class MachoProgramBuilder { sectionOffset += sectionDataType.getLength(); } } - else if (loadCommand instanceof DynamicLinkerCommand) { - DynamicLinkerCommand dynamicLinkerCommand = (DynamicLinkerCommand) loadCommand; + else if (loadCommand instanceof DynamicLinkerCommand dynamicLinkerCommand) { LoadCommandString name = dynamicLinkerCommand.getLoadCommandString(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof DynamicLibraryCommand) { - DynamicLibraryCommand dynamicLibraryCommand = - (DynamicLibraryCommand) loadCommand; + else if (loadCommand instanceof DynamicLibraryCommand dynamicLibraryCommand) { LoadCommandString name = dynamicLibraryCommand.getDynamicLibrary().getName(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof RunPathCommand) { - RunPathCommand runPathCommand = (RunPathCommand) loadCommand; + else if (loadCommand instanceof RunPathCommand runPathCommand) { LoadCommandString path = runPathCommand.getPath(); DataUtilities.createData(program, loadCommandAddr.add(path.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - path.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof SubFrameworkCommand) { - SubFrameworkCommand subFrameworkCommand = (SubFrameworkCommand) loadCommand; + else if (loadCommand instanceof SubFrameworkCommand subFrameworkCommand) { LoadCommandString name = subFrameworkCommand.getUmbrellaFrameworkName(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof SubClientCommand) { - SubClientCommand subClientCommand = (SubClientCommand) loadCommand; + else if (loadCommand instanceof SubClientCommand subClientCommand) { LoadCommandString name = subClientCommand.getClientName(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof SubLibraryCommand) { - SubLibraryCommand subLibraryCommand = (SubLibraryCommand) loadCommand; + else if (loadCommand instanceof SubLibraryCommand subLibraryCommand) { LoadCommandString name = subLibraryCommand.getSubLibraryName(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof SubUmbrellaCommand) { - SubUmbrellaCommand subUmbrellaCommand = (SubUmbrellaCommand) loadCommand; + else if (loadCommand instanceof SubUmbrellaCommand subUmbrellaCommand) { LoadCommandString name = subUmbrellaCommand.getSubUmbrellaFrameworkName(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), @@ -1064,15 +1062,13 @@ public class MachoProgramBuilder { StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof FileSetEntryCommand) { - FileSetEntryCommand fileSetEntryCommand = (FileSetEntryCommand) loadCommand; + else if (loadCommand instanceof FileSetEntryCommand fileSetEntryCommand) { LoadCommandString name = fileSetEntryCommand.getFileSetEntryId(); DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()), StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(), DataUtilities.ClearDataMode.CHECK_FOR_SPACE); } - else if (loadCommand instanceof LinkerOptionCommand) { - LinkerOptionCommand linkerOptionCommand = (LinkerOptionCommand) loadCommand; + else if (loadCommand instanceof LinkerOptionCommand linkerOptionCommand) { List linkerOptions = linkerOptionCommand.getLinkerOptions(); int offset = linkerOptionCommand.toDataType().getLength(); for (int i = 0; i < linkerOptions.size(); i++) { @@ -1813,27 +1809,36 @@ public class MachoProgramBuilder { } } - protected void setCompiler() throws CancelledException { - // Check for Rust + protected void setCompiler(String source) throws CancelledException { + if (ObjcUtils.isObjc(program)) { + try { + program.setCompiler(ObjcUtils.OBJC_COMPILER); + int count = ObjcUtils.addExtensions(program, monitor); + if (count > 0) { + log.appendMsg("%s: installed %d objc SpecExtensions".formatted(source, count)); + } + } + catch (IOException e) { + log.appendMsg("%s: objc error - %s".formatted(source, e.getMessage())); + } + return; + } + try { - SegmentCommand segment = machoHeader.getSegment(SegmentNames.SEG_TEXT); - if (segment == null) { - return; - } - Section section = segment.getSectionByName(SectionNames.TEXT_CONST); - if (section == null) { - return; - } - if (RustUtilities.isRust(program, + Section section = + machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.TEXT_CONST); + if (section != null && RustUtilities.isRust(program, memory.getBlock(space.getAddress(section.getAddress())), monitor)) { program.setCompiler(RustConstants.RUST_COMPILER); - int extensionCount = RustUtilities.addExtensions(program, monitor, + int count = RustUtilities.addExtensions(program, monitor, RustConstants.RUST_EXTENSIONS_UNIX); - log.appendMsg("Installed " + extensionCount + " Rust cspec extensions"); + if (count > 0) { + log.appendMsg("%s: installed %d rust SpecExtensions".formatted(source, count)); + } } } catch (IOException e) { - log.appendMsg("Rust error: " + e.getMessage()); + log.appendMsg("%s: Rust error - %s".formatted(source, e.getMessage())); } } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_DecompilerMessageAnalyzer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjcMessageAnalyzer.java similarity index 51% rename from Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_DecompilerMessageAnalyzer.java rename to Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjcMessageAnalyzer.java index fc584e74e1..3b18d0836b 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjectiveC2_DecompilerMessageAnalyzer.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/ObjcMessageAnalyzer.java @@ -15,73 +15,76 @@ */ package ghidra.app.plugin.core.analysis; +import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ObjectUtils; import ghidra.app.decompiler.*; import ghidra.app.decompiler.parallel.*; import ghidra.app.services.*; import ghidra.app.util.bin.format.macho.SectionNames; +import ghidra.app.util.bin.format.objc.ObjcUtils; import ghidra.app.util.bin.format.objc.objc1.Objc1Constants; -import ghidra.app.util.bin.format.objc.objc2.Objc2Constants; +import ghidra.app.util.bin.format.objc.objc2.*; import ghidra.app.util.importer.MessageLog; +import ghidra.app.util.opinion.MachoLoader; +import ghidra.app.util.opinion.MachoProgramBuilder; +import ghidra.framework.options.OptionType; +import ghidra.framework.options.Options; import ghidra.program.model.address.*; -import ghidra.program.model.data.DataType; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.pcode.*; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.*; -import ghidra.util.exception.CancelledException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; +import util.CollectionUtils; -public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { +/** + * Analyzes {@code _objc_msgSend} information + */ +public class ObjcMessageAnalyzer extends AbstractAnalyzer { + private static final String NAME = "Objective-C Message Analyzer"; + private static final String DESCRIPTION = "Analyzes _objc_msgSend information."; - private static final String NAME = "Objective-C 2 Decompiler Message"; - private static final String DESCRIPTION = - "An analyzer for extracting Objective-C 2.0 message information."; + private static final String OPTION_NAME_CALL_OVERRIDE_REFS = + "Use CALL_OVERRIDE_UNCONDITIONAL references"; + private static final String OPTION_DESCRIPTION_CALL_OVERRIDE_REFS = + "Applies CALL_OVERRIDE_UNCONDITIONAL references instead of UNCONDITIONAL_CALL references to _objc_msgSend calls. This makes the decompiler look nice."; + private static final String OPTION_NAME_LOG_MESSAGE_FAILURES = "Log message fix failures"; + private static final String OPTION_DESCRIPTION_LOG_MESSAGE_FAILURES = + "Log message fix failures during analysis (useful for debugging)."; + + private final static String STUB_NAMESPACE = "objc_stub"; private final int MAX_RECURSION_DEPTH = 10; - /* ************************************************************************** */ - /* ************************************************************************** */ - public ObjectiveC2_DecompilerMessageAnalyzer() { + private boolean useCallOverrides = true; + private boolean logMessageFailures = false; + private Objc2TypeMetadata typeMetadata; + private DataTypes dataTypes; + private Map> classMap; + private Map classExternalSymbolOffset = new HashMap<>(); + + private record DataTypes(DataType ptr, DataType id, DataType sel, DataType classT, + DataType messageRef, DataType messageRefPtr, DataType objcSuper, + DataType objcSuperPtr) {} + + private record Message(String receiver, String selector, Function function, PcodeOpAST op, + int varargParamIndex, Address addr) {} + + public ObjcMessageAnalyzer() { super(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER); setDefaultEnablement(true); - // The Objective-C 2.0 analyzer should always run after the class - // analyzer. And everything - // else apparently. - // It knows the deal! - setPriority(new AnalysisPriority(10000000)); - } - - @Override - public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) - throws CancelledException { - monitor.initialize(set.getNumAddresses()); - - AddressIterator iterator = set.getAddresses(true); - - ArrayList functions = new ArrayList<>(); - while (iterator.hasNext()) { - if (monitor.isCancelled()) { - break; - } - monitor.incrementProgress(1); - Address address = iterator.next(); - - Function function = program.getListing().getFunctionAt(address); - if (isFunctionInTextSection(program, function)) { - functions.add(function); - } - } - try { - runDecompilerAnalysis(program, functions, monitor); - } - catch (Exception e) { - // Oh well. - } - return true; + setPriority(AnalysisPriority.DATA_ANALYSIS.before().before()); } @Override @@ -89,98 +92,375 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return Objc2Constants.isObjectiveC2(program); } - /* ************************************************************************** */ - /* ************************************************************************** */ - - private void runDecompilerAnalysis(Program program, List functions, - TaskMonitor monitor) throws InterruptedException, Exception { - - DecompileConfigurer configurer = decompiler -> setupDecompiler(program, decompiler); - - DecompilerCallback callback = new DecompilerCallback(program, configurer) { - - @Override - public Void process(DecompileResults results, TaskMonitor m) throws Exception { - - inspectFunction(program, results, monitor); - return null; - } - }; + @Override + public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) + throws CancelledException { + set = set.intersect(program.getMemory().getLoadedAndInitializedAddressSet()); try { - ParallelDecompiler.decompileFunctions(callback, functions, monitor); + if (typeMetadata == null) { + typeMetadata = new Objc2TypeMetadata(program, monitor, log); + classMap = typeMetadata.getClasses() + .stream() + .filter(e -> e.getData() != null) + .collect(Collectors.groupingBy(e -> e.getData().getName())); + } + } + catch (IOException e) { + log.appendMsg("Failed to parse Objective-C type metadata: " + e.getMessage()); + return false; + } + + if (dataTypes == null) { + dataTypes = getDataTypes(program, log); + if (dataTypes == null) { + return false; + } + } + + // Fix __objc_msgSend() function signatures + if (!fixMsgSendSignatures(program, monitor, log)) { + return false; + } + + // Set up a standalone decompiler for later use + DecompileConfigurer configurer = d -> setupDecompiler(program, d); + DecompInterface decompiler = new DecompInterface(); + configurer.configure(decompiler); + decompiler.openProgram(program); + + // Use parallel decompiler to override _objc_msgSend() calls to their proper destinations + DecompilerCallback callback = + new DecompilerCallback<>(program, configurer) { + @Override + public Void process(DecompileResults results, TaskMonitor m) throws Exception { + fixMsgSendCalls(program, results.getHighFunction(), decompiler, log, monitor); + return null; + } + }; + try { + ParallelDecompiler.decompileFunctions(callback, getFunctionsInTextSection(program, set), + monitor); + } + catch (Exception e) { + if (e.getCause() instanceof CancelledException ce) { + throw ce; + } + log.appendException(e); } finally { callback.dispose(); + decompiler.closeProgram(); + } + return true; + } + + @Override + public void registerOptions(Options options, Program program) { + options.registerOption(OPTION_NAME_CALL_OVERRIDE_REFS, OptionType.BOOLEAN_TYPE, + useCallOverrides, null, OPTION_DESCRIPTION_CALL_OVERRIDE_REFS); + options.registerOption(OPTION_NAME_LOG_MESSAGE_FAILURES, OptionType.BOOLEAN_TYPE, + logMessageFailures, null, OPTION_DESCRIPTION_LOG_MESSAGE_FAILURES); + } + + @Override + public void optionsChanged(Options options, Program program) { + useCallOverrides = options.getBoolean(OPTION_NAME_CALL_OVERRIDE_REFS, useCallOverrides); + logMessageFailures = + options.getBoolean(OPTION_NAME_LOG_MESSAGE_FAILURES, logMessageFailures); + } + + @Override + public void analysisEnded(Program program) { + if (typeMetadata != null) { + typeMetadata.close(); + typeMetadata = null; } } - private void inspectFunction(Program program, DecompileResults results, TaskMonitor monitor) { - String currentClassName = null; - String currentMethodName = null; + private DataTypes getDataTypes(Program program, MessageLog log) { + // Get the data types that we'll need to use + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + CategoryPath cat = Objc2Constants.CATEGORY_PATH; + int ptrSize = program.getDefaultPointerSize(); + DataType ptr = new PointerDataType(null, program.getDefaultPointerSize()); + DataType id = dtm.getDataType(cat, "ID"); + DataType sel = dtm.getDataType(cat, "SEL"); + DataType classT = dtm.getDataType(cat, "class_t"); + DataType messageRef = dtm.getDataType(cat, "message_ref"); + if (messageRef == null) { + messageRef = id; + } + if (ObjectUtils.anyNull(id, sel, messageRef, classT)) { + log.appendMsg("ERROR: Required Objective-C data type not found in data type manager"); + log.appendMsg("Try adding libobjc.dylib"); + return null; + } + DataType messageRefPtr = new PointerDataType(messageRef, ptrSize); + StructureDataType objcSuper = new StructureDataType(cat, "objc_super", 0); + objcSuper.add(id, "receiver", null); + objcSuper.add(new PointerDataType(classT, ptrSize), "super_class", null); + DataType objcSuperPtr = new PointerDataType(objcSuper, program.getDefaultPointerSize()); - HighFunction highFunction = results.getHighFunction(); - if (highFunction == null) { - return; + return new DataTypes(ptr, id, sel, classT, messageRef, messageRefPtr, objcSuper, + objcSuperPtr); + } + + private boolean fixMsgSendSignatures(Program program, TaskMonitor monitor, MessageLog log) + throws CancelledException { + + for (Function func : program.getFunctionManager().getFunctions(program.getMemory(), true)) { + monitor.checkCancelled(); + + String name = func.getName(); + Namespace global = program.getGlobalNamespace(); + boolean isStub = isObjcMsgSendStub(program, func.getEntryPoint()); + + if (!name.startsWith(Objc1Constants.OBJC_MSG_SEND) && !isStub) { + continue; + } + + try { + // Set up the parameter list + List params = new ArrayList<>(); + switch (name) { + case "_objc_msgSend": + params.add(new ParameterImpl("self", dataTypes.id, program)); + params.add(new ParameterImpl("op", dataTypes.sel, program)); + break; + case "_objc_msgSend_fixup": + params.add(new ParameterImpl("self", dataTypes.id, program)); + params.add( + new ParameterImpl("message_ref", dataTypes.messageRefPtr, program)); + break; + case "_objc_msgSend_stret": + params.add(new ParameterImpl("stretAddr", dataTypes.ptr, program)); + params.add(new ParameterImpl("self", dataTypes.id, program)); + params.add(new ParameterImpl("op", dataTypes.sel, program)); + break; + case "_objc_msgSendSuper": + case "_objc_msgSendSuper2": + params.add(new ParameterImpl("super", dataTypes.objcSuperPtr, program)); + params.add(new ParameterImpl("op", dataTypes.sel, program)); + break; + case "_objc_msgSendSuper_fixup": + case "_objc_msgSendSuper2_fixup": + params.add(new ParameterImpl("super", dataTypes.objcSuperPtr, program)); + params.add( + new ParameterImpl("message_ref", dataTypes.messageRefPtr, program)); + break; + case String s when isStub: + params.add(new ParameterImpl("self", dataTypes.id, program)); + break; + default: + log.appendMsg("Unsupported _objc_msgSend variant: " + name); + + } + + // Set up the return value + Variable returnVar = new ReturnParameterImpl(dataTypes.id, program); + + // Set up the calling convention + String cc = CompilerSpec.CALLING_CONVENTION_unknown; + if (isStub) { + if (program.getDataTypeManager() + .getCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC) != null) { + cc = ObjcUtils.OBJC_MSGSEND_STUBS_CC; + } + } + + // Update the namespace + func.setParentNamespace(isStub ? getStubsNamespace(program) : global); + + // Update the function name + String stubPrefix = Objc1Constants.OBJC_MSG_SEND + "$"; + if (isStub && name.startsWith(stubPrefix)) { + func.setName(name.substring(stubPrefix.length()), SourceType.ANALYSIS); + } + + // Update the function + func.updateFunction(cc, returnVar, params, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS); + func.setVarArgs(true); + } + catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) { + log.appendMsg("Failed to fix up function signature function for: " + func); + } } - Function function = results.getFunction(); - Iterator pcodeOps = highFunction.getPcodeOps(); - while (pcodeOps.hasNext()) { - if (monitor.isCancelled()) { - return; - } - currentClassName = null; - currentMethodName = null; - PcodeOpAST op = pcodeOps.next(); - String mnemonic = op.getMnemonic(); - if (mnemonic == null || (!mnemonic.equals("CALL") && !mnemonic.equals("CALLIND"))) { + return true; + } + + private List findMessages(Program program, HighFunction highFunction, + DecompInterface decompiler, TaskMonitor monitor) throws CancelledException { + List messages = new ArrayList<>(); + Function function = highFunction.getFunction(); + for (PcodeOpAST op : CollectionUtils.asIterable(highFunction.getPcodeOps())) { + monitor.checkCancelled(); + + int opcode = op.getOpcode(); + if (opcode != PcodeOp.CALL && opcode != PcodeOp.CALLIND) { continue; } Varnode[] inputs = op.getInputs(); - if (!isObjcCall(program, inputs[0], monitor)) { - continue; - } - boolean isStret = isStretCall(program, inputs[0], monitor); - for (int i = 1; i < inputs.length; i++) { - String name; - boolean isClass = isClass(i, isStret); - boolean isMessage = isMessage(i, isStret); - name = getNameForVarnode(program, function, inputs[i], isClass, isMessage, 0, 1, - monitor); - if (isClass) { - currentClassName = name; - } - else if (isMessage) { - currentMethodName = name; - } - if (currentClassName != null && currentMethodName != null) { - break; - } - } - - if (currentClassName == null || currentMethodName == null) { + Address callTarget = getAddressFromVarnode(program, inputs[0], 0, monitor); + if (!isObjcMsgSendCall(program, inputs[0], callTarget, monitor)) { continue; } + int stretParamShift = isStructReturnCall(program, inputs[0], monitor) ? 1 : 0; + boolean isStub = isObjcMsgSendStub(program, callTarget); + Varnode receiverParam = inputs[1 + stretParamShift]; + Varnode selectorParam = !isStub ? inputs[2 + stretParamShift] : null; + String receiver = + getNameForVarnode(program, function, receiverParam, true, false, 0, 1, monitor); + String selector = isStub ? processStub(program, callTarget, decompiler, monitor) + : getNameForVarnode(program, function, selectorParam, false, true, 0, 1, + monitor); + if (ObjectUtils.allNotNull(receiver, selector)) { + messages.add(new Message(ObjcUtils.stripClassPrefix(receiver), selector, function, + op, 3 + stretParamShift, callTarget)); + } + } + return messages; + } + + private String processStub(Program program, Address stubAddr, DecompInterface decompiler, + TaskMonitor monitor) throws CancelledException { + Function func = program.getFunctionManager().getFunctionAt(stubAddr); + DecompileResults results = decompiler.decompileFunction(func, 5, monitor); + HighFunction highFunction = results.getHighFunction(); + if (highFunction == null) { + return null; + } + List messages = findMessages(program, highFunction, decompiler, monitor); + if (messages.isEmpty()) { + return null; + } + String selector = messages.getFirst().selector; + if (func.getName().startsWith("FUN_")) { + try { + func.setName(selector, SourceType.ANALYSIS); + } + catch (InvalidInputException | DuplicateNameException e) { + // oh well, just cosmetic + } + } + return messages.getFirst().selector; + } + + private void fixMsgSendCalls(Program program, HighFunction highFunction, + DecompInterface decompiler, MessageLog log, TaskMonitor monitor) + throws CancelledException { + if (highFunction == null) { + return; + } + Function function = highFunction.getFunction(); + List messages = findMessages(program, highFunction, decompiler, monitor); + for (Message msg : messages) { + monitor.checkCancelled(); List parameters = new ArrayList<>(); - int paramStart = isStret ? 4 : 3; + Varnode[] inputs = msg.op.getInputs(); + int paramStart = msg.varargParamIndex; for (int i = paramStart; i < inputs.length; i++) { String paramValue = getNameForVarnode(program, function, inputs[i], false, false, 0, 1, monitor); parameters.add(getIvarNameFromQualifiedName(paramValue)); } - setCommentAndReference(program, currentClassName, currentMethodName, op, parameters); + updateExternalBlock(program, msg, log); + setCommentAndReference(program, msg, parameters, log); + } + } + + private synchronized void updateExternalBlock(Program program, Message msg, MessageLog log) { + Memory mem = program.getMemory(); + FunctionManager funcMgr = program.getFunctionManager(); + ExternalManager extMgr = program.getExternalManager(); + SymbolTable symbolTable = program.getSymbolTable(); + String currentClassName = msg.receiver; + String currentMethodName = msg.selector; + + String objcClassName = ObjcUtils.OBJC_CLASS_SYMBOL_PREFIX + currentClassName; + List objcClassSymbols = symbolTable.getGlobalSymbols(objcClassName); + if (objcClassSymbols.isEmpty()) { + objcClassName = ObjcUtils.OBJC_META_CLASS_SYMBOL_PREFIX + currentClassName; + if (objcClassSymbols.isEmpty()) { + if (logMessageFailures) { + log.appendMsg("Couldn't find class symbol for %s".formatted(msg)); + } + return; + } } + if (!mem.getBlock(objcClassSymbols.getFirst().getAddress()).isExternalBlock()) { + return; + } + + try { + Symbol classSymbol = symbolTable.getClassSymbol(currentClassName, null); + if (classSymbol == null) { + classSymbol = symbolTable.createClass(null, currentClassName, SourceType.ANALYSIS) + .getSymbol(); + } + Namespace classNamespace = (Namespace) classSymbol.getObject(); + if (!symbolTable.getSymbols(currentMethodName, classNamespace).isEmpty()) { + return; + } + int offset = classExternalSymbolOffset.getOrDefault(currentClassName, 1); + int max = program.getExecutableFormat().equals(MachoLoader.MACH_O_NAME) + ? MachoProgramBuilder.UNDEFINED_SYMBOL_SPACING + : program.getDefaultPointerSize(); + if (offset >= max) { + log.appendMsg("No more space reserved in EXTERNAL block to create method: " + msg); + return; + } + Address funcAddr = objcClassSymbols.getFirst().getAddress().add(offset); + Function func = funcMgr.createFunction(currentMethodName, funcAddr, + new AddressSet(funcAddr), SourceType.ANALYSIS); + Symbol externalSymbol = symbolTable.getExternalSymbol(objcClassName); + if (externalSymbol != null) { + ExternalLocation loc = extMgr.addExtLocation(externalSymbol.getParentNamespace(), + currentMethodName, null, SourceType.IMPORTED); + func.setThunkedFunction(loc.createFunction()); + } + List params = List.of(new ParameterImpl("self", dataTypes.id, program), + new ParameterImpl("op", dataTypes.sel, program)); + Variable returnVar = new ReturnParameterImpl(dataTypes.id, program); + func.updateFunction(CompilerSpec.CALLING_CONVENTION_cdecl, returnVar, params, + FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.ANALYSIS); + func.setVarArgs(true); + func.setParentNamespace(classNamespace); + classExternalSymbolOffset.put(currentClassName, offset + 1); + } + catch (Exception e) { + log.appendMsg("ERROR: Failed to update EXTERNAL block for %s.%s - %s" + .formatted(currentClassName, currentMethodName, e.getMessage())); + } } - private void setCommentAndReference(Program program, String currentClassName, - String currentMethodName, PcodeOpAST op, List parameters) { - Address objcCallAddress = op.getSeqnum().getTarget(); + private Namespace getStubsNamespace(Program program) { + SymbolTable symTable = program.getSymbolTable(); + Namespace global = program.getGlobalNamespace(); + Namespace namespace = symTable.getNamespace(STUB_NAMESPACE, global); + if (namespace == null) { + try { + namespace = symTable.createNameSpace(global, STUB_NAMESPACE, SourceType.ANALYSIS); + } + catch (DuplicateNameException | InvalidInputException e) { + return null; + } + } + return namespace; + } + + private void setCommentAndReference(Program program, Message msg, List parameters, + MessageLog log) { + Address objcCallAddress = msg.op.getSeqnum().getTarget(); objcCallAddress = getAddressInProgram(program, objcCallAddress.getOffset()); Instruction instruction = program.getListing().getInstructionAt(objcCallAddress); + String currentClassName = msg.receiver; + String currentMethodName = msg.selector; String fullyQualifiedName = currentClassName; // If the target is an instance variable, we want to display the @@ -189,7 +469,7 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { if (currentClassName.contains("::")) { currentClassName = getClassNameFromQualifiedName(fullyQualifiedName); } - setReference(objcCallAddress, program, currentClassName, currentMethodName); + setReference(objcCallAddress, program, currentClassName, currentMethodName, log); if (instruction == null) { return; @@ -220,20 +500,39 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { instruction.setComment(CommentType.EOL, builder.toString()); } - private boolean isObjcCall(Program program, Varnode input, TaskMonitor monitor) { - Address address = getAddressFromVarnode(program, input, 0, monitor); - if (address == null) { + private boolean isObjcMsgSendCall(Program program, Varnode input, Address callTarget, + TaskMonitor monitor) throws CancelledException { + Symbol symbol = getSymbolFromVarnode(program, input, monitor); + if (symbol == null) { return false; } + String name = symbol.getName(); + if (name.startsWith(Objc1Constants.OBJC_MSG_SEND) || + name.equals(Objc1Constants.READ_UNIX2003) || + name.startsWith("thunk" + Objc1Constants.OBJC_MSG_SEND) || + name.startsWith("PTR_" + Objc1Constants.OBJC_MSG_SEND)) { + return true; + } + return isObjcMsgSendStub(program, callTarget); + } + + private boolean isObjcMsgSendStub(Program program, Address addr) { + MemoryBlock block = program.getMemory().getBlock(addr); + return block != null && block.getName().equals(Objc2Constants.OBJC2_STUBS); + } + + private boolean isObjcAllocCall(Program program, Varnode input, TaskMonitor monitor) + throws CancelledException { Symbol symbol = getSymbolFromVarnode(program, input, monitor); - return isObjcNameMatch(symbol); + if (symbol == null) { + return false; + } + String name = symbol.getName(); + return name.startsWith("_objc_alloc"); } private Address getAddressFromVarnode(Program program, Varnode input, int depth, - TaskMonitor monitor) { - if (monitor.isCancelled()) { - return null; - } + TaskMonitor monitor) throws CancelledException { if (input == null) { return null; } @@ -247,9 +546,7 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { } Varnode[] inputs = def.getInputs(); for (Varnode subInput : inputs) { - if (monitor.isCancelled()) { - return null; - } + monitor.checkCancelled(); Address address = getAddressFromVarnode(program, subInput, depth + 1, monitor); if (address == null) { continue; @@ -263,14 +560,14 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return input.getAddress(); } - private Symbol getSymbolFromVarnode(Program program, Varnode input, TaskMonitor monitor) { + private Symbol getSymbolFromVarnode(Program program, Varnode input, TaskMonitor monitor) + throws CancelledException { Address address = getAddressFromVarnode(program, input, 0, monitor); if (address == null) { return null; } SymbolTable symbolTable = program.getSymbolTable(); - Symbol symbol = symbolTable.getPrimarySymbol(address); - return symbol; + return symbolTable.getPrimarySymbol(address); } private String getNameForVarnode(Program program, Function function, Varnode input, @@ -299,8 +596,8 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return name; } Varnode[] inputs = def.getInputs(); - - if (isObjcCall(program, inputs[0], monitor)) { + Address addr = getAddressFromVarnode(program, inputs[0], 0, monitor); + if (isObjcMsgSendCall(program, inputs[0], addr, monitor)) { Symbol objcSymbol = getSymbolFromVarnode(program, inputs[0], monitor); int classIndex = 1; if (objcSymbol.getName().contains("stret")) { @@ -318,6 +615,10 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { } numInputs = 1; } + else if (isClass && isObjcAllocCall(program, inputs[0], monitor)) { + int classIndex = 1; + inputs = new Varnode[] { inputs[classIndex] }; + } int index = getIndexOfAddress(inputs); if (index != -1) { @@ -331,9 +632,6 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { // If a name was found, just unwind the recursion. If it is just // a constant (ex. when determining parameters) keep looking // to see if we can find an actual name. - if (name != null && !stringIsLong(name)) { - break; - } name = getNameForVarnode(program, function, subInput, isClass, isMethod, depth + 1, inputs.length, monitor); } @@ -383,6 +681,7 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { if (name != null && name.equals("param_1")) { if (numInputs == 1) { if (isClass) { + highVar.getDataType(); Namespace namespace = function.getParentNamespace(); if (namespace != null) { name = namespace.getName(); @@ -400,19 +699,6 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return name; } - private boolean stringIsLong(String value) { - if (value.startsWith("0x")) { - value = value.substring(2); - } - try { - Long.parseUnsignedLong(value, 16); - } - catch (NumberFormatException e) { - return false; - } - return true; - } - private String getNameFromOffset(Program program, long offset, Varnode input, boolean isClass, boolean isMethod) { String name; @@ -512,7 +798,7 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { } } else { - name = getClassName(program, address); + name = getClassName2(program, address); if (name == null) { name = getValueAtAddress(program, address); } @@ -653,10 +939,9 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return address; } - // Tries to lay down a reference to the function that is actually being - // called + // Tries to lay down a reference to the function that is actually being called private void setReference(Address fromAddress, Program program, String currentClassName, - String currentMethodName) { + String currentMethodName, MessageLog log) { SymbolTable symbolTable = program.getSymbolTable(); Symbol classSymbol = symbolTable.getClassSymbol(currentClassName, (Namespace) null); if (classSymbol == null) { @@ -664,12 +949,53 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { } Namespace namespace = (Namespace) classSymbol.getObject(); List functionSymbols = symbolTable.getSymbols(currentMethodName, namespace); + if (functionSymbols.isEmpty()) { + // Walk up the superclass chain to see if the method is inherited + List classList = classMap.get(namespace.getName()); + if (classList.size() == 1) { + Objc2Class superClass = classList.getFirst().getSuperClass(); + if (superClass != null) { + Objc2ClassRW data = superClass.getData(); + if (data != null) { + setReference(fromAddress, program, data.getName(), currentMethodName, log); + } + } + return; + } + } + if (functionSymbols.size() == 1) { - Address toAddress = functionSymbols.get(0).getAddress(); - ReferenceManager referenceManager = program.getReferenceManager(); - Reference reference = referenceManager.addMemoryReference(fromAddress, toAddress, - RefType.UNCONDITIONAL_CALL, SourceType.ANALYSIS, 0); - referenceManager.setPrimary(reference, true); + ReferenceManager refMgr = program.getReferenceManager(); + FunctionManager funcMgr = program.getFunctionManager(); + Reference[] origRefs = refMgr.getReferencesFrom(fromAddress); + Address originalToAddress = origRefs.length > 0 + ? origRefs[0].getToAddress() + : null; + Address newToAddress = functionSymbols.get(0).getAddress(); + Reference reference = refMgr.addMemoryReference(fromAddress, newToAddress, + useCallOverrides ? RefType.CALL_OVERRIDE_UNCONDITIONAL : RefType.UNCONDITIONAL_CALL, + SourceType.ANALYSIS, 0); + refMgr.setPrimary(reference, true); + + if (originalToAddress != null && isObjcMsgSendStub(program, originalToAddress)) { + Function func = funcMgr.getFunctionAt(newToAddress); + if (func != null) { + try { + FunctionDefinitionDataType signature = + new FunctionDefinitionDataType(func, true); + ParameterDefinition[] args = signature.getArguments(); + if (args.length >= 2 && args[1].getDataType().equals(dataTypes.sel)) { + signature.setArguments(ArrayUtils.remove(args, 1)); + } + signature.setCallingConvention(ObjcUtils.OBJC_MSGSEND_STUBS_CC); + HighFunctionDBUtil.writeOverride(funcMgr.getFunctionContaining(fromAddress), + fromAddress, signature); + } + catch (Exception e) { + log.appendException(e); + } + } + } } } @@ -682,13 +1008,13 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { if (symbolName.contains("_OBJC_CLASS_$_")) { symbolName = symbolName.substring("_OBJC_CLASS_$_".length()); } - else if (symbolName.contains("_objc_msgSend")) { + else if (symbolName.contains(Objc1Constants.OBJC_MSG_SEND)) { return null; } return symbolName; } - private String getClassName(Program program, Address toAddress) { + private String getClassName2(Program program, Address toAddress) { try { boolean is32Bit = false; @@ -731,42 +1057,22 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return null; } - private boolean isFunctionInTextSection(Program program, Function function) { - if (function == null) { - return false; + private List getFunctionsInTextSection(Program program, AddressSetView set) { + List ret = new ArrayList<>(); + Memory mem = program.getMemory(); + for (Function function : program.getFunctionManager().getFunctions(set, true)) { + Address address = function.getEntryPoint(); + MemoryBlock block = mem.getBlock(address); + if (block != null && block.getName().equals(SectionNames.TEXT)) { + ret.add(function); + } + } - Address address = function.getEntryPoint(); - Memory memory = program.getMemory(); - MemoryBlock block = memory.getBlock(address); - if (block.getName().equals("__text")) { - return true; - } - return false; + return ret; } - private boolean isClass(int index, boolean isStret) { - boolean isClass; - if (isStret) { - isClass = index == 2; - } - else { - isClass = index == 1; - } - return isClass; - } - - private boolean isMessage(int index, boolean isStret) { - boolean isMessage; - if (isStret) { - isMessage = index == 3; - } - else { - isMessage = index == 2; - } - return isMessage; - } - - private boolean isStretCall(Program program, Varnode input, TaskMonitor monitor) { + private boolean isStructReturnCall(Program program, Varnode input, TaskMonitor monitor) + throws CancelledException { Address address = getAddressFromVarnode(program, input, 0, monitor); if (address == null) { return false; @@ -785,22 +1091,13 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { return false; } Function function = program.getListing().getFunctionAt(address); - if (function.getName().equals("_objc_msgSendSuper2")) { + if (function.getName().startsWith("_objc_msgSendSuper2")) { return true; } } return false; } - private boolean isObjcNameMatch(Symbol symbol) { - if (symbol == null) { - return false; - } - String name = symbol.getName(); - return name.startsWith(Objc1Constants.OBJC_MSG_SEND) || - name.equals(Objc1Constants.READ_UNIX2003); - } - private boolean isMessageRefsBlock(MemoryBlock block) { return block.getName().equals(Objc2Constants.OBJC2_MESSAGE_REFS); } @@ -834,7 +1131,7 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { private boolean isDataBlock(MemoryBlock block) { if (block != null) { - if (block.getName().equals("__data")) { + if (block.getName().equals(SectionNames.DATA)) { return true; } } @@ -843,7 +1140,8 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { private boolean isObjcDataBlock(MemoryBlock block) { if (block != null) { - if (block.getName().equals("__objc_data")) { + + if (block.getName().equals(Objc2Constants.OBJC2_DATA)) { return true; } } @@ -877,4 +1175,5 @@ public class ObjectiveC2_DecompilerMessageAnalyzer extends AbstractAnalyzer { options.setEliminateUnreachable(false); decompiler.setOptions(options); } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java index 2944a4d58c..8259ce5a6f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/SpecExtension.java @@ -307,6 +307,16 @@ public class SpecExtension { return options.getString(optionName, null); } + /** + * Get the raw string making up an extension, given its {@link DocInfo} + * @param program is the program to extract the extension from + * @param docInfo is extension's {@link DocInfo} + * @return the extension string or null + */ + public static String getCompilerSpecExtension(Program program, DocInfo docInfo) { + return getCompilerSpecExtension(program, docInfo.getType(), docInfo.getFormalName()); + } + /** * Check the format version for spec extensions for a given program. * If the program reports a version that does not match the current diff --git a/Ghidra/Processors/AARCH64/certification.manifest b/Ghidra/Processors/AARCH64/certification.manifest index d634991026..28010dca3d 100644 --- a/Ghidra/Processors/AARCH64/certification.manifest +++ b/Ghidra/Processors/AARCH64/certification.manifest @@ -2,6 +2,14 @@ Module.manifest||GHIDRA||||END| README.md||GHIDRA||||END| data/aarch64-pltThunks.xml||GHIDRA||||END| +data/extensions/objc/chkstk_darwin_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_getProperty_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_load_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_msgSend_stub.xml||GHIDRA||||END| +data/extensions/objc/objc_release_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_retain_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_setProperty_fixup.xml||GHIDRA||||END| +data/extensions/objc/objc_store_fixup.xml||GHIDRA||||END| data/languages/AARCH64.cspec||GHIDRA||||END| data/languages/AARCH64.dwarf||GHIDRA||||END| data/languages/AARCH64.ldefs||GHIDRA||||END| diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/chkstk_darwin_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/chkstk_darwin_fixup.xml new file mode 100644 index 0000000000..75f2847b4b --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/chkstk_darwin_fixup.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_getProperty_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_getProperty_fixup.xml new file mode 100644 index 0000000000..1462b86fb9 --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_getProperty_fixup.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_load_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_load_fixup.xml new file mode 100644 index 0000000000..b199fb70bb --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_load_fixup.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_msgSend_stub.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_msgSend_stub.xml new file mode 100644 index 0000000000..139b600c2f --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_msgSend_stub.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_release_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_release_fixup.xml new file mode 100644 index 0000000000..997f1cb163 --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_release_fixup.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_retain_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_retain_fixup.xml new file mode 100644 index 0000000000..81a4cc9973 --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_retain_fixup.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_setProperty_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_setProperty_fixup.xml new file mode 100644 index 0000000000..e26655ea8a --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_setProperty_fixup.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/extensions/objc/objc_store_fixup.xml b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_store_fixup.xml new file mode 100644 index 0000000000..7c72808a48 --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/extensions/objc/objc_store_fixup.xml @@ -0,0 +1,9 @@ + + + + + + +