背景
在使用内网的某二进制组件 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进行绕过