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

一.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. day23(023-递归练习)

    23.01_File类递归练习(统计该文件夹大小) * 需求:1,从键盘接收一个文件夹路径,统计该文件夹大小(字节?) * * 从键盘接收一个文件夹路径 * 1,创建键盘录入对象 * 2,定义一个无限 ...

  2. vSphere Replication5.5安装

    vSphere Replication5.5概述 VMware vSphere Replication简称VR是 VMwarevCenter Server 的扩展,提供基于管理程序的虚拟机复制和恢复功 ...

  3. 2020/2/12 PHP编程学习

    感冒终于差不多好了.. 学了一天的tp框架商城开发,到此,一个小商城算是开发完了,写一个简单小总结吧233 首先说的编程方面,其实并没有质的提升orz,怎么可能几天就有大突破233 不过收获还是有的, ...

  4. python数据拼接: pd.concat

    1.concat concat函数是在pandas底下的方法,可以将数据根据不同的轴作简单的融合 pd.concat(objs, axis=0, join='outer', join_axes=Non ...

  5. 51nod 1080:两个数的平方和

    1080 两个数的平方和 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题  收藏  关注 给出一个整数N,将N表示为2个整数i j的平方和(i <= j),如果 ...

  6. 洛谷 P5662 纪念品

    题目传送门 解题思路: 一道DP,完全背包,不过有点不同于模板.因为本题的每件物品可自由在不同的时间买卖,且不同时间价格不同. 这道题的关键在于要明白一个非常傻逼的性质,就是我在某天买了第i个物品,然 ...

  7. 用户使用API函数对创建的文件进行读写操作

    HANDLE handle; //定义文件句柄 ]; //定义缓冲区 int i; //接收实际操作的字节数 CString str; //定义字符串变量 handle = ::CreateFile( ...

  8. 19 01 15 django 数据库设计模型 管理站点 注意:在引入外键在django 2以上改版

    模型设计 我们之前操作数据库是通过写sql语句  ORM框架    可以通过不写sql  语句来进行操作数据库 1.定义模型类 模型类定义在models.py文件中,继承自models.Model类. ...

  9. JS添加、设置属性以及鼠标移入移出事件

    源代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  10. 反编译查看printf()的方法

    源代码: package test2; public class ExplorationJDKSource { /** * @param args */ public static void main ...