圖/unsplash
相對于Win本來說,Mac的優勢在于生態系統的強大,其內置的辦公軟件Pages、Numbers、Keynote也可以與office辦公套件相媲美。一脈相承的App Store為Mac提供非常豐富的優質軟件資源,如果你不習慣于使用Pages的話,也可以在App Store上下載Micro office或者WPS辦公軟件。
圖/unsplash
盡管好用,但還是沒有讓我覺得滿意,因為入手的時機比較早,所以MacBook Air 17是沒有指紋識別和人臉識別功能的,也就是說每次我都得手動輸入密碼來解鎖電腦。現在手機都是人臉識別無感解鎖了,有沒有辦法讓電腦也實現這個功能呢?
辦法當然是有的,比如借助這款叫做【Near Lock】的軟件就能實現電腦的無感解鎖。當然了,想要實現這個功能的前提是你得有一臺蘋果電腦和一部蘋果手機。
圖/unsplash
Mac我已經準備好了,我的手機是iPhone 11,正好符合要求。首先我們在手機和電腦上分別下載安裝好Near Lock。Mac版的Near Lock需要到其的網站上下載,這里不在多做贅述。
在PC和手機端上打開Near Lock,然后按照軟件的提示進行連接即可,操作過程非常的簡單。
在連接上手機之后還可以進行各種自定以功能的設置,比如在設定范圍內鎖定和解鎖電腦、在手機上使用指紋/人臉識別來解鎖電腦等等,功能非常的強大。經過實測,實際鎖定和解鎖的距離需要比系統設置的更愿意一些,在系統判定你的iPhone已經超過了預設距離就會馬上鎖定電腦,進入預設距離時手機會提示解鎖電腦,非常的方便好用。
但是在我看來,這些功能都還趨于常規,進階操作的話還是說捷徑和Siri的使用。為了操作更方便,Near Lock還帶來了兩個捷徑指令-鎖定Mac和解鎖Mac,這兩條快捷指令可以按照自己的喜好來自定義命名和錄制語音指令。
從此鎖定和解鎖電腦都不需要自己動手了,真的是方便又實用。這個技能你get到了嗎?
【部分圖片來自unsplash,若有侵權,務請告知刪除!謝謝】
期,許多網友反饋說在Mac系統上無法輸入密碼,這樣就無法***任何界面,這究竟是什么情況?檢查鍵盤也都是好好的,其實這個主要的問題是發生在終端上。針對此疑問,小編就來說下蘋果Mac系統無法輸入密碼的解決方法。
1、點擊之后,輸入密碼,會有白點顯示的。
2、例如要在終端輸入的時候,則不是這樣子的,那么如何在終端輸入密碼的。點擊啟動臺,點擊其他。
3、點擊終端,按鍵盤shift。
4、把輸入法切換到英文,如下圖,右上角顯示英。
5、通常你看到passwd!之后,只需要輸入即可,在passwd!輸入期間不會看到有任何反應的,無白色的點的。這個是MacUnix等系統的特色,輸入看不到,無反應,實際上已經輸入了的。輸入之后,回車鍵即可。沒有提示error,說明密碼輸入正確。
以上小編和大家詳細介紹蘋果Mac系統無法輸入密碼的解決方法,設置步驟簡單,需要的朋友快去操作試試吧!想了解更多關于Mac相關內容,請關注macz.com吧!
♀? 編者按:本文作者是螞蟻集團客戶端工程師巴樂,通過逆向分析發現了 iOS 16 系統鍵盤存在重大 Bug,可能導致使用到鍵盤的業務場景出現嚴重 Crash。在支付寶 App 近期版本 10.5.16.6000 上,巴樂用匯編重新實現了一套 iOS 16 系統鍵盤 tryLock 方法后,問題得到完全修復,該版本上的對應 Crash 已降到 0。本文記錄了該問題解決的完整過程,包括問題發現、分析、修復以及驗證,歡迎查閱與交流~
在螞蟻集團內部,支付寶技術部及螞蟻終端技術委員會聯合發起了“技術挑戰英雄榜”活動,通過張榜一系列技術難題,尋找那些富有激情、敢于挑戰的同學,揭榜解題,攻克頑疾!
在難題榜中,有螞蟻內部同學張榜反饋了 iOS 支付寶 App Top 1 的 iOS 16 鍵盤 Crash(下文可簡稱“鍵盤 Crash“),即下圖 1 的 issue 1。該 Crash 量級大且持續時間長,線下不好復現又不好排查,對線上業務影響很大,急需攻堅。
本人基于對客戶端運行時技術的濃厚興趣,揭榜領題,挑戰解決該 Crash。
圖 1 螞蟻內部的技術挑戰英雄榜
Crash 日志關鍵信息如下:
Incident Identifier: 7C53A274-4184-4E38-B27E-07B4E1335277
CrashReporter Key:
Hardware Model: iPhone13 4
Process: AlipayWallet [89329]
Path: /private/var/containers/Bundle/Application/C5F00AEC-B96F-4BF1-8C9C-25B67BCA301E/AlipayWallet.app/AlipayWallet
Identifier: com.alipay.iphoneclient
Version: 10.5.0 (10.5.0.6000)
Code Type: ARM-64
Parent Process: [1]
Date/Time: 2023-08-30 04:37:48 +0000
OS Version: iPhone OS 16.6 (20G75)
Report Version: 104
Exception Type: SIGSEGV
Exception Codes: SEGV_MAPERR at 0x2ab3106e0
Crashed Thread: 0
Thread 0 Crashed:
0 libobjc.A.dylib 0x00000001a5183a7c _objc_retain :16 (in libobjc.A.dylib)
1 UIKitCore 0x00000001aed4d4d4 -[UIKeyboardTaskQueue performDeferredTaskIfIdle] :32 (in UIKitCore)
2 UIKitCore 0x00000001ae533148 -[UIKeyboardTaskQueue continueExecutionOnMainThread] :376 (in UIKitCore)
3 Foundation 0x00000001a63e878c ___NSThreadPerformPerform :264 (in Foundation)
4 CoreFoundation 0x00000001ac1ca128 ___CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ :28 (in CoreFoundation)
5 CoreFoundation 0x00000001ac1d67b4 ___CFRunLoopDoSource0 :176 (in CoreFoundation)
6 CoreFoundation 0x00000001ac15b648 ___CFRunLoopDoSources0 :340 (in CoreFoundation)
7 CoreFoundation 0x00000001ac1710d4 ___CFRunLoopRun :828 (in CoreFoundation)
8 CoreFoundation 0x00000001ac1763ec _CFRunLoopRunSpecific :612 (in CoreFoundation)
9 GraphicsServices 0x00000001e768c35c _GSEventRunModal :164 (in GraphicsServices)
10 UIKitCore 0x00000001ae502f58 -[UIApplication _run] :888 (in UIKitCore)
11 UIKitCore 0x00000001ae502bbc _UIApplicationMain :340 (in UIKitCore)
12 AlipayWallet 0x00000001074d539c main main.m:124 (in AlipayWallet)
13 ??? 0x00000001cb6a8dec 0x0000000000000000 + 0
Thread 1:
0 libsystem_kernel.dylib 0x00000001eb0b6ca4 _mach_msg2_trap :8 (in libsystem_kernel.dylib)
...
Thread State:
x8:0x0000000202aa4820 x9:0x0000000282d64100 lr:0x00000001aed4d548 fp:0x000000016b032700
x10:0x0000000000000000 x12:0x0000000000ec0e80 x11:0x000000000000001f x14:0x0100000202aaecc9
x13:0x0000010000000100 x16:0x0000bb12ab3106c0 sp:0x000000016b0326e0 x15:0x0000000202aaecc8
x18:0x0000000000000000 x17:0x00000002ab3106c0 x19:0x0000000283463d00 cpsr:0x0000000000001000
pc:0x00000001a5183a7c x21:0x0000000000000001 x20:0x0000000000000000 x0:0x0000000286f706c0
x23:0x0000000114841058 x1:0x0000000000000000 x22:0x000000028312e2c0 x2:0x0000000000000000
x25:0x0000000000000002 x3:0x00000002041bc480 x24:0x0000000000000000 x4:0x0000000000000000
x27:0x00000000211200d5 x5:0x0000000000000001 x26:0x0000000000000000 x6:0x00000001b55fb2c5
x7:0x00000001b55fb2b9 x28:0x0000000000000001
Binary Images:
0x0000000104dcc000 - 0x000000010f6f3fff AlipayWallet arm64 <fa235f8a8e253b4d81e7e6a4fecdd4c6> /private/var/containers/Bundle/Application/C5F00AEC-B96F-4BF1-8C9C-25B67BCA301E/AlipayWallet.app/AlipayWallet
...
0x00000001a5180000 - 0x00000001a51c3f9f libobjc.A.dylib arm64e <eb7faf215c9f37848907affa6d92bc3b> /usr/lib/libobjc.A.dylib
...
0x00000001ae166000 - 0x00000001af98afff UIKitCore arm64e <7d57a1d1856f338d97db880c4ec8b02e> /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore
...
提取 Crash 關鍵信息(后續分析基于該信息):
鍵盤 Crash 日 PV 一直處于大幾百次,持續至少半年多,從操作系統版本分布來看僅在 iOS 16 上出現(覆蓋所有機型)。
圖 2 鍵盤 Crash 日 PV 趨勢圖
圖 3 鍵盤 Crash 在不同機型及操作系統的量級分布
從 Crash 日志棧頂的objc_retain函數關鍵字和量級分布情況來看,該 Crash 很可能是由 iOS 16 系統鍵盤控件的內存管理異常導致。
下文分析推演涉及的知識點或技能:
——計算 Crash 函數的偏移
因 iOS 運行時加載到內存的 Image 的起始地址是動態的(對應 Binary Images 列表中的起始地址),但某指令地址與所屬 Image 的起始地址的偏移是固定的,所以可根據該偏移來查看 Crash 時是哪條指令。
—— Xcode 設置斷點模擬現場
圖 4 設置斷點模擬現場
從圖 4 的第 11 步可知 Crash 的直接原因是objc_retain的對象野指針了,導致讀取內存異常而觸發 Crash。
圖 5 查看上一層函數棧
從圖 5 可知兩點:
小結:UIKeyboardTaskQueue類的NSMutableArray類型的成員變量是關鍵數組(在實例對象偏移0x20的位置),懷疑是多線程讀寫該數組導致的。那么該成員變量名是啥,UIKeyboardTaskQueue類又是如何保證安全使用該數組的呢?
—— 獲取UIKeyboardTaskQueue類的全部信息
借助螞蟻自研的DebugKit.framework調試模塊可在運行時導出UIKeyboardTaskQueue類所有的實例方法、類方法、property和ivars成員變量。
圖 6 獲取 UIKeyboardTaskQueue 類的基礎信息
從圖 6 可知兩點:
圖 7 獲取 UIKeyboardTaskQueue 類的所有方法實現
圖 7 中第 2 步涉及的fetch_class_text_from_all.sh見下文附件中腳本源碼。
小結:通過分析圈定排查范圍在UIKeyboardTaskQueue類內,借助腳本可一鍵導出其所有方法的匯編,為進一步研究_deferredTasks和_lock的關系做基礎。
—— 研究_deferredTasks和_lock關系
理清以下重要的兩個關系:
圖 7 第 2 步導出的UIKeyboardTaskQueue的所有方法實現都是匯編的,為理清對_deferredTasks對象的所有讀寫有哪些指令,分別在哪些方法中(UIKeyboardTaskQueue實例對象偏移0x20的位置,該地址下存儲的 8 字節地址才是_deferredTasks對象),需要在文件中全文搜索正則表達式x.{1,2}, #0x20篩選出所有引用_deferredTasks的指令以及所屬方法,操作如下圖 8(Sublime Text)。
圖 8 全文搜索正則表達式的樣例
在匯編層面,面向對象語言中方法的第一個入參是self(C++ 稱this,Objective-C 稱self),存放在x0寄存器上,所以僅篩選出偏移是從方法入參時的x0或x0備份(如mov x19, x0,x19就是備份了x0的值)開始的,最后整理出所有UIKeyboardTaskQueue對_deferredTasks有引用并讀寫的指令及所屬方法,如下。
注:
-[UIKeyboardTaskQueue isEmpty]:
...
0000000189c816a4 ldr x0, [x19, #0x20] 讀
0000000189c816a8 bl _objc_msgSend$count
...
-[UIKeyboardTaskQueue finishExecution]:
...
00000001894677a8 ldr x0, [x19, #0x20] 讀
00000001894677ac bl _objc_msgSend$count
...
-[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]:
...
0000000189c8152c ldr x0, [x0, #0x20] 讀
0000000189c81530 bl _objc_msgSend$count
0000000189c81534 cbz x0, 0x189c81518
0000000189c81538 ldr x0, [x19, #0x20] 讀
0000000189c8153c mov x2, #0x0
0000000189c81540 bl "_objc_msgSend$objectAtIndex:"
0000000189c81544 bl 0x18c9deec0 Crash在這行
...
0000000189c81558 ldr x0, [x19, #0x20] 寫:刪除item
0000000189c8155c mov x2, #0x0
0000000189c81560 bl "_objc_msgSend$removeObjectAtIndex:"
...
-[UIKeyboardTaskQueue continueExecutionOnMainThread]:
...
0000000189467130 ldr x0, [x19, #0x20] 讀
0000000189467134 bl _objc_msgSend$count
...
-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished]:
...
000000018952a810 ldr x0, [x19, #0x20] 讀
000000018952a814 bl _objc_msgSend$count
...
-[UIKeyboardTaskQueue addDeferredTask:]:
...
0000000189c81640 ldr x0, [x19, #0x20] 寫:添加item
0000000189c81644 ldr x2, [sp, #0x8]
0000000189c81648 bl "_objc_msgSend$addObject:"
...
-[UIKeyboardTaskQueue init]:
...
0000000189543024 ldr x8, [x19, #0x20] 讀
0000000189543028 str x0, [x19, #0x20] 寫:創建數組實例
...
-[UIKeyboardTaskQueue .cxx_destruct]:
...
0000000189c817f4 add x0, x19, #0x20 寫:銷毀
0000000189c817f8 mov x1, #0x0
0000000189c817fc bl 0x18a1a4c64 ; symbol stub for: _objc_storeStrong
...
讀_deferredTasks的方法有 6 個:
寫_deferredTasks的方法有 4 個:
在文件中全文搜索正則表達式x.{1,2}, #0x10篩選出所有引用_lock的指令以及所屬方法,操作類似上述的_deferredTasks;從上可知,UIKeyboardTaskQueue類對_lock的使用封裝成 4 個方法(忽略init創建和.cxx_destruct銷毀的兩個方法,該兩方法不會有并發問題),也就是方法使用_lock必定會調用這 4 個方法。
解鎖方法有 1 個:
加鎖方法有 3 個:
串聯上述_deferredTasks和_lock兩個角度的方法調用(忽略init創建和.cxx_destruct銷毀的兩個方法),從原匯編的關鍵方法中列出簡版的關系描述,如下圖 9。
圖 9 串聯 _deferredTasks 和 _lock 的關系
為方便理清鎖的對應關系,圖 9 中用紅色表示加鎖,綠色表示解鎖,從中可知:
小結:-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內有 Bug,導致存在鎖失效的情況,猜測在多線程下并發讀寫_deferredTasks時就會偶現 Crash。
圖 10 重新推演鍵盤 Crash 過程
按時間軸重新推演鍵盤 Crash 過程:
注:不同語言的編譯器對應的符號名的生成規則是不同的,C 語言只是在原函數名前加一個前綴“_”,如objc_retain(A),編譯后符號名是_objc_retain,而 C++ 語言會根據方法名加上參數名生成的符號名,如__ZNSt3__16vectorIdNS_9allocatorIdEEEixB6v15006Em。
按推演的邏輯用本地 Xcode 重新起個 Demo 驗證下(可用下文附件中 Demo 關鍵代碼),通過調用[self test_crash]可模擬出 tryLock 失敗時導致的 Crash(如調用[self test_ok]就不會出現 Crash),現場如下。
圖 11 模擬 tryLock 加鎖失敗而導致的 Crash
從 Xcode 的 Console 控制臺的日志中可以看到出現多線程并發添加到_deferredTasks數組的情況,在后續removeEntry_crash方法內出現了objc_retain野指針對象導致的 Crash,與上述推演的邏輯相符。
圖 12 對比不同 iOS 版本的實現
通過對比發現僅 iOS 16 上有問題,iOS 15 或 iOS 17 上 tryLock 失敗后都會立即return的,也就是為什么 Crash 僅出現在 iOS 16 的原因。從中我們可以看出在 iOS 17 上蘋果技術同學也發現了該 Bug 并做了修復。
該問題已提交至蘋果“反饋助理”(圖 13),但截至目前未得到其官方的 iOS 16 上的解決方案。
圖 13 “反饋助理”截圖
通過上述分析推演,iOS 16 鍵盤 Crash 的根因已查明,即-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內執行-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]嘗試加鎖失敗后,不return繼續向下執行讀寫不安全內存以及解鎖,導致存在鎖失效的情況,使得UIKeyboardTaskQueue成員變量_deferredTasks數組在多線程下出現并發添加UIKeyboardTaskEntry實例而引起野指針,導致最終 Crash。
注:該根因除了導致數組讀寫異常而 Crash,也可能導致其他變量的狀態不一致性,只是不一定表現為 Crash 而已,建議用本文方案修復。
明確根因后,解決方案就比較明確了,寫一個 App 內置補丁代碼使得-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內執行-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]嘗試加鎖失敗后,正常return即可。補丁方案有兩個:
最終,支付寶 App 基于穩定性的考慮,采用第 2 種補丁方案修復鍵盤 Crash。
圖 14 修復鍵盤 Crash 的補丁原理
在-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]實現以下邏輯:
源碼return語句,對應匯編的 4 步:
本文補丁方案的原理中,tryLock 失敗時就是通過:恢復fp和lr寄存器 + 恢復callee-saved寄存器 + 恢復sp寄存器 + 再次恢復fp和lr寄存器 + 再次恢復callee-saved寄存器 + 再次恢復sp寄存器 + ret指令 來模擬在-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法內return 2 次直接返回到-[UIKeyboardTaskQueue continueExecutionOnMainThread]的函數棧的上一層函數的。
有兩部分組成:
重寫-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法實現,對應下文附件中補丁源碼的 fix_UIKeyboardTaskQueue.S 文件。
圖 15 重寫 -[UIKeyboardTaskQueue tryLockWhenReadyForMainThread] 方法實現
借助+ (void)load方法在 App 啟動時執行的特點實現對-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法的 Hook,僅在 iOS 16 的 Arm64 架構上生效,對應下文附件中補丁源碼的 fix_UIKeyboardTaskQueue.m 文件。
圖 16 Hook 入口的代碼
于 2023.8.25 在支付寶 App 近期版本 10.5.16.6000 上全量開啟解決方案的開關后,該版本上的 Crash 日 PV 已經降到 0 了。
圖 17 支付寶 App 近期版本 10.5.16.6000 上鍵盤 Crash 日 PV
同時,支付寶 App 的全量版本(包括所有歷史版本)的鍵盤 Crash 日 PV 下降了近 90%,隨著更多用戶升級到支付寶 App 最新版本,預計會降到個位數。
圖 18 方案上線后鍵盤 Crash 日 PV 明顯下降的趨勢圖
最終該方案由驗收人確認有效,鍵盤 Crash 已解決,揭榜挑戰成功,附上一張挑戰成功捷報圖收個尾。
圖 19 螞蟻內部的技術英雄榜捷報
補丁源碼包括兩部分:fix_UIKeyboardTaskQueue.S 和 fix_UIKeyboardTaskQueue.m。使用時將該兩文件直接內置在 App 中即可,也可在 App 啟動時加開關控制 Hook 入口的時機。
#ifdef __arm64__
//
// fix_UIKeyboardTaskQueue.S
// fix_UIKeyboardTaskQueue
//
// Created by Alipay on 2023/8/10.
// Copyright ? 2023 Alipay. All rights reserved.
//
/**
原實現
-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]:
ldr x0, [x0, #0x10]
mov x2, #0x0
b "_objc_msgSend$tryLockWhenCondition:"
*/
// 重寫實現
.section __TEXT,__cstring,cstring_literals
tryLockWhenCondition.str:
.asciz "tryLockWhenCondition:"
.text
.align 4
.global _fix_UIKeyboardTaskQueue_tryLockWhenReadyForMainThread
.cfi_startproc
_fix_UIKeyboardTaskQueue_tryLockWhenReadyForMainThread:
stp x20, x19, [sp, #-0x20]!
stp x29, x30, [sp, #0x10]
add x29, sp, #0x10
mov x19, x0 ; self
adrp x0, tryLockWhenCondition.str@PAGE
add x0, x0, tryLockWhenCondition.str@PAGEOFF
bl _sel_registerName ; @selector(tryLockWhenCondition:)
mov x1, x0
ldr x0, [x19, #0x10] ; _lock
mov x2, #0x0
bl _objc_msgSend ; -[_lock tryLockWhenCondition:0]
ldp x29, x30, [sp, #0x10] ; 恢復fp和lr
ldp x20, x19, [sp], #0x20 ; 恢復callee-saved寄存器、并恢復sp
cbz x0, 1f
// 如tryLock成功,則繼續執行-[UIKeyboardTaskQueue continueExecutionOnMainThread]的指令
ret
// 如tryLock失敗,則模擬從-[UIKeyboardTaskQueue continueExecutionOnMainThread] return,不再繼續執行
1:
ldp x29, x30, [sp, #0x20] ; 恢復fp和lr
ldp x20, x19, [sp, #0x10] ; 恢復callee-saved寄存器
add sp, sp, #0x30 ; 恢復sp
autibsp ; Authenticate Instruction address
ret
.cfi_endproc
#endif
//
// fix_UIKeyboardTaskQueue.m
// fix_UIKeyboardTaskQueue
//
// Created by Alipay on 2023/9/4.
//
#ifdef __arm64__
#import <UIKit/UIKit.h>
#include <objc/runtime.h>
@interface fix_UIKeyboardTaskQueue : NSObject
@end
@implementation fix_UIKeyboardTaskQueue
+ (void)load {
extern BOOL fix_UIKeyboardTaskQueue_tryLockWhenReadyForMainThread(id self, SEL selector);
if (@available(iOS 16.0, *)) {
NSString *systemVersion=[[UIDevice currentDevice] systemVersion];
NSArray *verInfos=[systemVersion componentsSeparatedByString:@"."];
NSUInteger count=[verInfos count];
if (count >=2) {
if ([verInfos[0] isEqualToString:@"16"]) {
class_replaceMethod(objc_getClass("UIKeyboardTaskQueue"), sel_getUid("tryLockWhenReadyForMainThread"), (IMP)fix_UIKeyboardTaskQueue_tryLockWhenReadyForMainThread, "B16@0:8");
}
}
}
}
@end
#endif
//
// ViewController.m
// UIKeyboardTaskQueueDemo
//
// Created by Alipay on 2023/8/30.
//
#import "ViewController.h"
#include <objc/runtime.h>
// #import <DebugKit/DebugKit.h>
@interface ViewController ()
@end
@implementation ViewController {
NSMutableArray *_tasks;
NSMutableArray *_deferredTasks;
NSConditionLock *_lock;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 輸出UIKeyboardTaskQueue的所有實例方法和類方法
// dk_print_all_methods_of_class("UIKeyboardTaskQueue");
// 輸出UIKeyboardTaskQueue的所有property
// dk_print_all_properties("UIKeyboardTaskQueue");
// 輸出UIKeyboardTaskQueue的所有ivars
// dk_print_class_all_ivars("UIKeyboardTaskQueue");
UITextView *textView=[[UITextView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:textView];
[self test_crash];
[self test_ok];
}
- (void)unlock {
[_lock unlockWithCondition:0];
}
- (void)lock {
[_lock lock];
}
- (BOOL)tryLock {
return [_lock tryLockWhenCondition:0];
}
- (void)addEntry_ok {
[self lock];
[_deferredTasks addObject:[[NSObject alloc] init]];
NSLog(@"add, %lu", _deferredTasks.count);
[self unlock];
}
- (void)removeEntry_crash {
[self tryLock];
if (_deferredTasks.count) {
[_tasks addObject:[_deferredTasks objectAtIndex:0]];
if (_deferredTasks.count) {
[_deferredTasks removeObjectAtIndex:0];
NSLog(@"remove, %lu", _deferredTasks.count);
// NSLog(@"%@, %lu -[_deferredTasks removeObjectAtIndex:0]", [NSThread currentThread], _deferredTasks.count);
}
}
[self unlock];
}
- (void)removeEntry_ok {
if (![self tryLock]) return;
if (_deferredTasks.count) {
[_tasks addObject:[_deferredTasks objectAtIndex:0]];
if (_deferredTasks.count) {
[_deferredTasks removeObjectAtIndex:0];
NSLog(@"remove, %lu", _deferredTasks.count);
// NSLog(@"%@, %lu -[_deferredTasks removeObjectAtIndex:0]", [NSThread currentThread], _deferredTasks.count);
}
}
[self unlock];
}
- (void)test_crash {
// init
_tasks=[NSMutableArray array];
_deferredTasks=[NSMutableArray array];
_lock=[[NSConditionLock alloc] initWithCondition:0];
for (int i=0; i < 10000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self addEntry_ok];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// dispatch_async(dispatch_get_main_queue(), ^{
[self removeEntry_crash];
// });
});
}
}
- (void)test_ok {
// init
_tasks=[NSMutableArray array];
_deferredTasks=[NSMutableArray array];
_lock=[[NSConditionLock alloc] initWithCondition:0];
for (int i=0; i < 1000; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self addEntry_ok];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self removeEntry_ok];
});
}
}
@end
#!/bin/sh
# File
TEXT_FILE="$1";
# Class
CLASS_NAME="$2";
cat "$TEXT_FILE" | tr '\n' '&' | sed 's/&\-\[/\n\-\[/g'|grep "^\-\[$CLASS_NAME " | tr '&' '\n';
將 iOS 16.6 的 iPhone 12 Pro Max(Hardware Mode: iPhone13 4)設備連接到 Xcode 后,按如下操作可獲取到 UIKeyboardTaskQueue 類的實現匯編,即UIKitCore_20G75_arm64e_TEXT.txt 文件。
otool -s __TEXT __text -v ~/Library/Developer/Xcode/iOS\ DeviceSupport/16.6\ \(20G75\)\ arm64e/Symbols/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore > ~/Desktop/UIKitCore_20G75_arm64e_TEXT.txt
./fetch_class_text_from_all.sh ~/Desktop/UIKitCore_20G75_arm64e_TEXT.txt UIKeyboardTaskQueue > ~/Desktop/UIKeyboardTaskQueue_20G75_arm64e_TEXT.txt
-[UIKeyboardTaskQueue continueExecutionOnMainThread]:
0000000189466fd0 pacibsp
0000000189466fd4 sub sp, sp, #0x30
0000000189466fd8 stp x20, x19, [sp, #0x10]
0000000189466fdc stp x29, x30, [sp, #0x20]
0000000189466fe0 add x29, sp, #0x20
0000000189466fe4 mov x19, x0
0000000189466fe8 bl 0x18c9df5e0
0000000189466fec cmp w0, #0x1
0000000189466ff0 b.ne 0x189467024
0000000189466ff4 mov x0, x19
0000000189466ff8 bl _objc_msgSend$tryLockWhenReadyForMainThread
0000000189466ffc ldr x8, [x19, #0x28]
0000000189467000 cbz x8, 0x189467058
0000000189467004 ldr x8, [x19, #0x30]
0000000189467008 cbz x8, 0x1894670b4
000000018946700c bl 0x18c9df2f0
0000000189467010 str x0, [sp, #0x8]
0000000189467014 ldr x8, [x19, #0x30]
0000000189467018 str xzr, [x19, #0x30]
000000018946701c bl 0x18c9df150
0000000189467020 b 0x1894670ac
0000000189467024 adrp x8, -26465 ; 0x182d06000
0000000189467028 add x2, x8, #0xe19
000000018946702c mov x0, x19
0000000189467030 mov x3, #0x0
0000000189467034 mov w4, #0x0
0000000189467038 ldp x29, x30, [sp, #0x20]
000000018946703c ldp x20, x19, [sp, #0x10]
0000000189467040 add sp, sp, #0x30
0000000189467044 autibsp
0000000189467048 eor x16, x30, x30, lsl #1
000000018946704c tbz x16, #0x3e, 0x189467054
0000000189467050 brk #0xc471
0000000189467054 b "_objc_msgSend$performSelectorOnMainThread:withObject:waitUntilDone:"
0000000189467058 ldr x0, [x19, #0x18]
000000018946705c bl _objc_msgSend$count
0000000189467060 cbz x0, 0x1894670b8
0000000189467064 adrp x8, 333019 ; 0x1da942000
0000000189467068 ldr x0, [x8, #0x500]
000000018946706c bl 0x18c9dee30
0000000189467070 mov x2, x19
0000000189467074 bl "_objc_msgSend$initWithExecutionQueue:"
0000000189467078 mov x20, x0
000000018946707c mov x0, x19
0000000189467080 mov x2, x20
0000000189467084 bl "_objc_msgSend$setExecutionContext:"
0000000189467088 bl 0x18c9df0a0
000000018946708c ldr x0, [x19, #0x18]
0000000189467090 mov x2, #0x0
0000000189467094 bl "_objc_msgSend$objectAtIndex:"
0000000189467098 bl 0x18c9deec0
000000018946709c str x0, [sp, #0x8]
00000001894670a0 ldr x0, [x19, #0x18]
00000001894670a4 mov x2, #0x0
00000001894670a8 bl "_objc_msgSend$removeObjectAtIndex:"
00000001894670ac ldr x0, [sp, #0x8]
00000001894670b0 b 0x1894670b8
00000001894670b4 mov x0, #0x0
00000001894670b8 str x0, [sp, #0x8]
00000001894670bc bl _objc_msgSend$originatingStack
00000001894670c0 bl 0x18c9deec0
00000001894670c4 mov x20, x0
00000001894670c8 mov x0, x19
00000001894670cc mov x2, x20
00000001894670d0 bl "_objc_msgSend$setActiveOriginator:"
00000001894670d4 bl 0x18c9df0a0
00000001894670d8 mov x0, x19
00000001894670dc bl _objc_msgSend$unlock
00000001894670e0 ldr x1, [sp, #0x8]
00000001894670e4 ldrb w20, [x19, #0x8]
00000001894670e8 mov w8, #0x1
00000001894670ec strb w8, [x19, #0x8]
00000001894670f0 ldr x2, [x19, #0x28]
00000001894670f4 cbz x1, 0x189467108
00000001894670f8 mov x0, x1
00000001894670fc bl "_objc_msgSend$execute:"
0000000189467100 ldr x1, [sp, #0x8]
0000000189467104 b 0x18946710c
0000000189467108 cbz x2, 0x189467130
000000018946710c strb w20, [x19, #0x8]
0000000189467110 ldp x29, x30, [sp, #0x20]
0000000189467114 ldp x20, x19, [sp, #0x10]
0000000189467118 add sp, sp, #0x30
000000018946711c autibsp
0000000189467120 eor x16, x30, x30, lsl #1
0000000189467124 tbz x16, #0x3e, 0x18946712c
0000000189467128 brk #0xc471
000000018946712c b 0x18c9df060
0000000189467130 ldr x0, [x19, #0x20]
0000000189467134 bl _objc_msgSend$count
0000000189467138 ldr x1, [sp, #0x8]
000000018946713c cbz x0, 0x18946710c
0000000189467140 mov x0, x19
0000000189467144 bl _objc_msgSend$performDeferredTaskIfIdle
0000000189467148 b 0x189467100
-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]:
0000000189467738 ldr x0, [x0, #0x10]
000000018946773c mov x2, #0x0
0000000189467740 b "_objc_msgSend$tryLockWhenCondition:"
-[UIKeyboardTaskQueue performDeferredTaskIfIdle]:
0000000189c814b4 pacibsp
0000000189c814b8 stp x20, x19, [sp, #-0x20]!
0000000189c814bc stp x29, x30, [sp, #0x10]
0000000189c814c0 add x29, sp, #0x10
0000000189c814c4 mov x19, x0
0000000189c814c8 bl _objc_msgSend$lock
0000000189c814cc mov x0, x19
0000000189c814d0 bl _objc_msgSend$promoteDeferredTaskIfIdle
0000000189c814d4 mov x0, x19
0000000189c814d8 bl _objc_msgSend$unlock
0000000189c814dc mov x0, x19
0000000189c814e0 ldp x29, x30, [sp, #0x10]
0000000189c814e4 ldp x20, x19, [sp], #0x20
0000000189c814e8 autibsp
0000000189c814ec eor x16, x30, x30, lsl #1
0000000189c814f0 tbz x16, #0x3e, 0x189c814f8
0000000189c814f4 brk #0xc471
0000000189c814f8 b _objc_msgSend$continueExecutionOnMainThread
-[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]:
0000000189c814fc pacibsp
0000000189c81500 sub sp, sp, #0x30
0000000189c81504 stp x20, x19, [sp, #0x10]
0000000189c81508 stp x29, x30, [sp, #0x20]
0000000189c8150c add x29, sp, #0x20
0000000189c81510 ldr x8, [x0, #0x28]
0000000189c81514 cbz x8, 0x189c81528
0000000189c81518 ldp x29, x30, [sp, #0x20]
0000000189c8151c ldp x20, x19, [sp, #0x10]
0000000189c81520 add sp, sp, #0x30
0000000189c81524 retab
0000000189c81528 mov x19, x0
0000000189c8152c ldr x0, [x0, #0x20]
0000000189c81530 bl _objc_msgSend$count
0000000189c81534 cbz x0, 0x189c81518
0000000189c81538 ldr x0, [x19, #0x20]
0000000189c8153c mov x2, #0x0
0000000189c81540 bl "_objc_msgSend$objectAtIndex:"
0000000189c81544 bl 0x18c9deec0
0000000189c81548 mov x2, x0
0000000189c8154c str x0, [sp, #0x8]
0000000189c81550 ldr x0, [x19, #0x18]
0000000189c81554 bl "_objc_msgSend$addObject:"
0000000189c81558 ldr x0, [x19, #0x20]
0000000189c8155c mov x2, #0x0
0000000189c81560 bl "_objc_msgSend$removeObjectAtIndex:"
0000000189c81564 ldr x0, [sp, #0x8]
0000000189c81568 ldp x29, x30, [sp, #0x20]
0000000189c8156c ldp x20, x19, [sp, #0x10]
0000000189c81570 add sp, sp, #0x30
0000000189c81574 autibsp
0000000189c81578 eor x16, x30, x30, lsl #1
0000000189c8157c tbz x16, #0x3e, 0x189c81584
0000000189c81580 brk #0xc471
0000000189c81584 b 0x18c9df050
-[UIKeyboardTaskQueue addDeferredTask:]:
0000000189c815fc pacibsp
0000000189c81600 sub sp, sp, #0x30
0000000189c81604 stp x20, x19, [sp, #0x10]
0000000189c81608 stp x29, x30, [sp, #0x20]
0000000189c8160c add x29, sp, #0x20
0000000189c81610 mov x19, x0
0000000189c81614 bl 0x18c9df200
0000000189c81618 mov x20, x0
0000000189c8161c mov x0, x19
0000000189c81620 bl _objc_msgSend$lock
0000000189c81624 adrp x8, 330945 ; 0x1da942000
0000000189c81628 ldr x0, [x8, #0x510]
0000000189c8162c bl 0x18c9dee30
0000000189c81630 mov x2, x20
0000000189c81634 bl "_objc_msgSend$initWithTask:"
0000000189c81638 str x0, [sp, #0x8]
0000000189c8163c bl 0x18c9df0a0
0000000189c81640 ldr x0, [x19, #0x20]
0000000189c81644 ldr x2, [sp, #0x8]
0000000189c81648 bl "_objc_msgSend$addObject:"
0000000189c8164c mov x0, x19
0000000189c81650 bl _objc_msgSend$unlock
0000000189c81654 mov x0, x19
0000000189c81658 bl _objc_msgSend$continueExecutionOnMainThread
0000000189c8165c ldr x0, [sp, #0x8]
0000000189c81660 ldp x29, x30, [sp, #0x20]
0000000189c81664 ldp x20, x19, [sp, #0x10]
0000000189c81668 add sp, sp, #0x30
0000000189c8166c autibsp
0000000189c81670 eor x16, x30, x30, lsl #1
0000000189c81674 tbz x16, #0x3e, 0x189c8167c
0000000189c81678 brk #0xc471
0000000189c8167c b 0x18c9df050
相關鏈接
[1] Arm64 寄存器說明:https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers
[2] Arm64 匯編指令集說明:https://documentation-service.arm.com/static/6023d5512cb3723f20208db2
[3] NSConditionLock 條件狀態鎖:https://developer.apple.com/documentation/foundation/nsconditionlock/
作者:巴樂
來源:微信公眾號:支付寶體驗科技
出處:https://mp.weixin.qq.com/s/salgoWNYfqjhNwu30aHRnA