zoukankan      html  css  js  c++  java
  • "Linux内核分析"第七周

    可执行程序的装载

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

    一、预处理、编译、链接和目标文件的格式

    可执行程序是怎么得来的?

    首先,编译器预处理:1、将头文件加载进来;2、将宏替换

    gcc -E -o hello.cpp hello.c (-m32)//c预处理成cpp文件

    第二步,将cpp(预处理后的文件)编译成汇编代码

    gcc -x cpp-output -S -o hello.s hello.cpp (-m32)//生成s汇编文件

    第三步,将汇编代码编译成二进制目标文件

    gcc -x assembler -c hello.s -o hello.o (-m32)//需要注意的是二进制文件不可读

    第四步,链接成可执行文件

    gcc -o hello.static hello.c (-m32) -static

    什么是ELF格式文件?

    Out是最古老的可执行文件,然后发展成COFF,然后就是PE和ELF。其中PE多用于WINDOWS,ELF多用于LINUX系统。

    目标文件的三种形式:

    1、可重定位文件//.o文件,用来和其他object文件一起创建下面两种文件

    2、可执行文件//指出了应该从哪里开始执行

    3、共享文件//主要是.so文件,用来被链接编辑器和动态链接器链接

    ELF目标文件的格式:

     

    ELF文件头格式:

     

    左半边是ELF格式,右半边是执行时候的格式。

    ELF头描述了该文件的组织情况,程序投标告诉系统如何创建一个进程的内存映像,section头表包含了描述文件sections的信息。

    entry代表(刚加载过新的可执行文件之后的)程序的入口地址(头部之后是代码和数据,进程的地址空间是4G。

    上面的1G是内核用,下面的3G是程序使用)

    默认的ELF头加载地址是0x8048000,头部大概要到0x48100处或者0x483000处,也就是可执行文件加载到内存之后执行的第一条代码地址。

    一般静态链接会将所有代码放在一个代码段;动态链接的进程会有多个代码段。

     

    二、可执行程序、共享库和动态链接

    装载可执行程序之前的工作

    可执行程序的执行环境

    一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。

    Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身

    例如,int main(int argc, char *argv[])

    又如, int main(int argc, char argv[], char envp[])//envp是shell的执行环境

    Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数

    int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

    命令行参数和环境串都放在用户态堆栈中

    fork子进程的时候完全复制了父进程;调用exec的时候,要加载的可执行程序把原来的进程环境覆盖掉,用户态堆栈也被清空

    命令行参数和环境变量进入新程序的堆栈:把环境变量和命令行参数压栈(如上图),也就相当于main函数启动

    shell程序-->execve-->sys_execve,然后在初始化新程序堆栈的时候拷贝进去先传递函数调用参数,再传递系统调用参数

    装载时动态链接和运行时动态链接应用

    动态链接分为可执行程序装载时动态链接和运行时动态链接

    共享库的动态链接:

    动态加载库:

    #ifdef __cplusplus

    extern "C" {

    #endif

    /*

    * Dynamical Loading Lib API Example

    * input : none

    * output : none

    * return : SUCCESS(0)/FAILURE(-1)

    *

    */

    int DynamicalLoadingLibApi();

    #ifdef __cplusplus

    }

    #endif

    #endif /* _DL_LIB_EXAMPLE_H_ */

    /*------------------------------------------------------*/

    #include <stdio.h>

    #include "dllibexample.h"

    #define SUCCESS 0

    #define FAILURE (-1)

    /*

    * Dynamical Loading Lib API Example

    * input : none

    * output : none

    * return : SUCCESS(0)/FAILURE(-1)

    *

    */

    int DynamicalLoadingLibApi()

    {

    printf("This is a Dynamical Loading libary! ");

    return SUCCESS;

    }

    dlopen函数参考http://baike.baidu.com/link?url=05ftxNgbVsyrGNLqJbo3TpCyn27QeKOKaU7D70O-3bMu9ZsvruBZIHBZz-mhJgviNf4obS5HBNlpMPzcrbABZK的说明,

    负责打开一个动态库并将其加载到内存;

    dlsym函数与上面的dlopen函数配合使用,通过dlopen函数返回的动态库句柄(由dlopen打开动态链接库后返回的指针handle)以及对应的符号返回符号对应的指针。

    三、可执行程序装载

    可执行程序装载的关键问题

    execve与fork是比较特殊的系统调用

    execve用它加载的可执行文件把当前的进程覆盖掉,返回之后就不是原来的程序而是新的可执行程序起点;

    fork函数的返回点ret_from_fork是用户态起点。

    sys_execve内核处理过程

    do_execve -> do_execve_common -> exec_binprm

    最后,根据文件头部信息寻找对应的文件格式处理模块

    Linux内核是如何支持多种不同的可执行文件格式的?

    庄周梦蝶——庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现自己是蝴蝶(被execve加载的可执行程序)

    load_elf_binary调用start_thread函数

    struct pt_regs *regs就是内核堆栈栈底的部分

    发生中断的时候,esp和ip都进行压栈

    通过修改内核堆栈中EIP的值(也就是把压入栈中的值用new_ip替换)作为新程序的起点

    sys_execve内部处理过程

    需要动态链接的可执行文件先加载连接器ld;否则直接把elf文件entry地址赋值给entry即可。

    start_thread(regs, elf_entry, bprm->p)会将CPU控制权交给ld来加载依赖库并完成动态链接;对于静态链接的文件elf_entry是新程序执行的起点

    使用gdb跟踪sys_execve内核函数的处理过程

    首先还是更新menu内核,然后查看test.c,可以看到最新添加的exec系统调用。

    查看Makefile,发现增加了gcc -o hello hello.c -m32 -static一句;

    补充cp hello ../rootfs以及cp init ../rootfs

    启动内核并验证execv函数

    启动GDB调试

    退出调试状态,输入redelf -h hello可以查看hello的EIF头部

    浅析动态链接的可执行程序的装载

    动态链接的过程中,内核做了什么

    ldd test

    可执行程序需要依赖动态链接库,而这个动态链接库可能会依赖其他的库,这样形成了一个关系图;

    interpreter:需要依赖动态链接器进行加载这些库并进行解析(这就是一个图的遍历),装载所有需要的动态链接库;

    之后ld将CPU的控制权交给可执行程序;

    所以,动态链接的过程主要是动态链接器在起作用。

  • 相关阅读:
    XCode颜色显示插件
    Xcode4的color theme保存路径
    Direct2D教程(四)Path Geometry
    Direct2D教程(六)图形也能做运算
    Direct2D教程(五)复合图形
    Direct2D教程(七)单色画刷
    Direct2D QuickStart
    Direct2D教程(一)Direct2D已经来了,谁是GDI的终结者?
    Direct2D入门
    Texture
  • 原文地址:https://www.cnblogs.com/20135121conan/p/5362980.html
Copyright © 2011-2022 走看看