gpt4 book ai didi

c - 内核如何获取在linux下运行的可执行二进制文件?

转载 作者:IT王子 更新时间:2023-10-29 00:14:43 26 4
gpt4 key购买 nike

内核如何获取在linux下运行的可执行二进制文件?

这似乎是一个简单的问题,但谁能帮我深入挖掘?如何将文件加载到内存以及如何开始执行代码?

谁能帮我一步一步地告诉我发生了什么?

最佳答案

最美瞬间exec Linux 4.0 上的系统调用

找出所有这些的最好方法是使用 QEMU 对 GDB 内核进行逐步调试:How to debug the Linux kernel with GDB and QEMU?

  • fs/exec.cSYSCALL_DEFINE3(execve 定义系统调用

    只需转发到 do_execve .
  • do_execve
    转发至 do_execveat_common .
  • do_execveat_common
    要查找下一个主要函数,请跟踪何时返回值 retval最后修改。

    开始构建 struct linux_binprm *bprm描述程序,并将其传递给 exec_binprm执行。
  • exec_binprm
    再次按照返回值查找下一个主要调用。
  • search_binary_handler
  • 处理程序由可执行文件的第一个魔术字节确定。

    两个最常见的处理程序是用于解释文件( #! 魔法)和用于 ELF( \x7fELF 魔法)的处理程序,但内核中还有其他内置处理程序,例如a.out .用户也可以通过/proc/sys/fs/binfmt_misc自行注册

    ELF 处理程序定义在 fs/binfmt_elf.c .

    另见:Why do people write the #!/usr/bin/env python shebang on the first line of a Python script?
  • formats列表包含所有处理程序。

    每个处理程序文件包含如下内容:
    static int __init init_elf_binfmt(void)
    {
    register_binfmt(&elf_format);
    return 0;
    }

    elf_formatstruct linux_binfmt在该文件中定义。
    __init是魔法并将该代码放入内核启动时调用的魔法部分:What does __init mean in the Linux kernel code?

    链接器级依赖注入(inject)!
  • 还有一个递归计数器,以防解释器无限执行。

    尝试这个:
    echo '#!/tmp/a' > /tmp/a
    chmod +x /tmp/a
    /tmp/a
  • 我们再次跟踪返回值,看看接下来会发生什么,看看它来自:
    retval = fmt->load_binary(bprm);

    哪里load_binary为结构上的每个处理程序定义:C 风格的多态性。
  • fs/binfmt_elf.c:load_binary
    是否实际工作:
  • 根据ELF规范解析ELF文件,这里是ELF文件格式的概述:How to make an executable ELF file in Linux using a hex editor?
  • 根据解析的 ELF 文件设置进程初始程序状态,最值得注意的是:
  • struct pt_regs 中的初始寄存器设置
  • 初始 virtual memory设置,内存在 ELF 段中指定:What's the difference of section and segment in ELF file format
  • 调用 start_thread ,这将进程标记为可供调度程序调度
  • 最终调度程序决定运行该进程,然后它必须跳转到存储在 struct pt_regs 中的 PC 地址。同时也转移到较低特权的 CPU 状态,例如 Ring 3/EL0:What are Ring 0 and Ring 3 in the context of operating systems?

    调度程序会被时钟硬件定期唤醒,该硬件会按照内核之前的配置定期生成中断,例如 the old x86 PITARM timer .内核还注册在定时器中断被触发时运行调度程序代码的处理程序。

  • TODO:继续进一步的源代码分析。我希望接下来会发生什么:
  • 内核解析 ELF 的 INTERP header 以找到动态加载器(通常设置为 /lib64/ld-linux-x86-64.so.2 )。
  • 如果存在:
  • 内核将动态加载器和要执行的 ELF 映射到内存
  • 动态加载器启动,获取指向内存中 ELF 的指针。
  • 现在在用户空间中,加载器以某种方式解析 elf header ,并执行 dlopen关于他们
  • dlopen使用可配置的搜索路径来查找这些库(ldd 和 friend ),将它们映射到内存,并以某种方式通知 ELF 在哪里可以找到其丢失的符号
  • loader 调用 _start Sprite 族
  • 否则,内核会直接将可执行文件加载到内存中,而无需动态加载器。

    因此,它必须特别检查可执行文件是否为 PIE 以及是否将其放置在内存中的随机位置:What is the -fPIE option for position-independent executables in gcc and ld?
  • 关于c - 内核如何获取在linux下运行的可执行二进制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8352535/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com