2003
01 02 03 04 05 06 07 08 09 10 11 12
2006
01 02 03 04 05 06 07 08 09 10 11 12
2007
01 02 03 04 05 06 07 08 09 10 11 12
2008
01 02 03 04 05 06 07 08 09 10 11 12
2009
01 02 03 04 05 06 07 08 09 10 11 12
2010
01 02 03 04 05 06 07 08 09 10 11 12
2011
01 02 03 04 05 06 07 08 09 10 11 12
2017
01 02 03 04 05 06 07 08 09 10 11 12
2018
01 02 03 04 05 06 07 08 09 10 11 12
 
Jun
16
2006

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 吧,原始碼網路上都抓得到。

 

Annotations RSS

“經過這樣的解說,就更明白了。Amazing work!”

---lukhnos. 9/16, 2006
 
 

Write Concisely