引入

前一段时间,我一直都在做一个软件保护的项目,上周一直都忙于一个赛事的组织筹备工作,没有空闲写一点东西,这周才补上。我所做的工作主要就是对多个不同平台下的动态链接库进行加密,防止用户对其进行反汇编。在加密的流程中,有一个过程需要实现在内存中先解密代码段,然后再执行解密后的代码。显然,这里需要进行内存中代码段的读写操作。

标题中说一个神奇的bug,那么到底是什么bug呢?我是之前在Ubuntu上实现了对动态链接库文件加密的程序,然后扩展到ARM平台上,但在实际运行Android程序调用动态链接库的过程中,总是会报一个错叫非法指令。起初我以为是解密出现了问题,我琢磨了一天,也没能修复这个bug。最终,我向恒牛(@陈宇恒)请教了这个问题,他用了不到两分钟得时间,调用了一个_clear_cache函数就把bug给解决了。

缓存一致性

其实这个bug就是因为缓存不一致导致的。那么什么是缓存不一致呢?我们都知道在计算机体系结构中,有一个很重要的概念叫(cache。缓存不一致就是cache和内存在某些时候的不一致而导致CPU执行了与内存中不一样的指令。然而,并不是所有体系结构都会有这种问题,x86的体系结构就是从硬件上确保了缓存一致性:监听总线协议(snoopy cache protocol)使当某片被缓存了的内存被修改时,会被立刻写回,确保cache与内存的一致性。然而这种实时监听方式显然是比较消耗性能的,所以ARM体系结构就没有采用硬件来保证缓存一致性,而是用软件的方法来保证。程序员需要手动调用如_clear_cache 这样类似的指令来保证缓存一致性。那么到这里,也就解释了为什么在Ubuntu(x86)上可以正常运行,但在Android(ARM)上却会报非法指令的错误了。

一点感触

做完这个项目之后,深感要想成为一个优秀的程序员,还是要懂得系统底层的。只是晓得几门编程语言,会写点前端,浮于表面,就以为自己无所不能了,这样是不可取的,毕竟系统底层(操作系统、体系结构、编译原理、数据库、网络)这些才是计算机科学的精髓。

参考资料: