我该如何让GCC编译的ELF二进制文件中的.text段可写?

11

我希望能够动态更改正在使用的库中的可执行代码。基本上,如果不需要某些函数,我希望能够动态地NOP掉它们。

然而,我使用的库的.text部分是不可写的(大多数程序也是这样)。我拥有该库的源代码,因此希望使用GCC将其编译为可写的。

有没有一种方法可以做到这一点?


这样做的实际原因是什么? - cateof
4个回答

10

一般而言,在 POSIX 兼容系统下,mprotect 是首选项,在 sys/mman.h 下实现 (请参考 http://linux.die.net/man/2/mprotect)。只需获取进程可执行部分的地址和系统页计数,调用 mprotect 请求权限权限,写入它,然后再次调用 mprotect 以释放写入权限。

然而,如果这是针对速度至关重要的低级例程(或者mprotect不可用),那么您将希望将库编译为其.text部分可写,因为调用mprotect很可能会导致翻译后备缓冲器(TLB)刷新,这在多处理器环境中(尤其是)可能会导致瓶颈。如果特定系统正在使用通过分页进行硬件保护(现在几乎所有系统都是如此),那么更改保护的唯一方法是执行TLB刷新,必须在每个引用的页面、引用的页面表(一组页面)、引用的页面目录(一组页面表)和每个处理器上执行。最重要的是,这必须在ring 0中执行,这需要一个系统调用,这只会增加额外的开销。
在后一种情况下,最简单的解决方案是正常编译库,然后使用--writable-text选项进行objcopy(正如ggiroux所提到的)。
另一个解决方案是自己定义链接器映射文件linker.ld。然后,您可以明确指定任何部分的权限。这并不太复杂;如果依赖于系统,请参阅http://www.math.utah.edu/docs/info/ld_3.html上的文档。您还可以查看系统提供的linker.ld文件,并从那里进行修改。将-Wl,--verbose传递给gcc将指示链接器输出所有相关文件(包括其默认的linker.ld),您可以在其中修改.text部分的权限,然后使用新的linker.ld文件重新编译库(永远)。
总之,我的建议是按照最后一段所述的方式进行操作,并使用稍微修改过的链接器脚本编译库。

6
尝试对编译后的库使用objcopy --writable-text命令,根据文档说明,它应该会使`.text`区域可写。

3
我发现最简单的方法(binutils 2.22)是使用-N链接。这可以通过gcc -XN传递给gcc。

这个选项的完整形式是 --omagic。是的,它使 .text 可读可写,就像 .data 一样。 - Peter Cordes

1

可能最好的方法是使用系统特定的API来改变您想要修改的内存的可写性,修改它,然后再改回来。

在Unix家族系统中,您需要查看 mprotect。只需记住它处理的是您系统页面大小的倍数的块。可能是4096,因此可能需要四舍五入。


我正在使用Genode操作系统,并尝试重新编写动态链接器的map_object()函数。在程序加载的早期阶段,mprotect()是否适用? - samoz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接