【问题标题】:Linux executable memory mappingLinux 可执行内存映射
【发布时间】:2016-06-16 16:17:14
【问题描述】:

elf 可执行文件包含一个节表。当程序加载到内存中时,这些部分被映射到段。 我认为是编译器决定了内存中的段地址? 你认为操作系统可以在程序加载时改变段地址吗?

我说的是单个 elf 可执行文件。这不是图书馆。 事实上,我有一个二进制文件。我知道内存中的一个函数地址,我想从外部程序中挂钩这个函数。我只是想确保这个地址永远不会改变。我不会重新编译或更改目标程序中的任何内容。

【问题讨论】:

    标签: elf


    【解决方案1】:

    elf 可执行文件包含一个节表。

    错误:从完全链接的ET_EXECET_DYN 二进制文件中剥离节表是完全有效的。

    当程序加载到内存中时,这些部分被映射到段。

    错误:节到段的映射发生在静态链接时,而不是在运行时。

    我认为是编译器决定了内存中的段地址?

    错误:它是为ET_EXEC 决定这个的静态链接器。对于ET_DYN,静态链接器和运行时加载器协同工作。

    你认为操作系统可以在程序加载时改变段地址吗?

    对于ET_EXEC,二进制文件总是加载到静态链接器链接该二进制文件的地址。将其加载到其他任何地方都会导致程序崩溃。

    对于ET_DYN,也称为PIE 二进制文件,在随机地址加载是可能的预期的。

    我有一个二进制文件。我知道内存中的一个函数地址,我想从外部程序中挂钩这个函数。我只是想确保这个地址永远不会改变。

    如果二进制文件的类型为ET_EXEC,则所有段总是加载到链接地址,所以是的。

    更新:

    PIE 二进制文件中,一切 将通过相同的重定位一起移动(mainfoo_start 等)(重定位通常会因运行而异运行;但 GDB 禁用地址空间随机化,因此必须执行 (gdb) set disable-randomization off)。

    要在 GDB 中查找重定位,您可以:

    (gdb) p &main
    (gdb) start
    (gdb) p &main
    

    进程开始前&main 的第一个值应与nm test | grep main 的输出相同。第二个值(进程启动后)应该是重定位值(main 在内存中的位置)。两者的区别在于(页面对齐)重定位。

    要在运行时(从程序本身)找到这个重定位,需要使用dl_iterate_phdr()dlpi_addrDocumentation.

    【讨论】:

    • 谢谢。当您说“对于 ET_DYN,也称为 PIE 二进制文件,在随机地址加载既可能也是预期的。”:假设我编译了一个包含 main 函数和 foo 函数的 elf 可执行文件(不是库)。这是否意味着这些函数也应该在随机地址加载?但是入口点不能在 2 次执行之间移动!
    • @Bob5421 如果您使用-pie 标志链接可执行文件,则该可执行文件将具有ET_DYN 类型,并且其行为类似于共享库。另请参阅stackoverflow.com/questions/1449987/…
    • 好的,在这种情况下 main 和 foo 将位于随机地址?
    • @Bob5421 在 PIE 二进制文件中,mainfoo 都将被重新定位到相同的页面对齐但其他随机地址,是的。
    • @Bob5421 要构建PIE 二进制文件,-fPIC 是不够的。 readelf -h test 将为您的二进制文件显示 ET_EXEC,而不是 ET_DYN。试试gcc -fPIE -pie test.cc -o test。此外,GDB 默认禁用地址随机化。从 GDB 内部运行二进制文件时,您需要 set disable-randomization off 启用随机化。
    猜你喜欢
    • 2013-09-02
    • 1970-01-01
    • 2014-02-15
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 2012-03-24
    相关资源
    最近更新 更多