Linux0.11内核源码解析-exec.c
主要实现对二进制可执行文件和shell文件的加载和执行,其中主要的函数是do_execve(),它是系统中断调用int 0x80的功能号__NR_execve()调用,是exec()函数的主要实现以下几点功能:
1.执行对参数和环境参数空间页面的初始化操作,初始化空间页面指针数组,根据执行文件名取执行对象的i节点,计算参数个数和环境变量个数,检查文件类型、执行权限
2.根据执行文件开始部分的头数据结构,对其中信息进行处理,根据被执行文件i节点读取文件头部信息,若是shell以#!开始,则分析shell程序名和其参数;根据幻数和段长度等信息判断是否可执行
3.对当前调用进程进行运行新文件前初始化操作,指向新执行文件的i节点,复位信号处理句柄,根据头结构信息设置局部描述符地址和段长;设置参数和环境参数页面指针;修改进程各执行字段内容
4.替换堆栈上原调用execve()程序的返回地址为新执行程序运行地址,运行新加载的程序

create_tables函数用于根据给定的当前堆栈指针值p以及参数个数argc和环境变量个数envc,在新的程序堆栈中创建环境和参数变量指针表,并返回此时的堆栈指针值sp

create_tables
用戶内存中解析环境变量和参数字符串,创建指针表,并将地址放到堆栈上,返回栈指针
/** MAX_ARG_PAGES defines the number of pages allocated for arguments* and envelope for the new program. 32 should suffice, this gives* a maximum env+arg of 128kB !*/
#define MAX_ARG_PAGES 32/** create_tables() parses the env- and arg-strings in new user* memory and creates the pointer tables from them, and puts their* addresses on the "stack", returning the new stack pointer value.*/
static unsigned long * create_tables(char * p,int argc,int envc)
{unsigned long *argv,*envp;unsigned long * sp;//堆栈指针是以4字节为边界寻址sp = (unsigned long *) (0xfffffffc & (unsigned long) p);//sp向下移动,空出环境参数占用空间个数,让环境参数指针envp指向该处sp -= envc+1;envp = sp;//sp向下移动,空出环境参数占用的空间个数,并让环境参数指针envp指向该处sp -= argc+1;argv = sp;//argc、argv、envpput_fs_long((unsigned long)envp,--sp);put_fs_long((unsigned long)argv,--sp);put_fs_long((unsigned long)argc,--sp);//argcwhile (argc-->0) {put_fs_long((unsigned long) p,argv++);while (get_fs_byte(p++)) /* nothing */ ;}//最后放置NULLput_fs_long(0,argv);//envcwhile (envc-->0) {put_fs_long((unsigned long) p,envp++);while (get_fs_byte(p++)) /* nothing */ ;}//最后放置NULLput_fs_long(0,envp);return sp;
}
count
计算参数个数
/** count() counts the number of arguments/envelopes*/
static int count(char ** argv)
{int i=0;char ** tmp;if ((tmp = argv))//判断为NULL就退出while (get_fs_long((unsigned long *) (tmp++)))i++;return i;
}
copy_string
函数从用户内存空间拷贝参数和环境字符串到内核空闲页面内存中,argc
/** 'copy_string()' copies argument/envelope strings from user* memory to free pages in kernel mem. These are in a format ready* to be put directly into the top of new user memory.** Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies* whether the string and the string array are from user or kernel segments:* * from_kmem argv * argv *** 0 user space user space* 1 kernel space user space* 2 kernel space kernel space* * We do this by playing games with the fs segment register. Since it* it is expensive to load a segment register, we try to avoid calling* set_fs() unless we absolutely have to.*/
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,unsigned long p, int from_kmem)
{char *tmp, *pag=NULL;int len, offset = 0;unsigned long old_fs, new_fs;if (!p)return 0; /* bullet-proofing */new_fs = get_ds();old_fs = get_fs();if (from_kmem==2)set_fs(new_fs);while (argc-- > 0) {if (from_kmem == 1)set_fs(new_fs);if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))panic("argc is wrong");if (from_kmem == 1)set_fs(old_fs);len=0; /* remember zero-padding *///计算字符串的长度do {len++;} while (get_fs_byte(tmp++));//字符串长度判断if (p-len < 0) { /* this shouldn't happen - 128kB */set_fs(old_fs);return 0;}while (len) {--p; --tmp; --len;//首次复制字符串,offset复制为0if (--offset < 0) {//p指针在页内的偏移offset = p % PAGE_SIZE;//恢复fs段寄存器if (from_kmem==2)set_fs(old_fs);//如果pag不为NULL,说明页面在之前的拷贝过程得到分配//pag指针为NULL,说明该页面还没有分配内存空间if (!(pag = (char *) page[p/PAGE_SIZE]) &&!(pag = (char *) (page[p/PAGE_SIZE] =(unsigned long *) get_free_page()))) return 0;if (from_kmem==2)set_fs(new_fs);}*(pag + offset) = get_fs_byte(tmp);}}//还原fsif (from_kmem==2)set_fs(old_fs);return p;
}
修改局部描述符表中的描述符基址和段限长,并将参数和环境页面放置在数据段末端
参数:text_size 执行文件头部中a_text字段给出的代码段长度值,page参数和环境空间页面指针数组
static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
{unsigned long code_limit,data_limit,code_base,data_base;int i;
//根据执行文件头部a_text的值,计算以页面长度为边界代码段限长,并设置数据段长度为64MBcode_limit = text_size+PAGE_SIZE -1;code_limit &= 0xFFFFF000;data_limit = 0x4000000;
//取当前进程中局部描述符代码段描述符中代码段基地址,代码段基址和数据段基址相同code_base = get_base(current->ldt[1]);data_base = code_base;
//重新设置局部表中代码段和数据描述符的基址和段限长set_base(current->ldt[1],code_base);set_limit(current->ldt[1],code_limit);set_base(current->ldt[2],data_base);set_limit(current->ldt[2],data_limit);
/* make sure fs points to the NEW data segment */__asm__("pushl $0x17\n\tpop %%fs"::);data_base += data_limit;for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {data_base -= PAGE_SIZE;if (page[i])put_page(page[i],data_base);}return data_limit;
}
execve系统中断调用函数,加载并执行子进程
函数系统中断调用int 0x80功能号__NR_execve,
参数:eip指向堆栈中调用系统中断的程序代码指针eip,
tmp:_sys_execve时返回地址
filename 被执行程序文件名
argv命令行参数指针数组
envp环境变量指针数组
/** 'do_execve()' executes a new program.*/
int do_execve(unsigned long * eip,long tmp,char * filename,char ** argv, char ** envp)
{struct m_inode * inode;struct buffer_head * bh;struct exec ex;unsigned long page[MAX_ARG_PAGES];int i,argc,envc;int e_uid, e_gid;int retval;int sh_bang = 0;unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;if ((0xffff & eip[1]) != 0x000f)panic("execve called from supervisor mode");for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */page[i]=0;if (!(inode=namei(filename))) /* get executables inode */return -ENOENT;argc = count(argv);envc = count(envp);restart_interp:if (!S_ISREG(inode->i_mode)) { /* must be regular file */retval = -EACCES;goto exec_error2;}i = inode->i_mode;e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;if (current->euid == inode->i_uid)i >>= 6;else if (current->egid == inode->i_gid)i >>= 3;if (!(i & 1) &&!((inode->i_mode & 0111) && suser())) {retval = -ENOEXEC;goto exec_error2;}if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {retval = -EACCES;goto exec_error2;}ex = *((struct exec *) bh->b_data); /* read exec-header */if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {/** This section does the #! interpretation.* Sorta complicated, but hopefully it will work. -TYT*/char buf[1023], *cp, *interp, *i_name, *i_arg;unsigned long old_fs;strncpy(buf, bh->b_data+2, 1022);brelse(bh);iput(inode);buf[1022] = '\0';if ((cp = strchr(buf, '\n'))) {*cp = '\0';for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);}if (!cp || *cp == '\0') {retval = -ENOEXEC; /* No interpreter name found */goto exec_error1;}interp = i_name = cp;i_arg = 0;for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {if (*cp == '/')i_name = cp+1;}if (*cp) {*cp++ = '\0';i_arg = cp;}/** OK, we've parsed out the interpreter name and* (optional) argument.*/if (sh_bang++ == 0) {p = copy_strings(envc, envp, page, p, 0);p = copy_strings(--argc, argv+1, page, p, 0);}/** Splice in (1) the interpreter's name for argv[0]* (2) (optional) argument to interpreter* (3) filename of shell script** This is done in reverse order, because of how the* user environment and arguments are stored.*/p = copy_strings(1, &filename, page, p, 1);argc++;if (i_arg) {p = copy_strings(1, &i_arg, page, p, 2);argc++;}p = copy_strings(1, &i_name, page, p, 2);argc++;if (!p) {retval = -ENOMEM;goto exec_error1;}/** OK, now restart the process with the interpreter's inode.*/old_fs = get_fs();set_fs(get_ds());if (!(inode=namei(interp))) { /* get executables inode */set_fs(old_fs);retval = -ENOENT;goto exec_error1;}set_fs(old_fs);goto restart_interp;}brelse(bh);if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {retval = -ENOEXEC;goto exec_error2;}if (N_TXTOFF(ex) != BLOCK_SIZE) {printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);retval = -ENOEXEC;goto exec_error2;}if (!sh_bang) {p = copy_strings(envc,envp,page,p,0);p = copy_strings(argc,argv,page,p,0);if (!p) {retval = -ENOMEM;goto exec_error2;}}
/* OK, This is the point of no return */if (current->executable)iput(current->executable);current->executable = inode;for (i=0 ; i<32 ; i++)current->sigaction[i].sa_handler = NULL;for (i=0 ; i<NR_OPEN ; i++)if ((current->close_on_exec>>i)&1)sys_close(i);current->close_on_exec = 0;free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));free_page_tables(get_base(current->ldt[2]),get_limit(0x17));if (last_task_used_math == current)last_task_used_math = NULL;current->used_math = 0;p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;p = (unsigned long) create_tables((char *)p,argc,envc);current->brk = ex.a_bss +(current->end_data = ex.a_data +(current->end_code = ex.a_text));current->start_stack = p & 0xfffff000;current->euid = e_uid;current->egid = e_gid;i = ex.a_text+ex.a_data;while (i&0xfff)put_fs_byte(0,(char *) (i++));eip[0] = ex.a_entry; /* eip, magic happens :-) */eip[3] = p; /* stack pointer */return 0;
exec_error2:iput(inode);
exec_error1:for (i=0 ; i<MAX_ARG_PAGES ; i++)free_page(page[i]);return(retval);
}
相关文章:
Linux0.11内核源码解析-exec.c
主要实现对二进制可执行文件和shell文件的加载和执行,其中主要的函数是do_execve(),它是系统中断调用int 0x80的功能号__NR_execve()调用,是exec()函数的主要实现以下几点功能: 1.执行对参数和环境参数空间页面的初始化操作,初始…...
百度竞价排名推广对比自然排名哪一个更具优势-华媒舍
在搜索引擎结论网页页面(SERP)中,我们经常会看到一些网站链接及其广告栏。这种连接一般分为两种类型:百度竞价推广排名推广与自然排名。究竟哪个更有优势?本文将对这几种排名形式进行科谱详细介绍。 什么叫百度竞价推广…...
python第一课 变量
1.离线的情况下首选txt文档 2.有道云笔记 3.思维导图 xmind mindmaster 4.博客 5.wps流程图 # 变量的命名规则 1.变量名只能由数字字母下划线组成 2.变量名不能以数字开头 3.变量名不能与关键字重名 快捷键 撤销:Ctrl/Command Z 新建:Ctrl/Com…...
shell之netstat的用法
shell之netstat的用法 所有参数应用举例 所有参数 1)-A<网络类型>或–<网络类型> 列出该网络类型连线中的相关地址。 2)-i 显示所有网络接口的信息。 3)-r 显示路由表的信息。 4)-s 显示按各个协议的统计信息。 5&am…...
MSQL系列(十二) Mysql实战-为什么索引要建立在被驱动表上
Mysql实战-为什么索引要建立在被驱动表上 前面我们讲解了BTree的索引结构,也详细讲解下 left Join的底层驱动表 选择原理,那么今天我们来看看到底如何用以及如何建立索引和索引优化 开始之前我们先提一个问题, 为什么索引要建立在被驱动表上…...
C语言,数据结构指针,结构构体操作符 •,->,*的区别,看这篇就够了
在朋友们学习指针和数据结构这一章的时候,对各种操作符云里雾里。当你看到这么文章之后你就会明白了。 一 • 和 ->运算符 • 运算符:是结构变量访问结构体成员时用的操作符 -> 运算符:这是结构体指针访问结构体成员时调用的运算符。 …...
axios 多个baseURL配置、实现不同前缀代理到不同的服务器的几种方式
前言: 在开发中,有可能遇到每部分的功能的需要调用另一台服务器的地址。这个时候就需要设置不同的请求前缀首先代理到不同的服务器地址。 一、axios封装实例以及代理:(不是完整的封装实例,重点在于baseURL的区别) 文件路径&…...
Diango项目-简易个人博客项目
项目实现功能 在admin后台自定义添加上传文档。对展示在首页的文章分页显示。在首页点击文章的阅读全文按钮可进入该文章全文详情页进行浏览。对文章实现了内容分类何以发布时间进行归档分类。使用django的whoose搜索引擎对全文实现内容的搜索。 项目涉及技术 Mysql Djan…...
思维训练3
题目描述1 Problem - A - Codeforces 题目分析 样例1解释: 对于此题,我们采用贪心的想法,从1到n块数越少越好,故刚好符合最少的块数即可,由于第1块与第n块是我们必须要走的路,所以我们可以根据这两块砖的…...
初识FFmpeg
前言 无意间见到群里的小伙伴展示视频工具。功能比较多,包括视频编码修改,画质处理,比例处理、名称提取,剪辑、标题拆解。因此开始了FFmpeg学习。以下摘自百度百科的解释。 FFmpeg是一套可以用来记录、转换数字音频、视频…...
分布式多主关系数据库的底线业务优势
当今的应用程序(包括企业应用程序)需要始终开启且始终可用,并且通常必须为全球用户提供服务,这些用户无论身在何处都希望获得几乎即时的响应时间。 应对这些挑战不仅仅意味着让用户更满意:每个能够解决低延迟和超高可…...
JMM讲解
一:为什么要有JMM,它为什么出现? CPU的运行并不是直接操作内存而是先把内存里面的数据读到缓存,而内存的读和写操作的时候会造成不一致的问题。JVM规范中试图定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异&…...
小程序获取头像和昵称的思路
小程序获取头像和昵称的基本方法是调用小程序自带的API wx.getUserProfile(),这也是小程序官方目前最推荐的做法。成功获取用户名头像之后,小程序允许保存调用的结果,以便下一次打开页面的时候自动显示头像和名字。保存用户名和头像并不是保存…...
关于docker网络实践中遇到的问题
1.禁用docker自动修改iptables规则 查看docker.service文件/usr/lib/systemd/system/docker.service 默认在宿主机部署容器,映射了端口的话,docker能自己修改iptables规则,把这些端口暴露到公网。 如果要求这些端口不能暴露到公网…...
C#完成XML文档节点的自动计算功能
一个项目涉及XML文档中节点的自动计算,就是XML文档的每个节点都参与运算,要求: ⑴如果节点有计算公式则按照计算公式进行; ⑵如果节点没有计算公式则该节点的值就是所有子节点的值之和; ⑶节点有4种类型,计…...
体验SOLIDWORKS旋转反侧切除增强 硕迪科技
大家在设计中经常使用的旋转切除命令在solidworks2024版本中迎来了新的增强,添加了旋转反侧切除选项。在设计过程中不必修改复杂的草图即可切除掉我们不需要的部分。使设计工作更加方便快捷。 打开零部件后,点击键盘上的S键并输入旋转切除以搜索该命令&a…...
分布式ID系统设计(3)
分布式ID系统设计第三集 id-service-SnowFlake方案 第二集说了id-service-Segment-DB可以生成趋势递增的ID,但是ID号是可以计算的。不太适用于一些订单ID生成的场景。因为存在数据暴露的风险 比如我可以对比两天的订单ID号来大致计算出公司一天的订单量。这个有点危险。 所以…...
工作备忘录【微信】
这工作备忘录【微信】里写自定义目录标题 unionid获取用户基本信息无 unionid EasyWeChat"overtrue/wechat": "^4.6" 与 "overtrue/wechat": "~3.1" 使用方式有异 unionid 微信 unionid 有关备忘录 获取用户基本信息无 unionid htt…...
Window下SRS服务器的搭建
---2023.7.23 准备材料 srs下载:GitHub - ossrs/srs at 3.0release 目前srs release到5.0版本。 srs官方文档:Introduction | SRS (ossrs.net) Docker下载:Download Docker Desktop | Docker 进入docker官网选择window版本直接下载。由…...
Canvas绘制简易雨滴碰撞效果
实现会动的图形,向下播放多张静态的图片。一秒内要大于屏幕刷新的帧数(60) 也就是每隔1/60s执行一次函数在每次绘制的正方形上添加一个背景色为白色蒙板。 效果图 源代码 <!DOCTYPE html> <html lang"en"><head><meta charset"…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
