WebKit並不是完全開源的, Apple封裝了一部代碼在一個靜態庫(libWebKitSystemInterfaceXXXX.a)中,並沒有提供原始碼。而且不同版本的OS (包括iOS),會有特定的版本。
這就是我今天從分析中瞭解到的, 覺得過程比較有趣,記錄一下。
1. 在WebKit中有這樣一段原始碼 (Laguage.mm):
static String httpStyleLanguageCode(NSString *languageCode){ ASSERT(isMainThread()); // Look up the language code using CFBundle. RetainPtr<CFStringRef> preferredLanguageCode(AdoptCF, wkCopyCFLocalizationPreferredName((CFStringRef)languageCode));
其中wkCopyCFLocalizationPreferredName只有如下的定義,找不到實現代碼:
extern CFStringRef (*wkCopyCFLocalizationPreferredName)(CFStringRef);
2. 在運行時,查詢wkCopyCFLocalizationPreferredName的地址資訊:
(lldb) p wkCopyCFLocalizationPreferredName(CFStringRef (*)(CFStringRef)) $0 = 0x0000000100478a49 (WebKit2`WKCopyCFLocalizationPreferredName)(lldb) image lookup -s WKCopyCFLocalizationPreferredName1 symbols match 'WKCopyCFLocalizationPreferredName' in /Volumes/Data/Project/Webkit/webkitSvn/Build/Products/Debug/WebKit2.framework/Versions/A/WebKit2: Address: WebKit2[0x0000000000464a49] (WebKit2.__TEXT.__text + 4600745) Summary: WebKit2`WKCopyCFLocalizationPreferredName1 symbols match 'WKCopyCFLocalizationPreferredName' in /Volumes/Data/Project/Webkit/webkitSvn/Build/Products/Debug/WebKit.framework/Versions/A/WebKit: Address: WebKit[0x00000000001b8275] (WebKit.__TEXT.__text + 1795141) Summary: WebKit`WKCopyCFLocalizationPreferredName
可以看到代碼是位於WebKit2.framework中的, 但沒有原始碼資訊,所以必然是link了一個靜態庫進來。
3. 查看項目的編譯設定,可以發現要連結進來的庫:
還可以在WEBKIT_SYSTEM_INTERFACE_LIBRARY的定義中發現有不同系統的版本。
可以找到一個正在使用的庫看看:
4. 用IDA(試用版)開啟找找裡面有沒有WKCopyCFLocalizationPreferredName:
也可以使用nm看看源檔案,顯然WebKitSystemInterface.o找不到對應的原始碼:
nm -a libWebKitSystemInterfaceLion.a/XXXXXX/Build/Products/Debug/libWebKitSystemInterfaceLion.a(WebKitSystemInterface.o):0000000000002591 t -[NSWindowGraphicsContext(WebKitSystemInterface) _WebKitSystemInterface_setGraphicsPort:]0000000000008d98 s -[NSWindowGraphicsContext(WebKitSystemInterface) _WebKitSystemInterface_setGraphicsPort:].eh00000000000090a8 S _WKCopyBundleURLForExecutableURL.eh00000000000006c7 T _WKCopyCFLocalizationPreferredName0000000000008200 S _WKCopyCFLocalizationPreferredName.eh
反組譯碼可以看到如下的代碼:
function _WKCopyCFLocalizationPreferredName { CFBundleGetLocalizationInfoForLocalization(arg_0, &var_32, &var_28, &var_24, &var_20); rax = CFBundleCopyLocalizationForLocalizationInfo(var_32, var_28, var_24, var_20); return rax;}
很簡單,就是涉及到兩個函數調用。問題是,這兩個函數並沒有在開發文檔裡。
5 繼續尋找libWebKitSystemInterfaceXXX.a的來曆。WebKit的工程都是通過gyp來產生的,開啟果然裡面提到了這樣一段描述:
WebCore.gyp
['OS == "mac"', {
'targets': [
{
# On the Mac, libWebKitSystemInterface*.a is used to help WebCore
# interface with the system. This library is supplied as a static
# library in binary format. At present, it contains many global
# symbols not marked private_extern. It should be considered an
# implementation detail of WebCore, and does not need these symbols
# to be exposed so widely.
#
# This target contains an action that cracks open the existing
# static library and rebuilds it with these global symbols
# transformed to private_extern.
'target_name': 'webkit_system_interface',
它只以二進位形式提供。沒問題,有反組譯碼的結果,寫出一個一樣的函數也不是問題。
6. 找到兩個私人API的來曆。
使用image lookup -s可以很容易確定兩個函數是在CoreFundation.framework中的。在Apple Open Source中也可以查得到聲明(class-dump對C介面就不靈光了):
CFBundlePriv.h on opensource.apple.com
CFBundleGetLocalizationInfoForLocalization
CFBundleCopyLocalizationForLocalizationInfo
最後添加如下的聲明就可以使用了:
extern "C" {CF_EXPORT Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding);CF_EXPORT CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding);}