可执行程序的生成过程
1.可执行程序的生成过程
以一个简单的输出hello,world的程序作为示例
#include <stdio.h> int main() { printf("hello,world"); }
1)首先进行预处理:在命令行中输入gcc -E -o hello.cpp hello.c -m32,
- -m32 -m64 生成32位/64位机器上的代码
- -E 仅作预处理,不进行编译、汇编和链接
2)编译到汇编语言 gcc -x cpp-output -S -o hello.s hello.cpp -m32
-S 仅编译到汇编语言,不进行汇编和链接
3)汇编成目标文件:gcc -x assembler -c hello.s -o hello.o -m32,这时输出文件hello.o已经是一个二进制文件了
-x language 指明使用的编程语言。允许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语 言。
-c 编译、汇编到目标代码,不进行链接
4) 链接成可执行文件 gcc -o hello hello.o,接着输入./hello可看到输出结果
-o outfile 指定输出文件
5) 若使用静态编译可添加选项-static , gcc -o hello.static hello.o -static
-static 在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效
二.可执行文件内部的结构
ABI:应用程序二进制接口
二进制兼容:某个程序(或库)有依赖其他库A,当库A升级时可以做到向后兼容,则程序(或库)功能不受影响。
ELF格式有三种目标文件:
可重定位文件(relocatable)保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件,主要值.o文件
可执行文件(executable)保存着一个用来执行的程序,该文件指出了exec(BA_OS)如何来闯进啊程序进程映像
共享object文件:保存有代码和合适的数据,用来被下面的两个链接器链接。第一个是链接编辑器,可以和其他的可重定位和共享object文件来创建其他 的object。第二个是动态链接器,联合一个可执行文件和其他共享object文件来创建一个进程映像
目标文件:参与程序的链接(创建一个程序)和程序的执行(运行一个程序 )
下图为ELF文件的两个视角:Linking View和Execution View
ELF header:保存了路线图(road map),描述了该文件的组织情况
程序头表(program header table):告诉系统如何闯进啊一个进程的内存映像
Section 头表(section header table):包含描述文件section的信息。每个section在这个表中有一个入口,每个入口给出了该section的名字,大小
可以使用 readelf -h 选项来查看ELF文件的头部信息,如下图所示
当创建或增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到虚拟内存段。
如上图所示,Text segment将拷贝到虚拟内存的0x8048100地址,可执行程序的格式和进程的地址空间有映射关系。
进程加载一个可执行文件的时候从虚拟地址空间的0x8048000处加载,首先是ELF头,程序开始执行的第一行代码的地址有ELF头里的enter point (程序入口点地址给出),一般静态链接文件只有一个代码段,动态链接有多个代码段。
三.可执行程序的装载
1.执行环境
命令行参数和shell环境,一般我们执行一个程序的Shell环境
- Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身
例如,int main(int argc, char *argv[])
又如, int main(int argc, char *argv[], char *envp[]),envp 是shell的执行环境,一帮由shell设置。下面是一段示例程序
2.Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
库函数exec*都是execve的封装例程
调用execve时先是通过函数调用参数传递的方式把参数传递给execve,接着execve再构造一个main函数的参数传递栈。
3.可执行程序装载时动态链接和运行时动态链接
动态链接分为可执行程序装载时动态链接和运行时动态链接,如下代码演示了这两种动态链接。
-
准备.so文件
shlibexample.h (1.3 KB) - Interface of Shared Lib Example
/********************************************************************/ /* Copyright (C) SSE@USTCSZ, 2012 */ /* */ /* FILE NAME : shlibexample.h */ /* PRINCIPAL AUTHOR : Mengning */ /* SUBSYSTEM NAME : */ /* MODULE NAME : */ /* LANGUAGE : C */ /* TARGET ENVIRONMENT : ANY */ /* DATE OF FIRST RELEASE : 2012/5/3 */ /* DESCRIPTION : Interface of Shared Lib Example */ /********************************************************************/ /* * Revision log: * * Created by Mengning,2012/5/3 * */ #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_ */
shlibexample.c (1.2 KB) - Implement of Shared Lib Example
/********************************************************************/ /* Copyright (C) SSE@USTCSZ, 2012 */ /* */ /* FILE NAME : shlibexample.c */ /* PRINCIPAL AUTHOR : Mengning */ /* SUBSYSTEM NAME : */ /* MODULE NAME : */ /* LANGUAGE : C */ /* TARGET ENVIRONMENT : ANY */ /* DATE OF FIRST RELEASE : 2012/5/3 */ /* DESCRIPTION : Implement of Shared Lib Example */ /********************************************************************/ /* * Revision log: * * Created by Mengning,2012/5/3 * */ #include <stdio.h> #include "shlibexample.h" /* * Shared Lib API Example * input : none * output : none * return : SUCCESS(0)/FAILURE(-1) * */ int SharedLibApi() { printf("This is a shared libary!\n"); return SUCCESS; }
编译成libshlibexample.so文件
- $ gcc -shared shlibexample.c -o libshlibexample.so -m32
dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example
/********************************************************************/ /* Copyright (C) SSE@USTCSZ, 2012 */ /* */ /* FILE NAME : dllibexample.h */ /* PRINCIPAL AUTHOR : Mengning */ /* SUBSYSTEM NAME : */ /* MODULE NAME : */ /* LANGUAGE : C */ /* TARGET ENVIRONMENT : ANY */ /* DATE OF FIRST RELEASE : 2012/5/3 */ /* DESCRIPTION : Interface of Dynamical Loading */ /* Lib Example */ /********************************************************************/ /* * Revision log: * * Created by Mengning,2012/5/3 * */ #ifndef _DL_LIB_EXAMPLE_H_ #define _DL_LIB_EXAMPLE_H_ #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_ */
dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example
/********************************************************************/ /* Copyright (C) SSE@USTCSZ, 2012 */ /* */ /* FILE NAME : dllibexample.c */ /* PRINCIPAL AUTHOR : Mengning */ /* SUBSYSTEM NAME : */ /* MODULE NAME : */ /* LANGUAGE : C */ /* TARGET ENVIRONMENT : ANY */ /* DATE OF FIRST RELEASE : 2012/5/3 */ /* DESCRIPTION : Implement of Dynamical Loading */ /* Lib Example */ /********************************************************************/ /* * Revision log: * * Created by Mengning,2012/5/3 * */ #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!\n"); return SUCCESS; }
编译成libdllibexample.so文件
- $ gcc -shared dllibexample.c -o libdllibexample.so -m32
-
分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件
main.c (1.9 KB) - Main program
/********************************************************************/ /* Copyright (C) SSE@USTCSZ, 2012 */ /* */ /* FILE NAME : main.c */ /* PRINCIPAL AUTHOR : Mengning */ /* SUBSYSTEM NAME : */ /* MODULE NAME : */ /* LANGUAGE : C */ /* TARGET ENVIRONMENT : ANY */ /* DATE OF FIRST RELEASE : 2012/5/3 */ /* DESCRIPTION : Main program */ /********************************************************************/ /* * Revision log: * * Created by Mengning,2012/5/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; }
编译main,注意这里只提供shlibexample的-L(库对应的接口头文件所在目录)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并没有提供dllibexample的相关信息,只是指明了-ldl
- $ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
- $ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件copy到默认路径下。
- $ ./main
- This is a Main program!
- Calling SharedLibApi() function of libshlibexample.so!
- This is a shared libary!
- Calling DynamicalLoadingLibApi() function of libdllibexample.so!
- This is a Dynamical Loading libary!
-
sys_execve内部会解析可执行文件格式
-
do_execve -> do_execve_common -> exec_binprm
-
search_binary_handler符合寻找文件格式对应的解析模块,如下:
- 1369 list_for_each_entry(fmt, &formats, lh) {
- 1370 if (!try_module_get(fmt->module))
- 1371 continue;
- 1372 read_unlock(&binfmt_lock);
- 1373 bprm->recursion_depth++;
- 1374 retval = fmt->load_binary(bprm);
- 1375 read_lock(&binfmt_lock);
-
对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary 其内部是和ELF文件格式解析的部分需要和ELF文件格式标准结合起来阅读
-
Linux内核是如何支持多种不同的可执行文件格式的?
- 82static struct linux_binfmt elf_format = {
- 83 .module = THIS_MODULE,
- 84 .load_binary = load_elf_binary,
- 85 .load_shlib = load_elf_library,
- 86 .core_dump = elf_core_dump,
- 87 .min_coredump = ELF_EXEC_PAGESIZE,
- 88};
- 2198static int __init init_elf_binfmt(void)
- 2199{
- 2200 register_binfmt(&elf_format);
- 2201 return 0;
- 2202}
elf_format和 init_elf_binfmt,就是观察者模式中的观察者
execve系统调用返回到用户态
修改int 0x80压入内核堆栈的EIP
load_elf_binary->start_thread
start_thread的new_ip从ELF文件中获得
动态加载时将elf_entry的值设为动态库加载程序的入口地址。
SYS_execve的执行过程
三.GDB跟踪execve系统调用