zoukankan      html  css  js  c++  java
  • Linux内核学习第七周 分析 Linux 操作系统如何装载链接并执行程

    一、编译链接过程

    #include<stdio.h>
    
    int main()
    {
        printf("Hello World!");
        return 0;
    }
    

    1.预处理,处理代码中的宏定义和 include 文件,并做语法检查.

    gcc -E -o hello.cpp hello.c -32

    2.编译成会变代码

    gcc -x cpp-output -S -o hello.s hello.cpp -m32

    3.汇编成目标代码

    gcc -x assembler -c hello.s -o hello.o -m32

    4.链接成可执行文件

    gcc -o hello hello.o -m32

    5.执行程序

    ./hello

    6.选择静态编译

    gcc -o hello.static hello.o -m32 -static

    二、ELF文件格式

    ELF 格式:可执行和可链接格式 (Executable and Linkable Format) 是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。

    可重定位文件,如:.o 文件,包含代码和数据,可以被链接成可执行文件或共享目标文件,静态链接库属于这一类。 可执行文件,如:/bin/bash 文件,包含可直接执行的程序,没有扩展名。 共享目标文件,如:.so 文件,包含代码和数据,可以跟其他可重定位文件和共享目标文件链接产生新的目标文件,也可以跟可执行文件结合作为进程映像的一部分

    ELF 文件包括 ELF header 和文件数据。其中文件数据包括:Program header table, 程序头:描述段信息 .text, 代码段:保存编译后得到的指令数据 .data, 数据段:保存已经初始化的全局静态变量和局部静态变量 Section header table, 节头表:链接与重定位需要的数据。

    三、静态链接和动态链接

    对于32位x86的机器来讲,进程地址空间共有4G,最上面1G供内核,下面3G供用户态使用。默认进程是从0x8048000开始加载,首先是ELF文件头部,再把代码和数据加载到进程的地址空间,ELF Header中的Entry point address即是可执行文件加载到内存开始执行的第一行代码。一般静态链接会将所有代码放在一个代码段,而动态链接的进程会有多个代码段。

    可执行程序的执行环境

    • 命令行参数和shell环境,一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。

      • $ ls -l /usr/bin 列出/usr/bin下的目录信息

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

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

        • 又如, int main(int argc, char *argv[], char *envp[])

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

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

        • 库函数exec*都是execve的封装例程

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

    命令行参数和环境变量的保存和传递是当我们创建一个子进程时,(fork是复制父进程),然后调用exece系统调用,它把要加载的可执行程序把原来的进程环境给覆盖掉了,覆盖了以后它的用户态堆栈也被清空。这时命令行参数和环境变量会被压栈。

    shell程序 -> execve -> sys_execve 然后在初始化新进程堆栈时拷贝进去。

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

    创建了一个新的用户态堆栈的时候,实际上是把命令行参数(argv[])的内容和环境变量(envp[])的内容通过指针的方式传递给系统调用内核处理函数的,然后内核处理函数再建一个可执行程序新的用户态堆栈的时候,会把这些拷贝到用户态堆栈,初始化新的可执行程序执行的上下文环境。先函数调用参数传递,再系统调用参数传递。

    四、装载

    五、总结

    Linux 系统通过用户态 execve 函数调用内核态 sys_execve 系统调用,sys_execve()服务例程修改当前进程的执行上下文,将新的程序代码和数据替换到新的进程中,打开可执行文件,载入依赖的库文件,申请新的内存空间,最后执行 start_thread 函数设置 new_ip 和 new_sp,完成新进程的代码和数据替换,然后返回并执行新的进程代码。

  • 相关阅读:
    51Nod
    51Nod
    51Nod
    51Nod
    51Nod
    51Nod
    51Nod
    51Nod --1133 不重叠的线段
    bzoj2440: [中山市选2011]完全平方数
    第三章:基本HTML结构
  • 原文地址:https://www.cnblogs.com/zmingh/p/5374700.html
Copyright © 2011-2022 走看看