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进行绕过

相关推荐
权咚7 小时前
阿权的开发经验小集
git·ios·xcode
用户098 小时前
TipKit与CloudKit同步完全指南
ios·swift
法的空间14 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
2501_9159184115 小时前
iOS 上架全流程指南 iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传 ipa 与审核实战经验分享
android·ios·小程序·uni-app·cocoa·iphone·webview
00后程序员张16 小时前
iOS App 混淆与加固对比 源码混淆与ipa文件混淆的区别、iOS代码保护与应用安全场景最佳实践
android·安全·ios·小程序·uni-app·iphone·webview
东坡肘子17 小时前
完成 Liquid Glass 的适配了吗?| 肘子的 Swift 周报 #0102
swiftui·swift·apple
Magnetic_h1 天前
【iOS】设计模式复习
笔记·学习·ios·设计模式·objective-c·cocoa
00后程序员张1 天前
详细解析苹果iOS应用上架到App Store的完整步骤与指南
android·ios·小程序·https·uni-app·iphone·webview
前端小超超1 天前
capacitor配置ios应用图标不同尺寸
ios·蓝桥杯·cocoa
2501_915106321 天前
Xcode 上传 ipa 全流程详解 App Store 上架流程、uni-app 生成 ipa 文件上传与审核指南
android·macos·ios·小程序·uni-app·iphone·xcode