• Linux内核中可执行程序的装载和启动


    作者:xujianguo

     原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

    ——————————————————————————————————————————————————————-————

    实验目的:

    •  使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve验证您对Linux系统加载可执行程序所需处理过程的理解;
    •  理解编译链接的过程和ELF可执行文件格式;

    实验环境:

      

            实验楼:www.shiyanlou.com。

    实验步骤

       1.配置环境,登录实验楼网站。

    按照上次实验的基本步骤,结合老师视频所讲,完成相关实验。

    cd LinuxKernel

    删除menu

    然后从github上克隆相应的mengning/menu.git

     

    环境配置验证:

    配置成功。

    2. 编译链接的过程。

    gcc -x cpp-output -S -o hello.s hello.cpp -m32
    gcc -x assembler -c hello.s -o hello.o -m32
    gcc -o hello hello.o -m32
    gcc -o hello.static hello.o -m32 -static

     

    实验结果:

    3.配置调试系统,和gdb调试配置

       qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S //配置调试环境

     利用file linux-3.18.6/vmlinux和target remote:1234来配置加载初始调试环境.

     

    4.设置断点sys_execve,load_elf_binary,start_thread

     

    5.调试与验证所学和理解的sys_execve的知识:

    断点1与展示:

    单步进入调试查看sys_execve:

    断点2,load_elf_binary:

    断点3,start_thread,利用po new_ip 定位执行地址:

    相应的地址验证:

    栈内变量处理:

    继续观察后续处理情况:

    相关细节的处理

    利用c命令完成相关调试工作:

    实验分析:

               实验过程中使用的代码:

               main:

                

                 调用函数:

                 

                 elf文件格式:

                

                 ELF的文件主要分以下三种:

    •                      一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件。
    •                      一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了exec(BA_OS)如何来创建程序进程映象。
    •                      一个共享object文件保存着代码和合适的数据,用来被动态链接器和连接编辑器两个链接器链接。

                对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary

          其中,elf_map()映射到进程空间 0x804 8000或 0x804 8300地址;而elf_entry是入口。

             Shell本身不限制命令行参数的个数, 命令行参数的个数受限于命令自身;Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数;int execve(const char * filename,char * const argv[ ],char * const envp[ ]);库函数exec*都是execve的封装例程;而 exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接。exec对应的系统调用就是sys_execve,sys_execve内部会解析可执行文件格式,执行大体步骤为

    do_execve -> do_execve_common -> exec_binprm

                do_execve:

                do_execve_common:

              do_execve_common {

                 ...

                 do_open_exec //打开执行文件

                      ......

                   exec_binprm(bprm);//调用相关文件与程序处理

               ... }

           exec_binprm:

           

           主要是search_binary_handler的匹配。

          直至load_elf_binary:

           ELF格式的二进制映像的认领、装入和启动是由load_elf_binary()完成的。而“共享库”、即动态连接库映像的装入则由load_elf_library()完成。实际上共享库的映像也是二进制的,但是一般说“二进制”映像是指带有main()函数的、可以独立运行并构成一个进程主体的可执行程序的二进制映像。

          gcc -shared shlibexample.c -o libshlibexample.so -m32

        gcc -shared dllibexample.c -o libdllibexample.so -m32

        gcc main.c -o main -L/media/sda_m/SharedLibDynamicLink -lshlibexample -ldl -m32  (需要指明路径)

        静态链接的可执行程序,execve中修订的ip地址是新进程映射到进程空间的地址;动态链接的可执行程序,execve中修订的ip地址是动态连接器的程序起点。
         

        可执行程序的起始位置,一般是new_ip指向的位置 ;execve返回后,“新的进程上下文已经安装好”,新的可执行程序在老进程“一觉睡醒”后开始执行 。


    实验总结:

               从本次实验中收获颇多。

               内核中实际执行execv()或execve()系统调用的程序是do_execve(),这个函数先打开目标映像文件,并从目标文件的头部(从第一个字节开始)读入若干(128)字节,然后调用另一个函数search_binary_handler(),在那里面让各种可执行程序的处理程序前来认领和处理。内核所支持的每种可执行程序都有个struct linux_binfmt数据结构,通过向内核登记挂入一个队列。而search_binary_handler(),则扫描这个队列,让各个数据结构所提供的处理程序、即各种映像格式、逐一前来认领。如果某个格式的处理程序发现特征相符而,便执行该格式映像的装入和启动。简而言之,庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现自己是蝴蝶(被execve加载的可执行程序)。

    参考资料:

             1.http://www.cnblogs.com/xmphoenix/archive/2011/10/23/2221879.html

             2.http://blog.csdn.net/morphad/article/details/8967000

  • 相关阅读:
    ASIHTTPREQUEST 文档
    本地通知
    ASIHttpRequest 使用过程中,中文编码的问题
    讲讲最近自己的学习,谈谈未来的想法
    关于 ASP.NET MVC 4 如果管理用户
    [转贴]超级懒汉编写的基于.NET的微信SDK
    [转贴]实践:C++平台迁移以及如何用C#做C++包装层
    [转贴]Linq之动态查询
    [转贴]watin的一些例子
    [转贴]xcode帮助文档
  • 原文地址:https://www.cnblogs.com/emochuanshuo/p/4442969.html
Copyright © 2020-2023  润新知