点击(此处)折叠或打开
-
#include <stdio.h>
-
-
int main (int argc, char *argv[])
-
{
-
printf ("Hello World\n");
-
-
return 0;
- }
保存为hello.c,我们可以通过gcc hello.c -o hello得到可执行程序hello.
当我们在shell命令行终端调用./hello的时候, 实际上busybox执行execve函数来执行hello程序。用strace ./hello可以看到它的系统调用。点击(此处)折叠或打开
-
$strace ./hello
-
execve("./hello", ["./hello"], [/* 33 vars */]) = 0
-
brk(NULL) = 0xe91000
-
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
-
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
-
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
-
fstat(3, {st_mode=S_IFREG|0644, st_size=118062, ...}) = 0
-
mmap(NULL, 118062, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5181fce000
-
close(3) = 0
-
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
-
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
-
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
-
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
-
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5181fcd000
-
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f51819fc000
-
mprotect(0x7f5181bbc000, 2097152, PROT_NONE) = 0
-
mmap(0x7f5181dbc000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f5181dbc000
-
mmap(0x7f5181dc2000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0)= 0x7f5181dc2000
-
close(3) = 0
-
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5181fcc000
-
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5181fcb000
-
arch_prctl(ARCH_SET_FS, 0x7f5181fcc700) = 0
-
mprotect(0x7f5181dbc000, 16384, PROT_READ) = 0
-
mprotect(0x600000, 4096, PROT_READ) = 0
-
mprotect(0x7f5181feb000, 4096, PROT_READ) = 0
-
munmap(0x7f5181fce000, 118062) = 0
-
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 21), ...}) = 0
-
brk(NULL) = 0xe91000
-
brk(0xeb2000) = 0xeb2000
-
write(1, "Hello World\n", 12Hello World
-
) = 12
-
exit_group(0) = ?
- +++ exited with 0 +++
在Linux当中,execve函数的内核实现为:
点击(此处)折叠或打开
-
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);
-
}
// Code in "fs/exec.c"
点击(此处)折叠或打开
- execve (user space)
- ---|----------------------------------------------------------------------------
- v (kernel space)
- el0_sync (arm64同步异常中断处理函数)
-
el0_svc (查找sys_call_table,获取到sys_execve函数地址,并运行)
-
sys_execve
- do_execve
- do_execveat_common
-
__do_execve_file
点击(此处)折叠或打开
- // 这里fd值默认为AT_FDCWD,表示当前进程的工作空间
-
static int __do_execve_file(int fd, struct filename *filename,
-
struct user_arg_ptr argv,
-
struct user_arg_ptr envp,
-
int flags, struct file *file)
-
{
- ...........
-
// 为hello文件的bprm申请内存空间, bprm是可执行程序文件,底层的一个结构描述
-
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
-
if (!bprm)
- goto out_files;
- ............
-
-
// 打开hello文件,并返回文件描述结构体struct file
-
if (!file)
-
file = do_open_execat(fd, filename, flags);
-
retval = PTR_ERR(file);
-
if (IS_ERR(file))
-
goto out_unmark;
-
-
// SMP才使能,多核应用程序负载均衡调用。
-
sched_exec();
-
-
bprm->file = file;
-
// 获取hello文件的路径
-
if (!filename) {
-
bprm->filename = "none";
-
} else if (fd == AT_FDCWD || filename->name[0] == '/') {
-
bprm->filename = filename->name;
-
} else {
-
if (filename->name[0] == '\0')
-
pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
-
else
-
pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
-
fd, filename->name);
-
if (!pathbuf) {
-
retval = -ENOMEM;
-
goto out_unmark;
- }
-
if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
-
bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
-
bprm->filename = pathbuf;
-
}
-
bprm->interp = bprm->filename;
-
// 根据当前进程信息,为新程序hello的bprm结构初始化应用程序需要的内存,主要是mm_struct结构
-
retval = bprm_mm_init(bprm);
-
if (retval)
-
goto out_unmark;
-
// 将hello程序环境变量,参数和bprm结构做出预处理,主要是获取参数个数和环境变量个数
-
retval = prepare_arg_pages(bprm, argv, envp);
-
if (retval < 0)
-
goto out;
- // 读取hello文件的前128字节到bprm的buf,这里elf头部只有64字节
-
retval = prepare_binprm(bprm);
-
if (retval < 0)
-
goto out;
-
// 将hello文件名字拷贝的新进程的内存空间
-
retval = copy_strings_kernel(1, &bprm->filename, bprm);
-
if (retval < 0)
-
goto out;
-
-
bprm->exec = bprm->p;
-
// 将hello的环境变量拷贝到新进程的上下文内存空间
-
retval = copy_strings(bprm->envc, envp, bprm);
-
if (retval < 0)
-
goto out;
-
-
// 将hello的参数拷贝到新进程上下文的内存空间
-
retval = copy_strings(bprm->argc, argv, bprm);
-
if (retval < 0)
-
goto out;
-
-
would_dump(bprm, bprm->file);
-
// 运行bprm指向的应用程序, 这里是hello程序
-
retval = exec_binprm(bprm);
-
if (retval < 0)
-
goto out;
-
-
return retval;
- }
点击(此处)折叠或打开
-
static int exec_binprm(struct linux_binprm *bprm)
-
{
- ..........
- // 通过search_binary_handler函数来查找bprm到底是什么文件格式,以便于调用对应的处理函数
-
ret = search_binary_handler(bprm);
- ..........
-
return ret;
- }
点击(此处)折叠或打开
-
/*
-
* cycle the list of binary formats handler, until one recognizes the image
-
*/
-
int search_binary_handler(struct linux_binprm *bprm)
-
{
- ........
-
// 遍历formats链表,去查找链表formats里面的所有linux支持的文件处理格式
-
list_for_each_entry(fmt, &formats, lh) {
-
if (!try_module_get(fmt->module))
-
continue;
-
-
// 调用load_binary函数去加载指定格式
-
retval = fmt->load_binary(bprm);
- ........
-
}
-
read_unlock(&binfmt_lock);
- .........
-
return retval;
- }
点击(此处)折叠或打开
-
static struct linux_binfmt elf_format = {
-
.module = THIS_MODULE,
-
.load_binary = load_elf_binary,
-
.load_shlib = load_elf_library,
-
.core_dump = elf_core_dump,
-
.min_coredump = ELF_EXEC_PAGESIZE,
-
};
-
-
static int __init init_elf_binfmt(void)
-
{
-
// 注册一种可执行的文件格式
-
register_binfmt(&elf_format);
-
return 0;
-
}
-
-
static void __exit exit_elf_binfmt(void)
- {
-
unregister_binfmt(&elf_format);
-
}
-
-
core_initcall(init_elf_binfmt);
- module_exit(exit_elf_binfmt);
- // 代码在 fs/binfmt_elf.c
点击(此处)折叠或打开
-
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);
-
}
-
-
static inline void register_binfmt(struct linux_binfmt *fmt)
-
{
-
__register_binfmt(fmt, 0);
- }
点击(此处)折叠或打开
-
static int load_elf_binary(struct linux_binprm *bprm)
- {
-
........
- // 从前面知道bprm->buf为elf文件的前128字节,因此,这里的操作是获取elf头部信息
- loc->elf_ex = *((struct elfhdr *)bprm->buf);
- retval = -ENOEXEC;
-
// 比较elf幻术magic,判断是否为elf,不是就退出
- if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
- goto out;
-
............
-
// 获取elf的program header
- elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
-
........
-
// 这个for循环中间省略许多判断条件,主要是读取elf的动态解释器路径,并保存到elf_interpreter
- for (i = 0; i < loc->elf_ex.e_phnum; i++) {
- if (elf_ppnt->p_type == PT_INTERP) {
-
.......
-
retval = kernel_read(bprm->file, elf_interpreter, elf_ppnt->p_filesz, &pos);
-
.......
-
}
-
}
-
........
-
// 加载动态解释器的program header
- if (elf_interpreter) {
-
.......
-
interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,interpreter);
-
.......
-
}
-
........
-
// 设置起始栈
- current->mm->start_stack = bprm->p;
-
// 根据program header提供的信息对elf做必要的内存映射
-
for(i = 0, elf_ppnt = elf_phdata; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
-
.......
- error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags, total_size);
-
.......
-
}
-
........
-
// 从动态解释器中获取动态解释器的入口地址,并赋值给elf_entry
- if (elf_interpreter) {
-
........
-
elf_entry = load_elf_interp(&loc->interp_elf_ex,interpreter,&interp_map_addr,load_bias, interp_elf_phdata);
-
........
-
}
-
...........
-
// 设置pc指针为动态解释器的入口地址
- start_thread(regs, elf_entry, bprm->p);
-
........
- }
点击(此处)折叠或打开
-
static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
-
{
-
memset(regs, 0, sizeof(*regs));
-
regs->syscallno = ~0UL;
-
regs->pc = pc;
-
}
-
-
static inline void start_thread(struct pt_regs *regs, unsigned long pc,
-
unsigned long sp)
-
{
-
start_thread_common(regs, pc);
-
regs->pstate = PSR_MODE_EL0t;
-
regs->sp = sp;
- }
调用栈如下:
