objc_msgSend_rtp
記得以前和 lukhnos 討論過 Objective-C 有沒有辦法將 dynamic binding 變成 static binding 。雖然 gcc 有個最佳化選項: -fobjc-direct-dispatch
會讓很多人誤以為是在 compile time 就決定了 method binding ,其實不然。Objective-C 的 runtime 是由 /usr/lib/libobjc.dylib
所提供的,如同副檔名,它是一個 dynamic library。一般用 gcc 編譯出來 objective-c 程式,在 message passing 的部份會先透過一個用來做 lazily binding ,叫做 dyld_objc_msgSend_stub
的 stub function 去呼叫 objc_msgSend
。
我到底在說啥?解釋一下。當 gcc 遇到要在 runtime 才 link 的 undefined function,由於無法預測 undefined function 的位址,因此 gcc 會產生一個 stub function 以及一個 lazy_pointer 指標。這個 stub 的動作很簡單,就是跳到 lazy_pointer 所指的地方;而 lazy_pointer 一開始會指到位於 /lib/crt1.o
(如果是執行檔的話啦)的 dyld_stub_binding_helper
,而 dyld_stub_binding_helper
幫助 dynamic linker 根據 lazy_pointer 的位址查出應該要連結的 function,並且將此 function 的位址儲存在 lazy_pointer 內,並且跳到 lazy_pointer 所指的位址(現在就是應該要連結的 function 的位址啦)執行。
為什麼要這麼麻煩呢?全部直接叫 dynamic linker 查找不就好了?聰明的你應該已經知道,這是為了效率考量。一開始 lazy_pointer 會指向 dyld_stub_binding_helper
並且做第一次 resolve & link ,之後,lazy_pointer 就會被改寫,指向欲 link 的 dynamic function ,stub 就會直接跳到 dynamic function 而不需要再次進行查找。
L_objc_msgSend$stub: ; 這是用來讓 linker 判斷到底該把哪些東西 link 起來的,重要 .indirect_symbol _objc_msgSend mflr r0 bcl 20,31,1f 1: mflr r11 addis r11,r11,ha16(L_objc_msgSend$lazy_ptr-1b) mtlr r0 ; 把 lazy_ptr 的值 load 進 r12 lwz r12,lo16(L_objc_msgSend$lazy_ptr-1b)(r11) ; 準備 jump 到 lazy_ptr 所指之處 mtctr r12 ; lazy_ptr 的位址當參數傳遞 addi r11,r11,lo16(L_objc_msgSend$lazy_ptr-1b) ; 不改變 return address 的 branch bctr .data .lazy_symbol_pointer L_objc_msgSend$lazy_ptr: ; 給linker 用 .indirect_symbol _objc_msgSend ; 一開始設定成這個 glue function,以後就會變 function 位址 .long dyld_stub_binding_helper .text .align 4 .globl _objc_msgSend_stub
所以實際上,當你對一個物件送訊息,會先經過 dyld_objc_msgSend_stub
,將一個位址 load 進來,做一個 branch 到 objc_msgSend ,才會進行 Objective-C runtime 的 dynamic binding。雖然那個 stub 很短,但如果程式中大量 sending message,stub 所浪費的時間比例也相對提高。 為了解決這個問題,Apple 在 10.4 以後提供了 objc_msgSend_rtp ,rtp 代表 RunTime Page。
意思就是,Apple 將 objc_msgSend_rtp 這個常常會用到的東西固定在記憶體中的某一個位址(0xfffeff00),要 send message 時直接靜態連結到 0xfffeff00 。如此一來,就不用經過 stub function ,直接去做 Objective-C runtime 的 dynamic binding。這也是 gcc 中,-fobjc-direct-dispatch
選項的意思。 因此,就算開了 -fobjc-direct-dispatch
,還是沒有辦法讓 Objective-C 不去做 dynamic binding,它的作用只是使得「去呼叫 Objective-C 做 dynamic binding」這件事情變成靜態而已,Objective-C 是完全的動態語言。至於 Objective-C 怎麼做 dynamic binding ,有空去研究看看 objc-msg-ppc.s 吧,原始碼網路上都抓得到。
“經過這樣的解說,就更明白了。Amazing work!”
---lukhnos. 9/16, 2006