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,完成新进程的代码和数据替换,然后返回并执行新的进程代码。

posted @ 2016-04-10 17:45  小剑灬  阅读(190)  评论(0编辑  收藏  举报