From 26e9bc6780ac186bc394e9249294fcdf81a48f29 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 31 Dec 2025 19:40:44 -0500 Subject: [PATCH] GP-6281: Swift type metadata fixes (#8607) --- .../util/bin/format/swift/SwiftSection.java | 2 +- .../bin/format/swift/SwiftTypeMetadata.java | 6 +- .../swift/types/InvertibleProtocolKind.java | 85 +++++++++++++++++++ .../swift/types/InvertibleProtocolSet.java | 74 ++++++++++++++++ .../types/MultiPayloadEnumDescriptor.java | 26 +++++- .../swift/types/TargetClassDescriptor.java | 13 ++- .../swift/types/TargetEnumDescriptor.java | 13 ++- .../swift/types/TargetStructDescriptor.java | 13 ++- 8 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolKind.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolSet.java 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 a8b3d6c7a7..64a30156c0 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,7 +35,7 @@ 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_type_metadata", ".sw5tymd"), + BLOCK_TYPES("__swift5_types", "__swift5_types2", "swift5_type_metadata", ".sw5tymd"), BLOCK_ENTRY("__swift5_entry", "swift5_entry", ".sw5entr"), BLOCK_SWIFTAST("__swift_ast", ".swift_ast", "swiftast"); 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 5a91358d1b..55e21a3bba 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 @@ -361,7 +361,11 @@ public class SwiftTypeMetadata { Address blockStart = block.getStart(); reader.setPointerIndex(blockStart.getOffset()); int i = skipZeroEntries(reader, 0, block.getSize()); - while (i < block.getSize()) { + while (i + MultiPayloadEnumDescriptor.PEEK_SIZE <= block.getSize()) { + int contentsSize = MultiPayloadEnumDescriptor.peekContentsSize(reader); + if (i + MultiPayloadEnumDescriptor.SIZE + contentsSize > block.getSize()) { + break; + } monitor.checkCancelled(); MultiPayloadEnumDescriptor descriptor = new MultiPayloadEnumDescriptor(reader); mpEnumDescriptors.add(descriptor); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolKind.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolKind.java new file mode 100644 index 0000000000..ea8c2ca916 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolKind.java @@ -0,0 +1,85 @@ +/* ### + * 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.types; + +import java.io.IOException; +import java.util.*; + +import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.EnumDataType; +import ghidra.util.exception.DuplicateNameException; + +/** + * Swift {@code InvertibleProtocolKind} values + * + * @see swift/ABI/InvertibleProtocols.h + * @see swift/ABI/InvertibleProtocols.def + */ +public enum InvertibleProtocolKind implements StructConverter { + + Copyable(0), + Escapable(1); + + private int bit; + + /** + * Creates a new {@link InvertibleProtocolKind} + * + * @param bit The bit number that represents the kind + */ + private InvertibleProtocolKind(int bit) { + this.bit = bit; + } + + /** + * {@return the bit number that represents the kind} + */ + public int getBit() { + return bit; + } + + /** + * {@return the {@link Set} of {@link InvertibleProtocolKind}s that map to the given kind value} + * + * @param value The kind value to get the value of + */ + public static Set valueOf(short value) { + Set set = new HashSet<>(); + for (int i = 0; i < 16; i++) { + final int bitPos = i; + int bit = (value >> bitPos) & 0x1; + if (bit != 0) { + Arrays.stream(values()) + .filter(e -> e.getBit() == bitPos) + .findFirst() + .ifPresent(set::add); + } + } + return set; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + EnumDataType dt = new EnumDataType(SwiftTypeMetadataStructure.CATEGORY_PATH, + InvertibleProtocolKind.class.getSimpleName(), 2); + for (InvertibleProtocolKind kind : values()) { + dt.add(kind.name(), 1 << kind.getBit()); + } + return dt; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolSet.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolSet.java new file mode 100644 index 0000000000..2cc3d8232a --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/InvertibleProtocolSet.java @@ -0,0 +1,74 @@ +/* ### + * 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.types; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.swift.SwiftTypeMetadataStructure; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.StructureDataType; +import ghidra.util.exception.DuplicateNameException; + +/** + * Represents a Swift {@code InvertibleProtocolSet} structure + * + * @see swift/ABI/InvertibleProtocols.h + */ +public class InvertibleProtocolSet extends SwiftTypeMetadataStructure { + + /** + * The size (in bytes) of a {@link InvertibleProtocolSet} structure + */ + public static final int SIZE = 2; + + private short bits; + + /** + * Create a new {@link InvertibleProtocolSet} + * + * @param reader A {@link BinaryReader} positioned at the start of the structure + * @throws IOException if there was an IO-related problem creating the structure + */ + public InvertibleProtocolSet(BinaryReader reader) throws IOException { + super(reader.getPointerIndex()); + bits = reader.readNextShort(); + } + + /** + * {@return the raw bits} + */ + public short getRawBits() { + return bits; + } + + @Override + public String getStructureName() { + return InvertibleProtocolSet.class.getSimpleName(); + } + + @Override + public String getDescription() { + return "invertible protocol set"; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType(CATEGORY_PATH, getStructureName(), 0); + struct.add(InvertibleProtocolKind.values()[0].toDataType(), "bits", "The storage bits"); + return struct; + } +} 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 af967f4277..09e9bae54d 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 @@ -38,6 +38,11 @@ public final class MultiPayloadEnumDescriptor extends SwiftTypeMetadataStructure */ public static final int SIZE = 4; + /** + * How many bytes it requires to peek at size of the {@code contents} array + */ + public static final int PEEK_SIZE = 8; + private String typeName; private int[] contents; @@ -70,12 +75,31 @@ public final class MultiPayloadEnumDescriptor extends SwiftTypeMetadataStructure } /** - * {@return The size of the contents in bytes} + * {@return the size of the contents in bytes} */ public long getContentsSize() { return contents.length * Integer.BYTES; } + /** + * {@return the size of the contents in bytes, without reading the contents} + *

+ * This method will leave the {@link BinaryReader}'s position unaffected. + * + * @param reader A {@link BinaryReader} positioned at the start of the structure + * @throws IOException if there was an IO-related problem creating the structure + */ + public static int peekContentsSize(BinaryReader reader) throws IOException { + long origIndex = reader.getPointerIndex(); + try { + reader.readNext(SwiftUtils::relativeString); + return (reader.readNextInt() >> 16) & 0xffff; + } + finally { + reader.setPointerIndex(origIndex); + } + } + @Override public String getStructureName() { return MultiPayloadEnumDescriptor.class.getSimpleName(); 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 736f705ee5..4f9719ea4e 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 @@ -51,6 +51,7 @@ public final class TargetClassDescriptor extends TargetTypeContextDescriptor { private TargetOverrideTableHeader overrideHeader; private List methodOverrideDescriptors = new ArrayList<>(); private TargetObjCResilientClassStubInfo objcResilientClassStub; + private InvertibleProtocolSet invertibleProtocolSet; /** * Creates a new {@link TargetClassDescriptor} @@ -113,7 +114,7 @@ public final class TargetClassDescriptor extends TargetTypeContextDescriptor { } if (flags.hasInvertableProtocols()) { - throw new IOException("Unimplemented InvertibleProtocolSet detected."); + invertibleProtocolSet = new InvertibleProtocolSet(reader); } if (!flags.isGeneric() && @@ -256,6 +257,13 @@ public final class TargetClassDescriptor extends TargetTypeContextDescriptor { return objcResilientClassStub; } + /** + * {@return the {@link InvertibleProtocolSet}, or {@code null} if it doens't exist} + */ + public InvertibleProtocolSet getInvertibleProtocolSet() { + return invertibleProtocolSet; + } + @Override public List getTrailingObjects() { List ret = new ArrayList<>(); @@ -283,6 +291,9 @@ public final class TargetClassDescriptor extends TargetTypeContextDescriptor { if (objcResilientClassStub != null) { ret.add(objcResilientClassStub); } + if (invertibleProtocolSet != null) { + ret.add(invertibleProtocolSet); + } return ret; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetEnumDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetEnumDescriptor.java index c1101bfd47..f8980792b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetEnumDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetEnumDescriptor.java @@ -39,6 +39,7 @@ public final class TargetEnumDescriptor extends TargetTypeContextDescriptor { private TargetTypeGenericContextDescriptorHeader genericHeader; private TargetSingletonMetadataInitialization singleton; private TargetForeignMetadataInitialization foreign; + private InvertibleProtocolSet invertibleProtocolSet; /** * Creates a new {@link TargetEnumDescriptor} @@ -72,7 +73,7 @@ public final class TargetEnumDescriptor extends TargetTypeContextDescriptor { } if (flags.hasInvertableProtocols()) { - throw new IOException("Unimplemented InvertibleProtocolSet detected."); + invertibleProtocolSet = new InvertibleProtocolSet(reader); } if (!flags.isGeneric() && @@ -122,6 +123,13 @@ public final class TargetEnumDescriptor extends TargetTypeContextDescriptor { return foreign; } + /** + * {@return the {@link InvertibleProtocolSet}, or {@code null} if it doens't exist} + */ + public InvertibleProtocolSet getInvertibleProtocolSet() { + return invertibleProtocolSet; + } + @Override public List getTrailingObjects() { List ret = new ArrayList<>(); @@ -135,6 +143,9 @@ public final class TargetEnumDescriptor extends TargetTypeContextDescriptor { if (foreign != null) { ret.add(foreign); } + if (invertibleProtocolSet != null) { + ret.add(invertibleProtocolSet); + } return ret; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetStructDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetStructDescriptor.java index 0983358c87..012e23d470 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetStructDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/swift/types/TargetStructDescriptor.java @@ -39,6 +39,7 @@ public final class TargetStructDescriptor extends TargetTypeContextDescriptor { private TargetTypeGenericContextDescriptorHeader genericHeader; private TargetSingletonMetadataInitialization singleton; private TargetForeignMetadataInitialization foreign; + private InvertibleProtocolSet invertibleProtocolSet; /** * Creates a new {@link TargetStructDescriptor} @@ -72,7 +73,7 @@ public final class TargetStructDescriptor extends TargetTypeContextDescriptor { } if (flags.hasInvertableProtocols()) { - throw new IOException("Unimplemented InvertibleProtocolSet detected."); + invertibleProtocolSet = new InvertibleProtocolSet(reader); } if (!flags.isGeneric() && @@ -121,6 +122,13 @@ public final class TargetStructDescriptor extends TargetTypeContextDescriptor { return foreign; } + /** + * {@return the {@link InvertibleProtocolSet}, or {@code null} if it doens't exist} + */ + public InvertibleProtocolSet getInvertibleProtocolSet() { + return invertibleProtocolSet; + } + @Override public List getTrailingObjects() { List ret = new ArrayList<>(); @@ -134,6 +142,9 @@ public final class TargetStructDescriptor extends TargetTypeContextDescriptor { if (foreign != null) { ret.add(foreign); } + if (invertibleProtocolSet != null) { + ret.add(invertibleProtocolSet); + } return ret; }