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)这一个步骤,这个步骤主要就是上面说的对地址标签化的过程。