zoukankan      html  css  js  c++  java
  • 2018-2019-1 20189215 《Linux内核原理与分析》第八周作业

    可执行程序工作原理

    《庖丁解牛》第七章书本知识总结

    1. “目标文件”是指编译器生成的文件,“目标”指的是目标平台,例如x86或x64,它决定了编译器使用的机器指令集。目标文件一般也叫做ABI(应用程序二进制接口),目标文件和目标平台是二进制兼容的。二进制兼容是指该目标文件已经是适应某一种CPU体系结构上的二进制指令。
    2. ELF即可执行的和可链接的格式,是一个目标文件格式的标准,用于存储Linux程序。
    3. ELF文件的类型

    经查资料,应该分类为4类:

    1. 可重定位文件(Relocatable File):保存着代码和适当的数据,用来和其它的目标文件一起来创建一个可执行文件、静态库文件或者是一个共享目标文件(主要是.o文件)
    2. 可执行文件(Executable File):保存着一个用来执行的程序,一般由多个可重定位文件结合生成,是完成了所有重定位工作和符号解析(除了运行时解析的共享库符号)的文件。
    3. 共享目标文件(Shared Object File):保存着代码和合适的数据,用来被两个链接器链接。第一个是链接编辑器(静态链接),可以和其它的可重定位和共享目标文件来创建其它的object。第二个是动态链接器,联合一个可执行文件和其它的共享目标文件来创建一个进程映象。
    4. 核心转储文件(Core Dump File):保存核心转储信息。
    1. ELF文件的主体是各种节,还有描述这些节属性的信息(Program header table和Section header table),以及ELF文件的整体描述信息(ELF header)。
    2. ELF Header会给出很多关于本ELF文件的属性信息,例如e_type体现了ELF文件类型,e_type值1、2、3、4分别代表可重定位文件、可执行文件、共享目标文件和核心转储文件。
    3. c代码的预处理、编译、汇编、链接(ESc、iso)
      gcc -E hello.c -o hello.i //预处理
      gcc -S hello.i -o hello.s //编译
      gcc -c hello.s -o hello.o //汇编
      gcc hello.o -o hello //链接
    4. readelf命令
      -a 等价于 -h -l -S -s -r -d -V -A -I
      -h 显示elf文件开始的文件头信息
      -S 显示节头信息
      -l 显示段头Program Header
      -s 显示符号表段中的项(如果有)
      -r 显示可重定位段的信息
      -H 显示readelf所支持的命令行选项
    5. 静态链接与动态链接

    静态链接:在编译链接时直接将需要的执行代码复制到最终可执行文件中,优点是代码的装载速度快,执行速度也比较快,对外部环境依赖度低。缺点是如果多个应用程序使用同一库函数,会被装载多次,浪费内存。
    动态链接:编译时不直接复制可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统。操作系统负责将需要的动态库加载到内存中,在程序运行到指定的代码时,去共享执行内存中已经加载的动态库去执行代码,最终达到运行时链接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个复制。缺点是在运行时加载可能会影响程序的前期执行性能,而且对使用的库依赖性较高。(分为装载时动态链接和运行时动态链接)
    再使用gcc hello.o -o hello.static -static进行静态链接,发现得到的可执行程序文件大小大概是动态链接的100倍。

    1. 装载时动态链接
      使用命令gcc -shared shlibexample.c -o libshlibexample.so -m32编译装载时链接动态库libshlibexample.so
      shlibexample.h:

      shlibexample.c:
    2. 运行时动态链接
      使用命令gcc -shared dllibexample.c -o libdllibexample.so -m32编译运行时链接动态库libdllibexample.so
      dllibexample.h

      dllibexample.c
    3. 动态链接运行测试
      使用命令gcc main.c -o main -L./ -l shlibexample -ldl -m32编译测试main函数
    #include <stdio.h>
    #include "shlibexample.h"
    #include <dlfcn.h>
    
    int main()
    {
    	printf("This is a Main program!
    ");
    
    	//sh
    	printf("Calling ShardLibApi() function of libshlibexample.so!
    ");
    	SharedLibApi();
    
    	//dl
    	void * handle = dlopen ("libdllibexample.so",RTLD_NOW);
    	if(handle == NULL)
    	{
    		printf("Open Lib libdllibexample.so Error:%s
    ",dlerror());
    		return FAILURE;
    	}
    	int (*func)(void);
    	char * error;
    	func = dlsym(handle,"DynamicalLoadingLibApi");
    	if((error = dlerror()) != NULL)
    	{
    		printf("DynamicalLoadingLibApi not found:%s
    ",error);
    		return FAILURE;
    	}
    	printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!
    ");
    	func();
    	dlclose(handle);
    	return SUCCESS;
    }
    

    在运行前,使用命令export LD_LIBRARY_PATH=$PWD将当前目录加入库文件搜索目录。

    实验:使用cgdb跟踪分析execve系统调用内核处理函数sys_execve

    1. make rootfs,发现没有包含sys_wait.h,qemu命令也不适用于本机器,先添加sys_wait.h
    2. 修改Makefile
    3. 看到exec命令的运行结果
    4. 冻结,准备调试
    5. 设置断点(先设置到sys_execve,再设置其他两个)
    6. 单步走并进入函数,发现sys_execve调用了do_execve
    7. 继续运行到load_elf_binary
    8. 继续执行到start_thread
    9. 使用po new_ip查看new_ip所指向的地址,new_ip是返回到用户态的第一条指令的地址
    10. 查看hello文件的elf头部

      发现定义的入口地址与new_ip所指向的地址是一致的。
    11. 继续单步执行

    总结

    装载时动态链接中,模块非常明确调用某个导出函数,使得它们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。 但是在装载时装载过多的库又会影响程序的启动速度。
    运行时动态链接中,不需要包含头文件,但需要在代码中进行读取、装载、关闭等一系列行为,比较繁琐而且增加了代码行数,出现错误的可能性也增加了。

    参考资料

    《庖丁解牛Linux》
    ELF文件格式解析
    ELF文件结构详解

  • 相关阅读:
    IDEA导入jar包
    怎么在idea中新建package包,只有directory选项
    Python在自动化运维时经常会用到的方法
    SocketServer 网络服务框架
    导入自定义包
    socket编程
    在线安全清空慢查询日志slowlog
    OS X中微信双开
    OS X中crt中文乱码
    谈谈TCP中的TIME_WAIT
  • 原文地址:https://www.cnblogs.com/jsjliyang/p/10053558.html
Copyright © 2011-2022 走看看