程序从源代码到可执行文件需要经过以下步骤:
预处理、编译、汇编、链接,如下图所示,每一步产生不同的文件。
各步骤的任务如下:
以下两张图分别展示的是汇编和链接之后的文件节区信息表,能够发现链接之后的大部分节Addr有了地址值,并且多了很多内容。
ELF(Excutable and Linking Format)是一个文件格式的标准。如下图所示,通过readelf -h test
查看可执行文件hello的头部,头部里面注明了目标文件类型ELF32,入口点地址为0x8048730,即可执行文件加载到内存中开始执行的第一行代码地址。可执行文件的格式和进程的地址空间有一个映射的关系,当程序要加载到内存中运行时,将ELF文件的代码段和数据段加载到进程的地址空间。
ELF文件的三种类型:
静态链接是在生成可执行文件时进行的。在目标模块中记录符号地址,而在可执行文件中改写为指令直接使用的数字地址。
在装入或运行时进行链接。通常被链接的共享代码称为动态链接库(DLL, Dynamic-Link Library)或共享库(shared library)。动态链接分为可执行程序装载时动态链接和运行时动态链接:
编写以下文件example.c和example.h生成共享库:
#include <stdio.h> #include "example.h" int fun_zzx() { printf("This is a shared libary\n"); return 0; }
#ifndef _EXAMPLE_H_ #define _EXAMPLE_H_ #define SUCCESS 0 #define FAILURE (-1) int fun_zzx(); #endif
测试文件test.c:
#include <stdio.h> int main() { fun_zzx(); return 0; }
测试结果如下图所示:
修改example.c文件为:
#include <stdio.h> #include "example.h" int fun_zzx() { printf("This is a Dynamical Loading libary!\n"); return SUCCESS; }
新的测试文件test.c代码如下:
#include <stdio.h> #include <dlfcn.h> #include "example.h" int main() { printf("This is a Main program!\n"); /* Use Dynamical Loading Lib */ void * handle = dlopen("libexample.so",RTLD_NOW); if(handle == NULL) { printf("Open Lib libexample.so Error:%s\n",dlerror()); return FAILURE; } int (*func)(void); char * error; func = dlsym(handle,"fun_zzx"); if((error = dlerror()) != NULL) { printf("fun_zzx not found:%s\n",error); return FAILURE; } printf("Calling fun_zzx() function of libexample.so!\n"); func(); dlclose(handle); return SUCCESS; return 0; }
测试结果如下图所示:
execve系统调用的整个过程的简单流程图如下:
按照之前调试MenuOS的方法,启动gdb在sys_execve、load_elf_binary和start_thread处设置断点,然后运行OS:
在MenuOS中执行exec命令,跟踪结果如下:
追踪到start_thread,用po new_ip
,得到的是0x8048730,通过readelf –h hello
可以看到hello这个可执行程序它的入口点地址也是0x8048730。
之后在执行hello程序的过程中,对寄存器的值进行修改以更新的执行环境:
如果要调用动态加载共享库,就要使用定义在dlfcn.h中的dlopen。给出文件名libexample.so和标志RTLD_NOW打开动态链接库,返回handle句柄。dlsym函数与上面的dlopen函数配合使用,根据操作句柄(由dlopen打开动态链接后返回的指针)handle与符号(要求获取的函数或全局变量的名称)fun_zzx,返回符号对应的地址。使用此地址可以获得库中特定函数的地址,并且调用库中的相应函数。这样就可以使用动态加载共享库里面所定义的函数了。