Linux如何启动并装载程序

理解编译链接的过程和ELF可执行文件格式



第一步:先编辑一个hello.c

第二步:生成预处理文件hello.cpp

gcc -E -o hello.cpp hello.c -m32

vi hello.cpp (

第三步:编译成汇编代码hello.s

gcc -x cpp-output -S -o hello.s hello.cpp -m32

vi hello.s

第四步:编译成目标代码,得到二进制文件hello.o,

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

vi hello.o

第五步:链接成可执行文件hello,

gcc -o hello hello.o -m32

vi hello

第六步:运行一下

./hello

gcc -o hello hello.c

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

hello.static 也是ELF格式文件

运行一下hello.static

./hello.static

ELF三种主要的目标文件:

ELF文件格式是一个开放标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型:

可重定位的目标文件(Relocatable,或者Object File):保存代码和适当数据,和其它Object文件一起创建可执行文件或共享文件。

可执行文件(Executable):文件保存一个用来执行的程序,该文件指出exec如何来程序进程映像。

共享库(Shared Object,或者Shared Library):保存代码和合适的数据,用来被连接编辑器和动态链接器进行链接。

ELF目标文件参与程序的链接和执行。ELF头文件里保存了文件的组织情况,告诉系统如何创建一个进程的内存映象。

当通过用父进程调用fork创建一个新进程时,系统实际上是拷贝了父进程的一个文件段和虚拟了一个内存段。虚拟内存是假想的内存,它其实是不存在的,而仅仅是由一些硬件和软件管理的一种“系统”。他提供了三个重要的能力:1,它将主存看成一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据(这里存在“交换空间”以及“页面调度”等概念),通过这种方式,高效地利用主存;2,它为每个进程提供了统一的地址空间(以虚拟地址编址),从而简化了存储器管理;3,操作系统会为每个进程提供独立的地址空间,从而保护了每个进程的地址空间不被其他进程破坏。

静态链接的ELF可执行文件与进程的地址空间:



ELF的格式如下:

装载可执行文件

LINUX 一般通过shell程序为执行环境来启动一个可执行程序。Shell本身不限制命令行参数的个数,它受限于命令自身;Shell会调用一个系统调用exece将命令参数和环境参数传递给可执行程序的main函数。

命令行参数与环境变量的保存与传递:

当fork一个子进程时,是先复制父进程,再调用exece,会把原来的进程环境覆盖掉,用户态堆栈也被清空。用户态堆栈以start_stack作为main函数的起点,把argv[ ]命令行参数 和envp[ ]环境变量的内容通过指针的方式传递到系统调用exeve(内核处理函数);exeve创建一个新的用户态堆栈时把上面的命令行参数与环境变量拷贝到新的用户态堆栈里,从而初始化新的可执行程序的上下文环境。exece在内核态下装载可执行程序,再返回用户态。所以它先进行函数调用参数传递,然后系统调用参数传递,最后又进行函数调用参数传递。

动态链接

动态链接是相对于共享对象而言的。动态链接器将程序所需要的所有共享库装载到进程的地址空间,并且将程序汇总所有为决议的符号绑定到相应的动态链接库(共享库)中,并进行重定位工作。

动态连接有两种形式:可执行程序装载时动态连接和运行时动态链接

实验过程

删除menu目录,git克隆一个新的menu目录,并使用test_exec.c覆盖test.c文件。



makerootfs,启动内核



关闭内核,重新执行qemu,并增加参数-s,-S冻结内核执行状态,打开gdb,连接端口1234





开始执行,在内核中执行exec命令,发现会卡住



查看hello的EIF信息



execve 处理过程:

linux/fs/exec.c

SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
return do_execve(getname(filename), argv, envp);
} int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp) //__user 用户态指针
{
struct user_arg_ptr argv = { .ptr.native = __argv };//命令行参数变成结构
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execve_common(filename, argv, envp);
} static int do_execve_common(struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp)

{

struct linux_binprm *bprm; //保存要执行的文件相关的信息(include/linux/binfmts.h)
...
file = do_open_exec(filename);//打开执行的可执行文件 //填充bprm结构
bprm->file = file;
bprm->filename = bprm->interp = filename->name;
...
retval = copy_strings(bprm->argc, argv, bprm);//命令行参数和环境变量copy到结构体里 retval = exec_binprm(bprm);//
} static int exec_binprm(struct linux_binprm *bprm)
{
pid_t old_pid, old_vpid;
int ret; /* Need to fetch pid before load_binary changes it */
old_pid = current->pid;
rcu_read_lock();
old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
rcu_read_unlock(); ret = search_binary_handler(bprm);//寻找可执行文件的处理函数
if (ret >= 0) {
audit_bprm(bprm);
trace_sched_process_exec(current, old_pid, bprm);
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
proc_exec_connector(current);
} return ret;
} /*
* cycle the list of binary formats handler, until one recognizes the image
*/
int search_binary_handler(struct linux_binprm *bprm)
{
struct linux_binfmt *fmt;
...
//循环寻找能够解析当前可执行文件的代码
list_for_each_entry(fmt, &formats, lh) {
if (!try_module_get(fmt->module))
continue;
read_unlock(&binfmt_lock);
bprm->recursion_depth++;
retval = fmt->load_binary(bprm);//加载可执行文件的处理函数,函数指针,实际调用load_elf_binary(linux/fs/binfmt_elf.c)
read_lock(&binfmt_lock);
put_binfmt(fmt);
bprm->recursion_depth--;
if (retval < 0 && !bprm->mm) {
/* we got to flush_old_exec() and failed after it */
read_unlock(&binfmt_lock);
force_sigsegv(SIGSEGV, current);
return retval;
}
if (retval != -ENOEXEC || !bprm->file) {
read_unlock(&binfmt_lock);
return retval;
}
}
read_unlock(&binfmt_lock); }

2019-2020-1 20199303《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. 【tensorflow2.0】处理图片数据-cifar2分类

    1.准备数据 cifar2数据集为cifar10数据集的子集,只包括前两种类别airplane和automobile. 训练集有airplane和automobile图片各5000张,测试集有airp ...

  2. CentOS 6.5 nginx+tomcat+ssl配置

    本文档用于指导在CentOS 6.5下使用nginx反向代理tomcat,并在nginx端支持ssl. 安装nginx.参见CentOS 6 nginx安装. SSL证书申请.参见腾讯SSL证书申请和 ...

  3. ensp,acl访问控制列表

    ACL分类: 基本ACL 编号范围: 2000-2999     参数:源ip地址 高级ACL 编号范围: 3000-3999     参数:源ip地址,目的ip地址,源端口,目的端口等 二层ACL ...

  4. 八、【Docker笔记】使用Dockerfile创建镜像

    在前面我们讲解了基于已有的镜像容器创建和基于本地模板导入两种方式来创建镜像,在这里我们就来说说第三种创建镜像的方式.Dockerfile是一个文本格式的配置文件,我们可以通过Dockerfile快速创 ...

  5. 3D城市

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  6. Shell:Day09-2.笔记

    4.模式匹配(地址定界)  1.空值,没有定义,默认就将文件中所有的行,放入awk进行循环  2.对固定的 1,3 行进行操作  sed -n '1,3p' /etc/passwd  awk '1,3 ...

  7. 怎样用scratch2.0谱写音乐

    打开scratch2.0将语言切换为简体中文: 如果需要播放特殊的声音,可以用播放声音,找到一些特有的音乐,或者通过录制,将自己的配音或者唱歌录制下来: 可以用弹奏鼓声命令弹奏各种击鼓音乐: 通过控制 ...

  8. python--算法相关

    一.时间复杂度排序 1.O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) ...

  9. 测试老司机都在用的表白神器你会吗?-Fiddler之AutoResponse线上调试

    一.Fiddler在线调试功能和表白神器介绍 ​ 在以往的工作中,线上有bug,就需要把文件弄到本地来改,但经常会碰见本地环境又和线上不一样,导致调试困难,闭着眼睛改好之后传到线上去看对不对,不对的话 ...

  10. Vulnhub DC-2靶机渗透

    信息搜集 nmap扫描端口 nmap -sV 192.168.146.140 -p1-10000 开了80端口,那就直接访问一下把.(7744端口是ssh端口,之后会用到) 输入ip,发现url处变成 ...