第七章 可执行程序工作原理

一.ELF目标文件格式

  • 目标文件:ABI,应用程序二进制接口,是编译器生成的文件。
  • ELF:可执行的和可链接的格式,是一个目标文件格式的标准。三种类型是:
    • 可重定位文件:Linux中每个内核源代码.c文件都会生成一个同名的.o文件,该文件即为可重定位目标文件。
    • 可执行文件:由多个可重定位文件结合生成。
    • 共享目标文件:共享库,指可以被可执行文件或者其他库文件使用的目标文件。Linux下共享库后缀为.so的文件。
  • ELF文件的作用:参与程序的连接(建立一个程序)和程序的执行(运行一个程序)。
  • ELF格式简介

可以使用入下命令将hello.c编译为一个32位静态链接ELF可执行文件 hello.m32.static 。

ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。

二.程序编译

预处理:.i 文件任然是文本文件,可用任意编辑工具打开查看。

gcc -E hello.c -o hello.i

编译-S 表示只进行编译而不进行汇编(仅生成汇编代码,不进一步翻译为机器指令),-m32生成32位平台格式文件,其与64位使用不同的寄存器名及指令集。

gcc -S hello.i -o hello.s -m32

汇编:汇编后形成的.o文件已经是ELF格式文件了。

gcc -c hello.s -o hello.o.m32 -m32

链接:将各种代码和数据部分收集起来并且组合成为一个单一文件的过程,这个文件被加载到内存中并执行。将输编译输出的.o文件与libc库文件进行链接,生成最终的可执行文件。

gcc hello.o.m32 -o hello.m32.static -m32 -static

三.链接与库

  • 链接从过程上分为:符号解析重定位
  • 符号解析:编译器需要到其他的共享库中找到printf的“定义(机器指令片段)”,找到后把该机器指令与hello.o拼接到一起,生成了执行文件hello。hello中printf就存在了(有定义即有了明确的地址)。
  • 重定位:把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。即在装入时对目标程序中指令和数据的修改过程。
  • 根据链接的时机不同,又分为静态链接动态链接
  • 静态链接:在编译链接时直接将需要的执行代码复制到最终的可执行文件中。
  • 动态链接:在编译时不直接复制可执行代码,是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统。
    • 在编译时候不加“ -static ”选项,编辑器会默认使用动态链接。静态链接在编译时会把需要的所有代码都链接进去所以应用程序相对比较大。如下图所示:
  • 动态链接分为可执行程序装载时动态链接运行时动态链接

装载时动态链接

使用如下命令:

gcc -shared shlibexample.c -o libshlibexample.so -m32

可以将一个动态库源码编译成libshlibexample.so文件。

运行时动态链接

运行时动态链接库的源文件为 dllibexample.h 和 dllibexample.c。

编译成 libdllibexample.so 文件的指令如下:

gcc -shared dllibexample.c -o libdllibexample.so -m32

动态链接实例

如下代码分别以装载时动态链接和运行时动态链接调用了两个动态链接库。

#include<stdio.h>
#include "shlibexample.h"
#include<dlfcn.h> int main()
{
printf("This is a Main program!\n"); /*装载时动态链接*/
printf("Calling SharedLibApi() function of libshlibexample.so!\n");
SharedLibApi(); /*运行时动态链接*/
void *handle=dlopen("libdllibexample.so",RTLD_NOW);
if(handle == NULL)
{
printf("Open Lib libdllipexample.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;
}

编译运行:

$ sudo gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
$ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件复制到默认路径下。
$ ./main

其中,参数-L指明头文件所在的目录,参数-l指明库文件名,如libshlibexample.so去掉lib和.so部分。参数-ldl指明其所需要使用共享库dlopen等函数。

最终的编译运行效果如下:

遇到的问题1:

解决办法:

libshlibexample.so是一个第三方库,并不存在于系统的默认路径中,搜索不到。因此一个简答的方法是把libshlibexample.so放到/usr/local/lib目录下,就可以解决这个问题。

进入libshlibexample.so所在的目录,打开终端,运行以下命令即可:

sudo cp libshlibexample.so /usr/local/lib/

遇到的问题2:

解决办法:

运行时动态链接的操作不在该目录下,因此找不到libdllibexample.so文件。在该目录下重新编译得到libdllibexample.so文件即可。

四.程序装载

  • shell本身不限制命令行参数的个数,命令行参数个数受限于命令本身。
  • shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
  • execve的函数原型:
int execve(const char *filename,char *const argv[],char * const envp[]);
  • 调用关系:sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()

  • fork与execve的区别和联系:

    • 都是比较特殊的系统调用;
    • fork在陷入内核态后有两次返回,第一次返回到原来父进程的位置继续向下执行(这和其他的系统调用是一样的),第二次是在子进程中fork也返回了一次,这次会返回到ret_from_fork,之后正常返回用户态;
    • execve在执行时陷入内核态,在内核中调用execve加载的可执行文件把当前进程的可执行程序给覆盖了,当execve的系统调用返回时,返回的已经不是原来的那个可执行程序了,返回的是新的可执行程序执行的起点,即main函数的大致位置(一般地址为0x8048xxx,由编译器设定)。
  • 执行 readelf -h 可以查看 ELF 可执行文件首部信息。

五.实验

使用gdb跟踪分析execve系统调用内核处理函数sys_execve

将menu目录删除,利用git命令克隆一个新的menu目录。

用test_exec.c将test.c覆盖。查看test.c文件可以看到新增加了exec系统调用。



启动内核,使用help命令可以发现增加了exec指令,执行exec指令发现比fork指令增加了一行输出“hello word!”。实际上是新加载了一个可执行程序来输出一行语句。

返回到LinuxKernel目录下,启动内核。在进行gdb调试之前先启动gdb,把3.18.6的内核加载进来,之后连接到target remote 1234。

设置断点:



执行到 start_thread 处的断点,使用 po new_ip 指令打印其指向的地址:

退出调试状态,输入readelf -h hello可以查看hello的EIF头部

参考资料连接:https://www.cnblogs.com/feifanrensheng/p/10039959.html

2019-2020-1 20199324《Linux内核原理与分析》第八周作业的更多相关文章

  1. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  2. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  3. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  4. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

  5. 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...

  6. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  7. 2019-2020-1 20209313《Linux内核原理与分析》第二周作业

    2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  10. 2018-2019-1 20189221《Linux内核原理与分析》第二周作业

    读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...

随机推荐

  1. 在线上Linux下,PHP扩展安装(使用yum安装)

    直接操作linux,在命令模式下用yum 来安装PHP的扩展: 扩展:mbstring 命令: yum install php-mbstring* 扩展:GD库 命令:yum install php- ...

  2. UNION和UNION ALL的作用和语法

    主要就是两句话区别: union是联合的意思,就是合并两个或多个select语句的结果集,并消除重复行: union all 当然也是联合的意思,也是合并两个或多个select语句的结果集,但是保留重 ...

  3. PAT Advanced 1151 LCA in a Binary Tree (30) [树的遍历,LCA算法]

    题目 The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both ...

  4. (递归)P1025 数的划分

    题解: #include<iostream>using namespace std;int ret=0,m_n;void p(int n,double k,int j){ if(k==1) ...

  5. k8s安装helm

    1.客户端安装 A.直接在github上下载二进制文件进行解压,下载地址:https://github.com/kubernetes/helm/releases B.将解压出来的二进制文件helm 拷 ...

  6. VS2019企业版产品密钥

    Visual Studio 2019 Enterprise产品密钥(激活码) BF8Y8-GN2QH-T84XB-QVY3B-RC4DF

  7. spyder.app制作图标

    安装了 anaconda3, 自带spyder, 但是只能在terminal 中打开, 非常不友好. 模仿 anaconda3/目录下 Anaconda-Navigator.app, 制作了 spyd ...

  8. mysql自关联和多表连接查询

    自关联操作         多表连接查询  inner  join 内查询   left  join  左查询   right  join  右查询                          ...

  9. 吴裕雄--天生自然 JAVASCRIPT开发学习:prototype(原型对象)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  10. [转帖西部数据的Zonefs将会登陆Linux 5.6内核]

    西部数据的Zonefs将会登陆Linux 5.6内核 https://www.cnbeta.com/articles/tech/948875.htm 据说SMR 能够提高25%的存储密度 但是会造成严 ...