linux二进制文件加载源码分析


linux二进制加载

结构

linux_binfmt 用来定义二进制格式的结构,代表某种 特定的格式,比如elf

struct linux_binfmt {
struct list_head lh; // 链表头部
struct module *module; // 模块
int (*load_binary)(struct linux_binprm *); // 加载函数
int (*load_shlib)(struct file *);
#ifdef CONFIG_COREDUMP
int (*core_dump)(struct coredump_params *cprm); // coredump处理函数,如elf会有定义
unsigned long min_coredump; /* minimal dump size */
#endif
} __randomize_layout;

linux_binprm 用来存放可执行 文件的各种参数,代表可执行文件的运行时结构

struct linux_binprm {
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
unsigned long vma_pages;
unsigned long argmin; /* rlimit marker for copy_strings() */
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
unsigned long p; /* current top of mem */
unsigned int
/* Should an execfd be passed to userspace? */
have_execfd:1,

/* Use the creds of a script (see binfmt_misc) */
execfd_creds:1,
/*
* Set by bprm_creds_for_exec hook to indicate a
* privilege-gaining exec has happened. Used to set
* AT_SECURE auxv for glibc.
*/
secureexec:1,
/*
* Set when errors can no longer be returned to the
* original userspace.
*/
point_of_no_return:1;
struct file *executable; /* Executable to pass to the interpreter */
struct file *interpreter;
struct file *file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
unsigned int per_clear; /* bits to clear in current->personality */
int argc, envc;
const char *filename; /* Name of binary as seen by procps */
const char *interp; /* Name of the binary really executed. Most
of the time same as filename, but could be
different for binfmt_{misc,script} */
const char *fdpath; /* generated filename for execveat */
unsigned interp_flags;
int execfd; /* File descriptor of the executable */
unsigned long loader, exec;

struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */

char buf[BINPRM_BUF_SIZE];
} __randomize_layout;

__register_binfmt和unregister_binfmt用于注册和取消注册格式

// 注册二进制格式
void __register_binfmt(struct linux_binfmt * fmt, int insert)
{
write_lock(&binfmt_lock);
// 如果是插入,则加入到链表头部,否则是链表尾部
insert ? list_add(&fmt->lh, &formats) :
list_add_tail(&fmt->lh, &formats);
write_unlock(&binfmt_lock);
}

EXPORT_SYMBOL(__register_binfmt);

void unregister_binfmt(struct linux_binfmt * fmt)
{
write_lock(&binfmt_lock);
// 从链表中删除该格式
list_del(&fmt->lh);
write_unlock(&binfmt_lock);
}

可执行文件种类

Binfmt 名称 描述 对应源码
ELF Executable and Linkable Format,标准的Linux可执行文件格式。 fs/binfmt_elf.c
flat 对FLAT二进制格式的支持,用于嵌入式系统 fs/binfmt_flat.c
Scripts 通过POSIX标准的shebang (#!)解释脚本文件。 fs/binfmt_script.c
Misc 使用binfmt_misc机制自定义的文件格式 fs/binfmt_misc.c

可执行文件的执行

内核用的可执行文件加载

graph TD
K[kernel_execve]
B[bprm_execve]
EB[exec_binprm]
SB[search_binary_handler]

K --> B
B --> EB
EB --> SB

kernel_execve在内核态主要用于1号进程的加载和用户模式助手(User Mode Helper, UMH),链路最终调用到search_binary_handler

系统调用相关的

graph TD
DE[do_execve]
DEC[do_execveat_common]
CDE[compat_do_execve]
CDEC[compat_do_execveat]
DEAT[do_execveat]
K[exec_binprm]
B[bprm_execve]

DE --> DEC
CDE --> DEC
CDEC --> DEC
DEAT --> DEC
DEC --> B
B --> K
K --> search_binary_handler

当执行execve、execveat系统调用的时候,调用链路如上图,最终也是调用到search_binary_handler

do_execveat_common

static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
int flags)
{
struct linux_binprm *bprm;
int retval;

if (IS_ERR(filename))
return PTR_ERR(filename);

/*
* 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) &&
is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, 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;


// 申请fd对应的brpm对象
bprm = alloc_bprm(fd, filename, flags);
if (IS_ERR(bprm)) {
retval = PTR_ERR(bprm);
goto out_ret;
}

retval = count(argv, MAX_ARG_STRINGS);
if (retval == 0)
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
current->comm, bprm->filename);
if (retval < 0)
goto out_free;
bprm->argc = retval;

retval = count(envp, MAX_ARG_STRINGS);
if (retval < 0)
goto out_free;
bprm->envc = retval;

retval = bprm_stack_limits(bprm);
if (retval < 0)
goto out_free;

retval = copy_string_kernel(bprm->filename, bprm);
if (retval < 0)
goto out_free;
bprm->exec = bprm->p;

retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out_free;

retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out_free;

/*
* When argv is empty, add an empty string ("") as argv[0] to
* ensure confused userspace programs that start processing
* from argv[1] won't end up walking envp. See also
* bprm_stack_limits().
*/
if (bprm->argc == 0) {
retval = copy_string_kernel("", bprm);
if (retval < 0)
goto out_free;
bprm->argc = 1;
}

retval = bprm_execve(bprm);
out_free:
free_bprm(bprm);

out_ret:
putname(filename);
return retval;
}

search_binary_handler

此函数遍历所有的handler,选取第一个可用的handler进行使用

会调用先前注册的binfmt的load_binary函数加载二进制代码

static int search_binary_handler(struct linux_binprm *bprm)
{
bool need_retry = IS_ENABLED(CONFIG_MODULES);
struct linux_binfmt *fmt;
int retval;

retval = prepare_binprm(bprm);
if (retval < 0)
return retval;

retval = security_bprm_check(bprm);
if (retval)
return retval;

retval = -ENOENT;
retry:
read_lock(&binfmt_lock);
// 依次尝试链表中的每种注册的处理器
list_for_each_entry(fmt, &formats, lh) {
if (!try_module_get(fmt->module))
continue;
read_unlock(&binfmt_lock);

// 尝试载入
retval = fmt->load_binary(bprm);

read_lock(&binfmt_lock);
put_binfmt(fmt);

// 如果载入成功就直接返回
if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
read_unlock(&binfmt_lock);
return retval;
}
}
read_unlock(&binfmt_lock);

if (need_retry) {
if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
printable(bprm->buf[2]) && printable(bprm->buf[3]))
return retval;
if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
return retval;
need_retry = false;
goto retry;
}

return retval;
}

Author: deepwzh
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source deepwzh !
  TOC