Chrome的小胡瓜(Courgette) 收藏

轉自http://blog.csdn.net/xingtian713/archive/2009/08/25/4483810.aspx


在Chrome中有一個很有意思的工具courgette,翻譯成中文是小胡瓜的意思。我很難把這個單詞和這個小工具聯系在一起,也許作者比較偏愛這個蔬菜吧!

背景
我們用C++編寫程序時,經常會出現修改了一行代碼,重新編譯一下之后,再去對比一下新的二進制文件,就可以發現千差萬別了。往往出現我們要發布一個修改了一行代碼的補丁,需要替換整個DLL或者EXE。這對于Chrome中類似于Chrome.dll(10M左右)文件來說,升級一次的代價還是挺高的。本人在去年下半年負責的一個升級服務器項目中,就曾動過念頭,是否有一種方法可以尋找出兩個二進制文件的差異,將這個差異打成一個補丁,用戶下載后,可以通過這個補丁,可以自動生成新的DLL。可惜功力不夠,最后放棄了。最近讀Chrome的代碼時,發現Chrome已經實現了該功能。這個就是Courgette的功勞了。

Courgette做什么的?
Courgette主要用于Chrome的升級過程,他的主要作用是,針對兩個版本不同的二進制文件(Binary File),尋找其中區別,生成補丁文件;另外就是根據這個補丁文件加上舊版本的二進制文件生成新版本的二進制文件的還原過程了。類似于加解密流程。

Courgette的實現原理?
Courgette構建在一個開源代碼bsdiff和bspatch 基礎上的。并在bsdiff的基礎上做了一些優化。

本人是半路出家的人,對編譯原理和匯編了解不深,按照我對bsdiff的算法 理解,一個二進制文件里面,包含了代碼部分(函數,數據),指向這些函數的指針列表(編譯鏈接產生,包含了如何定位函數等信息),由于這些地址是內部的相對地址,即使更改了一小行代碼,重新編譯后,函數的地址將發生變化了,指向這些函數的指針值也全部變化了。因此,即使更改了一個小小的變量,也會導致很多部分的修改。bsdiff的原理就是對二進制文件進行反匯編,將上面所說的兩部分進行分別處理,對于代碼部分,其實就和普通的文本文件類似了,改變不會太大,這部分體積基本上占去了整個二進制文件的80%左右。然后對動態指針部分進行一些更新處理,就基本上達到了打補丁的目標了。

    server:

        diff = bsdiff(original, update)

        transmit diff


    client:

        receive diff

        update = bspatch(original, diff)
大致流程如上,制作補丁時調用bsdiff函數,根據兩個不同版本的二進制文件,生成補丁文件。客戶端下載補丁后,調用bspatch函數,根據舊版本二進制文件和補丁生成新的二進制文件。

Chrome在bsdiff的基礎上做了一些優化,主要體現在動態指針列表部分,chrome對代碼部分的每一個模塊地址分配了一個標簽(Label),這些標簽都是整數,并把這些標簽保存到一個數組中,然后指針列表中映射的地址改為指向數組的索引,由于一些函數地址被調用多次,通過這種方法確實可以減少一些體積。做了一些優化后,在bsdiff的基礎上又減少了30%左右吧(這是google的說法,我沒驗證過)。
    server:
        asm_old = disassemble(original)
        asm_new = disassemble(update)
        asm_new_adjusted = adjust(asm_new, asm_old)
        asm_diff = bsdiff(asm_old, asm_new_adjusted)
        transmit asm_diff


    client:
        receive asm_diff
        asm_old = disassemble(original)
        asm_new_adjusted = bspatch(asm_old, asm_diff)
        update = assemble(asm_new_adjusted)
Chrome的大致處理流程如上,和bsdiff流程類似,多了 asm_new_adjusted = adjust(asm_new, asm_old)這一個步驟,這個步驟主要就是上面說的對地址標簽化的過程。