Swift 二进制组件getTypeContextDescr

背景

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

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

崩溃现场的堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

1
2
3
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

  • 相关汇编代码
1
2
3
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 源码
1
2
3
4
5
6
C复制代码  ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>
getTypeContextDescriptor() const {
switch (getKind()) {
...
}
}

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

Frame 1

Frame 1 SubstGenericParametersFromMetadata::SubstGenericParametersFromMetadata

  • 相关汇编代码
1
2
3
4
5
6
7
8
9
10
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 源码
1
2
3
4
5
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

  • 相关汇编代码
1
2
3
4
5
6
7
8
9
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 处

1
2
3
4
5
6
7
8
9
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 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 源码
1
2
3
4
5
6
7
8
9
10
11
12
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] 确认对应的值

1
2
3
4
5
6
7
8
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

1
2
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 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:) ()

1
2
3
4
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 添加断点并进入

1
2
3
4
5
6
7
8
9
10
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** **添加断点,然后进入

1
2
3
4
5
6
7
8
9
10
11
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 对应的地址添加断点并进入

1
2
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在优化中将二者实现合并到一起了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
1
2
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
1
2
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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进行绕过

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%