操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    于疫情的影響,返程的高鐵停運了,飛機也不飛了,所以我也只能在家開啟SOHO模式了。往年過年我也會帶電腦回家,但是大多數時候都是看劇刷電影,真正用來辦公的時間少之又少,由于辦公室里臺式機,所以現在正在使用的這臺17款MacBook Air也一直被我當做是一臺備用機,但是萬萬沒想到,這次帶它回家,還真的能派上用場。


    圖/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 信息

    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 關鍵信息(后續分析基于該信息):

    • 摘要信息:iPhone 12 Pro Max(Hardware Mode: iPhone13 4)、iOS 16.6、支付寶App 10.5.0.6000 版本、Crash 直接原因是讀內存地址0x2ab3106e0異常(一般讀內存報錯為SEGV_MAPERR,寫內存報錯為EXC_BAD_ACCESS
    • Crash 關鍵函數:0x00000001a5183a7c _objc_retain0x00000001aed4d4d4 -[UIKeyboardTaskQueue performDeferredTaskIfIdle]0x00000001ae533148 -[UIKeyboardTaskQueue continueExecutionOnMainThread]
    • Thread State:通用寄存器和浮點寄存器快照,用于查看運行時變量值及更深入的邏輯推測;
    • Binary Images:各 Image (運行時可執行指令的文件)二進制布局在內存起始位置及結束地址,起始位置可做基準,可用于計算 Crash 時的某指令地址相對于所屬 Image 起始地址的偏移。

    量級及分布

    鍵盤 Crash 日 PV 一直處于大幾百次,持續至少半年多,從操作系統版本分布來看僅在 iOS 16 上出現(覆蓋所有機型)。

    圖 2 鍵盤 Crash 日 PV 趨勢圖

    圖 3 鍵盤 Crash 在不同機型及操作系統的量級分布

    信息小結

    從 Crash 日志棧頂的objc_retain函數關鍵字和量級分布情況來看,該 Crash 很可能是由 iOS 16 系統鍵盤控件的內存管理異常導致

    分析推演

    下文分析推演涉及的知識點或技能:

    1. 使用軟件:Sublime Text、Xcode 及自帶的lldb命令,包括bcbtframe selectdiimage listp/xpox/1b
    2. 匯編能力:Arm64 寄存器說明 [1] 、Arm64 匯編指令集說明 [2] ;
    3. 腳本工具:otool、自研腳本fetch_class_text_from_all.sh
    4. 關鍵類:UIKeyboardTaskQueue 鍵盤核心類、NSConditionLock條件狀態鎖(具體使用見官方文檔 [3] );
    5. 依賴模塊:螞蟻自研的DebugKit.framework(后續考慮對外輸出)調試模塊。

    一、看現場,從 Crash 點開始

    ——計算 Crash 函數的偏移

    因 iOS 運行時加載到內存的 Image 的起始地址是動態的(對應 Binary Images 列表中的起始地址),但某指令地址與所屬 Image 的起始地址的偏移是固定的,所以可根據該偏移來查看 Crash 時是哪條指令。

    • 0x00000001a5183a7c _objc_retain所屬的libobjc.A.dylib的起始地址是0x00000001a5180000,所以相對偏移=0x00000001a5183a7c - 0x00000001a5180000=0x3a7c
    • 0x00000001aed4d4d4 -[UIKeyboardTaskQueue performDeferredTaskIfIdle]所屬的UIKitCore的起始地址是0x00000001ae166000,所以相對偏移=0x00000001aed4d4d4 - 0x00000001ae166000=0xbe74d4

    二、模擬現場,尋找蛛絲馬跡

    —— Xcode 設置斷點模擬現場

    1. 為模擬與 Crash 時一樣的現場,需找一臺與 Crash 日志中一致的設備,即 iOS 16.6 的iPhone 12 Pro Max(Hardware Mode: iPhone13 4),只有這樣在下文中斷點時的函數棧以及各函數偏移對應的指令才能與 Crash 日志中的完全對上。
    2. 將找到的設備與 Mac 連接并用 Xcode 啟動 App(可用下文附件中 Demo 關鍵代碼調試)。
    3. 從上述計算出的關鍵函數的偏移加上所屬 Image 的起始地址,模擬出 Crash 時運行的函數棧,具體操作如下圖 4。

    圖 4 設置斷點模擬現場

    從圖 4 的第 11 步可知 Crash 的直接原因是objc_retain的對象野指針了,導致讀取內存異常而觸發 Crash。

    圖 5 查看上一層函數棧

    從圖 5 可知兩點:

    1. 先后調用關系是-[UIKeyboardTaskQueue performDeferredTaskIfIdle] -> -[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]該函數在 Crash 函數棧中未出現,所以只有模擬現場才能發現)-> objc_retain
    2. UIKeyboardTaskQueue類有個NSMutableArray類型的成員變量持有UIKeyboardTaskEntry對象(從圖 5 中第 8 步的輸出得出),而 Crash 的直接原因就是獲取該數組index=0UIKeyboardTaskEntry對象后,執行objc_retain該對象 Crash ,所以異常的原因需要從對該數組的讀寫排查。

    小結:UIKeyboardTaskQueue類的NSMutableArray類型的成員變量是關鍵數組(在實例對象偏移0x20的位置),懷疑是多線程讀寫該數組導致的。那么該成員變量名是啥,UIKeyboardTaskQueue類又是如何保證安全使用該數組的呢?

    三、全面排查,收集更多信息

    —— 獲取UIKeyboardTaskQueue類的全部信息

    借助螞蟻自研的DebugKit.framework調試模塊可在運行時導出UIKeyboardTaskQueue類所有的實例方法、類方法、propertyivars成員變量。

    圖 6 獲取 UIKeyboardTaskQueue 類的基礎信息

    從圖 6 可知兩點:

    1. UIKeyboardTaskQueue的成員變量_deferredTasks的類型是NSMutableArray(在實例對象起始地址偏移0x20的位置,從圖 6 中第 6 點可知)就是上述提到關鍵數組。野指針一般是有多線程讀寫對象導致的,對_deferredTasks數組讀寫時應該是有鎖來控制的,該類中類型為NSConditionLock的成員變量_lock(在實例對象偏移0x10的位置,從圖 6 中第 5 點可知)與_deferredTasks是啥關系?
    2. 發現該類的property列表只有executionContextactiveOriginator,不包含deferredTaskslock,所以對_deferredTasks_lock(類的成員變量名一般是在property名前多加前綴“_”)的所有讀寫全在該類中,不存在其他類直接引用,也就是 Crash 相關的全部邏輯都在UIKeyboardTaskQueue類中,所以破案的邊界也劃清楚了,圈定范圍。將UIKeyboardTaskQueue類的所有方法的匯編都導出來查看。

    圖 7 獲取 UIKeyboardTaskQueue 類的所有方法實現

    圖 7 中第 2 步涉及的fetch_class_text_from_all.sh見下文附件中腳本源碼。

    小結:通過分析圈定排查范圍在UIKeyboardTaskQueue類內,借助腳本可一鍵導出其所有方法的匯編,為進一步研究_deferredTasks_lock的關系做基礎。

    四、理清關系,找到突破口

    —— 研究_deferredTasks_lock關系

    理清以下重要的兩個關系:

    1. _deferredTasks角度:UIKeyboardTaskQueue類對_deferredTasks的多線程讀寫是如何保證安全的,哪些方法有用到,與_lock又是什么關系?
    2. _lock角度:UIKeyboardTaskQueue類對_lock又是如何使用的,哪些方法有用到,加鎖和解鎖是否配對?

    deferredTasks 角度

    圖 7 第 2 步導出的UIKeyboardTaskQueue的所有方法實現都是匯編的,為理清對_deferredTasks對象的所有讀寫有哪些指令,分別在哪些方法中(UIKeyboardTaskQueue實例對象偏移0x20的位置,該地址下存儲的 8 字節地址才是_deferredTasks對象),需要在文件中全文搜索正則表達式x.{1,2}, #0x20篩選出所有引用_deferredTasks的指令以及所屬方法,操作如下圖 8(Sublime Text)。

    圖 8 全文搜索正則表達式的樣例

    在匯編層面,面向對象語言中方法的第一個入參是self(C++ 稱this,Objective-C 稱self),存放在x0寄存器上,所以僅篩選出偏移是從方法入參時的x0x0備份(如mov x19, x0x19就是備份了x0的值)開始的,最后整理出所有UIKeyboardTaskQueue_deferredTasks有引用并讀寫的指令及所屬方法,如下。

    注:

    • 一般面向過程語言的代碼塊稱為函數,而面向對象語言的代碼塊稱為方法,為避免文章的混用造成困擾,這里特別說明。
    • 下列部分的“讀”或“寫”是指獲取到_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 個:

    1. -[UIKeyboardTaskQueue isEmpty]
    2. -[UIKeyboardTaskQueue finishExecution]
    3. -[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]
    4. -[UIKeyboardTaskQueue continueExecutionOnMainThread]
    5. -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished]
    6. -[UIKeyboardTaskQueue init]

    寫_deferredTasks的方法有 4 個:

    1. -[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]
    2. -[UIKeyboardTaskQueue addDeferredTask:]
    3. -[UIKeyboardTaskQueue init]
    4. -[UIKeyboardTaskQueue .cxx_destruct]

    _lock 角度

    在文件中全文搜索正則表達式x.{1,2}, #0x10篩選出所有引用_lock的指令以及所屬方法,操作類似上述的_deferredTasks;從上可知,UIKeyboardTaskQueue類對_lock的使用封裝成 4 個方法(忽略init創建和.cxx_destruct銷毀的兩個方法,該兩方法不會有并發問題),也就是方法使用_lock必定會調用這 4 個方法。

    解鎖方法有 1 個:

    1. -[UIKeyboardTaskQueue unlock]

    加鎖方法有 3 個:

    1. -[UIKeyboardTaskQueue lock]
    2. -[UIKeyboardTaskQueue lockWhenReadyForMainThread]
    3. -[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]

    串聯關系,發現 Bug

    串聯上述_deferredTasks_lock兩個角度的方法調用(忽略init創建和.cxx_destruct銷毀的兩個方法),從原匯編的關鍵方法中列出簡版的關系描述,如下圖 9。

    圖 9 串聯 _deferredTasks 和 _lock 的關系

    為方便理清鎖的對應關系,圖 9 中用紅色表示加鎖,綠色表示解鎖,從中可知:

    1. _deferredTasks的關鍵讀寫的方法內是有 1 個加鎖和 1 個解鎖對應的,預期是多線程下保護讀寫的安全性;
    2. 即使不讀寫_deferredTasks的方法內上也是有 1 個加鎖和 1 個解鎖對應的,用于多線程下保護其他成員變量的讀寫安全性;
    3. 發現問題,有 Bug-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內的0000000189466ff8 bl _objc_msgSend$tryLockWhenReadyForMainThread這行指令執行是返回BOOL類型的,即加鎖成功為YES,加鎖失敗為NO。(參看圖 6 中-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]的方法簽名為typeEncoding=B16@0:8,即返回為BOOL類型);如該行指令嘗試加鎖但失敗了,不會直接return,還會繼續執行紅色框內的指令并做解鎖操作,會導致多線程下UIKeyboardTaskQueue類的加鎖和解鎖的功能不配對,也就存在鎖失效的情況。

    小結:-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內有 Bug,導致存在鎖失效的情況,猜測在多線程下并發讀寫_deferredTasks時就會偶現 Crash。

    五、重新推演,確定根因

    推演圖

    圖 10 重新推演鍵盤 Crash 過程

    按時間軸重新推演鍵盤 Crash 過程:

    • T0:Thread A加鎖成功后執行指令bl _objc_msgSend$addObject:添加對象A到數組_deferredTasks。同時,因為Main Thread執行指令bl _objc_msgSend$tryLockWhenReadyForMainThread失敗后繼續執行指令bl _objc_msgSend$unlock,使得Thread B也加鎖成功后執行指令bl _objc_msgSend$addObject:添加對象B到數組_deferredTasks,導致出現多線程同時寫入數組_deferredTasks的異常情況
    • T1:Thread A解鎖后,Main Thread-[UIKeyboardTaskQueue performDeferredTaskIfIdle]方法內加鎖成功后,在-[UIKeyboardTaskQueue promoteDeferredTaskIfIdle]方法內執行指令bl _objc_msgSend$objectAtIndex:后獲取數組inde=0的對象地址時,因多線程寫入導致該對象地址被異常破壞而出現野指針(野指針存入x0寄存器)。
    • T2:Main Thread繼續執行下一條指令bl _objc_claimAutoreleasedReturnValue會間接觸發了_objc_retain并透傳x0寄存器的值,最終在該函數內執行指令ldr x17, [x17, #0x20]Crash 了。

    注:不同語言的編譯器對應的符號名的生成規則是不同的,C 語言只是在原函數名前加一個前綴“_”,如objc_retain(A),編譯后符號名是_objc_retain,而 C++ 語言會根據方法名加上參數名生成的符號名,如__ZNSt3__16vectorIdNS_9allocatorIdEEEixB6v15006Em

    模擬 Crash

    按推演的邏輯用本地 Xcode 重新起個 Demo 驗證下(可用下文附件中 Demo 關鍵代碼),通過調用[self test_crash]可模擬出 tryLock 失敗時導致的 Crash(如調用[self test_ok]就不會出現 Crash),現場如下。

    圖 11 模擬 tryLock 加鎖失敗而導致的 Crash

    從 Xcode 的 Console 控制臺的日志中可以看到出現多線程并發添加到_deferredTasks數組的情況,在后續removeEntry_crash方法內出現了objc_retain野指針對象導致的 Crash,與上述推演的邏輯相符。

    對比不同 iOS 版本

    圖 12 對比不同 iOS 版本的實現

    通過對比發現僅 iOS 16 上有問題,iOS 15 或 iOS 17 上 tryLock 失敗后都會立即return的,也就是為什么 Crash 僅出現在 iOS 16 的原因。從中我們可以看出在 iOS 17 上蘋果技術同學也發現了該 Bug 并做了修復

    給蘋果反饋 Bug

    該問題已提交至蘋果“反饋助理”(圖 13),但截至目前未得到其官方的 iOS 16 上的解決方案。

    圖 13 “反饋助理”截圖

    六、總結根因

    通過上述分析推演,iOS 16 鍵盤 Crash 的根因已查明,即-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內執行-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]嘗試加鎖失敗后,不return繼續向下執行讀寫不安全內存以及解鎖,導致存在鎖失效的情況,使得UIKeyboardTaskQueue成員變量_deferredTasks數組在多線程下出現并發添加UIKeyboardTaskEntry實例而引起野指針,導致最終 Crash。

    注:該根因除了導致數組讀寫異常而 Crash,也可能導致其他變量的狀態不一致性,只是不一定表現為 Crash 而已,建議用本文方案修復。

    解決方案(App 內置補丁源碼)

    明確根因后,解決方案就比較明確了,寫一個 App 內置補丁代碼使得-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法內執行-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]嘗試加鎖失敗后,正常return即可。補丁方案有兩個:

    1. 重寫-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法。在原匯編基礎上新增一條指令,即在bl _objc_msgSend$tryLockWhenReadyForMainThread后添加一條匯編指令cbz w0, return_labelreturn_label對應源碼return對應的匯編指令地址),如失敗則return。但該方案涉及的原匯編指令較多,有 95 條匯編指令(見下文附件中 iOS 系統匯編),容易踩坑。
    2. 重寫-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法。在該方法內如加鎖失敗則模擬兩次return,回到-[UIKeyboardTaskQueue continueExecutionOnMainThread]的上一個函數棧,改造的匯編指令較少,安全性較好,也確認了除-[UIKeyboardTaskQueue continueExecutionOnMainThread]調用外,無其他方法調用-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]

    最終,支付寶 App 基于穩定性的考慮,采用第 2 種補丁方案修復鍵盤 Crash。

    補丁原理

    圖 14 修復鍵盤 Crash 的補丁原理

    -[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]實現以下邏輯:

    • 如加鎖成功,則return 1 次,返回到-[UIKeyboardTaskQueue continueExecutionOnMainThread]方法的下一條指令繼續執行;
    • 如加鎖失敗,則模擬return 2 次,返回到-[UIKeyboardTaskQueue continueExecutionOnMainThread]的函數棧的上一層函數的地址繼續執行,也就是模擬了從-[UIKeyboardTaskQueue continueExecutionOnMainThread]中執行return操作。

    源碼return語句,對應匯編的 4 步:

    1. 恢復fplr寄存器。fp(也稱x29)記錄當前幀的內存地址,lr(也稱x30)記錄從當前函數返回時跳轉到哪個地址繼續執行。運行時就是通過fplr寄存器,輸出線程的函數棧的。如 Crash 函數棧,或從lldbbt輸出的函數棧;
    2. 恢復callee-saved寄存器。即x19-x28的寄存器,try-catch的實現就涉及該類寄存器,一般按需執行;
    3. 恢復sp寄存器。sp記錄當前幀的棧頂地址,,當前函數的局部變量所在的內存地址就在(fp, sp]之間;
    4. 執行ret指令。執行ret指令后,pc就指向lr寄存器的值,然后繼續執行;

    本文補丁方案的原理中,tryLock 失敗時就是通過:恢復fplr寄存器 + 恢復callee-saved寄存器 + 恢復sp寄存器 + 再次恢復fplr寄存器 + 再次恢復callee-saved寄存器 + 再次恢復sp寄存器 + ret指令 來模擬在-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法內return 2 次直接返回到-[UIKeyboardTaskQueue continueExecutionOnMainThread]的函數棧的上一層函數的。

    補丁實現

    有兩部分組成:

    1. 重寫方法:對應 fix_UIKeyboardTaskQueue.S 文件;
    2. Hook 入口:對應 fix_UIKeyboardTaskQueue.m 文件;

    重寫方法

    重寫-[UIKeyboardTaskQueue tryLockWhenReadyForMainThread]方法實現,對應下文附件中補丁源碼的 fix_UIKeyboardTaskQueue.S 文件。

    圖 15 重寫 -[UIKeyboardTaskQueue tryLockWhenReadyForMainThread] 方法實現

    Hook 入口

    借助+ (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 螞蟻內部的技術英雄榜捷報

    附件

    1、補丁源碼

    補丁源碼包括兩部分: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
    

    2、Demo 關鍵源碼

    //
    //  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
    
    

    3、腳本源碼

    #!/bin/sh
    
    # File
    TEXT_FILE="$1";
    
    # Class
    CLASS_NAME="$2"; 
    cat "$TEXT_FILE" | tr '\n' '&' | sed 's/&\-\[/\n\-\[/g'|grep "^\-\[$CLASS_NAME " | tr '&' '\n';
    

    4、iOS 系統匯編(關鍵方法)

    將 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

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有