Objective-C and Swift need more than symbols at run time: the classes, their methods and their properties. The compiler writes that metadata into named sections. Pointers in them are read like any other data pointer: chained fixups where the file uses them.
Objective-C sections
| Section | Holds |
|---|---|
__objc_classlist, __objc_catlist, __objc_protolist | Pointers to the classes, categories and protocols defined here. |
__objc_nlclslist, __objc_nlcatlist | Classes and categories with +load methods. |
__objc_selrefs | Pointers to selector strings. |
__objc_classrefs, __objc_superrefs | Pointers to classes. |
__objc_ivar | Instance variable offsets. |
__objc_const, __objc_data | Class records, method and property lists. |
__TEXT,__objc_methname, __objc_classname, __objc_methtype | Strings. |
__objc_imageinfo | A version (4 bytes) and flags (4 bytes): the Swift version is in the flags’ second byte. |
The legacy runtime of 32-bit Intel programs kept its metadata in a
__OBJC segment instead; the explorer does not decode it.
Classes
A class record is five pointer-sized fields: isa,
superclass, cache, vtable and data.
The low bits of data are flags (a Swift class sets one of the two lowest
bits); with them cleared it points at a class_ro_t:
| Field | Size | Meaning |
|---|---|---|
| flags | 4 | Flags. |
| instanceStart, instanceSize | 4 each | Where the instance variables start, and an object’s size. |
| reserved | 4 | 64-bit only. |
| ivarLayout | pointer | |
| name | pointer | The class’s name. |
| baseMethods, baseProtocols, ivars | pointers | Method list, protocol list, ivar list. |
| weakIvarLayout, baseProperties | pointers |
A category record is name, cls, instanceMethods,
classMethods, protocols and instanceProperties,
all pointers. A protocol record starts with isa and name.
Method lists
A list starts with entsizeAndFlags and count (4 bytes
each). The low 16 bits of the first give the size of an entry.
An ordinary entry is three pointers: name, types and
imp. When the top bit of entsizeAndFlags is set, the list
is relative: each entry is three 32-bit offsets, each measured from its own field,
and the first leads to a selector reference. When bit 30 is also set the first offset
leads straight to the selector’s string.
Swift sections
| Section | Holds |
|---|---|
__TEXT,__swift5_types | 32-bit relative pointers to the type descriptors. |
__TEXT,__swift5_proto | 32-bit relative pointers to protocol conformances. |
__TEXT,__swift5_fieldmd | Field descriptors, for reflection. |
__TEXT,__swift5_reflstr, __swift5_typeref | Strings and mangled type names. |
A relative pointer is a signed 32-bit offset from its own address. When its low bit is set the target holds a pointer to the real target.
A context descriptor starts with flags (the low 5 bits give the kind:
16 class, 17 struct, 18 enum, 0 module, 1 extension, 2 anonymous, 3 protocol), a
relative pointer to its parent and, at offset 8, a relative pointer to
its name.
A field descriptor is a mangledTypeName, a superclass (each
a relative pointer), kind and fieldRecordSize (2 bytes
each) and numFields (4 bytes), followed by its field records.
Swift’s metadata is otherwise large and changes between compiler versions. The explorer lists the types, the conformances and the field descriptors, and shows the rest as bytes.