diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/SwiftTypeMetadataAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/SwiftTypeMetadataAnalyzer.java index 73022a73e7..fef5b75c75 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/SwiftTypeMetadataAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/SwiftTypeMetadataAnalyzer.java @@ -31,6 +31,8 @@ public class SwiftTypeMetadataAnalyzer extends AbstractAnalyzer { private static final String NAME = "Swift Type Metadata Analyzer"; private static final String DESCRIPTION = "Discovers Swift type metadata records."; + private SwiftTypeMetadata typeMetadata; + public SwiftTypeMetadataAnalyzer() { super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); setDefaultEnablement(true); @@ -45,8 +47,11 @@ public class SwiftTypeMetadataAnalyzer extends AbstractAnalyzer { @Override public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { + if (typeMetadata != null) { + return true; + } try { - SwiftTypeMetadata typeMetadata = new SwiftTypeMetadata(program, monitor, log); + typeMetadata = new SwiftTypeMetadata(program, monitor, log); typeMetadata.markup(); } catch (IOException e) { @@ -54,4 +59,9 @@ public class SwiftTypeMetadataAnalyzer extends AbstractAnalyzer { } return true; } + + @Override + public void analysisEnded(Program program) { + typeMetadata = null; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftSection.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftSection.java index 42fc4207dd..4fbfab217e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftSection.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftSection.java @@ -35,8 +35,9 @@ public enum SwiftSection { BLOCK_PROTOCS("__swift5_protos", "swift5_protocols", ".sw5prt"), BLOCK_ACFUNCS("__swift5_acfuncs", "swift5_accessible_functions", ".sw5acfn"), BLOCK_MPENUM("__swift5_mpenum", "swift5_mpenum", ".sw5mpen"), - BLOCK_TYPES("__swift5_types", "swift5_types", ".sw5tymd"), - BLOCK_ENTRY("__swift5_entry", "swift5_entry", ".sw5entr"); + BLOCK_TYPES("__swift5_types", "swift5_type_metadata", ".sw5tymd"), + BLOCK_ENTRY("__swift5_entry", "swift5_entry", ".sw5entr"), + BLOCK_SWIFTAST("__swift_ast", ".swift_ast", "swiftast"); private List sectionNames; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadata.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadata.java index fc453f36ae..a6c638302e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadata.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadata.java @@ -16,8 +16,7 @@ package ghidra.app.util.bin.format.swift; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import ghidra.app.util.bin.*; import ghidra.app.util.bin.format.swift.types.*; @@ -43,11 +42,11 @@ public class SwiftTypeMetadata { private List entryPoints = new ArrayList<>(); private List builtinTypeDescriptors = new ArrayList<>(); - private List fieldDescriptors = new ArrayList<>(); + private Map fieldDescriptors = new HashMap<>(); private List associatedTypeDescriptors = new ArrayList<>(); private List captureDescriptors = new ArrayList<>(); private List mpEnumDescriptors = new ArrayList<>(); - private List typeDescriptors = new ArrayList<>(); + private Map typeDescriptors = new HashMap<>(); private List protocolDescriptors = new ArrayList<>(); private List protocolConformanceDescriptors = new ArrayList<>(); @@ -72,6 +71,69 @@ public class SwiftTypeMetadata { parse(); } + /** + * {@return the entry points} + */ + public List getEntryPoints() { + return entryPoints; + } + + /** + * {@return the built-in type descriptors} + */ + public List getBuiltinTypeDescriptors() { + return builtinTypeDescriptors; + } + + /** + * {@return the field descriptors} + */ + public Map getFieldDescriptors() { + return fieldDescriptors; + } + + /** + * {@return the associated type descriptors} + */ + public List getAssociatedTypeDescriptor() { + return associatedTypeDescriptors; + } + + /** + * {@return the capture descriptors} + */ + public List getCaptureDescriptors() { + return captureDescriptors; + } + + /** + * {@return the multi-payload enum descriptors} + */ + public List getMultiPayloadEnumDescriptors() { + return mpEnumDescriptors; + } + + /** + * {@return the type descriptors} + */ + public Map getTargetTypeContextDescriptors() { + return typeDescriptors; + } + + /** + * {@return the target protocol descriptors} + */ + public List getTargetProtocolDescriptors() { + return protocolDescriptors; + } + + /** + * {@return the target protocol conformance descriptors} + */ + public List getTargetProtocolConformanceDescriptors() { + return protocolConformanceDescriptors; + } + /** * Parses the {@link SwiftTypeMetadata} * @@ -137,7 +199,7 @@ public class SwiftTypeMetadata { for (MemoryBlock block : SwiftUtils.getSwiftBlocks(section, program)) { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); - int i = 0; + int i = skipZeroEntries(reader, 0, block.getSize()); while (i + BuiltinTypeDescriptor.SIZE <= block.getSize()) { monitor.checkCancelled(); BuiltinTypeDescriptor descriptor = new BuiltinTypeDescriptor(reader); @@ -145,6 +207,7 @@ public class SwiftTypeMetadata { markupList.add(new SwiftStructureInfo(descriptor, new SwiftStructureAddress(blockStart.add(i), null))); i += BuiltinTypeDescriptor.SIZE; + i = skipZeroEntries(reader, i, block.getSize()); } } } @@ -168,11 +231,11 @@ public class SwiftTypeMetadata { for (MemoryBlock block : SwiftUtils.getSwiftBlocks(section, program)) { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); - int i = 0; + int i = skipZeroEntries(reader, 0, block.getSize()); while (i + FieldDescriptor.SIZE <= block.getSize()) { monitor.checkCancelled(); FieldDescriptor descriptor = new FieldDescriptor(reader); - fieldDescriptors.add(descriptor); + fieldDescriptors.put(descriptor.getBase(), descriptor); markupList.add(new SwiftStructureInfo(descriptor, new SwiftStructureAddress(blockStart.add(i), null))); List records = descriptor.getFieldRecords(); @@ -184,6 +247,7 @@ public class SwiftTypeMetadata { null))); } i += descriptor.getNumFields() * FieldRecord.SIZE; + i = skipZeroEntries(reader, i, block.getSize()); } } } @@ -207,7 +271,7 @@ public class SwiftTypeMetadata { for (MemoryBlock block : SwiftUtils.getSwiftBlocks(section, program)) { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); - int i = 0; + int i = skipZeroEntries(reader, 0, block.getSize()); while (i + AssociatedTypeDescriptor.SIZE <= block.getSize()) { monitor.checkCancelled(); AssociatedTypeDescriptor descriptor = new AssociatedTypeDescriptor(reader); @@ -223,6 +287,7 @@ public class SwiftTypeMetadata { blockStart.add(i + j * AssociatedTypeRecord.SIZE), null))); } i += descriptor.getNumAssociatedTypes() * AssociatedTypeRecord.SIZE; + i = skipZeroEntries(reader, i, block.getSize()); } } } @@ -246,7 +311,7 @@ public class SwiftTypeMetadata { for (MemoryBlock block : SwiftUtils.getSwiftBlocks(section, program)) { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); - int i = 0; + int i = skipZeroEntries(reader, 0, block.getSize()); while (i + CaptureDescriptor.SIZE <= block.getSize()) { monitor.checkCancelled(); CaptureDescriptor descriptor = new CaptureDescriptor(reader); @@ -271,6 +336,7 @@ public class SwiftTypeMetadata { blockStart.add(i + j * MetadataSourceRecord.SIZE), null))); } i += descriptor.getNumMetadataSources() * MetadataSourceRecord.SIZE; + i = skipZeroEntries(reader, i, block.getSize()); } } } @@ -294,7 +360,7 @@ public class SwiftTypeMetadata { for (MemoryBlock block : SwiftUtils.getSwiftBlocks(section, program)) { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); - int i = 0; + int i = skipZeroEntries(reader, 0, block.getSize()); while (i < block.getSize()) { monitor.checkCancelled(); MultiPayloadEnumDescriptor descriptor = new MultiPayloadEnumDescriptor(reader); @@ -302,6 +368,7 @@ public class SwiftTypeMetadata { markupList.add(new SwiftStructureInfo(descriptor, new SwiftStructureAddress(blockStart.add(i), null))); i += MultiPayloadEnumDescriptor.SIZE + descriptor.getContentsSize(); + i = skipZeroEntries(reader, i, block.getSize()); } } } @@ -397,7 +464,7 @@ public class SwiftTypeMetadata { yield null; }; if (descriptor != null) { - typeDescriptors.add(descriptor); + typeDescriptors.put(descriptor.getName(), descriptor); markupList.add(new SwiftStructureInfo(descriptor, new SwiftStructureAddress(addrPair.structAddr(), addrPair.pointerAddr()))); } @@ -409,7 +476,7 @@ public class SwiftTypeMetadata { } /** - * Parses a table of pointers to {@link SwiftStructure}s found in the given section + * Parses a table of pointers to {@link SwiftTypeMetadataStructure}s found in the given section * * @param section The {@link SwiftSection} that contains the pointer table * @param reader A {@link BinaryReader} @@ -428,11 +495,10 @@ public class SwiftTypeMetadata { reader.setPointerIndex(blockAddr.getOffset() + i); Address pointerAddr = blockAddr.add(i); int offset = reader.readInt(pointerAddr.getOffset()); - if (offset == 0) { - break; + if (offset != 0) { + Address structAddr = pointerAddr.add(offset); + result.add(new SwiftStructureAddress(structAddr, pointerAddr)); } - Address structAddr = pointerAddr.add(offset); - result.add(new SwiftStructureAddress(structAddr, pointerAddr)); } } } @@ -454,7 +520,7 @@ public class SwiftTypeMetadata { monitor.checkCancelled(); monitor.incrementProgress(1); try { - SwiftStructure struct = structInfo.struct(); + SwiftTypeMetadataStructure struct = structInfo.struct(); DataType dt = struct.toDataType(); DataUtilities.createData(program, structInfo.addr().structAddr(), dt, -1, ClearDataMode.CLEAR_ALL_DEFAULT_CONFLICT_DATA); @@ -465,12 +531,37 @@ public class SwiftTypeMetadata { relativePtrDataType, -1, ClearDataMode.CLEAR_ALL_DEFAULT_CONFLICT_DATA); } } - catch (CodeUnitInsertionException | DuplicateNameException | IOException e) { + catch (CodeUnitInsertionException e) { + // Probably just called more than once + } + catch (DuplicateNameException | IOException e) { log("Failed to markup: " + structInfo); } } } + /** + * Reads past zeroed out entries in Swift type metadata sections + * + * @param reader A {@link BinaryReader} positioned within a type metadata section + * @param offset The current offset from the start of the type metadata section + * @param size The size of the type metadata section (in bytes) + * @return The offset from the start of the type metadata section that contains the next + * non-zero entry + * @throws IOException if an IO-related error occurred + */ + private int skipZeroEntries(BinaryReader reader, int offset, long size) throws IOException { + while (offset + 8 <= size) { + long possibleZero = reader.readNextLong(); + if (possibleZero != 0) { + reader.setPointerIndex(reader.getPointerIndex() - 8); + return offset; + } + offset += 8; + } + return offset; + } + /** * Convenience method to perform logging * @@ -481,22 +572,24 @@ public class SwiftTypeMetadata { } /** - * The {@link Address} of a {@link SwiftStructure} and the optional {@link Address} of its - * pointer + * The {@link Address} of a {@link SwiftTypeMetadataStructure} and the optional {@link Address} + * of its pointer * - * @param structAddr The {@link Address} of a {@link SwiftStructure} - * @param pointerAddr The {@link Address} of a pointer to a {@link SwiftStructure} (could be - * null if there is no associated pointer} + * @param structAddr The {@link Address} of a {@link SwiftTypeMetadataStructure} + * @param pointerAddr The {@link Address} of a pointer to a {@link SwiftTypeMetadataStructure} + * (could be null if there is no associated pointer} */ private record SwiftStructureAddress(Address structAddr, Address pointerAddr) {} /** - * Information about a {@link SwiftStructure} + * Information about a {@link SwiftTypeMetadataStructure} * - * @param struct The {@link SwiftStructure} - * @param addr The {@link SwiftStructureAddress address} of the {@link SwiftStructure} + * @param struct The {@link SwiftTypeMetadataStructure} + * @param addr The {@link SwiftStructureAddress address} of the + * {@link SwiftTypeMetadataStructure} */ - private record SwiftStructureInfo(SwiftStructure struct, SwiftStructureAddress addr) { + private record SwiftStructureInfo(SwiftTypeMetadataStructure struct, + SwiftStructureAddress addr) { @Override public String toString() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadataStructure.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadataStructure.java new file mode 100644 index 0000000000..2714e2c4b2 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftTypeMetadataStructure.java @@ -0,0 +1,55 @@ +/* ### + * 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.swift; + +import ghidra.app.util.bin.StructConverter; + +/** + * Implemented by all Swift type metadata structures + */ +public abstract class SwiftTypeMetadataStructure implements StructConverter { + + public static final String DATA_TYPE_CATEGORY = "/SwiftTypeMetadata"; + + private long base; + + public SwiftTypeMetadataStructure(long base) { + this.base = base; + } + + /** + * Gets the base "address" of this {@link SwiftTypeMetadataStructure} + * + * @return The base "address" of this {@link SwiftTypeMetadataStructure} + */ + public long getBase() { + return base; + } + + /** + * Gets the name of the {@link SwiftTypeMetadataStructure} + * + * @return The name of the {@link SwiftTypeMetadataStructure} + */ + public abstract String getStructureName(); + + /** + * Gets a short description of the {@link SwiftTypeMetadataStructure} + * + * @return A short description of the {@link SwiftTypeMetadataStructure} + */ + public abstract String getDescription(); +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftUtils.java index 2c3097c345..db35805e4e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftUtils.java @@ -57,6 +57,23 @@ public class SwiftUtils { return false; } + /** + * Checks if the given {@List} of section names contains a Swift section name + * + * @param sectionNames The {@link List} of section names to check + * @return True if the given {@List} of section names contains a Swift section name; otherwise, + * false + */ + public static boolean isSwift(List sectionNames) { + List prefixes = List.of("__swift", "swift", ".sw5"); + for (String sectionName : sectionNames) { + if (prefixes.stream().anyMatch(prefix -> sectionName.startsWith(prefix))) { + return true; + } + } + return false; + } + /** * Gets a {@link List} of {@link MemoryBlock}s that match the given {@link SwiftSection} * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeDescriptor.java index 5b32c6e1b0..9e58826b14 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeDescriptor.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.SwiftStructure; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -30,7 +30,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class AssociatedTypeDescriptor implements SwiftStructure { +public final class AssociatedTypeDescriptor extends SwiftTypeMetadataStructure { /** * The size (in bytes) of an {@link AssociatedTypeDescriptor} structure @@ -51,6 +51,7 @@ public final class AssociatedTypeDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public AssociatedTypeDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); conformingTypeName = reader.readNext(SwiftUtils::relativeString); protocolTypeName = reader.readNext(SwiftUtils::relativeString); numAssociatedTypes = reader.readNextInt(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeRecord.java index 04b43d7999..83884ac47c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/AssociatedTypeRecord.java @@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.SwiftStructure; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class AssociatedTypeRecord implements SwiftStructure { +public final class AssociatedTypeRecord extends SwiftTypeMetadataStructure { /** * The size (in bytes) of an {@link AssociatedTypeRecord} structure @@ -45,6 +45,7 @@ public final class AssociatedTypeRecord implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public AssociatedTypeRecord(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); name = reader.readNext(SwiftUtils::relativeString); substitutedTypeName = reader.readNext(SwiftUtils::relativeString); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/BuiltinTypeDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/BuiltinTypeDescriptor.java index 896608c967..39e54f4711 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/BuiltinTypeDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/BuiltinTypeDescriptor.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class BuiltinTypeDescriptor implements SwiftStructure { +public final class BuiltinTypeDescriptor extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link BuiltinTypeDescriptor} structure @@ -47,6 +48,7 @@ public final class BuiltinTypeDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public BuiltinTypeDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); typeName = reader.readNext(SwiftUtils::relativeString); size = reader.readNextInt(); alignmentAndFlags = reader.readNextInt(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureDescriptor.java index 96b1bb78a2..48aa442b2d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureDescriptor.java @@ -20,10 +20,8 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.SwiftStructure; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.StructureDataType; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; /** @@ -31,7 +29,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class CaptureDescriptor implements SwiftStructure { +public final class CaptureDescriptor extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link CaptureDescriptor} structure @@ -52,6 +50,7 @@ public final class CaptureDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public CaptureDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); numCaptureTypes = reader.readNextInt(); numMetadataSources = reader.readNextInt(); numBindings = reader.readNextInt(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureTypeRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureTypeRecord.java index 8101cc3c55..8b27f0b27b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureTypeRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/CaptureTypeRecord.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class CaptureTypeRecord implements SwiftStructure { +public final class CaptureTypeRecord extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link CaptureTypeRecord} structure @@ -43,6 +44,7 @@ public final class CaptureTypeRecord implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public CaptureTypeRecord(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); mangledTypeName = reader.readNext(SwiftUtils::relativeString); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/EntryPoint.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/EntryPoint.java index 31055a01f4..6829e62cf9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/EntryPoint.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/EntryPoint.java @@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.SwiftStructure; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a Swift entry point */ -public final class EntryPoint implements SwiftStructure { +public final class EntryPoint extends SwiftTypeMetadataStructure { /** * The size (in bytes) of an {@link EntryPoint} structure @@ -42,6 +42,7 @@ public final class EntryPoint implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public EntryPoint(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); entryPoint = reader.readNextInt(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldDescriptor.java index 5798be7c99..07d0726ec4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldDescriptor.java @@ -20,7 +20,8 @@ import java.util.ArrayList; import java.util.List; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -29,7 +30,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class FieldDescriptor implements SwiftStructure { +public final class FieldDescriptor extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link FieldDescriptor} structure @@ -51,6 +52,7 @@ public final class FieldDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public FieldDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); mangledTypeName = reader.readNext(SwiftUtils::relativeString); superclass = reader.readNextInt(); kind = reader.readNextUnsignedShort(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldRecord.java index b4a9a9a067..087f82448d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/FieldRecord.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class FieldRecord implements SwiftStructure { +public final class FieldRecord extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link FieldRecord} structure @@ -45,6 +46,7 @@ public final class FieldRecord implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public FieldRecord(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); flags = reader.readNextInt(); mangledTypeName = reader.readNext(SwiftUtils::relativeString); fieldName = reader.readNext(SwiftUtils::relativeString); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MetadataSourceRecord.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MetadataSourceRecord.java index 2d3d89df38..5f5f995b74 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MetadataSourceRecord.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MetadataSourceRecord.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class MetadataSourceRecord implements SwiftStructure { +public final class MetadataSourceRecord extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link MetadataSourceRecord} structure @@ -44,6 +45,7 @@ public final class MetadataSourceRecord implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public MetadataSourceRecord(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); mangledTypeName = reader.readNext(SwiftUtils::relativeString); mangledMetadataSource = reader.readNext(SwiftUtils::relativeString); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MultiPayloadEnumDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MultiPayloadEnumDescriptor.java index f8aa845839..ddf68f32f7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MultiPayloadEnumDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/MultiPayloadEnumDescriptor.java @@ -18,7 +18,7 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.SwiftStructure; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.DataType; import ghidra.util.exception.DuplicateNameException; @@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/RemoteInspection/Records.h */ -public final class MultiPayloadEnumDescriptor implements SwiftStructure { +public final class MultiPayloadEnumDescriptor extends SwiftTypeMetadataStructure { /** * The size (in bytes) of a {@link MultiPayloadEnumDescriptor} structure. This size does not @@ -48,6 +48,7 @@ public final class MultiPayloadEnumDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public MultiPayloadEnumDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); typeName = reader.readNext(SwiftUtils::relativeString); int size = (reader.readNextInt() >> 16) & 0xffff; reader.setPointerIndex(reader.getPointerIndex() - 4); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetClassDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetClassDescriptor.java index 26073e7b25..0c69fd2b6d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetClassDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetClassDescriptor.java @@ -19,9 +19,7 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.swift.SwiftUtils; -import ghidra.program.model.data.CategoryPath; -import ghidra.program.model.data.DataType; -import ghidra.program.model.data.StructureDataType; +import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetContextDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetContextDescriptor.java index ec464f2a6e..7cd26a242b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetContextDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetContextDescriptor.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,12 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/ABI/Metadata.h */ -public class TargetContextDescriptor implements SwiftStructure { +public class TargetContextDescriptor extends SwiftTypeMetadataStructure { + + /** + * The size (in bytes) of a {@link TargetContextDescriptor} structure + */ + public static final int SIZE = 8; private int flags; private int parent; @@ -39,6 +45,7 @@ public class TargetContextDescriptor implements SwiftStructure { * @throws IOException if there was an IO-related problem creating the structure */ public TargetContextDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); flags = reader.readNextInt(); parent = reader.readNextInt(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetProtocolConformanceDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetProtocolConformanceDescriptor.java index 74d81c3afc..f4d183a539 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetProtocolConformanceDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetProtocolConformanceDescriptor.java @@ -18,7 +18,8 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; -import ghidra.app.util.bin.format.swift.*; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; @@ -27,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; * * @see swift/ABI/Metadata.h */ -public final class TargetProtocolConformanceDescriptor implements SwiftStructure { +public final class TargetProtocolConformanceDescriptor extends SwiftTypeMetadataStructure { private int protocolDescriptor; private int nominalTypeDescriptor; @@ -41,6 +42,7 @@ public final class TargetProtocolConformanceDescriptor implements SwiftStructure * @throws IOException if there was an IO-related problem creating the structure */ public TargetProtocolConformanceDescriptor(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); protocolDescriptor = reader.readNextInt(); nominalTypeDescriptor = reader.readNextInt(); protocolWitnessTable = reader.readNextInt(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetTypeContextDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetTypeContextDescriptor.java index 3b2f10ea9c..d0640f974d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetTypeContextDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetTypeContextDescriptor.java @@ -16,6 +16,7 @@ package ghidra.app.util.bin.format.swift.types; import java.io.IOException; +import java.util.Map; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.swift.SwiftUtils; @@ -73,6 +74,20 @@ public class TargetTypeContextDescriptor extends TargetContextDescriptor { return fields; } + /** + * Gets this {@link TargetTypeContextDescriptor}'s {@link FieldDescriptor} + * + * @param fieldDescriptors A {@link Map} of {@link FieldDescriptor}'s keyed by their base + * addresses + * @return This {@link TargetTypeContextDescriptor}'s {@link FieldDescriptor}, or null if it + * doesn't have one + */ + public FieldDescriptor getFieldDescriptor(Map fieldDescriptors) { + FieldDescriptor fieldDescriptor = + fieldDescriptors.get(getBase() + TargetContextDescriptor.SIZE + 8 + fields); + return fieldDescriptor != null ? fieldDescriptor : null; + } + @Override public String getStructureName() { return getMyStructureName(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java index cc8d613277..8dcb1aa244 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledDataType.java @@ -73,6 +73,7 @@ public class DemangledDataType extends DemangledType { public final static String LONG = "long"; public final static String LONG_LONG = "long long"; public final static String FLOAT = "float"; + public final static String FLOAT2 = "float2"; public final static String DOUBLE = "double"; public final static String INT8 = "__int8"; public final static String INT16 = "__int16"; @@ -94,7 +95,7 @@ public class DemangledDataType extends DemangledType { public final static String[] PRIMITIVES = { VOID, BOOL, CHAR, WCHAR_T, WCHAR16, WCHAR32, SHORT, INT, INT0_T, LONG, - LONG_LONG, FLOAT, DOUBLE, INT128, FLOAT128, LONG_DOUBLE, }; + LONG_LONG, FLOAT, FLOAT2, DOUBLE, INT128, FLOAT128, LONG_DOUBLE, }; private int arrayDimensions = 0; private boolean isClass; @@ -293,6 +294,9 @@ public class DemangledDataType extends DemangledType { else if (FLOAT.equals(name)) { dt = FloatDataType.dataType; } + else if (FLOAT2.equals(name)) { + dt = Float2DataType.dataType; + } else if (FLOAT128.equals(name)) { dt = new TypedefDataType(FLOAT128, Float16DataType.dataType); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java index 9343ab55a0..b0826593fb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledFunction.java @@ -54,7 +54,7 @@ public class DemangledFunction extends DemangledObject { protected DemangledDataType returnType; protected String callingConvention;// __cdecl, __thiscall, etc. protected boolean thisPassedOnStack = true; - protected List parameters = new ArrayList<>(); + protected List parameters = new ArrayList<>(); protected DemangledTemplate template; protected boolean isOverloadedOperator = false; protected SourceType signatureSourceType = SourceType.ANALYSIS; @@ -140,11 +140,15 @@ public class DemangledFunction extends DemangledObject { this.isOverloadedOperator = isOverloadedOperator; } - public void addParameter(DemangledDataType parameter) { + public void addParameter(DemangledParameter parameter) { parameters.add(parameter); } - public List getParameters() { + public void addParameters(List params) { + parameters.addAll(params); + } + + public List getParameters() { return new ArrayList<>(parameters); } @@ -323,7 +327,7 @@ public class DemangledFunction extends DemangledObject { } protected void addParameters(StringBuilder buffer, boolean format) { - Iterator paramIterator = parameters.iterator(); + Iterator paramIterator = parameters.iterator(); buffer.append('('); int padLength = format ? buffer.length() : 0; String pad = StringUtils.rightPad("", padLength); @@ -332,7 +336,11 @@ public class DemangledFunction extends DemangledObject { } while (paramIterator.hasNext()) { - buffer.append(paramIterator.next().getSignature()); + DemangledParameter param = paramIterator.next(); + buffer.append(param.getType().getSignature()); + if (param.getLabel() != null) { + buffer.append(" " + param.getLabel()); + } if (paramIterator.hasNext()) { buffer.append(','); if (format) { @@ -353,9 +361,9 @@ public class DemangledFunction extends DemangledObject { public String getParameterString() { StringBuffer buffer = new StringBuffer(); buffer.append('('); - Iterator dditer = parameters.iterator(); + Iterator dditer = parameters.iterator(); while (dditer.hasNext()) { - buffer.append(dditer.next().getSignature()); + buffer.append(dditer.next().getType().getSignature()); if (dditer.hasNext()) { buffer.append(','); } @@ -576,13 +584,13 @@ public class DemangledFunction extends DemangledObject { return false; } - DemangledDataType lastType = parameters.get(parameters.size() - 1); + DemangledDataType lastType = parameters.get(parameters.size() - 1).getType(); return lastType.isVarArgs(); } private boolean hasVoidParams() { if (parameters.size() == 1) { - DemangledDataType ddt = parameters.get(0); + DemangledDataType ddt = parameters.get(0).getType(); return ddt.isVoid() && !ddt.isPointer(); } return false; @@ -634,7 +642,7 @@ public class DemangledFunction extends DemangledObject { // If returnType is null check for constructor or destructor names if (THIS_CALL.equals(function.getCallingConventionName())) { String n = getName(); - if (n.equals(namespace.getName())) { + if (namespace != null && n.equals(namespace.getName())) { // constructor return DataType.DEFAULT; } @@ -698,17 +706,18 @@ public class DemangledFunction extends DemangledObject { private List convertMangledToParamDef(Program program) { List args = new ArrayList<>(); - for (DemangledDataType param : parameters) { + for (DemangledParameter param : parameters) { // stop when a void parameter is hit. This probably the only defined parameter. - if (param.isVoid() && !param.isPointer()) { + DemangledDataType type = param.getType(); + if (type.isVoid() && !type.isPointer()) { break; } - if (param.isVarArgs()) { + if (type.isVarArgs()) { break; } - DataType dt = param.getDataType(program.getDataTypeManager()); - args.add(new ParameterDefinitionImpl(null, dt, null)); + DataType dt = type.getDataType(program.getDataTypeManager()); + args.add(new ParameterDefinitionImpl(param.getLabel(), dt, null)); } return args; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLabel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLabel.java new file mode 100644 index 0000000000..603c0f6634 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledLabel.java @@ -0,0 +1,52 @@ +/* ### + * 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.demangler; + +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.Symbol; +import ghidra.util.task.TaskMonitor; + +/** + * A class to represent a {@link DemangledObject} that should get represented as a Ghidra label + */ +public class DemangledLabel extends DemangledObject { + + /** + * Creates a new {@link DemangledLabel} + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The label name + */ + public DemangledLabel(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled); + setName(name); + } + + @Override + public boolean applyTo(Program program, Address address, DemanglerOptions options, + TaskMonitor monitor) throws Exception { + Symbol symbol = applyDemangledName(address, true, false, program); + return symbol != null; + } + + @Override + public String getSignature(boolean format) { + return originalDemangled; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledList.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledList.java new file mode 100644 index 0000000000..cf30a13906 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledList.java @@ -0,0 +1,93 @@ +/* ### + * 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.demangler; + +import java.util.ArrayList; +import java.util.List; + +/** + * An convenience {@link Demangled} object that holds a {@link List} of other + * {@link Demangled} objects + */ +public class DemangledList extends ArrayList implements Demangled { + + /** + * Creates a {@link DemangledList} and adds the given {@link List} to it + * + * @param demangledList The {@link List} of {@link Demangled} objects to add + */ + public DemangledList(List demangledList) { + super(demangledList); + } + + /** + * {@return true if this contains any null elements; otherwise, false} + */ + public boolean containsNull() { + return stream().anyMatch(e -> e == null); + } + + @Override + public String getMangledString() { + return null; + } + + @Override + public String getOriginalDemangled() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public void setName(String name) { + // Nothing to do + } + + @Override + public String getDemangledName() { + return null; + } + + @Override + public Demangled getNamespace() { + return null; + } + + @Override + public void setNamespace(Demangled ns) { + // Nothing to do + } + + @Override + public String getNamespaceString() { + return null; + } + + @Override + public String getNamespaceName() { + return null; + } + + @Override + public String getSignature() { + return null; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledParameter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledParameter.java new file mode 100644 index 0000000000..8a5b38eebf --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledParameter.java @@ -0,0 +1,69 @@ +/* ### + * 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.demangler; + +/** + * A class to represent a demangled function parameter. + *

+ * This extends {@link DemangledDataType} in order to associate an optional parameter label with + * its data type. + */ +public class DemangledParameter { + + private DemangledDataType type; + private String label; + + /** + * Creates a new {@link DemangledParameter} with the given type and no label + * + * @param type The parameter type + */ + public DemangledParameter(DemangledDataType type) { + this.type = type; + } + + /** + * {@return the parameter's type} + */ + public DemangledDataType getType() { + return type; + } + + /** + * {@return the parameter's label (could be null)} + */ + public String getLabel() { + return label; + } + + /** + * Sets the parameter's label + * + * @param label The label (null for no label) + */ + public void setLabel(String label) { + this.label = label; + } + + @Override + public String toString() { + String ret = type.toString(); + if (label != null) { + ret += " " + label; + } + return ret; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledStructure.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledStructure.java new file mode 100644 index 0000000000..0c5161ced9 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/demangler/DemangledStructure.java @@ -0,0 +1,105 @@ +/* ### + * 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.demangler; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.program.model.data.*; + +/** + * A class to represent a demangled structure + */ +public class DemangledStructure extends DemangledDataType { + + /** + * A field of a {@link DemangledStructure} + * + * @param name The field name + * @param description The field description + * @param type The field {@link DemangledDataType type} + */ + public record Field(String name, String description, DemangledDataType type) {} + + private List fields = new ArrayList<>(); + private String categoryPath; + private boolean packed; + + /** + * Creates a new {@link DemangledStructure} + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The structure name + * @param categoryPath The structure category path + * @param packed True if the structure should be packed; otherwise, false + */ + public DemangledStructure(String mangled, String originalDemangled, String name, + String categoryPath, boolean packed) { + super(mangled, originalDemangled, name); + setStruct(); + this.categoryPath = categoryPath; + this.packed = packed; + } + + /** + * Adds a new field to the structure. The field will not have a description. + * + * @param name The field name + * @param type The field {@link DemangledDataType type} + */ + public void addField(String name, DemangledDataType type) { + fields.add(new Field(name, null, type)); + } + + /** + * Adds a new field to the structure + * + * @param name The field name + * @param description The field description + * @param type The field {@link DemangledDataType type} + */ + public void addField(String name, String description, DemangledDataType type) { + fields.add(new Field(name, description, type)); + } + + /** + * Gets the {@link List} of {@link Field}s + * + * @return The {@link List} of {@link Field}s + */ + public List getFields() { + return fields; + } + + @Override + public DataType getDataType(DataTypeManager dataTypeManager) { + String name = getName(); + if (name == null) { + return DataType.DEFAULT; + } + + StructureDataType struct = new StructureDataType(name, 0, dataTypeManager); + for (Field field : fields) { + struct.add(field.type().getDataType(dataTypeManager), field.name(), + field.description()); + } + struct.setPackingEnabled(packed); + struct.setCategoryPath(new CategoryPath(categoryPath)); + return struct; + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java index 9f4d4bcb71..cbce444a2c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoLoader.java @@ -24,6 +24,7 @@ import ghidra.app.util.Option; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProviderWrapper; import ghidra.app.util.bin.format.macho.*; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.app.util.bin.format.ubi.*; import ghidra.app.util.importer.MessageLog; import ghidra.formats.gfilesystem.*; @@ -60,7 +61,13 @@ public class MachoLoader extends AbstractLibrarySupportLoader { MachHeader machHeader = new MachHeader(provider); String magic = CpuTypes.getMagicString(machHeader.getCpuType(), machHeader.getCpuSubType()); - List results = QueryOpinionService.query(MACH_O_NAME, magic, null); + List sectionNames = machHeader.parseSegments() + .stream() + .flatMap(seg -> seg.getSections().stream()) + .map(section -> section.getSectionName()) + .toList(); + String compiler = SwiftUtils.isSwift(sectionNames) ? "swift" : null; + List results = QueryOpinionService.query(MACH_O_NAME, magic, compiler); for (QueryResult result : results) { loadSpecs.add(new LoadSpec(this, machHeader.getImageBase(), result)); } 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 3e78175f41..553abe9978 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 @@ -37,6 +37,7 @@ import ghidra.app.util.bin.format.pe.ImageCor20Header.ImageCor20Flags; 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; +import ghidra.app.util.bin.format.swift.SwiftUtils; import ghidra.app.util.importer.MessageLog; import ghidra.framework.model.DomainObject; import ghidra.framework.options.Options; @@ -921,6 +922,7 @@ public class PeLoader extends AbstractPeDebugLoader { CLI("cli", "cli"), Rustc(RustConstants.RUST_COMPILER, RustConstants.RUST_COMPILER), GOLANG("golang", "golang"), + Swift("swift", "swift"), Unknown("unknown", "unknown"), // The following values represent the presence of ambiguous indicators @@ -986,6 +988,15 @@ public class PeLoader extends AbstractPeDebugLoader { } return CompilerEnum.Rustc; } + + // Check for Swift + List sectionNames = + Arrays.stream(pe.getNTHeader().getFileHeader().getSectionHeaders()) + .map(section -> section.getName()) + .toList(); + if (SwiftUtils.isSwift(sectionNames)) { + return CompilerEnum.Swift; + } // Check for managed code (.NET) if (pe.getNTHeader().getOptionalHeader().isCLI()) { diff --git a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java index aaa6b19137..7fef606dc6 100644 --- a/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java +++ b/Ghidra/Features/GnuDemangler/src/main/java/ghidra/app/util/demangler/gnu/GnuDemanglerParser.java @@ -485,7 +485,7 @@ public class GnuDemanglerParser { // setNameAndNamespace(function, simpleName); - for (DemangledDataType parameter : signatureParts.getParameters()) { + for (DemangledParameter parameter : signatureParts.getParameters()) { function.addParameter(parameter); } @@ -630,9 +630,9 @@ public class GnuDemanglerParser { * Reason being, you need to take into account nested templates * and function pointers. */ - private List parseParameters(String parameterString) { + private List parseParameters(String parameterString) { List parameterStrings = tokenizeParameters(parameterString); - List parameters = convertIntoParameters(parameterStrings); + List parameters = convertIntoParameters(parameterStrings); return parameters; } @@ -735,12 +735,12 @@ public class GnuDemanglerParser { * This method converts each parameter string into * actual DemangledDataType objects. */ - private List convertIntoParameters(List parameterStrings) { - List parameters = new ArrayList<>(); + private List convertIntoParameters(List parameterStrings) { + List parameters = new ArrayList<>(); for (String parameter : parameterStrings) { DemangledDataType dt = parseParameter(parameter); - parameters.add(dt); + parameters.add(new DemangledParameter(dt)); } return parameters; @@ -1311,10 +1311,10 @@ public class GnuDemanglerParser { contents = string.substring(1, string.length() - 1); } - List parameters = parseParameters(contents); + List parameters = parseParameters(contents); DemangledTemplate template = new DemangledTemplate(); - for (DemangledDataType parameter : parameters) { - template.addParameter(parameter); + for (DemangledParameter parameter : parameters) { + template.addParameter(parameter.getType()); } return template; } @@ -1399,16 +1399,16 @@ public class GnuDemanglerParser { return dfp; } - private DemangledFunctionPointer createFunctionPointer(String paramerterString, + private DemangledFunctionPointer createFunctionPointer(String parameterString, String returnType) { - List parameters = parseParameters(paramerterString); + List parameters = parseParameters(parameterString); DemangledFunctionPointer dfp = new DemangledFunctionPointer(mangledSource, demangledSource); DemangledDataType returnDataType = parseReturnType(returnType); dfp.setReturnType(returnDataType); - for (DemangledDataType parameter : parameters) { - dfp.addParameter(parameter); + for (DemangledParameter parameter : parameters) { + dfp.addParameter(parameter.getType()); } return dfp; } @@ -1889,8 +1889,8 @@ public class GnuDemanglerParser { // operator itself could be in a class namespace setNameAndNamespace(function, operatorText); - List parameters = parseParameters(parametersText); - for (DemangledDataType parameter : parameters) { + List parameters = parseParameters(parametersText); + for (DemangledParameter parameter : parameters) { function.addParameter(parameter); } @@ -2029,7 +2029,7 @@ public class GnuDemanglerParser { private String name; private String rawParameterPrefix; - private List parameters; + private List parameters; FunctionSignatureParts(String signatureString) { @@ -2077,7 +2077,7 @@ public class GnuDemanglerParser { return isFunction; } - List getParameters() { + List getParameters() { return parameters; } diff --git a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java index 24d2ee5c46..4ea5c01bfa 100644 --- a/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java +++ b/Ghidra/Features/GnuDemangler/src/test/java/ghidra/app/util/demangler/GnuDemanglerParserTest.java @@ -54,9 +54,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertName(object, "bob"); DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(1, parameters.size()); - DemangledDataType p1 = parameters.get(0); + DemangledDataType p1 = parameters.get(0).getType(); assertEquals("bob(int const[8] (*) [12])", p1.getOriginalDemangled()); assertEquals("undefined bob(int const *[])", object.getSignature(false)); } @@ -113,9 +113,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledObject object = parser.parse("fake", "Layout::graphNew(short[][][][], char*)"); assertType(object, DemangledFunction.class); DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(2, parameters.size()); - DemangledDataType p1 = parameters.get(0); + DemangledDataType p1 = parameters.get(0).getType(); assertEquals(4, p1.getArrayDimensions()); } @@ -153,12 +153,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction method = (DemangledFunction) object; - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(4, parameters.size()); - assertEquals("unsigned long (*)(long const &)", parameters.get(0).getSignature()); - assertEquals("unsigned long", parameters.get(1).getSignature()); - assertEquals("unsigned long", parameters.get(2).getSignature()); - assertEquals("float", parameters.get(3).getSignature()); + assertEquals("unsigned long (*)(long const &)", parameters.get(0).getType().getSignature()); + assertEquals("unsigned long", parameters.get(1).getType().getSignature()); + assertEquals("unsigned long", parameters.get(2).getType().getSignature()); + assertEquals("float", parameters.get(3).getType().getSignature()); } @Test @@ -176,9 +176,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { "_Rb_tree,std::less,std::allocator>"); DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(1, parameters.size()); - assertEquals("Location const &", parameters.get(0).getSignature()); + assertEquals("Location const &", parameters.get(0).getType().getSignature()); } @Test @@ -194,7 +194,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertType(object, DemangledFunction.class); DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals( "__insertion_sort<__gnu_cxx::__normal_iterator*,std::vector,std::allocator>>>,bool(*)(std::pairconst&,std::pairconst&)>", @@ -211,9 +211,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { "bool (*)(std::pair const &,std::pair const &)", parameters.get(2).toString()); - assertType(parameters.get(2), DemangledFunctionPointer.class); + assertType(parameters.get(2).getType(), DemangledFunctionPointer.class); - DemangledFunctionPointer fptr = (DemangledFunctionPointer) parameters.get(2); + DemangledFunctionPointer fptr = (DemangledFunctionPointer) parameters.get(2).getType(); assertEquals("bool", fptr.getReturnType().getName()); @@ -234,9 +234,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertEquals( "undefined std::set,std::allocator>::insert(bbnode const * &)", method.getSignature(false)); - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(1, parameters.size()); - assertEquals("bbnode const * &", parameters.get(0).getSignature()); + assertEquals("bbnode const * &", parameters.get(0).getType().getSignature()); } @Test @@ -252,9 +252,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction method = (DemangledFunction) object; assertEquals("undefined Bar::Fred::Fred(int)", method.getSignature(false)); - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(1, parameters.size()); - assertEquals("int", parameters.get(0).getSignature()); + assertEquals("int", parameters.get(0).getType().getSignature()); } @Test @@ -428,9 +428,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction function = (DemangledFunction) object; assertEquals("undefined Foo::getBool(float)", function.getSignature(false)); - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(1, parameters.size()); - assertEquals("float", parameters.get(0).getSignature()); + assertEquals("float", parameters.get(0).getType().getSignature()); } @Test @@ -447,13 +447,13 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(5, parameters.size()); - assertEquals("int", parameters.get(0).getSignature()); - assertEquals("double", parameters.get(1).getSignature()); - assertEquals("char", parameters.get(2).getSignature()); - assertEquals("long", parameters.get(3).getSignature()); - assertEquals("short", parameters.get(4).getSignature()); + assertEquals("int", parameters.get(0).getType().getSignature()); + assertEquals("double", parameters.get(1).getType().getSignature()); + assertEquals("char", parameters.get(2).getType().getSignature()); + assertEquals("long", parameters.get(3).getType().getSignature()); + assertEquals("short", parameters.get(4).getType().getSignature()); } @Test @@ -827,13 +827,13 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction method = (DemangledFunction) object; - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(5, parameters.size()); - assertEquals("SECTION_INFO *", parameters.get(0).getSignature()); - assertEquals("int *", parameters.get(1).getSignature()); - assertEquals("int *[]", parameters.get(2).getSignature()); - assertEquals("int", parameters.get(3).getSignature()); - assertEquals("short const *", parameters.get(4).getSignature()); + assertEquals("SECTION_INFO *", parameters.get(0).getType().getSignature()); + assertEquals("int *", parameters.get(1).getType().getSignature()); + assertEquals("int *[]", parameters.get(2).getType().getSignature()); + assertEquals("int", parameters.get(3).getType().getSignature()); + assertEquals("short const *", parameters.get(4).getType().getSignature()); } @Test @@ -935,10 +935,10 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { "undefined Magick::operator<(Magick::Coordinate const &,Magick::Coordinate const &)", method.getSignature(false)); - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(2, parameters.size()); - assertEquals("Magick::Coordinate const &", parameters.get(0).getSignature()); - assertEquals("Magick::Coordinate const &", parameters.get(1).getSignature()); + assertEquals("Magick::Coordinate const &", parameters.get(0).getType().getSignature()); + assertEquals("Magick::Coordinate const &", parameters.get(1).getType().getSignature()); } @Test @@ -1072,9 +1072,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertEquals("undefined Magick::pageImage::operator()(Magick::Image &)", method.getSignature(false)); - List parameters = method.getParameters(); + List parameters = method.getParameters(); assertEquals(1, parameters.size()); - assertEquals("Magick::Image &", parameters.get(0).getSignature()); + assertEquals("Magick::Image &", parameters.get(0).getType().getSignature()); } @Test @@ -1536,13 +1536,14 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction df = (DemangledFunction) object; - List parameters = df.getParameters(); + List parameters = df.getParameters(); assertEquals("Number of parameters", 1, parameters.size()); - assertEquals("Name of type parsed", "F", parameters.get(0).getName()); - assertEquals("Param Type Name parsed", "WTF", parameters.get(0).getNamespace().toString()); + assertEquals("Name of type parsed", "F", parameters.get(0).getType().getName()); + assertEquals("Param Type Name parsed", "WTF", + parameters.get(0).getType().getNamespace().toString()); assertEquals("Param Template was parsed", " (Core::File &)>", - parameters.get(0).getTemplate().toString()); + parameters.get(0).getType().getTemplate().toString()); assertEquals( "undefined Core::AsyncFile::perform(WTF::F (Core::File &)> &&)", @@ -1569,9 +1570,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction df = (DemangledFunction) object; - List parameters = df.getParameters(); + List parameters = df.getParameters(); assertEquals("Number of parameters", 1, parameters.size()); - DemangledDataType demangParamDT = parameters.get(0); + DemangledDataType demangParamDT = parameters.get(0).getType(); assertEquals("Name of type parsed", "function", demangParamDT.getName()); assertEquals("Param Type Name parsed", "boost", demangParamDT.getNamespace().toString()); @@ -1601,9 +1602,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { DemangledFunction df = (DemangledFunction) object; - List parameters = df.getParameters(); + List parameters = df.getParameters(); assertEquals("Number of parameters", 1, parameters.size()); - DemangledDataType demangParamDT = parameters.get(0); + DemangledDataType demangParamDT = parameters.get(0).getType(); assertEquals("Name of type parsed", "function", demangParamDT.getName()); assertEquals("Param Type Name parsed", "boost", demangParamDT.getNamespace().toString()); @@ -1705,9 +1706,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest { assertType(object, DemangledFunction.class); DemangledFunction function = (DemangledFunction) object; - List parameters = function.getParameters(); + List parameters = function.getParameters(); assertEquals(2, parameters.size()); - assertTrue(parameters.get(1).isVarArgs()); + assertTrue(parameters.get(1).getType().isVarArgs()); assertEquals("undefined testVarArgs(int,...)", object.getSignature(false)); } diff --git a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java index c1a6bc3f16..e9fc686053 100644 --- a/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java +++ b/Ghidra/Features/MicrosoftDmang/src/main/java/mdemangler/MDMangGhidra.java @@ -369,7 +369,8 @@ public class MDMangGhidra extends MDMang { MDArgumentsList args = functionType.getArgumentsList(); if (functionType.hasArgs() && args != null) { for (int index = 0; index < args.getNumArgs(); index++) { - function.addParameter(processDataType(null, args.getArg(index))); + function.addParameter( + new DemangledParameter(processDataType(null, args.getArg(index)))); } } if (functionType.isTypeCast()) { diff --git a/Ghidra/Features/SwiftDemangler/Module.manifest b/Ghidra/Features/SwiftDemangler/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Features/SwiftDemangler/build.gradle b/Ghidra/Features/SwiftDemangler/build.gradle new file mode 100644 index 0000000000..71ba191112 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/build.gradle @@ -0,0 +1,26 @@ +/* ### + * 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. + */ +apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" +apply from: "$rootProject.projectDir/gradle/javaProject.gradle" +apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" +apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" +apply plugin: 'eclipse' + +eclipse.project.name = 'Features SwiftDemangler' + +dependencies { + api project(":Base") +} diff --git a/Ghidra/Features/SwiftDemangler/certification.manifest b/Ghidra/Features/SwiftDemangler/certification.manifest new file mode 100644 index 0000000000..895ac4b698 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/certification.manifest @@ -0,0 +1,2 @@ +##VERSION: 2.0 +Module.manifest||GHIDRA||||END| diff --git a/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java b/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java new file mode 100644 index 0000000000..610991d576 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/ghidra_scripts/SwiftDemanglerScript.java @@ -0,0 +1,70 @@ +/* ### + * 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. + */ + +//Outputs the natively demangled Swift symbol found at the current address in expanded tree form. +//Mostly useful for debugging. +//@category Demangler +import ghidra.app.script.GhidraScript; +import ghidra.app.util.demangler.DemangledObject; +import ghidra.app.util.demangler.swift.*; +import ghidra.app.util.demangler.swift.SwiftNativeDemangler.SwiftNativeDemangledOutput; +import ghidra.program.model.symbol.*; + +public class SwiftDemanglerScript extends GhidraScript { + + @Override + protected void run() throws Exception { + SwiftDemangler demangler = new SwiftDemangler(); + SwiftDemanglerOptions options = new SwiftDemanglerOptions(); + if (!demangler.canDemangle(currentProgram)) { + println("Not a Swift program"); + return; + } + println("-------------------------------------------------"); + + String mangled = null; + SymbolTable symbolTable = currentProgram.getSymbolTable(); + for (Symbol symbol : symbolTable.getSymbols(currentAddress)) { + if (demangler.isSwiftMangledSymbol(symbol.getName())) { + mangled = symbol.getName(); + break; + } + for (LabelHistory history : symbolTable.getLabelHistory(currentAddress)) { + if (demangler.isSwiftMangledSymbol(history.getLabelString())) { + mangled = history.getLabelString(); + break; + } + } + } + if (mangled == null) { + println("No mangled Swift symbols found at " + currentAddress); + return; + } + + SwiftNativeDemangler nativeDemangler = new SwiftNativeDemangler(options.getSwiftDir()); + SwiftNativeDemangledOutput demangledOutput = nativeDemangler.demangle(mangled); + println(demangledOutput.toString()); + + DemangledObject demangledObject = demangler.demangle(mangled); + if (demangledObject != null) { + println(demangledObject.getClass().getSimpleName() + " " + mangled + " --> " + + demangledObject); + } + else { + println("Failed to demangle: " + mangled); + } + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java new file mode 100644 index 0000000000..dc61ca8421 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/plugin/core/analysis/SwiftDemanglerAnalyzer.java @@ -0,0 +1,127 @@ +/* ### + * 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; + +import java.io.File; +import java.io.IOException; + +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.*; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.OptionType; +import ghidra.framework.options.Options; +import ghidra.program.model.listing.Program; +import ghidra.util.HelpLocation; + +/** + * An analyzer to demangle Swift mangled symbols + */ +public class SwiftDemanglerAnalyzer extends AbstractDemanglerAnalyzer { + + private static final String NAME = "Demangler Swift"; + private static final String DESCRIPTION = + "Demangles Swift symbols and applies appropriate datatype and calling conventions where possible. Requires Swift to be installed."; + private static final String OPTION_NAME_SWIFT_DIR = "Swift binary directory"; + private static final String OPTION_DESCRIPTION_SWIFT_DIR = + "Path to the Swift installation binary directory, if not on PATH"; + + private static final String OPTION_NAME_INCOMPLETE_PREFIX = + "Use incomplete demangle label prefix (%s)" + .formatted(SwiftDemanglerOptions.INCOMPLETE_PREFIX); + private static final String OPTION_DESCRIPTION_INCOMPLETE_PREFIX = + "Prefix incomplete demangled labels with '%s'" + .formatted(SwiftDemanglerOptions.INCOMPLETE_PREFIX); + private static final String OPTION_NAME_UNSUPPORTED_PREFIX = + "Use unsupported demangle label prefix (%s)" + .formatted(SwiftDemanglerOptions.UNSUPPORTED_PREFIX); + private static final String OPTION_DESCRIPTION_UNSUPPORTED_PREFIX = + "Prefix unsupported demangled labels with '%s'" + .formatted(SwiftDemanglerOptions.UNSUPPORTED_PREFIX); + + private File swiftDir; + private boolean useIncompletePrefix = true; + private boolean useUnsupportedPrefix = true; + private SwiftDemangler demangler = new SwiftDemangler(); + + /** + * Creates a new {@link SwiftDemanglerAnalyzer} + */ + public SwiftDemanglerAnalyzer() { + super(NAME, DESCRIPTION); + setDefaultEnablement(true); + } + + @Override + public boolean canAnalyze(Program program) { + return demangler.canDemangle(program); + } + + @Override + protected DemangledObject doDemangle(String mangled, DemanglerOptions options, MessageLog log) + throws DemangledException { + return demangler.demangle(mangled, options); + } + + @Override + public void analysisEnded(Program program) { + demangler.clearCache(); + } + + @Override + public void registerOptions(Options options, Program program) { + HelpLocation help = new HelpLocation("AutoAnalysisPlugin", "Demangler_Analyzer"); + options.registerOption(OPTION_NAME_SWIFT_DIR, OptionType.FILE_TYPE, swiftDir, help, + OPTION_DESCRIPTION_SWIFT_DIR); + options.registerOption(OPTION_NAME_INCOMPLETE_PREFIX, OptionType.BOOLEAN_TYPE, + useIncompletePrefix, help, OPTION_DESCRIPTION_INCOMPLETE_PREFIX); + options.registerOption(OPTION_NAME_UNSUPPORTED_PREFIX, OptionType.BOOLEAN_TYPE, + useUnsupportedPrefix, help, OPTION_DESCRIPTION_UNSUPPORTED_PREFIX); + } + + @Override + protected boolean validateOptions(DemanglerOptions options, MessageLog log) { + if (options instanceof SwiftDemanglerOptions swiftDemanglerOptions) { + try { + new SwiftNativeDemangler(swiftDemanglerOptions.getSwiftDir()); + return true; + } + catch (IOException e) { + log.appendMsg(e.getMessage()); + log.appendMsg("You must have Swift installed to demangle Swift symbols.\n" + + "See the \"Demangler Swift\" analyzer options to configure."); + } + } + return false; + } + + @Override + public void optionsChanged(Options options, Program program) { + swiftDir = options.getFile(OPTION_NAME_SWIFT_DIR, swiftDir); + useIncompletePrefix = + options.getBoolean(OPTION_NAME_INCOMPLETE_PREFIX, useIncompletePrefix); + useUnsupportedPrefix = + options.getBoolean(OPTION_NAME_UNSUPPORTED_PREFIX, useUnsupportedPrefix); + } + + @Override + protected DemanglerOptions getOptions() { + SwiftDemanglerOptions swiftDemanglerOptions = new SwiftDemanglerOptions(); + swiftDemanglerOptions.setSwiftDir(swiftDir); + swiftDemanglerOptions.setIncompletePrefix(useIncompletePrefix); + swiftDemanglerOptions.setUnsupportedPrefix(useUnsupportedPrefix); + return swiftDemanglerOptions; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftStructure.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledBuiltinType.java similarity index 51% rename from Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftStructure.java rename to Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledBuiltinType.java index 169791c6d6..c7def18a13 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/SwiftStructure.java +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledBuiltinType.java @@ -13,28 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.util.bin.format.swift; +package ghidra.app.util.demangler.swift; -import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; /** - * Implemented by all Swift structures + * Kinds of Swift demangling {@link SwiftNode}s + * + * @see DemangleNodes.def */ -public interface SwiftStructure extends StructConverter { - - public static final String DATA_TYPE_CATEGORY = "/Swift"; +public enum SwiftDemangledBuiltinType { + //@formatter:off - /** - * Gets the name of the {@link SwiftStructure} - * - * @return The name of the {@link SwiftStructure} - */ - public String getStructureName(); - - /** - * Gets a short description of the {@link SwiftStructure} - * - * @return A short description of the {@link SwiftStructure} - */ - public String getDescription(); + Int1, + Word, + RawPointer, + Unsupported + + //@formatter:on } diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledNodeKind.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledNodeKind.java new file mode 100644 index 0000000000..ac21c313ba --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledNodeKind.java @@ -0,0 +1,94 @@ +/* ### + * 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.demangler.swift; + +import ghidra.app.util.demangler.swift.nodes.SwiftNode; + +/** + * Kinds of Swift demangling {@link SwiftNode}s + * + * @see DemangleNodes.def + */ +public enum SwiftDemangledNodeKind { + //@formatter:off + + Allocator, + AnonymousDescriptor, + ArgumentTuple, + BoundGenericStructure, + BuiltinTypeName, + Class, + Constructor, + Deallocator, + DefaultArgumentInitializer, + DependentGenericParamType, + DependentGenericType, + Destructor, + DispatchThunk, + Enum, + Extension, + FirstElementMarker, + Function, + FunctionType, + GenericSpecialization, + Getter, + Global, + GlobalVariableOnceDeclList, + GlobalVariableOnceFunction, + Identifier, + InfixOperator, + Initializer, + InOut, + LabelList, + LazyProtocolWitnessTableAccessor, + LocalDeclName, + MergedFunction, + ModifyAccessor, + Module, + ModuleDescriptor, + NominalTypeDescriptor, + Number, + ObjCAttribute, + OutlinedConsume, + OutlinedCopy, + Owned, + PrivateDeclName, + Protocol, + ProtocolConformance, + ProtocolConformanceDescriptor, + ProtocolDescriptor, + ProtocolWitness, + ReflectionMetadataBuiltinDescriptor, + ReflectionMetadataFieldDescriptor, + ReturnType, + Setter, + Static, + Structure, + Subscript, + Suffix, + Tuple, + TupleElement, + TupleElementName, + Type, + TypeAlias, + TypeList, + TypeMetadataAccessFunction, + UnsafeMutableAddressor, + Unsupported, + Variable; + + //@formatter:on +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledTree.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledTree.java new file mode 100644 index 0000000000..aa47078bfc --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangledTree.java @@ -0,0 +1,170 @@ +/* ### + * 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.demangler.swift; + +import java.io.IOException; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ghidra.app.util.demangler.swift.SwiftNativeDemangler.SwiftNativeDemangledOutput; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; +import ghidra.app.util.demangler.swift.nodes.SwiftNode.NodeProperties; +import ghidra.app.util.demangler.swift.nodes.SwiftUnsupportedNode; + +/** + * A Swift demangled symbol, structured as a tree of {@link SwiftNode nodes} + *

+ * For example, the Swift.print function's mangled form is + * _$ss5print_9separator10terminatoryypd_S2StF, and its demangled tree is: + *

+ * kind=Global
+ * kind=Function
+ *   kind=Module, text="Swift"
+ *   kind=Identifier, text="print"
+ *   kind=LabelList
+ *     kind=FirstElementMarker
+ *     kind=Identifier, text="separator"
+ *     kind=Identifier, text="terminator"
+ *   kind=Type
+ *     kind=FunctionType
+ *       kind=ArgumentTuple
+ *         kind=Type
+ *           kind=Tuple
+ *             kind=TupleElement
+ *               kind=VariadicMarker
+ *               kind=Type
+ *                 kind=ProtocolList
+ *                   kind=TypeList
+ *             kind=TupleElement
+ *               kind=Type
+ *                 kind=Structure
+ *                   kind=Module, text="Swift"
+ *                   kind=Identifier, text="String"
+ *             kind=TupleElement
+ *               kind=Type
+ *                 kind=Structure
+ *                   kind=Module, text="Swift"
+ *                   kind=Identifier, text="String"
+ *       kind=ReturnType
+ *         kind=Type
+ *           kind=Tuple
+ * 
+ */ +public class SwiftDemangledTree { + + private static final Pattern KIND_PATTERN = Pattern.compile("kind=([^,]+)"); + private static final Pattern TEXT_PATTERN = Pattern.compile("text=\"(.+)\""); + private static final Pattern INDEX_PATTERN = Pattern.compile("index=(.+)"); + + private SwiftNode root; + private String demangledString; + + /** + * Creates a new {@link SwiftDemangledTree} + * + * @param nativeDemangler The Swift native demangler + * @param mangled The mangled string + * @throws IOException If there was an IO-related error + */ + public SwiftDemangledTree(SwiftNativeDemangler nativeDemangler, String mangled) + throws IOException { + SwiftNativeDemangledOutput demangledOutput = nativeDemangler.demangle(mangled); + demangledString = demangledOutput.demangled(); + Stack stack = new Stack<>(); + for (String line : demangledOutput.tree()) { + int depth = depth(line); + String kind = match(line, KIND_PATTERN); + String text = match(line, TEXT_PATTERN); + String index = match(line, INDEX_PATTERN); + SwiftNode node; + try { + NodeProperties properties = new NodeProperties(SwiftDemangledNodeKind.valueOf(kind), + text, index, depth, mangled, demangledString); + node = SwiftNode.get(properties); + } + catch (IllegalArgumentException e) { + NodeProperties properties = new NodeProperties(SwiftDemangledNodeKind.Unsupported, + text, index, depth, mangled, demangledString); + node = new SwiftUnsupportedNode(kind, properties); + } + if (node.getDepth() == 0) { + root = node; + } + else { + if (node.getDepth() <= stack.peek().getDepth()) { + while (stack.peek().getDepth() > node.getDepth() - 1) { + stack.pop(); + } + } + node.setParent(stack.peek()); + stack.peek().getChildren().add(node); + } + stack.push(node); + } + } + + /** + * Gets the root {@link SwiftNode} of the tree + * + * @return The root {@link SwiftNode} of the tree. Could be null if demangling finished + * gracefully but did not return a result. + */ + public SwiftNode getRoot() { + return root; + } + + /** + * Gets the demangled string + * + * @return The demangled string. Could be null if demangling finished gracefully + * but did not return a result. + */ + public String getDemangledString() { + return demangledString; + } + + @Override + public String toString() { + return SwiftNode.toString(root, true); + } + + /** + * Gets the tree-depth of this {@link SwiftNode} + * + * @param line A line of output from swift demangle --tree-only + * @return The tree-depth of this {@link SwiftNode} + */ + private int depth(String line) { + int i = 0; + while (i < line.length() && line.charAt(i) == ' ') { + i++; + } + return i / 2; + } + + /** + * Gets a matched pattern on the given line + * + * @param line The line to match against + * @param pattern The {@link Pattern} to match + * @return The matched string, or null if there was no match + */ + private String match(String line, Pattern pattern) { + Matcher matcher = pattern.matcher(line); + return matcher.find() ? matcher.group(1) : null; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java new file mode 100644 index 0000000000..c8f05fb6de --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemangler.java @@ -0,0 +1,198 @@ +/* ### + * 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.demangler.swift; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.bin.format.swift.SwiftUtils; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.datatypes.SwiftDataTypeUtils; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.listing.Program; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * A demangler for mangled Swift symbols + */ +public class SwiftDemangler implements Demangler { + + private Program program; + private SwiftTypeMetadata typeMetadata; + private SwiftNativeDemangler nativeDemangler; + private SwiftDemanglerOptions options; + private Map cache; + private DemangledException initException; + + @Override + public boolean canDemangle(Program p) { + this.program = p; + return SwiftUtils.isSwift(program); + } + + @Override + public DemanglerOptions createDefaultOptions() { + return new SwiftDemanglerOptions(); + } + + @Override + public DemangledObject demangle(String mangled, boolean demangleOnlyKnownPatterns) + throws DemangledException { + return demangle(mangled); + } + + /** + * Initializes class variables + * + * @param opt The options + * @throws DemangledException If there was an issue with initialization + */ + private void init(DemanglerOptions opt) throws DemangledException { + if (initException != null) { + throw initException; + } + + if (program != null && typeMetadata == null) { + try { + program.setPreferredRootNamespaceCategoryPath( + SwiftDataTypeUtils.SWIFT_CATEGORY.getPath()); + typeMetadata = new SwiftTypeMetadata(program, TaskMonitor.DUMMY, new MessageLog()); + } + catch (CancelledException e) { + return; + } + catch (IOException e) { + initException = new DemangledException(e); + throw initException; + } + } + + if (opt != null) { + options = getSwiftDemanglerOptions(opt); + } + + if (nativeDemangler == null) { + try { + nativeDemangler = new SwiftNativeDemangler(options.getSwiftDir()); + } + catch (IOException e) { + throw new DemangledException(e); + } + } + + if (cache == null) { + cache = new HashMap<>(); + } + } + + /** + * Demangles the given mangled string + * + * @param mangled The mangled string + * @param originalDemangled The demangled string produced by the native Swift demangler + * @param meta The {@link SwiftTypeMetadata}, or null if unavailable + * @return The {@link Demangled} object, or null if the mangled string is not a supported Swift + * symbol + * @throws DemangledException if a problem occurred + */ + public Demangled demangle(String mangled, String originalDemangled, SwiftTypeMetadata meta) + throws DemangledException { + if (!isSwiftMangledSymbol(mangled)) { + return null; + } + + try { + SwiftNode root; + if (cache.containsKey(mangled)) { + root = cache.get(mangled); + } + else { + SwiftDemangledTree tree = new SwiftDemangledTree(nativeDemangler, mangled); + root = tree.getRoot(); + } + cache.put(mangled, root); + if (root == null) { + return null; + } + Demangled demangled = root.demangle(this, meta); + if (root.walkAndTest(node -> node.childWasSkipped())) { + demangled.setName(options.getIncompletePrefix() + demangled.getName()); + } + return demangled; + } + catch (IOException e) { + throw new DemangledException(e); + } + } + + @Override + public DemangledObject demangle(String mangled, DemanglerOptions opt) + throws DemangledException { + + init(opt); + + Demangled demangled = demangle(mangled, null, typeMetadata); + if (demangled instanceof DemangledFunction func) { + return func; + } + else if (demangled instanceof DemangledLabel label) { + return label; + } + else if (demangled instanceof DemangledUnknown unknown) { + return new DemangledLabel(mangled, unknown.getOriginalDemangled(), + options.getUnsupportedPrefix() + unknown.getOriginalDemangled()); + } + return null; + } + + /** + * Clears the cache + */ + public void clearCache() { + if (cache != null) { + cache.clear(); + } + } + + /** + * Checks to see whether the given symbol name is a mangled Swift symbol + * + * @param symbolName The symbol name to check + * @return True if the given symbol name is a mangled Swift symbol; otherwise, false + */ + public boolean isSwiftMangledSymbol(String symbolName) { + List prefixes = List.of("$S", "$s", "_$S", "_$s", "_T"); + return prefixes.stream().anyMatch(prefix -> symbolName.startsWith(prefix)); + } + + /** + * Gets the {@link SwiftDemanglerOptions} from the given {@link DemanglerOptions} + * + * @param opt The options + * @return The @link SwiftDemanglerOptions} + * @throws DemangledException If the given options are not {@link SwiftDemanglerOptions} + */ + public SwiftDemanglerOptions getSwiftDemanglerOptions(DemanglerOptions opt) + throws DemangledException { + if (!(opt instanceof SwiftDemanglerOptions)) { + opt = createDefaultOptions(); + } + return (SwiftDemanglerOptions) opt; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemanglerOptions.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemanglerOptions.java new file mode 100644 index 0000000000..0f1ecff644 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftDemanglerOptions.java @@ -0,0 +1,90 @@ +/* ### + * 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.demangler.swift; + +import java.io.File; + +import ghidra.app.util.demangler.DemanglerOptions; + +/** + * Swift demangler options + */ +public class SwiftDemanglerOptions extends DemanglerOptions { + + public static final String INCOMPLETE_PREFIX = "$"; + public static final String UNSUPPORTED_PREFIX = "$$"; + + private File swiftDir; + private boolean useIncompletePrefix; + private boolean useUnsupportedPrefix; + + /** + * Gets the Swift directory + *

+ * If the Swift directory is on the PATH environment variable, this may return null + * + * @return The Swift directory + */ + public File getSwiftDir() { + return swiftDir; + } + + /** + * Sets the Swift directory + *

+ * If the Swift directory is on the PATH environment variable, it is fine to set this to + * null + * + * @param swiftDir The Swift directory + */ + public void setSwiftDir(File swiftDir) { + this.swiftDir = swiftDir; + } + + /** + * {@return the "incomplete prefix" character to use in label names} + */ + public String getIncompletePrefix() { + return useIncompletePrefix ? INCOMPLETE_PREFIX : ""; + } + + /** + * Sets whether or not to use an "incomplete prefix" character in label names + * + * @param incompletePrefix True if labels should include an "incomplete prefix" character + * in their name; otherwise, false + */ + public void setIncompletePrefix(boolean incompletePrefix) { + this.useIncompletePrefix = incompletePrefix; + } + + /** + * {@return the "unsupported prefix" character to use in label names} + */ + public String getUnsupportedPrefix() { + return useUnsupportedPrefix ? UNSUPPORTED_PREFIX : ""; + } + + /** + * Sets whether or not to use an "unsupported prefix" character in label names + * + * @param unsupportedPrefix True if labels should include an "unsupported prefix" character + * in their name; otherwise, false + */ + public void setUnsupportedPrefix(boolean unsupportedPrefix) { + this.useUnsupportedPrefix = unsupportedPrefix; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftNativeDemangler.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftNativeDemangler.java new file mode 100644 index 0000000000..f964e9a0b2 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/SwiftNativeDemangler.java @@ -0,0 +1,156 @@ +/* ### + * 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.demangler.swift; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A class used to launch the Swift native demangler. + *

+ * The Swift native demangler binary comes in 2 forms, and can thus be invoked in 2 ways: + *

    + *
  • {@code ./swift demangle args}
  • + *
  • {@code ./swift-demangle args}
  • + *
+ * + * The latter is how it is done in the Windows version of Swift. We will refer to this version + * as the "standalone demangler binary". + */ +public class SwiftNativeDemangler { + + private String nativeDemanglerPath; + private boolean standaloneDemanglerBinary; + + /** + * The output of the native Swift demangler + * + * @param demangled The demangled string + * @param tree The lines of the demangled expanded tree + */ + public record SwiftNativeDemangledOutput(String demangled, List tree) { + @Override + public String toString() { + return "%s\n%s".formatted(demangled != null ? demangled : "", + tree.stream().collect(Collectors.joining("\n"))); + } + } + + /** + * Creates a new {@link SwiftNativeDemangler} + * + * @param swiftDir The Swift directory + * @throws IOException if there was a problem finding or running the Swift native demangler + */ + public SwiftNativeDemangler(File swiftDir) throws IOException { + List demanglerNames = List.of("swift-demangle", "swift"); + IOException ioe = null; + for (String demanglerName : demanglerNames) { + nativeDemanglerPath = demanglerName; + if (swiftDir != null) { + nativeDemanglerPath = swiftDir + File.separator + nativeDemanglerPath; + } + try { + int exitCode = + new ProcessBuilder(List.of(nativeDemanglerPath, "--version")).start() + .waitFor(); + if (exitCode == 0) { + ioe = null; + standaloneDemanglerBinary = + new File(nativeDemanglerPath).getName().contains("-demangle"); + break; + } + ioe = new IOException("Native Swift demangler exited with code: " + exitCode); + } + catch (IOException e) { + ioe = e; + } + catch (InterruptedException e) { + ioe = new IOException(e); + } + } + if (ioe != null) { + throw ioe; + } + } + + /** + * Uses the Swift executable to demangle the given mangled string + * + * @param mangled The mangled string to demangle + * @return The {@link SwiftNativeDemangledOutput} + * @throws IOException If there was an IO-related issue + * @see SwiftDemangledTree + */ + public SwiftNativeDemangledOutput demangle(String mangled) throws IOException { + List demanglerArgs = new ArrayList<>(); + demanglerArgs.add("--compact"); // Compact mode (only emit the demangled names) + demanglerArgs.add("--expand"); // Expand mode (show node structure of the demangling) + try (BufferedReader reader = demangle(mangled, demanglerArgs)) { + String demangled = null; + List treeLines = new ArrayList<>(); + String line = reader.readLine().trim(); + if (!line.startsWith("Demangling for")) { + throw new IOException("Unexpected output: " + line); + } + while ((line = reader.readLine()) != null) { + if (line.startsWith("<>")) { // Not a demangleable string + break; + } + if (line.isBlank()) { + continue; + } + if (treeLines.isEmpty() && !line.trim().startsWith("kind")) { + // This case is mainly for when the mangled string has newline characters in it, + // which are printed in the first "Demangling for..." line. We want to skip + // those and get to the tree. + continue; + } + if (!treeLines.isEmpty() && !line.startsWith(" ")) { + // This case should grab the last line after the tree, which is the full + // demangled string + demangled = line; + break; + } + treeLines.add(line); + } + return new SwiftNativeDemangledOutput(demangled, treeLines); + } + } + + /** + * Runs the Swift demangler to demangled the given mangled string with the given demangle + * options + * + * @param mangled The mangled string to demangle + * @param options Additional demangle options + * @return A {@link BufferedReader} used to read the output of the executed command + * @throws IOException If there was an IO-related issue + */ + private BufferedReader demangle(String mangled, List options) throws IOException { + List command = new ArrayList<>(); + command.add(nativeDemanglerPath); + if (!standaloneDemanglerBinary) { + command.add("demangle"); + } + command.addAll(options); + command.add(mangled); + Process p = new ProcessBuilder(command).redirectErrorStream(true).start(); + return new BufferedReader(new InputStreamReader(p.getInputStream())); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftArray.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftArray.java new file mode 100644 index 0000000000..1a564fd702 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftArray.java @@ -0,0 +1,57 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.demangler.DemangledDataType; + +/** + * A Swift array + */ +public class SwiftArray extends DemangledDataType { + + private DemangledDataType boundType; + + /** + * Creates a new Swift array bound to the "undefined" type + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + */ + public SwiftArray(String mangled, String originalDemangled) { + super(mangled, originalDemangled, "Array"); + setNamespace(SwiftDataTypeUtils.SWIFT_NAMESPACE); + setBoundType( + new DemangledDataType(mangled, originalDemangled, DemangledDataType.UNDEFINED)); + setArray(1); + } + + /** + * {@return the bound type} + */ + public DemangledDataType getBoundType() { + return boundType; + } + + /** + * Sets the bound type + * + * @param type The bound type + */ + public void setBoundType(DemangledDataType type) { + boundType = type; + setName("Array<%s>".formatted(type.getName())); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftCharacter.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftCharacter.java new file mode 100644 index 0000000000..abbf5b12b7 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftCharacter.java @@ -0,0 +1,46 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.demangler.DemangledDataType; +import ghidra.app.util.demangler.DemangledStructure; + +/** + * A Swift character + */ +public class SwiftCharacter extends DemangledStructure { + + /** + * Creates a new Swift character + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + */ + public SwiftCharacter(String mangled, String originalDemangled) { + super(mangled, originalDemangled, "Character", + SwiftDataTypeUtils.getCategoryPath(SwiftDataTypeUtils.SWIFT_NAMESPACE).getPath(), true); + setNamespace(SwiftDataTypeUtils.SWIFT_NAMESPACE); + + DemangledDataType stringDt = new DemangledDataType(mangled, null, DemangledDataType.CHAR); + stringDt.incrementPointerLevels(); + + DemangledDataType voidDt = new DemangledDataType(mangled, null, DemangledDataType.VOID); + voidDt.incrementPointerLevels(); + + addField("str", stringDt); + addField("bridgeObject", voidDt); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftDataTypeUtils.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftDataTypeUtils.java new file mode 100644 index 0000000000..a4803ae732 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftDataTypeUtils.java @@ -0,0 +1,91 @@ +/* ### + * 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.demangler.swift.datatypes; + +import java.util.*; + +import ghidra.app.util.demangler.*; +import ghidra.program.model.data.CategoryPath; + +/** + * Swift data type utilities + */ +public class SwiftDataTypeUtils { + + /** + * Default path to store Swift data structures in the data type manager + */ + public static final CategoryPath SWIFT_CATEGORY = new CategoryPath("/Demangler"); + + /** + * A {@link Demangled} to represent the standard Swift namespace + */ + public static final Demangled SWIFT_NAMESPACE = new DemangledUnknown("", null, "Swift"); + + /** + * Checks to see if the given namespace is the standard Swift namespace + * + * @param namespace The namespace to check + * @return True if the given namespace is the standard Swift namespace; otherwise, false + */ + public static boolean isSwiftNamespace(Demangled namespace) { + return namespace != null && namespace.getName().equals(SWIFT_NAMESPACE.getName()); + } + + /** + * Gets a {@link CategoryPath} based on the given namespace + * + * @param namespace The namespace + * @return A {@link CategoryPath} based on the given namespace + */ + public static CategoryPath getCategoryPath(Demangled namespace) { + if (namespace == null) { + return SWIFT_CATEGORY; + } + LinkedList path = new LinkedList<>(); + while (namespace != null) { + path.addFirst(namespace.getNamespaceName()); + namespace = namespace.getNamespace(); + } + return new CategoryPath(SWIFT_CATEGORY, path); + } + + /** + * Creates a {@link List} of {@link DemangledParameter parameters} found within the given + * {@link Demangled} object + * + * @param demangled A {@link Demangled} object + * @return A {@link List} of {@link DemangledParameter parameters} found within the given + * {@link Demangled} object + */ + public static List extractParameters(Demangled demangled) { + List params = new ArrayList<>(); + if (demangled instanceof DemangledVariable variable) { + demangled = variable.getDataType(); + } + if (demangled instanceof DemangledList list) { + for (Demangled d : list) { + if (d instanceof DemangledDataType type) { + params.add(new DemangledParameter(type)); + } + } + } + else if (demangled instanceof DemangledDataType type) { + params.add(new DemangledParameter(type)); + } + return params; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftEnum.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftEnum.java new file mode 100644 index 0000000000..4ee1e9c84a --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftEnum.java @@ -0,0 +1,52 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A Swift structure + */ +public class SwiftEnum extends DemangledStructure { + + /** + * Creates a new Swift enum + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The enum name + * @param namespace The enum namespace (could be null) + * @param typeMetadata The {@link SwiftTypeMetadata}, or null if it is not known + * @param demangler A {@link SwiftDemangler} + * @throws DemangledException if a problem occurred + */ + public SwiftEnum(String mangled, String originalDemangled, String name, + Demangled namespace, SwiftTypeMetadata typeMetadata, SwiftDemangler demangler) + throws DemangledException { + super(mangled, originalDemangled, name, + SwiftDataTypeUtils.getCategoryPath(namespace).getPath(), true); + setNamespace(namespace); + + // The mangled output doesn't seem to indicate what field of the enum is being used, so + // it's not currently clear how to query the type metadata for real type information. + // Raw enums seem to just be bytes, so for now we'll just use a struct with 1 byte entry. + DemangledDataType dt = + new DemangledDataType(mangled, originalDemangled, DemangledDataType.INT8); + addField("value", null, dt); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftFunction.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftFunction.java new file mode 100644 index 0000000000..356ffd879a --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftFunction.java @@ -0,0 +1,55 @@ +/* ### + * 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.demangler.swift.datatypes; + +import java.util.List; + +import ghidra.app.util.demangler.*; + +/** + * A Swift function + */ +public class SwiftFunction extends DemangledFunction { + + /** + * Creates a new Swift function + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The function name + * @param namespace The function namespace (could be null) + * @param callingConvention The function calling convention (could be null) + */ + public SwiftFunction(String mangled, String originalDemangled, String name, Demangled namespace, + String callingConvention) { + super(mangled, originalDemangled, name); + setNamespace(namespace); + setCallingConvention(callingConvention); + } + + public void setType(DemangledFunction type, Demangled labelList) { + setReturnType(type.getReturnType()); + List params = type.getParameters(); + for (int i = 0; i < params.size(); i++) { + DemangledParameter param = params.get(i); + if (labelList instanceof DemangledList list && i < list.size() && + list.get(i) instanceof DemangledLabel label) { + param.setLabel(label.getName()); + } + addParameter(param); + } + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftPrimitive.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftPrimitive.java new file mode 100644 index 0000000000..39a9eb9392 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftPrimitive.java @@ -0,0 +1,48 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.demangler.DemangledDataType; + +/** + * A Swift primitive + */ +public class SwiftPrimitive extends DemangledDataType { + + /** + * Creates a new Swift primitive + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The primitive name + */ + public SwiftPrimitive(String mangled, String originalDemangled, String name) { + super(mangled, originalDemangled, name); + } + + /** + * Creates a new Swift primitive + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The primitive name + * @param unsigned True if the primitive should be unsigned; otherwise, false + */ + public SwiftPrimitive(String mangled, String originalDemangled, String name, boolean unsigned) { + this(mangled, originalDemangled, name); + setUnsigned(); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftString.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftString.java new file mode 100644 index 0000000000..f644d52a63 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftString.java @@ -0,0 +1,46 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.demangler.DemangledDataType; +import ghidra.app.util.demangler.DemangledStructure; + +/** + * A Swift string + */ +public class SwiftString extends DemangledStructure { + + /** + * Creates a new Swift string + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + */ + public SwiftString(String mangled, String originalDemangled) { + super(mangled, originalDemangled, "String", + SwiftDataTypeUtils.getCategoryPath(SwiftDataTypeUtils.SWIFT_NAMESPACE).getPath(), true); + setNamespace(SwiftDataTypeUtils.SWIFT_NAMESPACE); + + DemangledDataType stringDt = new DemangledDataType(mangled, null, DemangledDataType.CHAR); + stringDt.incrementPointerLevels(); + + DemangledDataType voidDt = new DemangledDataType(mangled, null, DemangledDataType.VOID); + voidDt.incrementPointerLevels(); + + addField("str", stringDt); + addField("bridgeObject", voidDt); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftStructure.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftStructure.java new file mode 100644 index 0000000000..be52e31526 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftStructure.java @@ -0,0 +1,66 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.bin.format.swift.types.*; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A Swift structure + */ +public class SwiftStructure extends DemangledStructure { + + /** + * Creates a new Swift structure + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param name The structure name + * @param namespace The structure namespace (could be null) + * @param typeMetadata The {@link SwiftTypeMetadata}, or null if it is not known + * @param demangler A {@link SwiftDemangler} + * @throws DemangledException if a problem occurred + */ + public SwiftStructure(String mangled, String originalDemangled, String name, + Demangled namespace, SwiftTypeMetadata typeMetadata, SwiftDemangler demangler) + throws DemangledException { + super(mangled, originalDemangled, name, + SwiftDataTypeUtils.getCategoryPath(namespace).getPath(), true); + setNamespace(namespace); + + // Try to add structure fields from the type metadata + if (typeMetadata != null) { + TargetTypeContextDescriptor typeDescriptor = + typeMetadata.getTargetTypeContextDescriptors().get(name); + if (typeDescriptor != null) { + FieldDescriptor fieldDescriptor = + typeDescriptor.getFieldDescriptor(typeMetadata.getFieldDescriptors()); + if (fieldDescriptor != null) { + for (FieldRecord fieldRecord : fieldDescriptor.getFieldRecords()) { + String mangledType = "_T" + fieldRecord.getMangledTypeName(); + Demangled demangled = + demangler.demangle(mangledType, originalDemangled, typeMetadata); + if (demangled instanceof DemangledDataType ddt) { + addField(fieldRecord.getFieldName(), fieldRecord.getDescription(), ddt); + } + } + } + } + } + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftTuple.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftTuple.java new file mode 100644 index 0000000000..7691f6b1d0 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/datatypes/SwiftTuple.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.datatypes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A Swift tuple + */ +public class SwiftTuple extends SwiftStructure { + + /** + * Creates a new Swift tuple + * + * @param mangled The mangled string + * @param originalDemangled The natively demangled string + * @param list The elements of the tuple + * @param typeMetadata The {@link SwiftTypeMetadata}, or null if it is not known + * @param demangler A {@link SwiftDemangler} + * @throws DemangledException if a problem occurred + */ + public SwiftTuple(String mangled, String originalDemangled, DemangledList list, + SwiftTypeMetadata typeMetadata, SwiftDemangler demangler) throws DemangledException { + super(mangled, originalDemangled, "tuple%d".formatted(list.size()), null, typeMetadata, + demangler); + + int i = 0; + for (Demangled element : list) { + if (element instanceof DemangledDataType ddt) { + addField(Integer.toString(i), ddt); + } + else if (element instanceof DemangledVariable variable) { + addField(variable.getName(), variable.getDataType()); + } + i++; + } + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftAllocatorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftAllocatorNode.java new file mode 100644 index 0000000000..805de155a0 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftAllocatorNode.java @@ -0,0 +1,87 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Allocator} {@link SwiftNode} + */ +public class SwiftAllocatorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled type = null; + Demangled labelList = null; + String callingConvention = CompilerSpec.CALLING_CONVENTION_default; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Class: + namespace = child.demangle(demangler, typeMetadata); + name = "__allocating_init"; + callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + break; + case Extension: + namespace = child.demangle(demangler, typeMetadata); + if (child.hasChild(SwiftDemangledNodeKind.Class)) { + name = "__allocating_init"; + callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + } + else if (child.hasChild(SwiftDemangledNodeKind.Protocol) || + child.hasChild(SwiftDemangledNodeKind.Structure)) { + name = "init"; + } + else { + return getUnknown(); + } + break; + case Enum: + case Protocol: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + name = "init"; + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + case LabelList: + labelList = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = new SwiftFunction(properties.mangled(), + properties.originalDemangled(), name, namespace, callingConvention); + if (type instanceof SwiftFunction functionType) { + function.setType(functionType, labelList); + } + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBoundGenericStructureNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBoundGenericStructureNode.java new file mode 100644 index 0000000000..caf1502c76 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBoundGenericStructureNode.java @@ -0,0 +1,71 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.stream.Collectors; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftArray; + +/** + * A {@link SwiftDemangledNodeKind#BoundGenericStructure} {@link SwiftNode} + */ +public class SwiftBoundGenericStructureNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type = null; + Demangled typeList = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + type = child.demangle(demangler, typeMetadata); + break; + case TypeList: + typeList = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + + if (type instanceof SwiftArray arr) { + if (typeList instanceof DemangledList list && !list.isEmpty()) { + Demangled first = list.get(0); + if (first instanceof DemangledDataType ddt) { + arr.setBoundType(ddt); + } + } + return arr; + } + + if (typeList instanceof DemangledList list) { + String typeNames = list + .stream() + .map(e -> e.getName()) + .collect(Collectors.joining(",")); + type.setName("%s<%s>".formatted(type.getName(), typeNames)); + return type; + } + + return getUnknown(); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBuiltinTypeNameNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBuiltinTypeNameNode.java new file mode 100644 index 0000000000..627ef53ca2 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftBuiltinTypeNameNode.java @@ -0,0 +1,49 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledDataType; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#BuiltinTypeName} {@link SwiftNode} + */ +public class SwiftBuiltinTypeNameNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) { + String orig = getText(); + String name = switch (orig) { + case "Builtin.Int1": + yield DemangledDataType.INT8; + case "Builtin.Word": + yield DemangledDataType.INT16; + case "Builtin.RawPointer": + yield DemangledDataType.VOID; + default: + yield orig; + }; + DemangledDataType type = + new DemangledDataType(properties.mangled(), properties.originalDemangled(), name); + if (orig.equals("Builtin.RawPointer")) { + type.incrementPointerLevels(); + } + return type; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftClassNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftClassNode.java new file mode 100644 index 0000000000..d26bf82fa8 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftClassNode.java @@ -0,0 +1,64 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Class} {@link SwiftNode} + */ +public class SwiftClassNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled privateDeclNamespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case PrivateDeclName: + Demangled temp = child.demangle(demangler, typeMetadata); + name = temp.getName(); + privateDeclNamespace = temp.getNamespace(); + break; + case Class: + case Module: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + DemangledDataType type = + new DemangledDataType(properties.mangled(), properties.originalDemangled(), name); + type.setNamespace(SwiftNode.join(namespace, privateDeclNamespace)); + type.setClass(); + type.incrementPointerLevels(); + return type; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftConstructorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftConstructorNode.java new file mode 100644 index 0000000000..6cd2be92fa --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftConstructorNode.java @@ -0,0 +1,65 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Constructor} {@link SwiftNode} + */ +public class SwiftConstructorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled type = null; + Demangled labelList = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Class: + namespace = child.demangle(demangler, typeMetadata); + name = "init"; + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + case LabelList: + labelList = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, namespace, + CompilerSpec.CALLING_CONVENTION_thiscall); + if (type instanceof DemangledFunction functionType) { + function.setType(functionType, labelList); + } + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDeallocatorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDeallocatorNode.java new file mode 100644 index 0000000000..cd6df889a5 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDeallocatorNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Deallocator} {@link SwiftNode} + */ +public class SwiftDeallocatorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Class: + namespace = child.demangle(demangler, typeMetadata); + name = "__deallocating_init"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + return new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, + namespace, CompilerSpec.CALLING_CONVENTION_thiscall); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericParamTypeNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericParamTypeNode.java new file mode 100644 index 0000000000..f23f82decf --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericParamTypeNode.java @@ -0,0 +1,35 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledDataType; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#DependentGenericParamType} {@link SwiftNode} + */ +public class SwiftDependentGenericParamTypeNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) { + // We don't really support this yet + return new DemangledDataType(properties.mangled(), properties.originalDemangled(), + DemangledDataType.UNDEFINED); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericTypeNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericTypeNode.java new file mode 100644 index 0000000000..565db7604a --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDependentGenericTypeNode.java @@ -0,0 +1,45 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#DependentGenericType} {@link SwiftNode} + */ +public class SwiftDependentGenericTypeNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + return type; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDestructorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDestructorNode.java new file mode 100644 index 0000000000..91d1bdfd62 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftDestructorNode.java @@ -0,0 +1,57 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Destructor} {@link SwiftNode} + */ +public class SwiftDestructorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Class: + namespace = child.demangle(demangler, typeMetadata); + name = "deinit"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, namespace, + CompilerSpec.CALLING_CONVENTION_thiscall); + if (namespace instanceof DemangledDataType type) { + function.setReturnType(type); + } + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftEnumNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftEnumNode.java new file mode 100644 index 0000000000..82aba586fe --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftEnumNode.java @@ -0,0 +1,63 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftEnum; + +/** + * A {@link SwiftDemangledNodeKind#Enum} {@link SwiftNode} + */ +public class SwiftEnumNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled privateDeclNamespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case PrivateDeclName: + Demangled temp = child.demangle(demangler, typeMetadata); + name = temp.getName(); + privateDeclNamespace = temp.getNamespace(); + break; + case Class: + case Extension: + case Module: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + return new SwiftEnum(properties.mangled(), properties.originalDemangled(), name, + SwiftNode.join(namespace, privateDeclNamespace), typeMetadata, demangler); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftExtensionNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftExtensionNode.java new file mode 100644 index 0000000000..4a83073af5 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftExtensionNode.java @@ -0,0 +1,63 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Extension} {@link SwiftNode} + */ +public class SwiftExtensionNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled namespace = null; + Demangled type = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Module: + namespace = child.demangle(demangler, typeMetadata); + namespace.setName("(extension_" + namespace.getName() + ")"); + break; + case Class: + case Enum: + case Protocol: + case Structure: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (type == null || namespace == null) { + return getUnknown(); + } + Demangled typeNamespace = type.getNamespace(); + if (typeNamespace != null) { + typeNamespace.setNamespace(namespace); + } + else { + type.setNamespace(namespace); + } + return type; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionNode.java new file mode 100644 index 0000000000..9c3e3decba --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionNode.java @@ -0,0 +1,81 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Function} {@link SwiftNode} + */ +public class SwiftFunctionNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled type = null; + Demangled labelList = null; + String callingConvention = CompilerSpec.CALLING_CONVENTION_default; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case InfixOperator: + name = child.getText() + " infix"; + break; + case LocalDeclName: + name = child.demangle(demangler, typeMetadata).getName(); + break; + case Class: + callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + // Fall through + case Enum: + case Extension: + case Function: + case Module: + case Protocol: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + case LabelList: + labelList = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = new SwiftFunction(properties.mangled(), + properties.originalDemangled(), name, namespace, callingConvention); + if (type instanceof DemangledFunction functionType) { + function.setType(functionType, labelList); + } + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionTypeNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionTypeNode.java new file mode 100644 index 0000000000..1861cf2800 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftFunctionTypeNode.java @@ -0,0 +1,104 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.*; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#FunctionType} {@link SwiftNode} + */ +public class SwiftFunctionTypeNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled argumentTuple = null; + Demangled returnType = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case ArgumentTuple: + argumentTuple = child.demangle(demangler, typeMetadata); + break; + case ReturnType: + returnType = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), "", + null, CompilerSpec.CALLING_CONVENTION_default); + + // Parameters + function.addParameters(SwiftDataTypeUtils.extractParameters(argumentTuple)); + + // Seems like when calling a struct (or enum) method, the "this" struct is passed after the + // explicit parameters + SwiftNode functionAncestor = + getFirstAncestor(SwiftDemangledNodeKind.Function, SwiftDemangledNodeKind.Getter); + if (functionAncestor != null) { + if (functionAncestor.getKind().equals(SwiftDemangledNodeKind.Getter)) { + functionAncestor = functionAncestor.getChildren().get(0); + } + SwiftNode struct = functionAncestor.getChild(SwiftDemangledNodeKind.Structure); + SwiftNode enumm = functionAncestor.getChild(SwiftDemangledNodeKind.Enum); + if (struct != null) { + if (struct.demangle(demangler, typeMetadata) instanceof DemangledDataType type) { + function.addParameter(new DemangledParameter(type)); + } + } + else if (enumm != null) { + if (enumm.demangle(demangler, typeMetadata) instanceof DemangledDataType type) { + function.addParameter(new DemangledParameter(type)); + // Enums are currently represented as single field structures, but in reality, + // there could be more fields. Add a varargs parameter so these other fields + // can show up in the decompiler. + DemangledDataType varargs = new DemangledDataType(properties.mangled(), + properties.originalDemangled(), DemangledDataType.UNDEFINED); + varargs.setVarArgs(); + function.addParameter(new DemangledParameter(varargs)); + } + } + } + + // Return type + if (returnType instanceof DemangledDataType type) { + function.setReturnType(type); + } + else if (returnType instanceof DemangledList list && list.size() > 0) { + if (list.containsNull()) { + DemangledDataType dt = new DemangledDataType(properties.mangled(), + properties.originalDemangled(), DemangledDataType.UNDEFINED); + dt.incrementPointerLevels(); + function.setReturnType(dt); + } + else { + SwiftTuple tuple = new SwiftTuple(properties.mangled(), + properties.originalDemangled(), list, typeMetadata, demangler); + function.setReturnType(tuple); + } + } + + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGetterNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGetterNode.java new file mode 100644 index 0000000000..a69ff4f0cb --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGetterNode.java @@ -0,0 +1,68 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftDataTypeUtils; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Getter} {@link SwiftNode} + */ +public class SwiftGetterNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = null; + String name = "get_"; + String callingConvention = CompilerSpec.CALLING_CONVENTION_default; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Subscript: + demangled = child.demangle(demangler, typeMetadata); + break; + case Variable: + demangled = child.demangle(demangler, typeMetadata); + if (child.hasChild(SwiftDemangledNodeKind.Class)) { + callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + } + break; + default: + skip(child); + break; + } + } + if (demangled instanceof DemangledFunction function) { + function.setName(name + function.getName()); + function.setCallingConvention(callingConvention); + return function; + } + if (demangled instanceof DemangledVariable variable) { + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), + name + variable.getName(), variable.getNamespace(), callingConvention); + function.addParameters(SwiftDataTypeUtils.extractParameters(variable.getNamespace())); + function.setReturnType(variable.getDataType()); + return function; + } + return getUnknown(); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalNode.java new file mode 100644 index 0000000000..93f42201aa --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalNode.java @@ -0,0 +1,60 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftPrimitive; +import ghidra.program.model.data.DataUtilities; + +/** + * A {@link SwiftDemangledNodeKind#Global} {@link SwiftNode} + */ +public class SwiftGlobalNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = null; + Demangled suffix = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case GenericSpecialization: + case MergedFunction: + case ObjCAttribute: + continue; + case Suffix: + suffix = child.demangle(demangler, typeMetadata); + break; + default: + demangled = child.demangle(demangler, typeMetadata); + break; + } + } + if (demangled == null) { + return getUnknown(); + } + if (suffix != null && !(demangled instanceof SwiftPrimitive) && + DataUtilities.isValidDataTypeName(suffix.getName())) { + // Some suffix names aren't renderable, so validate them + demangled.setName(demangled.getName() + suffix.getName()); + } + return demangled; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceDeclListNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceDeclListNode.java new file mode 100644 index 0000000000..ac9dd2083c --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceDeclListNode.java @@ -0,0 +1,48 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#GlobalVariableOnceDeclList} {@link SwiftNode} + */ +public class SwiftGlobalVariableOnceDeclListNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + List elements = new ArrayList<>(); + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + elements.add(child.demangle(demangler, typeMetadata)); + break; + default: + skip(child); + break; + } + } + + return new DemangledList(elements); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceFunctionNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceFunctionNode.java new file mode 100644 index 0000000000..ba139c5d55 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftGlobalVariableOnceFunctionNode.java @@ -0,0 +1,63 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#GlobalVariableOnceFunction} {@link SwiftNode} + */ +public class SwiftGlobalVariableOnceFunctionNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled namespace = null; + DemangledList names = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case GlobalVariableOnceDeclList: + Demangled demangled = child.demangle(demangler, typeMetadata); + if (demangled instanceof DemangledList list) { + names = list; + } + break; + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + StringBuilder name = new StringBuilder("one_time_init"); + if (names != null) { + if (!names.isEmpty()) { + name.append("_for"); + } + for (Demangled entry : names) { + name.append("_" + entry.getDemangledName()); + } + } + return new SwiftFunction(properties.mangled(), properties.originalDemangled(), + name.toString(), namespace, CompilerSpec.CALLING_CONVENTION_default); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInOutNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInOutNode.java new file mode 100644 index 0000000000..244a585b0d --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInOutNode.java @@ -0,0 +1,37 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#InOut} {@link SwiftNode} + */ +public class SwiftInOutNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = demangleFirstChild(demangler, typeMetadata); + if (demangled instanceof DemangledDataType type) { + type.incrementPointerLevels(); + } + return demangled; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInitializerNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInitializerNode.java new file mode 100644 index 0000000000..c2a9c6c9a6 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftInitializerNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Initializer} {@link SwiftNode} + */ +public class SwiftInitializerNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Variable: + namespace = child.demangle(demangler, typeMetadata); + name = "init"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + return new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, + namespace, CompilerSpec.CALLING_CONVENTION_default); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLabelListNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLabelListNode.java new file mode 100644 index 0000000000..f7a9194c93 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLabelListNode.java @@ -0,0 +1,51 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#LabelList} {@link SwiftNode} + */ +public class SwiftLabelListNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + List elements = new ArrayList<>(); + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + elements.add(child.demangle(demangler, typeMetadata)); + break; + case FirstElementMarker: + elements.add(new DemangledUnknown(properties.mangled(), + properties.originalDemangled(), "")); + break; + default: + skip(child); + break; + } + } + return new DemangledList(elements); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLazyProtocolWitnessTableAccessorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLazyProtocolWitnessTableAccessorNode.java new file mode 100644 index 0000000000..9e6d2084d4 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLazyProtocolWitnessTableAccessorNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#LazyProtocolWitnessTableAccessor} {@link SwiftNode} + */ +public class SwiftLazyProtocolWitnessTableAccessorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + namespace = child.demangle(demangler, typeMetadata); + name = "lazy_protocol_witness_table_accessor"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + return new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, + namespace, CompilerSpec.CALLING_CONVENTION_default); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLocalDeclNameNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLocalDeclNameNode.java new file mode 100644 index 0000000000..7f641f2544 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftLocalDeclNameNode.java @@ -0,0 +1,59 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#LocalDeclName} {@link SwiftNode} + */ +public class SwiftLocalDeclNameNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Long number = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case Number: + try { + number = Long.decode(child.getIndex()); + } + catch (NumberFormatException e) { + throw new DemangledException(e); + } + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + if (number != null) { + name += "#" + (number + 1); + } + return new DemangledLabel(properties.mangled(), properties.originalDemangled(), name); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftModifyAccessorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftModifyAccessorNode.java new file mode 100644 index 0000000000..5afc4317d9 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftModifyAccessorNode.java @@ -0,0 +1,64 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftDataTypeUtils; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#ModifyAccessor} {@link SwiftNode} + */ +public class SwiftModifyAccessorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = null; + String name = "modify_"; + String callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Subscript: + demangled = child.demangle(demangler, typeMetadata); + break; + case Variable: + demangled = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (demangled instanceof DemangledFunction function) { + function.setName(name + function.getName()); + function.setCallingConvention(callingConvention); + return function; + } + if (demangled instanceof DemangledVariable variable) { + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), + name + variable.getName(), variable.getNamespace(), callingConvention); + function.addParameters(SwiftDataTypeUtils.extractParameters(variable)); + return function; + } + return getUnknown(); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftNode.java new file mode 100644 index 0000000000..993e111eee --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftNode.java @@ -0,0 +1,378 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.nodes.generic.*; + +/** + * A single Swift demangled symbol tree node + */ +public abstract class SwiftNode { + + protected NodeProperties properties; + private SwiftNode parent; + private List children = new ArrayList<>(); + private boolean childSkipped = false; + + /** + * Represents {@link SwiftNode} properties + * + * @param kind The {@link SwiftDemangledNodeKind kind} of {@link SwiftNode} + * @param text The text attribute, or null if it does not exist + * @param index The index attribute, or null if it does not exist + * @param depth The depth of the {@link SwiftNode} in the demangled symbol tree (root depth is + * 0) + * @param mangled The mangled string associated with this {@link SwiftNode} + * @param originalDemangled The natively demangled string + */ + public record NodeProperties(SwiftDemangledNodeKind kind, String text, String index, + int depth, String mangled, String originalDemangled) {} + + /** + * Gets a new {@link SwiftNode} with the given with the given {@link NodeProperties} + * + * @param props The {@link NodeProperties} + * @return A {@link SwiftNode} with the given {@link NodeProperties} + */ + public static SwiftNode get(NodeProperties props) { + SwiftNode node = switch (props.kind()) { + case Allocator -> new SwiftAllocatorNode(); + case AnonymousDescriptor -> new SwiftGenericDescriptorNode(); + case ArgumentTuple -> new SwiftGenericPassthroughNode(); + case BoundGenericStructure -> new SwiftBoundGenericStructureNode(); + case BuiltinTypeName -> new SwiftBuiltinTypeNameNode(); + case Class -> new SwiftClassNode(); + case Constructor -> new SwiftConstructorNode(); + case Deallocator -> new SwiftDeallocatorNode(); + case DefaultArgumentInitializer -> new SwiftGenericPassthroughNode(); + case DependentGenericParamType -> new SwiftDependentGenericParamTypeNode(); + case DependentGenericType -> new SwiftDependentGenericTypeNode(); + case Destructor -> new SwiftDestructorNode(); + case DispatchThunk -> new SwiftGenericPassthroughNode(); + case Enum -> new SwiftEnumNode(); + case Extension -> new ghidra.app.util.demangler.swift.nodes.SwiftExtensionNode(); + case Function -> new SwiftFunctionNode(); + case FunctionType -> new SwiftFunctionTypeNode(); + case GenericSpecialization -> new SwiftGenericTextNode(); + case Getter -> new SwiftGetterNode(); + case Global -> new SwiftGlobalNode(); + case GlobalVariableOnceDeclList -> new SwiftGlobalVariableOnceDeclListNode(); + case GlobalVariableOnceFunction -> new SwiftGlobalVariableOnceFunctionNode(); + case Identifier -> new SwiftGenericTextNode(); + case InfixOperator -> new SwiftGenericTextNode(); + case Initializer -> new SwiftInitializerNode(); + case InOut -> new SwiftInOutNode(); + case LabelList -> new SwiftLabelListNode(); + case LazyProtocolWitnessTableAccessor -> new SwiftLazyProtocolWitnessTableAccessorNode(); + case LocalDeclName -> new SwiftLocalDeclNameNode(); + case MergedFunction -> new SwiftGenericPassthroughNode(); + case ModifyAccessor -> new SwiftModifyAccessorNode(); + case Module -> new SwiftGenericTextNode(); + case ModuleDescriptor -> new SwiftGenericDescriptorNode(); + case NominalTypeDescriptor -> new SwiftGenericDescriptorNode(); + case Number -> new SwiftGenericIndexNode(); + case ObjCAttribute -> new SwiftGenericTextNode(); + case OutlinedConsume -> new SwiftOutlinedConsumeNode(); + case OutlinedCopy -> new SwiftOutlinedCopyNode(); + case Owned -> new SwiftGenericPassthroughNode(); + case PrivateDeclName -> new SwiftPrivateDeclNameNode(); + case Protocol -> new SwiftProtocolNode(); + case ProtocolConformance -> new SwiftProtocolConformanceNode(); + case ProtocolConformanceDescriptor -> new SwiftGenericDescriptorNode(); + case ProtocolDescriptor -> new SwiftGenericDescriptorNode(); + case ProtocolWitness -> new SwiftGenericPassthroughNode(); + case ReflectionMetadataBuiltinDescriptor -> new SwiftGenericDescriptorNode(); + case ReflectionMetadataFieldDescriptor -> new SwiftGenericDescriptorNode(); + case ReturnType -> new SwiftGenericPassthroughNode(); + case Setter -> new SwiftSetterNode(); + case Static -> new SwiftGenericPassthroughNode(); + case Structure -> new SwiftStructureNode(); + case Subscript -> new SwiftSubscriptNode(); + case Suffix -> new SwiftGenericTextNode(); + case Tuple -> new SwiftTupleNode(); + case TupleElement -> new SwiftTupleElementNode(); + case TupleElementName -> new SwiftGenericTextNode(); + case Type -> new SwiftTypeNode(); + case TypeAlias -> new SwiftTypeAliasNode(); + case TypeList -> new SwiftTypeListNode(); + case TypeMetadataAccessFunction -> new SwiftTypeMetadataAccessFunctionNode(); + case UnsafeMutableAddressor -> new SwiftUnsafeMutableAddressorNode(); + case Variable -> new SwiftVariableNode(); + default -> new SwiftUnsupportedNode(props.kind().toString(), props); + }; + node.properties = props; + return node; + } + + /** + * Demangles this {@link SwiftNode} + * + * @param demangler The {@link SwiftDemangler} + * @param typeMetadata The {@link SwiftTypeMetadata}, or null if it is not known + * @return The demangled {@link SwiftNode} + * @throws DemangledException if a problem occurred + */ + public abstract Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException; + + /** + * Gets the {@link SwiftDemangledNodeKind kind} of {@link SwiftNode} + * + * @return The {@link SwiftDemangledNodeKind kind} of {@link SwiftNode} + */ + public SwiftDemangledNodeKind getKind() { + return properties.kind(); + } + + /** + * Gets the "text" property + * + * @return The "text" attribute, or null if it does not exist + */ + public String getText() { + return properties.text(); + } + + /** + * Gets the "index" property + * + * @return The "index" attribute, or null if it does not exist + */ + public String getIndex() { + return properties.index(); + } + + /** + * Gets the depth of the {@link SwiftNode} in the demangled symbol tree (root depth is 0) + * + * @return The depth of the {@link SwiftNode} in the demangled symbol tree (root depth is 0) + */ + public int getDepth() { + return properties.depth(); + } + + /** + * Gets the parent {@link SwiftNode} + * + * @return The parent {@link SwiftNode}, or null if this is the root {@link SwiftNode} + */ + public SwiftNode getParent() { + return parent; + } + + /** + * Sets the parent {@link SwiftNode} + * + * @param parent The parent {@link SwiftNode} + */ + public void setParent(SwiftNode parent) { + this.parent = parent; + } + + /** + * Gets the {@link List} of child {@link SwiftNode}s + * + * @return The {@link List} of child {@link SwiftNode} (original, not a copy) + */ + public List getChildren() { + return children; + } + + /** + * Walks down the tree rooted at this {@link SwiftNode}, returning true if the given condition + * is met on any {@link SwiftNode} encountered + * + * @param predicate The condition to test for + * @return True if the given condition is true on any {@link SwiftNode} encountered; otherwise, + * false + */ + public boolean walkAndTest(Predicate predicate) { + if (predicate.test(this)) { + return true; + } + for (SwiftNode child : children) { + if (child.walkAndTest(predicate)) { + return true; + } + } + return false; + } + + /** + * Checks to see if the {@link SwiftNode} has any direct children of the given + * {@link SwiftDemangledNodeKind kind} + * + * @param childKind The {@link SwiftDemangledNodeKind kind} of child to check for + * @return True if the {@link SwiftNode} has any direct children of the given + * {@link SwiftDemangledNodeKind kind}; otherwise false + */ + public boolean hasChild(SwiftDemangledNodeKind childKind) { + return children.stream().anyMatch(child -> child.getKind().equals(childKind)); + } + + /** + * Gets the first direct child {@link SwiftNode} of the given + * {@link SwiftDemangledNodeKind kind} + * + * @param childKind The {@link SwiftDemangledNodeKind kind} of child to get + * @return The first direct child {@link SwiftNode} of the given + * {@link SwiftDemangledNodeKind kind}, or null if one could not be found + */ + public SwiftNode getChild(SwiftDemangledNodeKind childKind) { + return children.stream() + .filter(child -> child.getKind().equals(childKind)) + .findFirst() + .orElse(null); + } + + /** + * Gets the first ancestor {@link SwiftNode} of the given kind(s) + * + * @param ancestorKinds The ancestor kinds + * @return The first ancestor {@link SwiftNode} of the given kind + */ + public SwiftNode getFirstAncestor(SwiftDemangledNodeKind... ancestorKinds) { + if (ancestorKinds != null && ancestorKinds.length > 0) { + for (SwiftNode p = parent; p != null; p = p.getParent()) { + for (SwiftDemangledNodeKind ancestorKind : ancestorKinds) { + if (p.getKind().equals(ancestorKind)) { + return p; + } + } + } + } + return null; + } + + /** + * Called when this {@link SwiftNode} skipped processing a child during the demangling process. + * Used to identify and/or debug missing implementations. + * + * @param child The skipped child {@link SwiftNode} + */ + public void skip(SwiftNode child) { + childSkipped = true; + } + + /** + * Returns whether or not this {@link SwiftNode} skipped processing any children during the + * demangling process + * + * @return True if a child {@link SwiftNode} was skipped; otherwise, false + */ + public boolean childWasSkipped() { + return childSkipped; + } + + /** + * Gets a new {@link DemangledUnknown} created from this {@link SwiftNode} + * + * @return A new {@link DemangledUnknown} created from this {@link SwiftNode} + */ + public DemangledUnknown getUnknown() { + return new DemangledUnknown(properties.mangled(), properties.originalDemangled(), + properties.originalDemangled()); + } + + /** + * Joins the first {@link Demangled} with the second. The name of the first will become the + * top-level namespace of the second. + * + * @param a The first {@link Demangled} to join + * @param b The second {@link Demangled} to join + * @return The joined {@link Demangled}s + */ + public static Demangled join(Demangled a, Demangled b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + Demangled topNamespace = b; + while (topNamespace.getNamespace() != null) { + topNamespace = topNamespace.getNamespace(); + } + topNamespace.setNamespace(a); + return b; + } + + /** + * Converts the given {@link SwiftNode} to a string + * + * @param node The {@link SwiftNode} to convert to a string + * @param recurse True if the {@link SwiftNode} should be recursed; otherwise, false + * @return The given {@link SwiftNode} in string form + */ + public static String toString(SwiftNode node, boolean recurse) { + StringBuilder sb = new StringBuilder(node.toString()); + if (recurse) { + sb.append("\n"); + for (SwiftNode child : node.getChildren()) { + sb.append(toString(child, true)); + } + } + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(" ".repeat(properties.depth() * 2)); + sb.append("kind=" + properties.kind()); + if (properties.text() != null) { + sb.append(", text=\"" + properties.text() + "\""); + } + if (properties.index() != null) { + sb.append(", index=" + properties.index() + ""); + } + return sb.toString(); + } + + /** + * Demangles the first child {@link SwiftNode}, if it exists + * + * @param demangler The {@link SwiftDemangler} + * @param typeMetadata The {@link SwiftTypeMetadata}, or null if it is not known + * @return The demangled first child {@link SwiftNode} + * @throws DemangledException if there are no children or another problem occurred + */ + protected Demangled demangleFirstChild(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled first = null; + for (int i = 0; i < children.size(); i++) { + SwiftNode child = children.get(i); + if (i == 0) { + first = child.demangle(demangler, typeMetadata); + } + else { + child.skip(child); + } + } + if (first == null) { + throw new DemangledException("No children"); + } + return first; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedConsumeNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedConsumeNode.java new file mode 100644 index 0000000000..31e59d3795 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedConsumeNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#OutlinedConsume} {@link SwiftNode} + */ +public class SwiftOutlinedConsumeNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), + "outlined_consume", null, CompilerSpec.CALLING_CONVENTION_default); + if (type instanceof DemangledDataType dt) { + function.addParameter(new DemangledParameter(dt)); + } + return function; + } +} + diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedCopyNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedCopyNode.java new file mode 100644 index 0000000000..98bfa31df0 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftOutlinedCopyNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#OutlinedCopy} {@link SwiftNode} + */ +public class SwiftOutlinedCopyNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), + "outlined_copy", null, CompilerSpec.CALLING_CONVENTION_default); + if (type instanceof DemangledDataType dt) { + function.addParameter(new DemangledParameter(dt)); + } + return function; + } +} + diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftPrivateDeclNameNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftPrivateDeclNameNode.java new file mode 100644 index 0000000000..ae62710585 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftPrivateDeclNameNode.java @@ -0,0 +1,61 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#PrivateDeclName} {@link SwiftNode} + */ +public class SwiftPrivateDeclNameNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + if (namespace == null) { + namespace = child.demangle(demangler, typeMetadata); + } + else { + name = child.getText(); + } + break; + default: + skip(child); + break; + } + } + + if (name == null) { + if (namespace == null) { + return getUnknown(); + } + name = namespace.getNamespace().getName(); + namespace = null; + } + DemangledUnknown demangled = + new DemangledUnknown(properties.mangled(), properties.originalDemangled(), name); + demangled.setNamespace(namespace); + return demangled; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolConformanceNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolConformanceNode.java new file mode 100644 index 0000000000..b0f8faf58e --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolConformanceNode.java @@ -0,0 +1,60 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#ProtocolConformance} {@link SwiftNode} + */ +public class SwiftProtocolConformanceNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type1 = null; + Demangled type2 = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Module: + namespace = child.demangle(demangler, typeMetadata); + break; + case Type: + if (type1 == null) { + type1 = child.demangle(demangler, typeMetadata); + } + else { + type2 = child.demangle(demangler, typeMetadata); + } + break; + default: + skip(child); + break; + } + } + if (type1 == null && type2 == null) { + return getUnknown(); + } + DemangledUnknown demangled = new DemangledUnknown(properties.mangled(), + properties.originalDemangled(), SwiftNode.join(type2, type1).getNamespaceString()); + demangled.setNamespace(namespace); + return demangled; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolNode.java new file mode 100644 index 0000000000..197d6f6795 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftProtocolNode.java @@ -0,0 +1,54 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Protocol} {@link SwiftNode} + */ +public class SwiftProtocolNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case Module: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + DemangledUnknown demangled = + new DemangledUnknown(properties.mangled(), properties.originalDemangled(), name); + demangled.setNamespace(namespace); + return demangled; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSetterNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSetterNode.java new file mode 100644 index 0000000000..89e70a2877 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSetterNode.java @@ -0,0 +1,64 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftDataTypeUtils; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Setter} {@link SwiftNode} + */ +public class SwiftSetterNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = null; + String name = "set_"; + String callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Subscript: + demangled = child.demangle(demangler, typeMetadata); + break; + case Variable: + demangled = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (demangled instanceof DemangledFunction function) { + function.setName(name + function.getName()); + function.setCallingConvention(callingConvention); + return function; + } + if (demangled instanceof DemangledVariable variable) { + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), + name + variable.getName(), variable.getNamespace(), callingConvention); + function.addParameters(SwiftDataTypeUtils.extractParameters(variable)); + return function; + } + return getUnknown(); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftStructureNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftStructureNode.java new file mode 100644 index 0000000000..c532b6cdc5 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftStructureNode.java @@ -0,0 +1,101 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.*; + +/** + * A {@link SwiftDemangledNodeKind#Structure} {@link SwiftNode} + */ +public class SwiftStructureNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled privateDeclNamespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case PrivateDeclName: + Demangled temp = child.demangle(demangler, typeMetadata); + name = temp.getName(); + privateDeclNamespace = temp.getNamespace(); + break; + case Class: + case Module: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + + String mangled = properties.mangled(); + String orig = properties.originalDemangled(); + if (SwiftDataTypeUtils.isSwiftNamespace(namespace)) { + DemangledDataType type = switch (name) { + case "Bool" -> new SwiftPrimitive(mangled, orig, DemangledDataType.BOOL); + case "Int" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT); + case "Int8" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT8); + case "Int16" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT16); + case "Int32" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT32); + case "Int64" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT64); + case "UInt" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT, true); + case "UInt8" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT8, true); + case "UInt16" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT16, true); + case "UInt32" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT32, true); + case "UInt64" -> new SwiftPrimitive(mangled, orig, DemangledDataType.INT64, true); + case "Float" -> new SwiftPrimitive(mangled, orig, DemangledDataType.FLOAT); + case "Float16" -> new SwiftPrimitive(mangled, orig, DemangledDataType.FLOAT2); + case "Double" -> new SwiftPrimitive(mangled, orig, DemangledDataType.DOUBLE); + case "Array" -> new SwiftArray(mangled, orig); + case "Character" -> new SwiftCharacter(mangled, orig); + case "String" -> new SwiftString(mangled, orig); + default -> null; + }; + if (type != null) { + return type; + } + } + + SwiftStructure struct = new SwiftStructure(mangled, orig, name, + SwiftNode.join(namespace, privateDeclNamespace), typeMetadata, demangler); + + // The structure has no fields, which behaves poorly in the decompiler. Give it one + // undefined* field instead. + if (struct.getFields().isEmpty()) { + DemangledDataType undefined = + new DemangledDataType(mangled, orig, DemangledDataType.UNDEFINED); + undefined.incrementPointerLevels(); + struct.addField("unknown", null, undefined); + } + + return struct; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSubscriptNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSubscriptNode.java new file mode 100644 index 0000000000..ec7f40acac --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftSubscriptNode.java @@ -0,0 +1,68 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#Subscript} {@link SwiftNode} + */ +public class SwiftSubscriptNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled type = null; + Demangled labelList = null; + String callingConvention = CompilerSpec.CALLING_CONVENTION_default; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Class: + callingConvention = CompilerSpec.CALLING_CONVENTION_thiscall; + // Fall through + case Structure: + namespace = child.demangle(demangler, typeMetadata); + name = "subscript"; + break; + case LabelList: + labelList = child.demangle(demangler, typeMetadata); + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = new SwiftFunction(properties.mangled(), + properties.originalDemangled(), name, namespace, callingConvention); + if (type instanceof DemangledFunction functionType) { + function.setType(functionType, labelList); + } + return function; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleElementNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleElementNode.java new file mode 100644 index 0000000000..923a52e38f --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleElementNode.java @@ -0,0 +1,58 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#TupleElement} {@link SwiftNode} + */ +public class SwiftTupleElementNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled type = null; + String name = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case TupleElementName: + name = child.getText(); + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (type == null) { + return getUnknown(); + } + if (name != null && type instanceof DemangledDataType ddt) { + DemangledVariable variable = + new DemangledVariable(properties.mangled(), properties.originalDemangled(), name); + variable.setDatatype(ddt); + return variable; + } + return type; + } + +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleNode.java new file mode 100644 index 0000000000..c534b06d57 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTupleNode.java @@ -0,0 +1,47 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Tuple} {@link SwiftNode} + */ +public class SwiftTupleNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + List elements = new ArrayList<>(); + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case TupleElement: + elements.add(child.demangle(demangler, typeMetadata)); + break; + default: + skip(child); + break; + } + } + return new DemangledList(elements); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeAliasNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeAliasNode.java new file mode 100644 index 0000000000..b29a4b0535 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeAliasNode.java @@ -0,0 +1,56 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#TypeAlias} {@link SwiftNode} + */ +public class SwiftTypeAliasNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case Module: + namespace = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + DemangledDataType type = + new DemangledDataType(properties.mangled(), properties.originalDemangled(), name); + type.setNamespace(namespace); + type.setClass(); + return type; + } +} + diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeListNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeListNode.java new file mode 100644 index 0000000000..1cadf76020 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeListNode.java @@ -0,0 +1,47 @@ +/* ### + * 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.demangler.swift.nodes; + +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#TypeList} {@link SwiftNode} + */ +public class SwiftTypeListNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + List elements = new ArrayList<>(); + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + elements.add(child.demangle(demangler, typeMetadata)); + break; + default: + skip(child); + break; + } + } + return new DemangledList(elements); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeMetadataAccessFunctionNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeMetadataAccessFunctionNode.java new file mode 100644 index 0000000000..e16c24897b --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeMetadataAccessFunctionNode.java @@ -0,0 +1,58 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#TypeMetadataAccessFunction} {@link SwiftNode} + */ +public class SwiftTypeMetadataAccessFunctionNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Type: + namespace = child.demangle(demangler, typeMetadata); + name = "typeMetadataAccessor"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + SwiftFunction function = + new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, namespace, + CompilerSpec.CALLING_CONVENTION_default); + if (namespace instanceof DemangledDataType type) { + function.setReturnType(type); + } + return function; + + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeNode.java new file mode 100644 index 0000000000..8750edaab7 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftTypeNode.java @@ -0,0 +1,34 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Type} {@link SwiftNode} + */ +public class SwiftTypeNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + return demangleFirstChild(demangler, typeMetadata); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsafeMutableAddressorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsafeMutableAddressorNode.java new file mode 100644 index 0000000000..e2e9f15465 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsafeMutableAddressorNode.java @@ -0,0 +1,53 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.datatypes.SwiftFunction; +import ghidra.program.model.lang.CompilerSpec; + +/** + * A {@link SwiftDemangledNodeKind#UnsafeMutableAddressor} {@link SwiftNode} + */ +public class SwiftUnsafeMutableAddressorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Variable: + namespace = child.demangle(demangler, typeMetadata); + name = "unsafeMutableAddressor"; + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + return new SwiftFunction(properties.mangled(), properties.originalDemangled(), name, + namespace, CompilerSpec.CALLING_CONVENTION_default); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsupportedNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsupportedNode.java new file mode 100644 index 0000000000..f33433c2a5 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftUnsupportedNode.java @@ -0,0 +1,52 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Unsupported} {@link SwiftNode} + */ +public class SwiftUnsupportedNode extends SwiftNode { + + private String originalKind; + + /** + * Create a new {@link SwiftUnsupportedNode} {@link SwiftNode} + * + * @param originalKind The original {@link SwiftDemangledNodeKind kind} of {@link SwiftNode} that is + * not supported + * @param props The {@link ghidra.app.util.demangler.swift.nodes.SwiftNode.NodeProperties} + */ + public SwiftUnsupportedNode(String originalKind, NodeProperties props) { + this.originalKind = originalKind; + this.properties = props; + } + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) { + skip(this); + return getUnknown(); + } + + @Override + public String toString() { + return super.toString() + " (" + originalKind + ")"; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftVariableNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftVariableNode.java new file mode 100644 index 0000000000..6ed2ab327d --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/SwiftVariableNode.java @@ -0,0 +1,72 @@ +/* ### + * 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.demangler.swift.nodes; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangledNodeKind; +import ghidra.app.util.demangler.swift.SwiftDemangler; + +/** + * A {@link SwiftDemangledNodeKind#Variable} {@link SwiftNode} + */ +public class SwiftVariableNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + String name = null; + Demangled namespace = null; + Demangled privateDeclNamespace = null; + Demangled type = null; + for (SwiftNode child : getChildren()) { + switch (child.getKind()) { + case Identifier: + name = child.getText(); + break; + case PrivateDeclName: + Demangled temp = child.demangle(demangler, typeMetadata); + name = temp.getName(); + privateDeclNamespace = temp.getNamespace(); + break; + case Class: + case Enum: + case Extension: + case Module: + case Protocol: + case Structure: + namespace = child.demangle(demangler, typeMetadata); + break; + case Type: + type = child.demangle(demangler, typeMetadata); + break; + default: + skip(child); + break; + } + } + if (name == null) { + return getUnknown(); + } + DemangledVariable variable = + new DemangledVariable(properties.mangled(), properties.originalDemangled(), name); + if (type instanceof DemangledDataType ddt) { + variable.setDatatype(ddt); + } + variable.setNamespace(SwiftNode.join(namespace, privateDeclNamespace)); + return variable; + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericDescriptorNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericDescriptorNode.java new file mode 100644 index 0000000000..a104fa31a1 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericDescriptorNode.java @@ -0,0 +1,35 @@ +/* ### + * 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.demangler.swift.nodes.generic; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.*; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; + +/** + * A {@link SwiftNode} to represent a generic descriptor + */ +public class SwiftGenericDescriptorNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + Demangled demangled = demangleFirstChild(demangler, typeMetadata); + return new DemangledLabel(properties.mangled(), properties.originalDemangled(), + demangled.getNamespaceString()); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericIndexNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericIndexNode.java new file mode 100644 index 0000000000..4e0350fa68 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericIndexNode.java @@ -0,0 +1,34 @@ +/* ### + * 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.demangler.swift.nodes.generic; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledLabel; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; + +/** + * A {@link SwiftNode} that just contains an index + */ +public class SwiftGenericIndexNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) { + return new DemangledLabel(properties.mangled(), properties.originalDemangled(), + getIndex()); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericPassthroughNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericPassthroughNode.java new file mode 100644 index 0000000000..0f520ae38b --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericPassthroughNode.java @@ -0,0 +1,34 @@ +/* ### + * 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.demangler.swift.nodes.generic; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledException; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; + +/** + * A {@link SwiftNode} that simply passes through to its child {@link SwiftNode} + */ +public class SwiftGenericPassthroughNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) + throws DemangledException { + return demangleFirstChild(demangler, typeMetadata); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericTextNode.java b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericTextNode.java new file mode 100644 index 0000000000..1a9f1a4b66 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/main/java/ghidra/app/util/demangler/swift/nodes/generic/SwiftGenericTextNode.java @@ -0,0 +1,34 @@ +/* ### + * 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.demangler.swift.nodes.generic; + +import ghidra.app.util.bin.format.swift.SwiftTypeMetadata; +import ghidra.app.util.demangler.Demangled; +import ghidra.app.util.demangler.DemangledLabel; +import ghidra.app.util.demangler.swift.SwiftDemangler; +import ghidra.app.util.demangler.swift.nodes.SwiftNode; + +/** + * A {@link SwiftNode} that just contains text + */ +public class SwiftGenericTextNode extends SwiftNode { + + @Override + public Demangled demangle(SwiftDemangler demangler, SwiftTypeMetadata typeMetadata) { + return new DemangledLabel(properties.mangled(), properties.originalDemangled(), + getText()); + } +} diff --git a/Ghidra/Features/SwiftDemangler/src/test/java/ghidra/app/util/demangler/swift/SwiftDemanglerTest.java b/Ghidra/Features/SwiftDemangler/src/test/java/ghidra/app/util/demangler/swift/SwiftDemanglerTest.java new file mode 100644 index 0000000000..fc4744e5a8 --- /dev/null +++ b/Ghidra/Features/SwiftDemangler/src/test/java/ghidra/app/util/demangler/swift/SwiftDemanglerTest.java @@ -0,0 +1,705 @@ +/* ### + * 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.demangler.swift; + +import static org.junit.Assert.*; +import static org.junit.Assume.*; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import generic.test.AbstractGenericTest; +import ghidra.app.util.demangler.DemangledFunction; +import ghidra.app.util.demangler.DemangledStructure; +import ghidra.util.exception.AssertException; + +/** + * Unit tests for the {@link SwiftDemangler}. Requires Swift to be installed on the test system. + * If it is not, these tests will be skipped. + */ +public class SwiftDemanglerTest extends AbstractGenericTest { + + private SwiftDemangler demangler; + + @Before + public void setUp() throws Exception { + demangler = new SwiftDemangler(); + + // Ghidra does not ship the native Swift demangler binary, so it may not be present to run + // these tests. In this scenario, we just want these tests skipped (as opposed to failing). + try { + new SwiftNativeDemangler(new SwiftDemanglerOptions().getSwiftDir()); + } + catch (IOException e) { + assumeNoException(e); // skip test, don't fail + } + } + + @Test + public void testFunctionAndTypes() throws Exception { + /*-******************************************************************** + kind=Global + kind=Function + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="testJunitFunctionAndTypes" + kind=LabelList + kind=FirstElementMarker + kind=Identifier, text="label2" + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=Identifier, text="label14" + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=FirstElementMarker + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=InOut + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int8" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int16" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int32" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int64" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="UInt" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="UInt8" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="UInt16" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="UInt32" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="UInt64" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Float" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Float" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Double" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Double" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="String" + kind=TupleElement + kind=Type + kind=BoundGenericStructure + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Array" + kind=TypeList + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Bool" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Character" + kind=ReturnType + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Float" + SwiftDemanglerTest.testJunitFunctionAndTypes(_: inout Swift.Int, label2: Swift.Int8, _: Swift.Int16, _: Swift.Int32, _: Swift.Int64, _: Swift.UInt, _: Swift.UInt8, _: Swift.UInt16, _: Swift.UInt32, _: Swift.UInt64, _: Swift.Float, _: Swift.Float, _: Swift.Double, label14: Swift.Double, _: Swift.String, _: [Swift.Int], _: Swift.Bool, _: Swift.Character) -> (Swift.Int, Swift.Float) + **********************************************************************/ + String mangled = + "_$s18SwiftDemanglerTest25testJunitFunctionAndTypes_6label2___________7label14____Si_SftSiz_s4Int8Vs5Int16Vs5Int32Vs5Int64VSus5UInt8Vs6UInt16Vs6UInt32Vs6UInt64VS2fS2dSSSaySiGSbSJtF"; + String demangled = + "struct tuple2 default SwiftDemanglerTest::testJunitFunctionAndTypes(int *,__int8 label2,__int16,__int32,__int64,unsigned int,unsigned __int8,unsigned __int16,unsigned __int32,unsigned __int64,float,float,double,double label14,struct Swift::String,Swift::Array[],bool,struct Swift::Character)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + + if (!(function.getReturnType() instanceof DemangledStructure struct)) { + throw new AssertException("Demangled return type is not a structure"); + } + assertEquals(struct.getFields().size(), 2); + assertEquals(struct.getFields().get(0).type().toString(), "int"); + assertEquals(struct.getFields().get(1).type().toString(), "float"); + } + + @Test + public void testStructureAllocator() throws Exception { + /*-******************************************************************** + kind=Global + kind=Allocator + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=LabelList + kind=Identifier, text="label1" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + SwiftDemanglerTest.MyStructure.init(label1: Swift.Int) -> SwiftDemanglerTest.MyStructure + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureV6label1ACSi_tcfC"; + String demangled = + "struct SwiftDemanglerTest::MyStructure default SwiftDemanglerTest::MyStructure::init(int label1)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testStructureFunction() throws Exception { + /*-******************************************************************** + kind=Global + kind=Function + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=Identifier, text="myMethod" + kind=LabelList + kind=Identifier, text="label1" + kind=Identifier, text="label2" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyStructure.myMethod(label1: Swift.Int, label2: Swift.Int) -> Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureV8myMethod6label16label2S2i_SitF"; + String demangled = + "int default SwiftDemanglerTest::MyStructure::myMethod(int label1,int label2,struct SwiftDemanglerTest::MyStructure)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testStructureGetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Getter + kind=Variable + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=Identifier, text="z" + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyStructure.z.getter : Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureV1zSivg"; + String demangled = + "int default SwiftDemanglerTest::MyStructure::get_z(struct SwiftDemanglerTest::MyStructure)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testStructureSetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Setter + kind=Variable + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=Identifier, text="z" + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyStructure.z.setter : Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureV1zSivs"; + String demangled = "__thiscall SwiftDemanglerTest::MyStructure::set_z(int)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testStructureSubscriptGetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Getter + kind=Subscript + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=LabelList + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyStructure.subscript.getter : (Swift.Int) -> Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureVyS2icig"; + String demangled = + "int default SwiftDemanglerTest::MyStructure::get_subscript(int,struct SwiftDemanglerTest::MyStructure)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testStructureSubscriptSetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Setter + kind=Subscript + kind=Structure + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyStructure" + kind=LabelList + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyStructure.subscript.setter : (Swift.Int) -> Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest11MyStructureVyS2icis"; + String demangled = "int __thiscall SwiftDemanglerTest::MyStructure::set_subscript(int)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassTypeMetadataAccessor() throws Exception { + /*-******************************************************************** + kind=Global + kind=TypeMetadataAccessFunction + kind=Type + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + type metadata accessor for SwiftDemanglerTest.MyClass + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassCMa"; + String demangled = + "class SwiftDemanglerTest::MyClass * default SwiftDemanglerTest::MyClass::typeMetadataAccessor(void)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassAllocator() throws Exception { + /*-******************************************************************** + kind=Global + kind=Allocator + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=LabelList + kind=Identifier, text="label1" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + SwiftDemanglerTest.MyClass.__allocating_init(label1: Swift.Int) -> SwiftDemanglerTest.MyClass + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC6label1ACSi_tcfC"; + String demangled = + "class SwiftDemanglerTest::MyClass * __thiscall SwiftDemanglerTest::MyClass::__allocating_init(int label1)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassConstructor() throws Exception { + /*-******************************************************************** + kind=Global + kind=Constructor + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=LabelList + kind=Identifier, text="label1" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + SwiftDemanglerTest.MyClass.init(label1: Swift.Int) -> SwiftDemanglerTest.MyClass + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC6label1ACSi_tcfc"; + String demangled = + "class SwiftDemanglerTest::MyClass * __thiscall SwiftDemanglerTest::MyClass::init(int label1)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassFunction() throws Exception { + /*-******************************************************************** + kind=Global + kind=Function + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=Identifier, text="myMethod" + kind=LabelList + kind=Identifier, text="label1" + kind=Identifier, text="label2" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyClass.myMethod(label1: Swift.Int, label2: Swift.Int) -> Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC8myMethod6label16label2S2i_SitF"; + String demangled = + "int __thiscall SwiftDemanglerTest::MyClass::myMethod(int label1,int label2)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassGetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Getter + kind=Variable + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=Identifier, text="z" + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyClass.z.getter : Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC1zSivg"; + String demangled = + "int __thiscall SwiftDemanglerTest::MyClass::get_z(class SwiftDemanglerTest::MyClass *)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassSetter() throws Exception { + /*-******************************************************************** + kind=Global + kind=Setter + kind=Variable + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=Identifier, text="z" + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyClass.z.setter : Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC1zSivs"; + String demangled = "__thiscall SwiftDemanglerTest::MyClass::set_z(int)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassModifyAccessor() throws Exception { + /*-******************************************************************** + kind=Global + kind=ModifyAccessor + kind=Variable + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + kind=Identifier, text="z" + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + SwiftDemanglerTest.MyClass.z.modify : Swift.Int + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassC1zSivM"; + String demangled = "__thiscall SwiftDemanglerTest::MyClass::modify_z(int)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassDeallocator() throws Exception { + /*-******************************************************************** + kind=Global + kind=Deallocator + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + SwiftDemanglerTest.MyClass.__deallocating_deinit + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassCfD"; + String demangled = "__thiscall SwiftDemanglerTest::MyClass::__deallocating_init(void)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testClassDestructor() throws Exception { + /*-******************************************************************** + kind=Global + kind=Destructor + kind=Class + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyClass" + SwiftDemanglerTest.MyClass.deinit + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest7MyClassCfd"; + String demangled = + "class SwiftDemanglerTest::MyClass * __thiscall SwiftDemanglerTest::MyClass::deinit(void)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } + + @Test + public void testEnumFunction() throws Exception { + /*-******************************************************************** + kind=Global + kind=Function + kind=Enum + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyAssociatedEnum" + kind=Identifier, text="myMethod" + kind=LabelList + kind=Identifier, text="label1" + kind=Type + kind=FunctionType + kind=ArgumentTuple + kind=Type + kind=Tuple + kind=TupleElement + kind=Type + kind=Structure + kind=Module, text="Swift" + kind=Identifier, text="Int" + kind=ReturnType + kind=Type + kind=Enum + kind=Module, text="SwiftDemanglerTest" + kind=Identifier, text="MyAssociatedEnum" + SwiftDemanglerTest.MyAssociatedEnum.myMethod(label1: Swift.Int) -> SwiftDemanglerTest.MyAssociatedEnum + **********************************************************************/ + String mangled = "_$s18SwiftDemanglerTest16MyAssociatedEnumO8myMethod6label1ACSi_tF"; + String demangled = + "struct SwiftDemanglerTest::MyAssociatedEnum default SwiftDemanglerTest::MyAssociatedEnum::myMethod(int label1,struct SwiftDemanglerTest::MyAssociatedEnum,undefined)"; + + if (!(demangler.demangle(mangled) instanceof DemangledFunction function)) { + throw new AssertException("Demangled object is not a function"); + } + assertEquals(demangled, function.toString()); + } +} diff --git a/Ghidra/Processors/AARCH64/certification.manifest b/Ghidra/Processors/AARCH64/certification.manifest index 1390a55a38..864d98b74b 100644 --- a/Ghidra/Processors/AARCH64/certification.manifest +++ b/Ghidra/Processors/AARCH64/certification.manifest @@ -12,6 +12,7 @@ data/languages/AARCH64_AMXext.sinc||GHIDRA||||END| data/languages/AARCH64_AppleSilicon.slaspec||GHIDRA||||END| data/languages/AARCH64_base_PACoptions.sinc||GHIDRA||||END| data/languages/AARCH64_ilp32.cspec||GHIDRA||||END| +data/languages/AARCH64_swift.cspec||GHIDRA||||END| data/languages/AARCH64_win.cspec||GHIDRA||||END| data/languages/AARCH64base.sinc||GHIDRA||||END| data/languages/AARCH64instructions.sinc||GHIDRA||||END| diff --git a/Ghidra/Processors/AARCH64/data/languages/AARCH64.opinion b/Ghidra/Processors/AARCH64/data/languages/AARCH64.opinion index fe833d3095..e01f88493a 100644 --- a/Ghidra/Processors/AARCH64/data/languages/AARCH64.opinion +++ b/Ghidra/Processors/AARCH64/data/languages/AARCH64.opinion @@ -9,6 +9,9 @@ + + + diff --git a/Ghidra/Processors/AARCH64/data/languages/AARCH64_swift.cspec b/Ghidra/Processors/AARCH64/data/languages/AARCH64_swift.cspec new file mode 100644 index 0000000000..1ad1f4d34f --- /dev/null +++ b/Ghidra/Processors/AARCH64/data/languages/AARCH64_swift.cspec @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/AARCH64/data/languages/AppleSilicon.ldefs b/Ghidra/Processors/AARCH64/data/languages/AppleSilicon.ldefs index f8c0e540b8..8164e24320 100644 --- a/Ghidra/Processors/AARCH64/data/languages/AppleSilicon.ldefs +++ b/Ghidra/Processors/AARCH64/data/languages/AppleSilicon.ldefs @@ -11,6 +11,7 @@ id="AARCH64:LE:64:AppleSilicon"> AppleSilicon ARM v8.5-A LE instructions, LE data, AMX extensions + diff --git a/Ghidra/Processors/x86/certification.manifest b/Ghidra/Processors/x86/certification.manifest index ee601a6774..0dc715e5b1 100644 --- a/Ghidra/Processors/x86/certification.manifest +++ b/Ghidra/Processors/x86/certification.manifest @@ -55,6 +55,7 @@ data/languages/x86-64-compat32.pspec||GHIDRA||||END| data/languages/x86-64-gcc.cspec||GHIDRA||||END| data/languages/x86-64-golang.cspec||GHIDRA||||END| data/languages/x86-64-golang.register.info||GHIDRA||||END| +data/languages/x86-64-swift.cspec||GHIDRA||||END| data/languages/x86-64-win.cspec||GHIDRA||||END| data/languages/x86-64.dwarf||GHIDRA||||END| data/languages/x86-64.pspec||GHIDRA||||END| diff --git a/Ghidra/Processors/x86/data/languages/x86-64-swift.cspec b/Ghidra/Processors/x86/data/languages/x86-64-swift.cspec new file mode 100644 index 0000000000..a88fbea255 --- /dev/null +++ b/Ghidra/Processors/x86/data/languages/x86-64-swift.cspec @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/x86/data/languages/x86.ldefs b/Ghidra/Processors/x86/data/languages/x86.ldefs index a682e14915..85c0c6801b 100644 --- a/Ghidra/Processors/x86/data/languages/x86.ldefs +++ b/Ghidra/Processors/x86/data/languages/x86.ldefs @@ -93,6 +93,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86.opinion b/Ghidra/Processors/x86/data/languages/x86.opinion index 33e60083a4..c086a56d69 100644 --- a/Ghidra/Processors/x86/data/languages/x86.opinion +++ b/Ghidra/Processors/x86/data/languages/x86.opinion @@ -27,6 +27,9 @@ + + + @@ -56,6 +59,9 @@ + + +