李辰希 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
编译链接的过程
1.预处理阶段
gcc -E -o XX.cpp XX.c -m32
XX.cpp是预处理文件
2.编译器生成汇编代码阶段
gcc -x cpp-output -S -o hello.s hello.cpp -m32
XX.s是汇编代码
3.汇编器生成目标代码阶段
gcc -x assembler -c hello.s -o hello.o -m32
XX.o是目标代码
4.链接器生成可执行文件阶段
gcc -o hello.static hello.c -m32 -static
5.查看hello和hello.static的区别:
ls -l
6.补充:
二.目标文件的格式ELF
1.常见的目标文件格式:
2.目标文件也叫ABI:应用程序二进制接口
3.ABI是二进制兼容:目标文件已经适应某种CPU体系结构上的二进制指令
4.ELF格式中3种目标文件:
ELF可执行文件加载到进程的地址空间时:
Entry point address
,即是可执行文件加载到内存中开始执行的第一行代码三.可执行文件、共享库和动态链接
2.命令行参数和环境串都放在用户态堆栈中
3.命令行参数和环境变量是如何保存和传递的?
创建的子进程完全复制父进程,调用execve时加载的可执行程序把子进程环境覆盖掉了,子进程用户态堆栈也被清空了,命令行参数和环境变量是如何保存在新的可执行程序的用户态堆栈呢?
参数传递过程:shell程序 -> execve系统调用 -> sys_execve 内核处理函数在初始化新程序堆栈时拷贝进去
先函数调用参数传递,再系统调用参数传递:调用execve时,参数压在shell程序当前进程的堆栈上,加载完新的可执行程序时被清空了,内核又创建了新的进程的用户态堆栈,把参数拷贝进去。
4.装载时动态链接和运行时动态链接应用
准备.so文件(在Linux下动态链接文件格式,在Windows中是.dll)
#ifndef _SH_LIB_EXAMPLE_H_
#define _SH_LIB_EXAMPLE_H_
#define SUCCESS 0
#define FAILURE (-1)
#ifdef __cplusplus
extern "C" {
#endif
/*
* Shared Lib API Example
* input : none
* output : none
* return : SUCCESS(0)/FAILURE(-1)
*
*/
int SharedLibApi();//内容只有一个函数头定义
#ifdef __cplusplus
}
#endif
#endif /* _SH_LIB_EXAMPLE_H_ */
/*------------------------------------------------------*/
#include <stdio.h>
#include "shlibexample.h"
int SharedLibApi()
{
printf("This is a shared libary!\n");
return SUCCESS;
}/* _SH_LIB_EXAMPLE_C_ */
编译成.so文件
$ gcc -shared shlibexample.c -o libshlibexample.so -m32
5.编译
1.$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32 #这里只提供shlibexample的-L(库对应的接口头文件所在目录,也就是path to your dir)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并没有提供dllibexample的相关信息,只是指明了-ldl
2.$ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件copy到默认路径下。
3.$ ./main
4.This is a Main program!
5.Calling SharedLibApi() function of libshlibexample.so!
6.This is a shared libary!
7.Calling DynamicalLoadingLibApi() function of libdllibexample.so!
8.This is a Dynamical Loading libary!
四.可执行程序的装载
1.特殊系统调用:
2.sys_execve内部会解析可执行文件格式:
do_ execve -> do_ execve_ common -> exec_binprm
do_ execve(getname(filename),argv,envp) -> do_ execve_ common(filename,argv,envp) 中:
load_ elf_binary
:严格的解析ELF格式文件,核心工作:把ELF可执行文件映射到进程的地址空间(默认加载起始地址是0x8048000)
start_ thread(regs,elf_ entry,bprm->p):
五.实验
1.搭建环境:
cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_exec.c test.c
vi test.c //增加了exec
vi MakeFile
make rootfs
2.更新menu内核
查看test.c文件
直接e hello.c切换到hello.c
查看Makefile
启动内核并验证execv函数
冻结内核,启动GDB调试
进行调试
退出调试状态,输入redelf -h hello可以查看hello的EIF头部
六.总结
分析exec*函数对应的系统调用处理过程:
1.关于动态链接的可执行程序的装载:ELF格式文件依赖动态链接库,动态链接库.so也可能依赖其他动态链接库,实际上动态链接库的依赖关系会形成一个图。需要动态链接的可执行文件先加载连接器ld,elf_entry指向动态链接器的起点,再看这个动态链接库是否还依赖与其他动态链接库,其实这整个过程是对一个图的遍历,把所有依赖的动态链接库都装载起来之后,ld将CPU的控制权交给可执行程序,主要由ld完成,不是内核。
2.庄周梦蝶——庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现自己是蝴蝶(被execve加载的可执行程序)
20135201李辰希 《Linux内核分析》第七周 可执行程序的装载
原文:http://www.cnblogs.com/20135201lcx2/p/5372816.html