《linux 内核分析》 第7周 可执行程序的装载
王一+《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-100002900
一、理解编译链接的过程和ELF可执行文件格式;
1、编译链接过程
- 编译器预处理成cpp文件
gcc -E -o q.cpp q.c -m32 -
汇编器编译成汇编代码 -x是将编译cpp输出文件,输出汇编文件
gcc -x cpp-output -S -o hello.s hello.cpp -m32 - 汇编代码编译成二进制目标文件 -x将汇编编译为object文件
gcc -x assembler -c hello.s -o hello.o -m32
4.链接成可执行文件 静态链接成hello.static文件
gcc -o hello.static hello.c -m32 -static
2、查看elf头文件格式
readelf -h hello
Entry_point_address代表程序的入口地址,默认的加载地址是0x8048000,也就是可执行文件加载到内存之后执行的第一条代码地址,
一般静态链接会将所有代码放在一个代码段;动态链接的进程会有多个代码段.
二、编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式;
elf(Executable and Linking Format)
其三种类型:
(1)可重定位文件
也就是通常称的目标文件,后缀为.o。链接器将它作为输入和其他目标文件,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件。
(2)共享文件
这些就是所谓的动态库文件,也即 .so
文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉物理内存。如果将静态库换成动态库,那么这些问题都不会出现。
(3)可执行文件
保存着一个用来执行的文件,该文件指出了exec如何创建程序进程映像。
动态链接实验截图:

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);
//使用加载时的dll库文件,返回句柄
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;
}
//动态链接不同于静态链接,动态链接只是在链接时加入一些描述信息,而静态链接是是一开始把所有的模块载入到程序中,载入内存,而载入时的动态链接是将功能模块读入内存时把动态库中相关模块载入内存,运行时动态链接是把执行程序调用时,将相应模块载入。
三、使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证对Linux系统加载可执行程序所需处理过程的理解
特别关注新的可执行程序是从哪里开始执行的?为什么execve系统调用返回后新的可执行程序能顺利执行?对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?
使用gdb调试啮合处理函数:

分析execve的执行过程:
1、int do_execve(struct filename *filename, const char __user *const __user *__argv, const char __user *const __user *__envp)
{ return do_execve_common(filename, argv, envp); }
2、static int do_execve_common(struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp)
{ retval = prepare_binprm(bprm); // 填充 linux_binprm结构体
retval = copy_strings_kernel(1, &bprm->filename, bprm); // copy传递进来的参数
retval = copy_strings(bprm->envc, envp, bprm);
retval = copy_strings(bprm->argc, argv, bprm);
retval = exec_binprm(bprm);//查找对应的处理函数search_binary_handler
3、static int exec_binprm(struct linux_binprm *bprm)
{
ret = search_binary_handler(bprm); //搜索formaats链表,根据bprm找相应的处理函数// 扫描formats链表,根据不同的文本格式,选择不同的load函数 // ... return ret; }
4、int search_binary_handler(struct linux_binprm *bprm)
{ list_for_each_entry(fmt, &formats, lh)
{ if (!try_module_get(fmt->module)) continue;
read_unlock(&binfmt_lock);
bprm->recursion_depth++; // 应用每种格式的load_binary方法
retval = fmt->load_binary(bprm); //调用的是load_elf_binary函数
/*struct linux_binfmt
{ struct list_head lh;
struct module *module;
int (*load_binary)(struct linux_binprm *);//load_binary的定义 fmt->load_binary(bprm)
int (*load_shlib)(struct file *);
int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
};
static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE,
};
static int __init init_elf_binfmt(void)
{
register_binfmt(&elf_format); //注册
return 0;
}
*/
read_lock(&binfmt_lock);
put_binfmt(fmt);
bprm->recursion_depth--;
// ... }
return retval; }
5、static int load_elf_binary(struct linux_binprm *bprm) {
if (elf_interpreter) //动态链接的话
{ unsigned long interp_map_addr = 0;
elf_entry = load_elf_interp(&loc->interp_elf_ex, interpreter, &interp_map_addr, load_bias);//指向一个动态链接程序的入口
// ... }
else{
elf_entry = loc->elf_ex.e_entry;//否则程序入口
// .... }
start_thread(regs, elf_entry, bprm->p);//修改eip和esp
retval = 0;
}
6、
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
//..
regs->ip = new_ip; // 新进程开始执行的地址
regs->sp = new_sp; // 用户态的栈顶
}

浙公网安备 33010602011771号