Swift 二进制组件getTypeContextDescriptor crash 分析

背景

在使用内网的某二进制组件 BinaryPodA 时发现了部分情况下会导致触发 swift::TargetMetadata<swift::InProcess>::getTypeContextDescriptor 崩溃

其他公司的也有过类似问题反馈 github.com/facebook/fa...

崩溃现场的堆栈

PlainText 复制代码
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x00000001877243e8 libswiftCore.dylib`swift_getAssociatedTypeWitnessSlowImpl(swift::MetadataRequest, swift::TargetWitnessTable<swift::InProcess>*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*, swift::TargetProtocolRequirement<swift::InProcess> const*) + 352
    frame #1: 0x00000001877223b4 libswiftCore.dylib`swift_getAssociatedTypeWitness + 236
    frame #2: 0x000000018750a980 libswiftCore.dylib`Swift._SwiftNewtypeWrapper< where τ_0_0: Swift.Hashable, τ_0_0.Swift.RawRepresentable.RawValue: Swift.Hashable>._rawHashValue(seed: Swift.Int) -> Swift.Int + 72
    frame #3: 0x000000010278485c SwiftOptimizerBugPoC_Example`protocol witness for Hashable._rawHashValue(seed:) in conformance NSRunLoopMode at <compiler-generated>:0
    frame #4: 0x0000000187023948 libswiftFoundation.dylib`Swift._NativeDictionary.init(_unsafeUninitializedCapacity: Swift.Int, allowingDuplicates: Swift.Bool, initializingWith: (Swift.UnsafeMutableBufferPointer<τ_0_0>, Swift.UnsafeMutableBufferPointer<τ_0_1>) -> Swift.Int) -> Swift._NativeDictionary<τ_0_0, τ_0_1> + 704
    frame #5: 0x00000001870250e0 libswiftFoundation.dylib`static Swift.Dictionary._unconditionallyBridgeFromObjectiveC(Swift.Optional<__C.NSDictionary>) -> Swift.Dictionary<τ_0_0, τ_0_1> + 1328
    frame #6: 0x00000001034cd628 DemoApp`reabstraction thunk helper from @escaping @callee_guaranteed (@guaranteed Swift.Dictionary<__C.NSAttributedStringKey, Any>, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () to @callee_unowned @convention(block) (@unowned __C.NSDictionary, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () + 88
    frame #7: 0x00000001841ebdd8 Foundation`-[NSAttributedString enumerateAttributesInRange:options:usingBlock:] + 252
    frame #8: 0x00000001034cdb5c DemoApp`BinaryPodA.SymbolB + 716
   ...
    frame #72: 0x00000001035292d4 DemoApp`@objc BinaryPodA.SymbolB() -> () + 24
    frame #73: 0x0000000184f91cc8 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2620
    frame #74: 0x00000001866eb280 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 536
    frame #75: 0x00000001866ddaa8 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 144
    frame #76: 0x00000001866f20b0 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double, double*) + 500
    frame #77: 0x00000001866fb174 QuartzCore`CA::Transaction::commit() + 680
    frame #78: 0x000000018515c0e8 UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 44
    frame #79: 0x00000001829cd934 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
    frame #80: 0x00000001829ce830 CoreFoundation`__CFRunLoopDoBlocks + 412
    frame #81: 0x0000000182966818 CoreFoundation`__CFRunLoopRun + 840
    frame #82: 0x000000018297a3c8 CoreFoundation`CFRunLoopRunSpecific + 600
    frame #83: 0x000000019e18b38c GraphicsServices`GSEventRunModal + 164
    frame #84: 0x000000018531f0bc UIKitCore`-[UIApplication _run] + 1100
    frame #85: 0x000000018509cbe8 UIKitCore`UIApplicationMain + 2124
    frame #86: 0x00000001025e1088 DemoApp`main at AppDelegate.swift:14:7
    frame #87: 0x0000000105e09a24 dyld`start + 520

堆栈分析

简单地看了下崩溃堆栈上能看到与 NSRunLoopMode 和 NSAttributedStringKey 的 Hashable 相关调用有关

发现在 Demo 中添加如下代码可以规避此 crash

Swift 复制代码
func magic() { // No need to call magic() explicit
    let _ = NSAttributedString.Key.font.hashValue
}

我们按照堆栈逆着从上往下进行分析

先在 BinaryPodA.SymbolB 处添加符号断点,触发crash进行frame 0

Frame 0

Frame 0 TargetMetadata::getTypeContextDescriptor

  • 相关汇编代码
C 复制代码
libswiftCore.dylib`swift::TargetMetadata<swift::InProcess>::getTypeContextDescriptor:
    0x187770990 <+0>:   mov    x16, #0x0
->  0x187770994 <+4>:   ldr    x8, [x0] // Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
  • 相关 Swift 源码
C 复制代码
  ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>
  getTypeContextDescriptor() const {
    switch (getKind()) {
    ...
    }
  }

可以得知是上游调用传入的x0/this指针为NULL导致

Frame 1

Frame 1 SubstGenericParametersFromMetadata::SubstGenericParametersFromMetadata

  • 相关汇编代码
C 复制代码
libswiftCore.dylib`swift::SubstGenericParametersFromMetadata::SubstGenericParametersFromMetadata:
    0x18779ab50 <+0>:   pacibsp 
    0x18779ab54 <+4>:   stp    x20, x19, [sp, #-0x20]!
    0x18779ab58 <+8>:   stp    x29, x30, [sp, #0x10]
    0x18779ab5c <+12>:  add    x29, sp, #0x10
    0x18779ab60 <+16>:  mov    x20, x1
    0x18779ab64 <+20>:  mov    x19, x0
    0x18779ab68 <+24>:  str    wzr, [x0]
    0x18779ab6c <+28>:  mov    x0, x1
    0x18779ab70 <+32>:  bl     0x187770990               ; swift::TargetMetadata<swift::InProcess>::getTypeContextDescriptor() const // Crash
  • 相关 Swift 源码
C 复制代码
    explicit SubstGenericParametersFromMetadata(const Metadata *base)
        : sourceKind(SourceKind::Metadata),
          baseContext(base->getTypeContextDescriptor()),
          genericArgs(base ? (const void *const *)base->getGenericArgs()
                           : nullptr) {}

Frame 0 的 x0 为此处的 x1,可以得知是上游传入的 x1/base 为NULL导致

Frame 2

Frame 2 swift_getAssociatedTypeWitnessSlowImpl

  • 相关汇编代码
C++ 复制代码
    0x1877a4610 <+296>: b.eq   0x1877a46e8               ; <+512>
    0x1877a4614 <+300>: ldur   x0, [x29, #-0x60]
    0x1877a4618 <+304>: mov    x1, x23

->  0x1877a461c <+308>: bl     0x1877d6674               ; swift::findConformingSuperclass(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolConformanceDescriptor<swift::InProcess> const*)
    0x1877a4620 <+312>: mov    x1, x0
    0x1877a4624 <+316>: add    x23, sp, #0xc0
    0x1877a4628 <+320>: add    x0, sp, #0xc0
    0x1877a462c <+324>: bl     0x18779ab50               ; swift::SubstGenericParametersFromMetadata::SubstGenericParametersFromMetadata(swift::TargetMetadata<swift::InProcess> const*)

x1 = x0 = findConformingSuperclass(xx) result = NULL 导致

和加入 magic 函数后不会 crash 的 case 对传入 findConformingSuperclass 的 x0 和 x1 进行对比,发现问题可能出现在这里的 x1 处

PlainText 复制代码
// crash case
(lldb) reg read x0 x1
      x0 = 0x0000000102176060  DemoApp`full type metadata for __C.NSAttributedStringKey + 16
      x1 = 0x0000000101f3df30  DemoApp`protocol conformance descriptor for __C.NSRunLoopMode : Swift.RawRepresentable in __C_Synthesized
      
// normal case
(lldb) reg read x0 x1
      x0 = 0x0000000105b55b70  DemoApp`full type metadata for __C.NSAttributedStringKey + 16
      x1 = 0x0000000105959ed8  DemoApp`protocol conformance descriptor for __C.NSAttributedStringKey : Swift.RawRepresentable in __C_Synthesized
  • 相关 Swift 源码
C++ 复制代码
SWIFT_CC(swift)
static MetadataResponse
swift_getAssociatedTypeWitnessSlowImpl(
                                      MetadataRequest request,
                                      WitnessTable *wtable,
                                      const Metadata *conformingType,
                                      const ProtocolRequirement *reqBase,
                                      const ProtocolRequirement *assocType) {
                                      
(lldb) reg read
General Purpose Registers:
        x0 = 0x0000000000000000
        x1 = 0x0000000145d36800  ; wtable
        x2 = 0x0000000105b36060  DemoKit`full type metadata for __C.NSAttributedStringKey + 16 ; conformingType
        x3 = 0x000000018790dc88  libswiftCore.dylib`protocol requirements base descriptor for Swift.RawRepresentable ; reqBase
        x4 = 0x000000018790dc90  libswiftCore.dylib`associated type descriptor for Swift.RawRepresentable.RawValue ; assocType                           
   ...
  // Dig out the protocol.
  const ProtocolConformanceDescriptor *conformance = wtable->getDescription();
  const ProtocolDescriptor *protocol = conformance->getProtocol();

  // Extract the mangled name itself.
  StringRef mangledName =
    Demangle::makeSymbolicMangledNameStringRef(mangledNameBase);

  // Demangle the associated type.
  TypeLookupErrorOr<TypeInfo> result;
  if (inProtocolContext) {
    ...
  } else {
    // The generic parameters in the associated type name are those of the
    // conforming type.

    // For a class, chase the superclass chain up until we hit the
    // type that specified the conformance.
    auto originalConformingType = findConformingSuperclass(conformingType,
                                                           conformance);
    SubstGenericParametersFromMetadata substitutions(originalConformingType);
    ...
}

上面 frame 1 异常的 conformance 是通过本次入口的 x1/wtable 进行 getDescription() 调用获取的

  • 相关 Swift 源码
C++ 复制代码
template <typename Runtime>
class TargetWitnessTable {
  /// The protocol conformance descriptor from which this witness table
  /// was generated.
  ConstTargetMetadataPointer<Runtime, TargetProtocolConformanceDescriptor>
    Description;

public:
  const TargetProtocolConformanceDescriptor<Runtime> *getDescription() const {
    return Description;
  }
};

可以得知这里的 description 就是对应的 [wtable+0x0]

在函数入口处我们已确认 x1/wtable = 0x0000000145d36800,使用 memory read 查看对应的内存地址,并用 disassemble -a [$x1+0x0] 确认对应的值

PlainText 复制代码
(lldb) mem read 0x0000000145d36800 -fx -s8
0x145d36800: 0x00000001058fdf30 0x000000010584182b
0x145d36810: 0x0000000104874608 0x00000001048746a4
0x145d36820: 0x00000001059db2e0 0x0000000104874720
0x145d36830: 0x0000000000000000 0x0000000000000000

(lldb) dis -a 0x00000001058fdf30
DemoKit`protocol conformance descriptor for __C.NSRunLoopMode : Swift.RawRepresentable in __C_Synthesized:

发现这里上游传入的 x1/wtable 异常为 PWT NSRunLoopMode**: **RawRepresentable,因此会导致后续的 findConformingSuperclass 结果为空

同时对比加上magic function不会crash的相关结果为正常的 PWT NSAttributedStringKey: RawRepresentable

PlainText 复制代码
(lldb) dis -a 0x0000000101879ed8
Demo`protocol conformance descriptor for __C.NSAttributedStringKey : Swift.RawRepresentable in __C_Synthesized:

综上说明是上游 Frame 3 调用 Frame 2 时传入了错误的 x1 导致

[Skip] Frame 3

Frame 3 swift_getAssociatedTypeWitness

  • 相关 Swift 源码
C++ 复制代码
MetadataResponse
swift::swift_getAssociatedTypeWitness(MetadataRequest request,
                                      WitnessTable *wtable,
                                      const Metadata *conformingType,
                                      const ProtocolRequirement *reqBase,
                                      const ProtocolRequirement *assocType) {
  assert(assocType->Flags.getKind() ==
           ProtocolRequirementFlags::Kind::AssociatedTypeAccessFunction);

  // If the low bit of the witness is clear, it's already a metadata pointer.
  unsigned witnessIndex = assocType - reqBase;
  auto *witnessAddr = &((const AssociatedTypeWitness *)wtable)[witnessIndex];
  auto witness = witnessAddr->load(std::memory_order_acquire);

#if SWIFT_PTRAUTH
  uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator();
  witness = ptrauth_auth_data(witness, swift_ptrauth_key_associated_type,
                              ptrauth_blend_discriminator(witnessAddr,
                                                          extraDiscriminator));
#endif

  if (SWIFT_LIKELY((reinterpret_cast<uintptr_t>(witness) &
                    ProtocolRequirementFlags::AssociatedTypeMangledNameBit) ==
                   0)) {
    // Cached metadata pointers are always complete.
    return MetadataResponse{(const Metadata *)witness, MetadataState::Complete};
  }

  return swift_getAssociatedTypeWitnessSlow(request, wtable, conformingType,
                                            reqBase, assocType);
}

这里没有对 x1/wtable 进行任何改动,透传了上游的 x1/wtable 参数

[Skip] Frame 4

Frame 4 Swift._SwiftNewtypeWrapper._rawHashValue

C 复制代码
libswiftCore.dylib`Swift._SwiftNewtypeWrapper< where τ_0_0: Swift.Hashable, τ_0_0.Swift.RawRepresentable.RawValue: Swift.Hashable>._rawHashValue(seed: Swift.Int) -> Swift.Int:
    0x18756e178 <+0>:   pacibsp 
    0x18756e17c <+4>:   stp    x26, x25, [sp, #-0x50]!
    0x18756e180 <+8>:   stp    x24, x23, [sp, #0x10]
    0x18756e184 <+12>:  stp    x22, x21, [sp, #0x20]
    0x18756e188 <+16>:  stp    x20, x19, [sp, #0x30]
    0x18756e18c <+20>:  stp    x29, x30, [sp, #0x40]
    0x18756e190 <+24>:  add    x29, sp, #0x40
    0x18756e194 <+28>:  mov    x19, x4
    0x18756e198 <+32>:  mov    x21, x1
    0x18756e19c <+36>:  mov    x22, x0
    0x18756e1a0 <+40>:  ldr    x23, [x3, #0x8]
    0x18756e1a4 <+44>:  adrp   x3, 927
    0x18756e1a8 <+48>:  add    x3, x3, #0xc88            ; protocol requirements base descriptor for Swift.RawRepresentable
    0x18756e1ac <+52>:  add    x4, x3, #0x8
    0x18756e1b0 <+56>:  mov    x0, #0x0
    0x18756e1b4 <+60>:  mov    x1, x23
    0x18756e1b8 <+64>:  mov    x2, x21
    0x18756e1bc <+68>:  bl     0x1877a2678               ; swift_getAssociatedTypeWitness

x1 = x23 = [x3+0x8]

[Skip] Frame 5

Frame 5 protocol witness for Hashable._rawHashValue(seed:) in conformance NSRunLoopMode

理论上不应该在这里触发这个符号

C++ 复制代码
DemoKit`protocol witness for Hashable._rawHashValue(seed:) in conformance NSRunLoopMode:
    0x1028c47e8 <+0>:  sub    sp, sp, #0x30
    0x1028c47ec <+4>:  stp    x29, x30, [sp, #0x20]
    0x1028c47f0 <+8>:  add    x29, sp, #0x20
    0x1028c47f4 <+12>: stur   x0, [x29, #-0x8]
    0x1028c47f8 <+16>: str    x1, [sp, #0x8]
    0x1028c47fc <+20>: str    x2, [sp, #0x10]
    0x1028c4800 <+24>: bl     0x1028c4a00               ; lazy protocol witness table accessor for type __C.NSRunLoopMode and conformance __C.NSRunLoopMode : Swift._SwiftNewtypeWrapper in __C_Synthesized at <compiler-generated>
    0x1028c4804 <+28>: ldr    x1, [sp, #0x8]
    0x1028c4808 <+32>: ldr    x2, [sp, #0x10]
    0x1028c480c <+36>: mov    x3, x0
    0x1028c4810 <+40>: ldur   x0, [x29, #-0x8]
    0x1028c4814 <+44>: adrp   x4, 4731
    0x1028c4818 <+48>: ldr    x4, [x4, #0x5e0]
    0x1028c481c <+52>: bl     0x103830e18               ; symbol stub for: Swift._SwiftNewtypeWrapper< where τ_0_0: Swift.Hashable, τ_0_0.Swift.RawRepresentable.RawValue: Swift.Hashable>._rawHashValue(seed: Swift.Int) -> Swift.Int
->  0x1028c4820 <+56>: ldp    x29, x30, [sp, #0x20]
    0x1028c4824 <+60>: add    sp, sp, #0x30
    0x1028c4828 <+64>: ret    

预期行为 / 加入magic()函数后不会崩的crash对应的Frame

  • protocol witness for Hashable._rawHashValue(seed:) in conformance NSAttributeString:

Frame 6

Frame 6 _NativeDictionary.init(_unsafeUninitializedCapacity:allowingDuplicates:initializingWith:) ()

C 复制代码
Foundation`Swift._NativeDictionary.init(_unsafeUninitializedCapacity: Swift.Int, allowingDuplicates: Swift.Bool, initializingWith: (Swift.UnsafeMutableBufferPointer<τ_0_0>, Swift.UnsafeMutableBufferPointer<τ_0_1>) -> Swift.Int) -> Swift._NativeDictionary<τ_0_0, τ_0_1>:
    0x187971924 <+0>:    pacibsp 
    ...
    0x187971bdc <+696>:  bl     0x18c862a10

先添加 BinaryPodA.SymbolB 的符合断点,然后给 0x18c862a10 添加断点并进入

C++ 复制代码
->  0x18c862a14: add    x16, x16, #0xa78
(lldb) reg read x16
     x16 = 0x000000018774aa78  libswiftCore.dylib`dispatch thunk of Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int
    0x18c862a18: br     x16
    0x18c862a1c: brk    #0x1
    0x18c862a20: adrp   x16, -20760
    0x18c862a24: add    x16, x16, #0xa64          ; dispatch thunk of Swift.Hashable.hash(into: inout Swift.Hasher) -> ()
    0x18c862a28: br     x16
    0x18c862a2c: brk    #0x1
    0x18c862a30: adrp   x16, -21058

继续给 x16 对应的地址 0x000000018774aa78** **添加断点,然后进入

C 复制代码
libswiftCore.dylib`dispatch thunk of Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int:
->  0x18774aa78 <+0>:  mov    x3, x2
    0x18774aa7c <+4>:  ldr    x4, [x3, #0x20]!
    0x18774aa80 <+8>:  mov    x16, x3
    0x18774aa84 <+12>: movk   x16, #0xc41a, lsl #48
    
(lldb) reg read x4 x16
      x4 = 0x0000000102ef2c7c  DemoKit`protocol witness for Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int in conformance __C.NSAttributedStringKey : Swift.Hashable in __C_Synthesized
     x16 = 0xc41a000131d36108

    0x18774aa88 <+16>: braa   x4, x16

这里的 x4 目前为止看起来仍然正常,继续给 x4 对应的地址添加断点并进入

C 复制代码
DemoKit`protocol witness for Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int in conformance __C.NSAttributedStringKey : Swift.Hashable in __C_Synthesized:
->  0x102ef2c7c <+0>: b      0x1027887e8               ; protocol witness for Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int in conformance __C.NSRunLoopMode : Swift.Hashable in __C_Synthesized at <compiler-generated>

发现了异常的情况是 witness _rawHashValue of PWT NSAttributedStringKey: Hashable 走了 NSRunLoopMode 的对应实现

初步结论是compiler/linker在优化中将二者实现合并到一起了

Assembly 复制代码
                     _$sSo13NSRunLoopModeaSHSCSH13_rawHashValue4seedS2i_tFTW:        // protocol witness for Swift.Hashable._rawHashValue(seed: Swift.Int) -> Swift.Int in conformance __C.NSRunLoopMode : Swift.Hashable in __C_Synthesized
00000001001a87e8         sub        sp, sp, #0x30                               ; CODE XREF=_$sSo38UIApplicationOpenExternalURLOptionsKeyaSHSCSH13_rawHashValue4seedS2i_tFTW, _$sSo21NSAttributedStringKeyaSHSCSH13_rawHashValue4seedS2i_tFTW, _$sSo18NSNotificationNameaSHSCSH13_rawHashValue4seedS2i_tFTW
00000001001a87ec         stp        fp, lr, [sp, #0x20]
00000001001a87f0         add        fp, sp, #0x20
00000001001a87f4         stur       x0, [fp, var_8]
00000001001a87f8         str        x1, [sp, #0x20 + var_18]
00000001001a87fc         str        x2, [sp, #0x20 + var_10]
00000001001a8800         bl         _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl ; lazy protocol witness table accessor for type __C.NSRunLoopMode and conformance __C.NSRunLoopMode : Swift._SwiftNewtypeWrapper in __C_Synthesized
00000001001a8804         ldr        x1, [sp, #0x20 + var_18]
00000001001a8808         ldr        x2, [sp, #0x20 + var_10]
00000001001a880c         mov        x3, x0
00000001001a8810         ldur       x0, [fp, var_8]
00000001001a8814         adrp       x4, #0x101423000                            ; 0x1014235e0@PAGE
00000001001a8818         ldr        x4, [x4, #0x5e0]                            ; 0x1014235e0@PAGEOFF, _$sSSSHsWP_1014235e0,_$sSSSHsWP
00000001001a881c         bl         imp___stubs__$ss20_SwiftNewtypeWrapperPsSHRzSH8RawValueSYRpzrlE08_rawHashE04seedS2i_tF ; (extension in Swift):Swift._SwiftNewtypeWrapper< where A: Swift.Hashable, A.RawValue: Swift.Hashable>._rawHashValue(seed: Swift.Int) -> Swift.Int
00000001001a8820         ldp        fp, lr, [sp, #0x20]
00000001001a8824         add        sp, sp, #0x30
00000001001a8828         ret
                        ; endp
Assembly 复制代码
                     _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl:        // lazy protocol witness table accessor for type __C.NSRunLoopMode and conformance __C.NSRunLoopMode : Swift._SwiftNewtypeWrapper in __C_Synthesized
00000001001a8a00         sub        sp, sp, #0x20                               ; CODE XREF=_$sSo13NSRunLoopModeas21_ObjectiveCBridgeableSCsACP09_bridgeToD1C01_D5CTypeQzyFTW+16, _$sSo13NSRunLoopModeas21_ObjectiveCBridgeableSCsACP016_forceBridgeFromD1C_6resulty01_D5CTypeQz_xSgztFZTW+24, _$sSo13NSRunLoopModeas21_ObjectiveCBridgeableSCsACP024_conditionallyBridgeFromD1C_6resultSb01_D5CTypeQz_xSgztFZTW+24, _$sSo13NSRunLoopModeas21_ObjectiveCBridgeableSCsACP026_unconditionallyBridgeFromD1Cyx01_D5CTypeQzSgFZTW+24, _$sSo13NSRunLoopModeas35_HasCustomAnyHashableRepresentationSCsACP03_toefG0s0fG0VSgyFTW+28, _$sSo13NSRunLoopModeaSHSCSH9hashValueSivgTW+20, _$sSo13NSRunLoopModeaSHSCSH4hash4intoys6HasherVz_tFTW+24, _$sSo13NSRunLoopModeaSHSCSH13_rawHashValue4seedS2i_tFTW+24
00000001001a8a04         stp        fp, lr, [sp, #0x10]
00000001001a8a08         add        fp, sp, #0x10
00000001001a8a0c         adrp       x8, #0x1015d9000
00000001001a8a10         ldr        x0, [x8, #0x2a8]                            ; _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWL
00000001001a8a14         subs       x8, x0, #0x0
00000001001a8a18         cset       w8, ne
00000001001a8a1c         str        x0, [sp, #0x10 + var_8]
00000001001a8a20         tbnz       w8, 0x0, loc_1001a8a60

00000001001a8a24         b          loc_1001a8a28

                     loc_1001a8a28:
00000001001a8a28         mov        w8, #0xff                                   ; CODE XREF=_$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl+36
00000001001a8a2c         mov        x0, x8                                      ; argument #1 for method _$sSo13NSRunLoopModeaMa
00000001001a8a30         bl         _$sSo13NSRunLoopModeaMa                     ; type metadata accessor for __C.NSRunLoopMode
00000001001a8a34         ldr        x2, [sp, #0x10 + var_10]                    ; argument "instantiationArgs" for method imp___stubs__swift_getWitnessTable
00000001001a8a38         mov        x1, x0                                      ; argument "type" for method imp___stubs__swift_getWitnessTable
00000001001a8a3c         adrp       x0, #0x10130f000                            ; 0x10130f318@PAGE
00000001001a8a40         add        x0, x0, #0x318                              ; 0x10130f318@PAGEOFF, argument "conf" for method imp___stubs__swift_getWitnessTable, _$sSo13NSRunLoopModeas20_SwiftNewtypeWrapperSCMc
00000001001a8a44         bl         imp___stubs__swift_getWitnessTable          ; swift_getWitnessTable
00000001001a8a48         adrp       x9, #0x1015d9000                            ; 0x1015d92a8@PAGE
00000001001a8a4c         add        x9, x9, #0x2a8                              ; 0x1015d92a8@PAGEOFF, _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWL
00000001001a8a50         mov        x8, x0
00000001001a8a54         stlr       x8, [x9]
00000001001a8a58         str        x0, [sp, #0x10 + var_8]
00000001001a8a5c         b          loc_1001a8a60

                     loc_1001a8a60:
00000001001a8a60         ldr        x0, [sp, #0x10 + var_8]                     ; CODE XREF=_$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl+32, _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl+92
00000001001a8a64         ldp        fp, lr, [sp, #0x10]
00000001001a8a68         add        sp, sp, #0x20
00000001001a8a6c         ret
                        ; endp
Assembly 复制代码
                     _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWL:        // lazy protocol witness table cache variable for type __C.NSRunLoopMode and conformance __C.NSRunLoopMode : Swift._SwiftNewtypeWrapper in __C_Synthesized
00000001015d92a8         dq         0x0000000000000000                          ; DATA XREF=_$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl+16, _$sSo13NSRunLoopModeaABs20_SwiftNewtypeWrapperSCWl+76
Assembly 复制代码
                     _$sSo21NSAttributedStringKeyaABs20_SwiftNewtypeWrapperSCWL:        // lazy protocol witness table cache variable for type __C.NSAttributedStringKey and conformance __C.NSAttributedStringKey : Swift._SwiftNewtypeWrapper in __C_Synthesized
00000001015dec68         dq         0x0000000000000000                          ; DATA XREF=_$sSo21NSAttributedStringKeyas35_HasCustomAnyHashableRepresentationSCsACP03_toefG0s0fG0VSgyFTW+68

查看对应实现,发现里面的实现逻辑包含一个 lazy protocol witness table cache variable for type __C.NSRunLoopMode and conformance __C.NSRunLoopMode : Swift._SwiftNewtypeWrapper in __C_Synthesized

而 NSAttributedStringKey 也含有一个自己的 lazy protocol witness table cache variable,说明这里不应该进行合并

符号分析

发现出现问题的二者都是原本定义在 NSFoundation 中的 NSString * 的 typedef 并通过 swift_newtype(struct) 重新以 struct 导入到了 Swift

Swift 复制代码
/// An implementation detail used to implement support importing
/// (Objective-)C entities marked with the swift_newtype Clang
/// attribute.
public protocol _SwiftNewtypeWrapper
: RawRepresentable, _HasCustomAnyHashableRepresentation { }

extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue: Hashable {
  /// The hash value.
  @inlinable
  public var hashValue: Int {
    return rawValue.hashValue
  }

  /// Hashes the essential components of this value by feeding them into the
  /// given hasher.
  ///
  /// - Parameter hasher: The hasher to use when combining the components
  ///   of this instance.
  @inlinable
  public func hash(into hasher: inout Hasher) {
    hasher.combine(rawValue)
  }

  @inlinable
  public func _rawHashValue(seed: Int) -> Int {
    return rawValue._rawHashValue(seed: seed)
  }
}

修复方案

需要对上游的 compiler / linker 进行修复,通过咨询相关同学发现之前 Lark 等业务方也发现过类似问题

并最终在llvm上进行了修复,在内部工具链上得到了验证

长期方案为确认相关修复是否已合入上游并被 Xcode Toolchain 使用以及是否有其他 bug 需要进一步 patch

短期方案为使用类似 magic() 函数类似的方案,通过主动调用来规避同名跳板符号异常merge的bug进行绕过

相关推荐
OKXLIN4 小时前
IOS UITextField 无法隐藏键盘问题
ios·objective-c
AL.千灯学长12 小时前
DeepSeek接入Siri(已升级支持苹果手表)完整版硅基流动DeepSeek-R1部署
人工智能·gpt·ios·ai·苹果vision pro
openinstall全渠道统计1 天前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
貂蝉空大2 天前
uni-app开发安卓和ios app 真机调试
android·ios·uni-app
胖虎12 天前
iOS 中的圆角与平滑圆角:从新特性到老项目适配
ios·圆角·平滑圆角·cornercurve
志飞2 天前
ios UICollectionView使用自定义UICollectionViewCell
ios·collectionview·自定义cell
Neo Evolution2 天前
Flutter与移动开发的未来:谷歌的技术愿景与实现路径
android·人工智能·学习·ios·前端框架·webview·着色器
没头脑的ht3 天前
ios App的启动过程和启动优化
ios
敲代码的鱼哇3 天前
设备唯一ID获取,支持安卓/iOS/鸿蒙Next(uni-device-id)UTS插件
android·ios·uniapp·harmonyos