记得以前如果一个.so 被 load 了,应该和程序本身一样,不能覆盖啊,强行覆盖也不会覆盖原来的 inode,而是创建新的 inode 并替换当前目录里面的 inode, 原来打开的文件还是正确的。

但是最近发现在 Linux i386 上面 load .so 以后覆盖 .so ,没有任何提示,正在运行的程序退出时侯则通常都会 segfault.  和 sparc 很像了。

难道我记错了吗?

用 strace 看了一下,cp 打开目标文件的时侯,都是在 open 加 O_WRONLY|O_TRUNC|O_LARGEFILE 参数,但是打开正在使用的程序时,就会返回 ETXTBSY, 动态库直接成功。

怀疑 ld-linux.so 在处理 .so 时侯是不是有什么问题,于是 strace 了一下动态库的 load 过程, 发现就是 open O_RDONLY 以后 mmap 加 MAP_DENYWRITE 参数,但是 man 了一下 mmap, man page 说这个 MAP_DENYWRITE 参数现在已经被忽略了,原因是会造成 denial of service (我不是很明白这个为啥会造成 DoS),看起来就是这个原因了,mmap 这样并不能阻挡其他程序改写 .so 文件。

但是为啥主程序就可以呢?还是得看 kernel 代码。发现 kernel 内部在加载主程序的时侯,是用的 do_mmap 函数来 mmap 文件的(binfmt_elf.c),参数也有 MAP_DENYWRITE,因为是 kernel 内部函数,所以这个 MAP_DENYWRITE 实际是有效的,在 do_mmap_pg_off 函数(mmap.c)里面也确实可以看到对 MAP_DENYWRITE 参数的处理。

结论就是,Linux ,至少是现在的 Linux 下面无法像 Windows 一样,保护正在使用的 .so 文件,因此开发过程中如果要更新代码,请不要使用 cp 这么粗暴原始的替换方式,还是用 install 来做吧