热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux内核如何装载和启动一个可执行程序

20135311傅冬菁原创作品转载请注明出处《Linux内核分析》MOOC课程http:mooc.study.163.comcourseUSTC-1000029000Linux内核如何装载

20135311傅冬菁 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

Linux内核如何装载和启动一个可执行程序 

一、内容分析

一、预处理、编译、链接和目标文件的格式

1.预处理阶段 :编译器将C源代码中包含的头文件编译进来和执行宏替换等工作。

gcc -E -o XX.cpp XX.c -m32 (XX.cpp是预处理文件

 

2.编译器生成汇编代码阶段:gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。

gcc -x cpp-output -S -o hello.s hello.cpp -m32 (XX.s是汇编代码)

 

3.汇编器生成目标代码阶段:把编译阶段生成的XX.S文件转成二进制目标代码。

gcc -x assembler -c hello.s -o hello.o -m32 (XX.o是目标代码)

 

4.链接器生成可执行文件阶段(将编译输出XX.o文件链接成最终的可执行文件)。

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

5.运行(若链接没有-o指明,则生成可执行文件默认为a.out)

./a.out

 

PS: hello和hello.static的区别:ls -l

 

目标文件的格式ELF:

 A.out是最古老的可执行文件,目前Windows系统上多是PE,Linux系统上多是ELF。ELF文件已经是适应到某一种CPU体系结构的二进制兼容文件了

ELF格式分类:

  • 可重定位文件.o,用来和其他object文件一起创建可执行文件和共享文件
  • 可执行文件,指出应该从哪里开始执行
  • 共享文件,主要是.so文件,用来被链接编辑器和动态链接器链接

对ELF头的描述告诉系统如何创建一个进程的内存映像,section头表包含了描述文件sections的信息。当创建或增加一个进程映像时,理论上它会把程序段拷贝到虚拟内存中某个段

ELF文件的头部规定了许多与二进制兼容性相关的信息。所以在加载ELF文件的时候,必须先加载头部,分析ELF的具体信息

entry代表刚加载过新的可执行文件之后的程序的入口地址,头部后是代码和数据,进程的地址空间是4G,上面的1G是内核用,下面的3G是程序使用。默认的ELF头加载地址是0x8048000

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

1. 可执行文件加载到内存时:

  • 加载效果:将代码段数据加载到内存中,再把数据加载到内存,默认从0x8048000地址开始加载
  • 启动一个刚加载过可执行文件的进程时,可执行文件加载到内存之后执行的第一条代码地址
  • 一般静态链接会将所有代码放在一个代码段,而动态链接的进程会有多个代码段

2. 流程

  • 分析头部
  • 查看是否需要动态链接。如果是静态链接的ELF文件,那么直接加载文件即可。如果是动态链接的可执行文件,那么需要加载的是动态链接器
  • 装载文件,为其准备进程映像
  • 为新的代码段设定寄存器以及堆栈信息 

默认从0x8048000处开始加载件加载到内存中开始执行的第一行代码

一般静态链接会将所有代码放在一个代码段

动态链接的进程会有多个代码段

可执行程序、共享库和动态加载

1.装载可执行程序之前的工作

(1)可执行程序的执行环境

一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。

Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身。

int main(int argc, char *argv[]) 
int main(int argc, char argv[], char envp[])

其中, envp是shell的执行环境

Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数

int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

 

库函数exec*都是execve的封装例程

shell中使用fork()来创建新进程。

命令行参数和环境串都放在用户态堆栈中。

  在创建一个新的用户态堆栈的时候,实际上是把命令行参数的内容和环境变量的内容通过指针的方式传递到系统调用的内核处理函数的,内核处理函数在创建一个新的可执行堆栈的时候会将命令行参数的内容和环境变量的内容拷贝到用户态堆栈里面来初始化新的可执行程序执行的上下文环境
shell程序 -> execve -> sys_execve
先函数调用参数传递,在系统调用参数传递

装载时动态链接和运行时动态链接应用举例:

动态链接分为可执行程序装载时动态链接和运行时动态链接,如下代码演示了这两种动态链接。

  1. 准备.so文件
  2. 编译成libshlibexample.so文件
  3. 编译成libdllibexample.so文件
  4. 分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件
  5. 编译main

可执行程序的装载:

可执行程序的装载相关关键问题分析
sys_execve内部会解析可执行文件格式
do_execve -> do_execve_common -> exec_binprm

sys_execve的内部处理过程:

装载和启动一个可执行程序依次调用以下函数:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()

二、实验过程

test.c文件的代码

三、总结

在创建一个新的用户态堆栈的时候,实际上是把命令行参数的内容和环境变量的内容通过指针的方式传递到系统调用的内核处理函数的,内核处理函数在创建一个新的可执行堆栈的时候会将命令行参数的内容和环境变量的内容拷贝到用户态堆栈里面来初始化新的可执行程序执行的上下文环境
shell程序 -> execve -> sys_execve
先函数调用参数传递,在系统调用参数传递

sys_execve的内部处理过程:

装载和启动一个可执行程序依次调用以下函数:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()


推荐阅读
  • Lunix历史及如何学习
    1.Lunix是什么1.1Lunix是操作系统还是应用程序Lunix是一套操作系统,它提供了一个完整的操作系统当中最底层的硬件控制与资源管理的完整架构, ... [详细]
  • linux filesystem_如何使用cgdb + qemu调试linux内核模块
    如何使用cgdbqemu调试linux内核模块前言Linux代码庞大而繁杂,光看代码会使人头晕目眩,如果能通过调试工具对其代码执行流程进行调试ÿ ... [详细]
  • GPS 校验和 代码_Linux recovery 移除签名校验
    原创作者:王锐,多年Linux系统、龙芯平台移植与优化研发经验,LinuxContributor、Mozillian。背景某个设备配套的刷 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • 获取原始语音系统图使用matlab生成一个测试用的单频信号,1KHz,1Vrmsclcclearall;closeall;f1000;%定义信号频 ... [详细]
  • nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 编程语言是从哪蹦出来的——大型伦理寻根现场
    Hello,我是Alex007,一个热爱计算机编程和硬件设计的小白,为啥是007呢?因为叫Alex的人太多了,再加上每天007的生活,Alex007就诞生了。聊一聊编程到底是啥,怎 ... [详细]
  • iic协议
    IIC简介IIC,Inter-IntegratedCircuit,集成电路总线,需要2根线连接拓扑,是半双工,适用于”字节型”设备。I2C总线物理拓扑结构IIC通信原理: 通过对S ... [详细]
  • Linux是一套免费使用和***传播的类UNIX操作系统,主要用于基于Intel x86系列CPU的计算机上。Linux系统是由全世界各地的成千上万的程序员合适的方式定制自己的Li ... [详细]
  • 文章目录前言必知必会的软件服务器分类机架式服务器塔式服务器刀片式服务器三者的区别虚拟服务器(云服务器)服务器的硬件组成服务器常见的品牌前言本文是循序渐进学linux的第一课,为 ... [详细]
  • 实验七、绕过ASLR 第二部分
    7.1实验环境VM配置:Ubuntu12.04(x86)7.2实验原理什么是爆破?使用爆破技巧,来绕过共享库地址随机化。7.3实验过程7. ... [详细]
  • Unity3D平台宏定义之美
    Unity包含一个“平台相关的编译”功能。这包括一些预处理指令,让你分割你的脚本编译和专为支持的平台之一执行代码段。您可以Unity编辑器中运行代码,这 ... [详细]
  • 1.trigraph三字符组据说是为了照顾旧式键盘,还是为了键盘坏了,或者是使用非ASCII字符编码的语言输入方便,设计了一些三元字符组& ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
author-avatar
相见亦无语
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有