6.Linux内核如何装载和启动一个可执行程序

路过的小游侠 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”

第一节:
1。可执行文件的创建预处理编译和链接
gcc -E :完成预处理输出文件
gcc -S : 完成预处理和编译,
gcc -c :完成预处理,编译和汇编
最后链接

2。目标文件格式
a.out coff 最后elf and pe
elf: exeuctable and linkable format
elf已经是一种abi文件
ELF有三种主要的目标文件:
可重定位 such as .o
可执行文件:指出了如何创建进程映象
共享object文件:保存代码和合适的数据。用于连接编辑器和动态 链接器
使用readelf来查看代码 -h 查看头文件 下面是代码 ,然后是数据

链接步骤1.空间与地址分配,2.符号解析与重定位。
静态链接会把代码放在一起,动态链接会有许多代码段
使用静态链接的好处是,依赖的动态链接库较少,对动态链接库的版本更新不会很敏感,具有较好的兼容性;不好地方主要是生成的程序比较大,占用资源多。使用动态链接的好处是生成的程序小,占用资源少。动态链接分为可执行程序装载时动态链接和运行时动态链接。
浅析动态链接的可执行程序的装载

第三节最后浅析动态链接的可执行程序的加载
(1)可以关注ELF格式中的interp和dynamic。

(2)动态链接库的装载过程是一个图的遍历。

(3)装载和连接之后ld将CPU的控制权交给可执行程序

第二节:可执行程序,共享库和动态链接
1、shell环境下用参数执行程序
shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数。
库函数exec*都是execve的封装例程.

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

先函数调用参数传递,再系统调用参数传递。
2、装载程序
设置断点 :
第一个断点sys_execve
do_execve
do_execve_common
exec_binprm
start_thread
6.Linux内核如何装载和启动一个可执行程序_第1张图片
6.Linux内核如何装载和启动一个可执行程序_第2张图片
6.Linux内核如何装载和启动一个可执行程序_第3张图片

代码
#include <stdio.h>
#include "shlibexample.h" 
#include <dlfcn.h>
/* * Main program * input : none * output : none * return : SUCCESS(0)/FAILURE(-1) * */
int main()
{
    printf("This is a Main program!\n");
    /* Use Shared Lib */
    printf("Calling SharedLibApi() function of libshlibexample.so!\n");
    SharedLibApi();
    /* Use Dynamical Loading Lib */
    void * handle = dlopen("libdllibexample.so",RTLD_NOW);
    if(handle == NULL)
    {
        printf("Open Lib libdllibexample.so Error:%s\n",dlerror());
        return   FAILURE;
    }
    int (*func)(void);
    char * error;
    func = dlsym(handle,"DynamicalLoadingLibApi");
    if((error = dlerror()) != NULL)
    {
        printf("DynamicalLoadingLibApi not found:%s\n",error);
        return   FAILURE;
    }    
    printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!\n");
    func();  
    dlclose(handle);       
    return SUCCESS;
}

新程序开始地点:0x804800
为什么execve系统调用返回后新的可执行程序能顺利执行:
当sys_+execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到ELF程序的入口地址,于是新的程序开始执行,ELF可执行文件装载完成

对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?
静态链接的ELF可执行文件,程序入口就是ELF文件的文件头中e_entry所指的地址:对于动态链接的ELF可执行文件,程序入口点是动态连接器

你可能感兴趣的:(linux,内核)