linux系统下程序的执行方式,Linux系统ELF程序的执行过程

本文将介绍linux程序的执行过程,并以实际问题为切入点简单介绍下ELF程序的加载过程。【正文】用后态执行我们知道在linux系统中可以通过诸如"./debug"方式执行一个程序,那么这个程序的执行过程中linux系统都做了什么?本文以debug程序为例,介绍linux内核是如何一步步将debug进程执行起来的.1 执行过程:以system()实现为例,它是一种典型的可执行程序运行过程:[cpp]...
摘要由CSDN通过智能技术生成

本文将介绍linux程序的执行过程,并以实际问题为切入点简单介绍下ELF程序的加载过程。

64ea8a37897e05640ae24f28f88d5816.png

【正文】用后态执行

我们知道在linux系统中可以通过诸如"./debug"方式执行一个程序,那么这个程序的执行过程中linux系统都做了什么?

本文以debug程序为例,介绍linux内核是如何一步步将debug进程执行起来的.

1 执行过程:

以system()实现为例,它是一种典型的可执行程序运行过程:

[cpp] view plain copy

#include

#include

#include

#include

int system(const char * cmdstring)

{

pid_t pid;

int status;

if(cmdstring == NULL){

return (1);

}

if((pid = fork())<0){

status = -1;

}

else if(pid = 0){

execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

-exit(127); //子进程正常执行则不会执行此语句

}

else{

while(waitpid(pid, &status, 0)

if(errno != EINTER){

status = -1;

break;

}

}

}

return status;

}

观察上面system实现:

1)system在当前进程中fork创建了一个子进程,并执行execl函数运行可执行文件;

2)execl/execve系列函数执行elf文件;

实际上系统通过execve->do_execve_common函数,将上步创建的子进程,完全替换成了可执行程序.

这个替换过程,其实也就是可执行程序的加载过程,也是本文着重介绍的内容.

3) execve使用实例:

#include

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

[cpp] view plain copy

#include

#include

int main(int arg, char **args)

{

char *argv[]={"ls","-al","/home/", NULL};

char *envp[]={0,NULL};

execve("/bin/ls",argv,envp);

}

【正文】内核态执行

linux系统中,可执行程序大多属于ELF文件格式.

本节以实例介绍:execve("/home/debug",NULL,NULL);其中debug程序是elf格式.

当用后执行execve时,系统都做了什么?下面逐层分析:

1 系统调用:execve->do_execve->do_execve_common

[cpp] view plain copy

/* filename为可执行文件:/home/debug;

argv为NULL,表示可行程序不带参数;

envp为NULL,表示没有指定环境变量;  */

int do_execve(const char *filename,const char __user *const __user *__argv,

const char __user *const __user *__envp)

{

struct user_arg_ptr argv = { .ptr.native = __argv };

struct user_arg_ptr envp = { .ptr.naTIve = __envp };

return do_execve_common(filename, argv, envp);

}

2 execve->do_execve->do_execve_common()注意此时当前进程是上文中创建的子进程。

bprm_mm_init()完成进程地址空间vma(包括栈)的初始化.

[cpp] view plain copy

/*

* sys_execve() executes a new program.

*/

staTIc int do_execve_common(const char *filename,

struct user_arg_ptr argv,

struct user_arg_ptr envp)

{

/*注意linux_binprm是核心数据结构,它保存了可执行文件的信息;*/

struct linux_binprm *bprm;

struct file *file;

struct files_struct *displaced;

bool clear_in_exec;

int retval;

const struct cred *cred = current_cred();

/*

* We move the actual failure in case of RLIMIT_NPROC excess from

* set*uid() to execve() because too many poorly written programs

* don't check setuid() return code.  Here we addiTIonally recheck

* whether NPROC limit is sTIll exceeded.

*/

if ((current->flags & PF_NPROC_EXCEEDED) &&

atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) {

retval = -EAGAIN;

goto out_ret;

}

/* We're below the limit (still or again), so we don't want to make

* further execve() calls fail. */

current->flags &= ~PF_NPROC_EXCEEDED;

retval = unshare_files(&displaced);

if (retval)

goto out_ret;

retval = -ENOMEM;

/*申请linux_binprm描述符,用以保存ELF可执行文件信息*/

bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);

if (!bprm)

goto out_files;

/*生成bprm->cred即准备可执行程序运行的用户和组信息,主要根据当前进程的task->cred信息生成*/

retval = prepare_bprm_creds(bprm);

if (retval)

goto out_free;

retval = check_unsafe_exec(bprm);

if (retval

goto out_free;

clear_in_exec = retval;

current->in_execve = 1;

/*

1:打开可执行程序 /home/debug;

*/

file = open_exec(filename);

retval = PTR_ERR(file);

if (IS_ERR(file))

goto out_unmark;

sched_exec();

/*bprm->file为/home/debug文件描述符*/

bprm->file = file;

/*可执行文件名保存到bprm->filename中*/

bprm->filename = filename;

bprm->interp = filename;

/*生成bprm->mm,即准备可执行程序的mm_struct信息,

注意此时生成栈空间信息,不过后面会对栈空间再次调整

注意此处的bprm->mm不是当前进程的,是bprm_mm_init申请的

以后用作/home/debug进程的mm_struct;

*/

retval = bprm_mm_init(bprm);

if (retval)

goto out_file;

/*可执行文件参数个数,对/home/debug来说argc=0,因为指定参数为NULL*/

bprm->argc = count(argv, MAX_ARG_STRINGS);

if ((retval = bprm->argc)

goto out;

/*envc=0参加bprm->argc*/

bprm->envc = count(envp, MAX_ARG_STRINGS);

if ((retval = bprm->envc)

goto out;

/*

elf头保存到bprm->buf中;

实现方式: kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);//128bytes

*/

retval = prepare_binprm(bprm);

if (retval

goto out;

retval = copy_strings_kernel(1, &bprm->filename, bprm);

if (retval

goto out;

/*保存execve中指定的环境变量到linux_binprm结构中*/

bprm->exec = bprm->p;

retval = copy_strings(bprm->envc, envp, bprm);

if (retval

goto out;

/*保存execve中指定的可执行程序参数到linux_binprm结构中*/

retval = copy_strings(bprm->argc, argv, bprm);

if (retval

goto out;

/*

该函数负责从flash上加载ELF文件:并将当前子进程信息替换为可执行文件中读取的信息.

elf_format->load_binary=load_elf_binary->arch_setup_additional_pages : register_binfmt中注册的elf_format

->install_special_mapping->insert_vm_struct

*/

retval = search_binary_handler(bprm);

if (retval

goto out;

/* execve succeeded */

current->fs->in_exec = 0;

current->in_execve = 0;

acct_update_integrals(current);

free_bprm(bprm);

if (displaced)

put_files_struct(displaced);

return retval;

out:

if (bprm->mm) {

acct_arg_size(bprm, 0);

mmput(bprm->mm);

}

out_file:

if (bprm->file) {

allow_write_access(bprm->file);

fput(bprm->file);

}

out_unmark:

if (clear_in_exec)

current->fs->in_exec = 0;

current->in_execve = 0;

out_free:

free_bprm(bprm);

out_files:

if (displaced)

reset_files_struct(displaced);

out_ret:

return retval;

}

2.1 ELF头读取过程:do_execve_common()->prepare_binprm()

[cpp] view plain copy

int prepare_binprm(struct linux_binprm *bprm)

{

umode_t mode;

struct inode * inode = file_inode(bprm->file);

int retval;

mode = inode->i_mode;

if (bprm->file->f_op == NULL)

return -EACCES;

/* clear any previous set[ug]id data from a previous binary */

bprm->cred->euid = current_euid();

bprm->cred->egid = current_egid();

if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&

!current->no_new_privs &&

kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&

kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) {

/* Set-uid? */

if (mode & S_ISUID) {

bprm->per_clear |= PER_CLEAR_ON_SETID;

bprm->cred->euid = inode->i_uid;

}

/* Set-gid? */

/*

* If setgid is set but no group execute bit then this

* is a candidate for mandatory locking, not a setgid

* executable.

*/

if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {

bprm->per_clear |= PER_CLEAR_ON_SETID;

bprm->cred->egid = inode->i_gid;

}

}

/* fill in binprm security blob */

retval = security_bprm_set_creds(bprm);

if (retval)

return retval;

bprm->cred_prepared = 1;

memset(bprm->buf, 0, BINPRM_BUF_SIZE);

/*

elf头保存到bprm->buf中

*/

return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);

}

[ELF文件加载]

ELF文件格式:https://baike.baidu.com/item/ELF/7120560?fr=aladdin

3.1文件头(Elf header) :

Elf头在程序的开始部位,作为引路表描述整个ELF的文件结构,其信息大致分为四部分:一是系统相关信息,二是目标文件类型,三是加载相关信息,四是链接相关信息。

其中系统相关信息包括elf文件魔数(标识elf文件),平台位数,数据编码方式,elf头部版本,硬件平台e_machine,目标文件版本 e_version,处理器特定标志e_ftags:这些信息的引入极大增强了elf文件的可移植性,使交叉编译成为可能。目标文件类型用e_type的值表示,可重定位文件为1,可执行文件为2,共享文件为3;加载相关信息有:程序进入点e_entry.程序头表偏移量e_phoff,elf头部长度 e_ehsize,程序头表中一个条目的长度e_phentsize,程序头表条目数目e_phnum;链接相关信息有:节头表偏移量e_shoff,节头表中一个条目的长度e_shentsize,节头表条目个数e_shnum ,节头表字符索引e shstmdx。可使用命令"readelf -h filename"来察看文件头的内容。

文件头的数据结构如下:

[cpp] view plain copy

typedef struct elf32_hdr{

unsigned char e_ident[EI_NIDENT];

Elf32_Half e_type;//目标文件类型

Elf32_Half e_machine;//硬件平台

Elf32_Word e_version;//elf头部版本

Elf32_Addr e_entry;//程序进入点

<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值