Linux execve系统调用流程解析

execve 系统调用

在 Linux 上,新程序通过调用 execve 来执行。这是一个特殊的系统调用,它让调用进程切换到另一程序
调用 execve 时需要传入三个参数:

  • 要执行的文件路径(filename)
  • 参数列表(argv)
  • 环境块(envp)

使用 strace 跟踪系统调用execve

1
2
3
4
charles@DESKTOP-K189HCG:~$ strace -e execve which ls
execve("/usr/bin/which", ["which", "ls"], 0x7ffcae45c3f8 /* 33 vars */) = 0
/usr/bin/ls
+++ exited with 0 +++

可以看到:

  • 第一个参数是 whoami 的完整路径
  • 第二个参数是命令行参数列表
  • 第三个参数是环境块
  • 最终输出结果与预期一致

构建可用于 GDB 调试的 Linux 内核

为了深入调试内核,需要构建一个支持 GDB 调试的自定义内核。

生成默认配置

1
2
3
4
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.12.16.tar.xz
tar -xf linux-6.12.16.tar.xz
cd linux-6.12.16
make defconfig

使用 menuconfig 调整配置

1
make menuconfig

禁用的功能

1
2
3
4
5
Processor type and features  --->  
        [ ]   Randomize the address of the kernel image (KASLR)
[ ] Virtualization  ---- 
[ ] Enable loadable module support  ---- 
[ ] Networking support  ----  
功能 原因
内核镜像地址随机化(KASLR) 会干扰 GDB 调试,GDB 期望内核加载到固定内存地址
虚拟化支持 非必要功能,精简内核
可加载模块支持 非必要功能
网络支持 非必要功能

开启的调试选项

进入 Kernel hacking(内核调试):

  • Compile-time checks and compiler options
    • 开启 Debug info(调试信息)
      • 选择 Rely on the toolchain’s implicit default DWARF version
    • 开启 Provide GDB scripts for kernel debugging
  • Generic Kernel Debugging Instruments
    • 开启 KGDB: kernel debugger

编译内核

1
2
make -j4
make scripts_gdb

编译成功后会生成 bzImage 内核镜像:Kernel: arch/x86/boot/bzImage is ready (#1)


制作 initramfs

initramfs 是一个内存文件系统,内核启动时会将其作为初始根文件系统加载。我们需要把 bash 和一个测试程序放进去。

编译静态 Bash

下载 bash 源代码后,配置为静态编译

1
2
3
4
5
6
7
8
9
mkdir fun
cd fun
wget https://ftp.gnu.org/gnu/bash/bash-5.2.37.tar.gz
cd bash-5.2.37
 # 查找静态链接选项
./configure --help | grep -i static
# 启用静态链接
./configure --enable-static-link
make -j4

为什么静态编译? 希望所有东西都静态链接,不需要动态库,让设置过程越简单越好。

编译完成后,将 bash 重命名为 init,作为系统的 init 进程(PID 1):

1
2
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun/bash-5.2.37$ cd ../
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ cp  bash-5.2.37/bash init

编写测试程序 hello.c

编写一个简单的 C 程序,使用 write 系统调用输出内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <unistd.h>

int main() {
    // write(fd=1, buf, count) — 向标准输出写入
    write(1, "nice!\n", 6);   // 6 个字符(含换行符)

    // 退出进程,状态码 0xA4(便于在反汇编中识别)
    _exit(0xA4);
    return 0;
}

编译

1
gcc -static -o hello --entry main ./hello.c
选项 说明
-static 静态编译,不依赖动态库
-e main 指定入口点为 main 函数,而非 C 库的通用 _start

指定入口点为 main 的原因:希望程序执行从我们自己的 main 函数开始,而不是通用的 C 库启动函数,这样在反汇编和调试时能清楚看到自己的代码。

验证静态链接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ vi hello.c
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ gcc -static -o hello --entry main ./hello.c
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ ./hello
nice!
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ echo $?
164
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ ldd hello
        not a dynamic executable
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ ldd init
        not a dynamic executable

生成 initramfs 归档

创建文件列表 list

1
2
hello
init

使用 cpio 生成内核支持的 initramfs 格式:

1
2
3
4
5
6
7
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ echo hello >> list
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ echo init >> list
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ cat list
hello
init
charles@DESKTOP-K189HCG:~/linux-6.12.16/fun$ cat list | cpio -o -H newc > init.cpio
13055 blocks
参数 说明
-o 创建新归档
-H newc 使用 newc 格式(内核支持的格式)

配置 GDB 安全路径

https://docs.kernel.org/6.12/dev-tools/gdb-kernel-debugging.html~/.gdbinit 中添加:

1
2
charles@DESKTOP-K189HCG:~$ cat .gdbinit
add-auto-load-safe-path ~/linux-6.12.16/

这样 GDB 就能从内核源码目录加载调试脚本。

使用 QEMU 启动系统并连接 GDB

启动 QEMU

1
2
3
4
5
6
charles@DESKTOP-K189HCG:~/linux-6.12.16$ sudo apt install -y qemu-utils qemu-system-x86
charles@DESKTOP-K189HCG:~/linux-6.12.16$ 
qemu-system-x86_64 \
    -kernel ./arch/x86/boot/bzImage \
    -initrd ./fun/init.cpio \
    -s
参数 说明
-kernel 指定内核镜像
-initrd 指定 initramfs
-s 以调试模式启动,监听 1234 端口等待 GDB 连接

系统启动后会进入 bash shell。

连接 GDB

1
2
gdb ./linux/vmlinux
(gdb) target remote :1234

GDB 调试 execve 系统调用全流程

设置断点并触发 execve

execve 系统调用定义位于 fs/exec.c。三个参数的execve 系统调用有三个参数:文件名、参数列表、环境变量。

1
2
3
4
5
6
7
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);
}

在 GDB 中设置断点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
(gdb) b do_execve
Breakpoint 1 at 0xffffffff81298c77: do_execve. (2 locations)
(gdb) c
Continuing.

Breakpoint 1.1, 0xffffffff81298c77 in do_execve (__envp=<optimized out>, __argv=<optimized out>, filename=<optimized out>) at fs/exec.c:2056
2056            return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
(gdb) s
2132            return do_execve(getname(filename), argv, envp);
(gdb) s
do_execve (__envp=0x519d410, __argv=0x51a0a10, filename=0xffff888003359000) at fs/exec.c:2056
2056            return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
(gdb) s
do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at ./include/linux/err.h:67
67              return IS_ERR_VALUE((unsigned long)ptr);
(gdb) n
1923            if ((current->flags & PF_NPROC_EXCEEDED) &&
(gdb)
47                      return this_cpu_read_const(const_pcpu_hot.current_task);
(gdb)
1933            bprm = alloc_bprm(fd, filename, flags);
(gdb)
67              return IS_ERR_VALUE((unsigned long)ptr);
(gdb)
1939            retval = count(argv, MAX_ARG_STRINGS);
(gdb)
1940            if (retval == 0)
(gdb)
1943            if (retval < 0)
(gdb)
1945            bprm->argc = retval;

然后在 QEMU 的 bash 中运行:

1
/hello

程序会在 do_execve 处断住。

binary 参数结构体(bprm)

bprm = alloc_bprm(fd, filename, flags); 内核会分配一个 bprm(binary parameter)结构体,用于表示要运行的二进制程序。

1
2
3
4
5
6
(gdb) print *bprm
$1 = {vma = 0xffff888003d4ec80, vma_pages = 0, argmin = 0, mm = 0xffff88800304cfc0, p = 140737488351224, have_execfd = 0, execfd_creds = 0, secureexec = 0,
  point_of_no_return = 0, comm_from_dentry = 0, executable = 0x0 <fixed_percpu_data>, interpreter = 0x0 <fixed_percpu_data>, file = 0xffff8880032cb180,
  cred = 0x0 <fixed_percpu_data>, unsafe = 0, per_clear = 0, argc = 0, envc = 0, filename = 0xffff888003359020 "/hello", interp = 0xffff888003359020 "/hello",
  fdpath = 0x0 <fixed_percpu_data>, interp_flags = 0, execfd = 0, loader = 0, exec = 0, rlim_stack = {rlim_cur = 8388608, rlim_max = 18446744073709551615},
  buf = '\000' <repeats 255 times>}

bprm 结构体包含:

  • 程序本身的信息
  • 文件名(如 /hello
  • 参数数量 argc
  • 环境变量数量 envc
  • 文件描述符等

内核首先统计参数数量,将 argc 存入 bprm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

```gdb
1851    static int bprm_execve(struct linux_binprm *bprm)
1852    {
1853            int retval;
1854
1855            retval = prepare_bprm_creds(bprm);
(gdb)
1856            if (retval)
1857                    return retval;
1858
1859            /*
1860             * Check for unsafe execution states before exec_binprm(), which
1861             * will call back into begin_new_exec(), into bprm_creds_from_file(),
1862             * where setuid-ness is evaluated.
1863             */
1864            check_unsafe_exec(bprm);
1865            current->in_execve = 1;
(gdb)
1866            sched_mm_cid_before_execve(current);
1867
1868            sched_exec();
1869
1870            /* Set the unchanging part of bprm->cred */
1871            retval = security_bprm_creds_for_exec(bprm);
1872            if (retval)
1873                    goto out;
1874
1875            retval = exec_binprm(bprm);

search_binary_handler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
(gdb) advance 1875
exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1812
1812            old_pid = current->pid;
(gdb) list
1807    {
1808            pid_t old_pid, old_vpid;
1809            int ret, depth;
1810
1811            /* Need to fetch pid before load_binary changes it */
1812            old_pid = current->pid;
1813            rcu_read_lock();
1814            old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
1815            rcu_read_unlock();
1816
(gdb)
1817            /* This allows 4 levels of binfmt rewrites before failing hard. */
1818            for (depth = 0;; depth++) {
1819                    struct file *exec;
1820                    if (depth > 5)
1821                            return -ELOOP;
1822
1823                    ret = search_binary_handler(bprm);
1824                    if (ret < 0)
1825                            return ret;
1826                    if (!bprm->interpreter)
(gdb) advance search_binary_handler
search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1765
1765            retval = prepare_binprm(bprm);
(gdb) list
1760    {
1761            bool need_retry = IS_ENABLED(CONFIG_MODULES);
1762            struct linux_binfmt *fmt;
1763            int retval;
1764
1765            retval = prepare_binprm(bprm);
1766            if (retval < 0)
1767                    return retval;
1768
1769            retval = security_bprm_check(bprm);
(gdb)
1770            if (retval)
1771                    return retval;
1772
1773            retval = -ENOENT;
1774     retry:
1775            read_lock(&binfmt_lock);
1776            list_for_each_entry(fmt, &formats, lh) {
1777                    if (!try_module_get(fmt->module))
1778                            continue;
1779                    read_unlock(&binfmt_lock);
(gdb)
1780
1781                    retval = fmt->load_binary(bprm);

内核调用 search_binary_handler 来搜索合适的二进制格式处理器。

基本思路:内核并不确切知道文件是什么类型的可执行文件,需要逐个尝试不同的格式处理器。

常见的可执行格式:

格式 说明
ELF Executable and Linkable Format,Linux 标准可执行格式
脚本(Script) #!(shebang)开头,如 #!/usr/bin/python
Miscellaneous 其他格式

内核维护一个格式处理器列表,逐个遍历,执行每个的 load_binary 函数,直到找到正确的格式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(gdb) b 1781
Breakpoint 2 at 0xffffffff812965dc: file fs/exec.c, line 1781.
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$2 = (struct linux_binfmt *) 0xffffffff82573700 <misc_format>
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$3 = (struct linux_binfmt *) 0xffffffff825737a0 <script_format>
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$4 = (struct linux_binfmt *) 0xffffffff82573800 <elf_format>
(gdb) s
__x86_indirect_thunk_array () at ./arch/x86/include/asm/GEN-for-each-reg.h:6
6       GEN(rax)
(gdb) n
load_elf_binary (bprm=0xffff888003d44200) at fs/binfmt_elf.c:820

调试过程中可以看到:

  1. 第一次匹配到 Miscellaneous 格式 → 不相关
  2. 第二次匹配到 Script 格式(shebang)→ 不相关
  3. 第三次匹配到 ELF 格式 → 正确!

ELF 文件加载(load_elf_binary)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(gdb) n
load_elf_binary (bprm=0xffff888003d44200) at fs/binfmt_elf.c:820
820     {
(gdb) list
815
816             return ret == -ENOENT ? 0 : ret;
817     }
818
819     static int load_elf_binary(struct linux_binprm *bprm)
820     {
...
842             /* First of all, some simple consistency checks */
843             if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
844                     goto out;
(gdb)
845
846             if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN)
847                     goto out;
848             if (!elf_check_arch(elf_ex))
849                     goto out;
850             if (elf_check_fdpic(elf_ex))
851                     goto out;
852             if (!bprm->file->f_op->mmap)
853                     goto out;
854
(gdb) advance 843
load_elf_binary (bprm=0xffff888003d44200) at fs/binfmt_elf.c:843
843             if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
(gdb) print elf_ex.e_ident
$5 = "\177ELF\002\001\001\003\000\000\000\000\000\000\000"

进入 load_elf_binary 函数后,内核开始解析 ELF 文件。

ELF magic mnumber

函数首先检查 ELF 文件的magic number:

1
2
3
/* ELF magic number: 0x7f 'E' 'L' 'F' */
if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
    goto out;

这确保文件是一个有效的 ELF 格式文件。

解析 ELF 结构

函数继续解析 ELF 文件的各个部分:

  • ELF 头部(ELF header)
  • 程序头表(Program header table)
  • 段映射(Segment mapping)

这个函数相当长,核心逻辑是解析 ELF 文件并为其建立内存映射。

初始化寄存器与指令指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
1340            finalize_exec(bprm);
1341            START_THREAD(elf_ex, regs, elf_entry, bprm->p);
1342            retval = 0;
1343    out:
1344            return retval;
1345
1346            /* error cleanup */
1347    out_free_dentry:
(gdb) advance 1341
load_elf_binary (bprm=<optimized out>) at fs/binfmt_elf.c:1341
1341            START_THREAD(elf_ex, regs, elf_entry, bprm->p);

在完成大量 ELF 解析逻辑后,内核调用 START_THREAD 宏:

注意start_thread 名字有误导性,它不会真的启动一个线程,而是初始化 CPU 的新寄存器,为刚加载的新可执行文件准备执行环境。

START_THREAD

start_thread 是架构相关的函数(x86_64 下),它调用 start_thread_common,初始化各种寄存器。

关键寄存器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
(gdb) advance 1341
load_elf_binary (bprm=<optimized out>) at fs/binfmt_elf.c:1341
1341            START_THREAD(elf_ex, regs, elf_entry, bprm->p);
(gdb) s
start_thread (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920) at arch/x86/kernel/process_64.c:583
583     {
(gdb) list
578             regs->flags     = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
579     }
580
581     void
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
583     {
584             start_thread_common(regs, new_ip, new_sp,
585                                 __USER_CS, __USER_DS, 0);
586     }
587     EXPORT_SYMBOL_GPL(start_thread);
(gdb) s
584             start_thread_common(regs, new_ip, new_sp,
(gdb) list
579     }
580
581     void
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
583     {
584             start_thread_common(regs, new_ip, new_sp,
585                                 __USER_CS, __USER_DS, 0);
586     }
587     EXPORT_SYMBOL_GPL(start_thread);
588
(gdb) s
start_thread_common (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920, _cs=_cs@entry=51, _ds=_ds@entry=0, _ss=43)
    at arch/x86/kernel/process_64.c:534
534             WARN_ON_ONCE(regs != current_pt_regs());
(gdb) list
529     static void
530     start_thread_common(struct pt_regs *regs, unsigned long new_ip,
531                         unsigned long new_sp,
532                         u16 _cs, u16 _ss, u16 _ds)
533     {
534             WARN_ON_ONCE(regs != current_pt_regs());
...
(gdb)
549             regs->ip        = new_ip;
550             regs->sp        = new_sp;
551             regs->csx       = _cs;
552             regs->ssx       = _ss;
(gdb) advance 550
start_thread_common (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920, _cs=_cs@entry=51, _ds=_ds@entry=0, _ss=43)
    at arch/x86/kernel/process_64.c:550
550             regs->sp        = new_sp;
(gdb) print regs
$6 = (struct pt_regs *) 0xffffc9000015bf58
(gdb) print regs.ip
$7 = 4200549
(gdb) print/x regs.ip
$8 = 0x401865

regs->ip(指令指针 Instruction Pointer) 被设置为新程序的入口点。

验证入口点

使用 GDB 查看 hello 可执行文件的入口点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
gdb ./hello
(gdb) info file
Symbols from "/home/charles/linux-6.12.16/fun/hello".
Local exec file:
        `/home/charles/linux-6.12.16/fun/hello', file type elf64-x86-64.
        Entry point: 0x401865
        0x0000000000400270 - 0x00000000004002a0 is .note.gnu.property
        0x00000000004002a0 - 0x00000000004002c4 is .note.gnu.build-id
        0x00000000004002c4 - 0x00000000004002e4 is .note.ABI-tag
        0x00000000004002e8 - 0x00000000004004f8 is .rela.plt
        0x0000000000401000 - 0x000000000040101b is .init
        0x0000000000401020 - 0x0000000000401180 is .plt
        0x0000000000401180 - 0x000000000047e610 is .text
        0x000000000047e610 - 0x000000000047e61d is .fini
        0x000000000047f000 - 0x000000000049b1a4 is .rodata
        0x000000000049b1a4 - 0x000000000049b1a5 is .stapsdt.base
        0x000000000049b1c0 - 0x000000000049b220 is rodata.cst32
        0x000000000049b220 - 0x00000000004a4678 is .eh_frame
        0x00000000004a4678 - 0x00000000004a4754 is .gcc_except_table
        0x00000000004a5f50 - 0x00000000004a5f68 is .tdata
        0x00000000004a5f68 - 0x00000000004a5fa8 is .tbss
        0x00000000004a5f68 - 0x00000000004a5f70 is .init_array
        0x00000000004a5f70 - 0x00000000004a5f80 is .fini_array
        0x00000000004a5f80 - 0x00000000004a9f48 is .data.rel.ro
        0x00000000004a9f48 - 0x00000000004a9fd8 is .got
        0x00000000004a9fe8 - 0x00000000004aa0b0 is .got.plt
        0x00000000004aa0c0 - 0x00000000004abac8 is .data
        0x00000000004abae0 - 0x00000000004b1248 is .bss

输出中显示的入口点地址这个地址与 regs->ip 中的值完全对应
Linux 内核将下一条指令指针初始化为 hello 程序的入口点,系统调用返回后,CPU 将从这个地址开始执行用户程序。

从内核态返回用户态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
(gdb) n
load_elf_binary (bprm=<optimized out>) at fs/binfmt_elf.c:1344
1344            return retval;
(gdb)
1356            goto out;
(gdb)
search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1783
1783                    read_lock(&binfmt_lock);
(gdb) list
1778                            continue;
1779                    read_unlock(&binfmt_lock);
1780
1781                    retval = fmt->load_binary(bprm);
1782
...
1801
1802            return retval;
1803    }
1804
1805    /* binfmt handlers will call back into begin_new_exec() on success. */
1806    static int exec_binprm(struct linux_binprm *bprm)
1807    {
(gdb) finish
Run till exit from #0  search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1783
exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1824
1824                    if (ret < 0)
(gdb) finish
Run till exit from #0  exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1824
bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1879
1879            sched_mm_cid_after_execve(current);
(gdb) finish
Run till exit from #0  bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1879
bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1903
1903            return retval;
(gdb) finish
Run till exit from #0  bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1903
0xffffffff81297dba in do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at fs/exec.c:1982
1982            retval = bprm_execve(bprm);
Value returned is $9 = 0
(gdb) finish
Run till exit from #0  0xffffffff81297dba in do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at fs/exec.c:1982
__x64_sys_execve (regs=<optimized out>) at fs/exec.c:2127
2127    SYSCALL_DEFINE3(execve,
Value returned is $10 = 0
(gdb) list
2122                    return;
2123
2124            set_mask_bits(&mm->flags, MMF_DUMPABLE_MASK, value);
2125    }
2126
2127    SYSCALL_DEFINE3(execve,
2128                    const char __user *, filename,
2129                    const char __user *const __user *, argv,
2130                    const char __user *const __user *, envp)
2131    {
(gdb)
2132            return do_execve(getname(filename), argv, envp);
2133    }
2134
2135    SYSCALL_DEFINE5(execveat,
2136                    int, fd, const char __user *, filename,
2137                    const char __user *const __user *, argv,
2138                    const char __user *const __user *, envp,
2139                    int, flags)
2140    {
2141            return do_execveat(fd,
(gdb) n
do_syscall_64 (regs=0xffffc9000015bf58, nr=<optimized out>) at arch/x86/entry/common.c:88
88              instrumentation_end();
(gdb) n
89              syscall_exit_to_user_mode(regs);
(gdb)
102             if (unlikely(regs->cx != regs->ip || regs->r11 != regs->flags))
(gdb)
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:130
130             ALTERNATIVE "testb %al, %al; jz swapgs_restore_regs_and_return_to_usermode", \
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:561
561             IBRS_EXIT
(gdb)
570             POP_REGS
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:571
571             add     $8, %rsp        /* orig_ax */
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:575
575             swapgs
(gdb)
578             testb   $3, 8(%rsp)
(gdb)
579             jnz     .Lnative_iret
(gdb)
647             testb   $4, (SS-RIP)(%rsp)
(gdb)
648             jnz     native_irq_return_ldt
(gdb)
659             iretq

load_elf_binary 执行完毕后,逐层返回到系统调用入口。

执行流程:

  1. load_elf_binary 返回 → 回到 search_binary_handler
  2. search_binary_handler 返回 → 回到 do_execve
  3. do_execve 返回 → 回到系统调用汇编代码

最终到达系统调用返回路径,执行 iretq 指令:

1
iretq    # Interrupt Return,从中断/系统调用返回

iretq 会:

  • 恢复用户态寄存器
  • 切换到用户态栈
  • 将 CPU 特权级从内核态切换到用户态
  • 跳转到 regs->ip 指定的用户态地址

请求分页与缺页异常

回到用户态后,尝试查看指令指针处的内存:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
0x0000000000401865 in ?? ()
(gdb) x/i $rip
=> 0x401865:    Cannot access memory at address 0x401865
(gdb) ni
asm_exc_page_fault () at ./arch/x86/include/asm/idtentry.h:623
623     DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_PF,     exc_page_fault);
(gdb) advance *0x0000000000401865
0x0000000000401865 in ?? ()
(gdb) x/i $rip
=> 0x401865:    endbr64

可能会得到错误:“无法访问该地址的内存”

  • 请求分页(Demand Paging)内核使用了请求分页技术:

    • 程序内容尚未加载到物理内存
    • 只有在实际访问时,内核才会从磁盘将对应页面加载到内存
    • 这是一种节省物理内存(RAM)的技术
  • 执行下一条指令时(ni):会触发缺页异常(Page Fault)

    • 用户态尝试访问一个尚未映射到物理内存的地址
    • CPU 触发缺页异常,陷入内核态
    • 内核的缺页异常处理程序将对应页面从磁盘加载到物理内存
    • 建立页表映射
    • 返回用户态,重新执行触发异常的指令
  • 处理完缺页异常后,再次查看指令指针寄存器(Register Instruction Pointer)– CPU 下一条要执行的指令的内存地址

现在可以看到程序的指令了,包括:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(gdb) x/i $rip
=> 0x401865:    endbr64
(gdb)
   0x401869:    push   %rbp
(gdb)
   0x40186a:    mov    %rsp,%rbp
(gdb)
   0x40186d:    mov    $0x6,%edx
(gdb)
   0x401872:    lea    0x7d797(%rip),%rax        # 0x47f010
(gdb)
   0x401879:    mov    %rax,%rsi
(gdb)
   0x40187c:    mov    $0x1,%edi
(gdb)
   0x401881:    call   0x411310     ===>write()
(gdb)
   0x401886:    mov    $0xa4,%edi   ===>_exit(0xA4);
(gdb) x/s 0x47f010
0x47f010:       <error: Cannot access memory at address 0x47f010>
(gdb) advance 0x401886
Function "0x401886" not defined.
(gdb) advance *0x401886
0x0000000000401886 in ?? ()
(gdb) x/s 0x47f010
0x47f010:       "nice!\n"
(gdb) c

数据页面的请求分页
同样,字符串数据也使用请求分页:
初始可能无法访问,因为数据页面也尚未加载。只有当程序实际访问该地址时(如 write 调用读取字符串),内核才会将数据页面加载到内存。
最终程序执行,输出 `nice!’。

gdb command

GDB 命令 说明 常见用法
gdb <文件> 启动 GDB 并加载指定的可执行文件(带调试符号) gdb ./vmlinux
target remote <主机:端口> 连接到远程调试目标(如 QEMU、gdbserver) target remote :1234
break / b 设置断点,可指定函数名、文件名:行号、地址 b do_execveb fs/exec.c:1781
continue / c 继续执行被调试程序,直到遇到断点或异常 c
step / s 单步执行,会进入函数内部 s
next / n 单步执行,不会进入函数内部(视为一条语句) n
print / p 打印变量、表达式或结构体的值 print *bprmp fmt
list / l 显示当前执行点附近的源代码(默认 10 行) listl
advance <位置> 继续执行到指定的位置(行号、函数名或地址)后暂停 advance 1875advance search_binary_handler
finish 持续执行直到当前函数返回,然后暂停并显示返回值 finish
x/<格式> <地址> 查看(examine)内存内容,可指定显示格式和长度 x/i $rip(显示指令)、x/s 0x47f010(显示字符串)

总结

execve 系统调用完整流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
用户态调用 execve(filename, argv, envp)
        
        
  陷入内核态(syscall
        
        
  do_execve()  系统调用入口
        
        
  分配 bprm 结构体,统计参数/环境变量数量
        
        
  search_binary_handler()  搜索二进制格式处理器
        
        ├── Miscellaneous 格式  不匹配
        ├── Script 格式(shebang)→ 不匹配
        └── ELF 格式  匹配!
                
                
        load_elf_binary()  加载 ELF 文件
                
                ├── 校验 ELF 魔术号
                ├── 解析 ELF 头部和程序头
                ├── 建立内存映射(请求分页)
                └── start_thread()  设置 regs->ip = 程序入口点
                        
                        
                返回到系统调用路径
                        
                        
                iretq  从内核态返回用户态
                        
                        
                CPU 从程序入口点开始执行
                        
                        
                触发缺页异常  内核加载页面  继续执行
                        
                        
                程序正常运行(输出 "nice!"

关键概念速查表

概念 说明
execve 执行新程序的系统调用,替换当前进程的地址空间
bprm binary parameter,描述待执行二进制程序的结构体
ELF Executable and Linkable Format,Linux 标准可执行文件格式
shebang(#!) 脚本文件开头的标记,指定解释器路径
search_binary_handler 内核遍历格式处理器列表,找到匹配的加载器
load_elf_binary ELF 格式的加载函数,解析 ELF 并建立内存映射
start_thread 初始化 CPU 寄存器,设置指令指针为程序入口点(非真的启动线程)
iretq 中断返回指令,从内核态切换回用户态
请求分页(Demand Paging) 仅在实际访问时才将页面加载到物理内存
缺页异常(Page Fault) 访问未加载页面时触发的异常,由内核处理加载
  1. execve 与 fork 的区别fork 创建新进程(复制父进程),execve 替换当前进程的地址空间为新程序。
  2. 为什么需要搜索二进制格式:内核支持多种可执行格式(ELF、脚本、a.out 等),需要逐个尝试。
  3. 静态链接 vs 动态链接:静态链接将所有库代码编入可执行文件,运行时不需要动态加载器;动态链接在运行时通过 ld.so 加载共享库。
  4. 请求分页的优势:节省物理内存,程序启动更快(不需要一次性加载全部内容)。
  5. 程序入口点:ELF 文件头中记录了入口点地址(e_entry),内核通过 start_threadrip 设置为该地址。

gdb log

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
charles@DESKTOP-K189HCG:~/linux-6.12.16$ gdb ./vmlinux
GNU gdb (Ubuntu 15.1-1ubuntu1~24.04.1) 15.1
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./vmlinux...
(gdb) target remote :1234
Remote debugging using :1234
native_irq_disable () at ./arch/x86/include/asm/irqflags.h:37
37              asm volatile("cli": : :"memory");
(gdb) b do_execve
Breakpoint 1 at 0xffffffff81298c77: do_execve. (2 locations)
(gdb) c
Continuing.

Breakpoint 1.1, 0xffffffff81298c77 in do_execve (__envp=<optimized out>, __argv=<optimized out>, filename=<optimized out>) at fs/exec.c:2056
2056            return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
(gdb) s
2132            return do_execve(getname(filename), argv, envp);
(gdb) s
do_execve (__envp=0x519d410, __argv=0x51a0a10, filename=0xffff888003359000) at fs/exec.c:2056
2056            return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
(gdb) s
do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at ./include/linux/err.h:67
67              return IS_ERR_VALUE((unsigned long)ptr);
(gdb) n
1923            if ((current->flags & PF_NPROC_EXCEEDED) &&
(gdb)
47                      return this_cpu_read_const(const_pcpu_hot.current_task);
(gdb)
1933            bprm = alloc_bprm(fd, filename, flags);
(gdb)
67              return IS_ERR_VALUE((unsigned long)ptr);
(gdb)
1939            retval = count(argv, MAX_ARG_STRINGS);
(gdb)
1940            if (retval == 0)
(gdb)
1943            if (retval < 0)
(gdb)
1945            bprm->argc = retval;
(gdb) print *bprm
$1 = {vma = 0xffff888003d4ec80, vma_pages = 0, argmin = 0, mm = 0xffff88800304cfc0, p = 140737488351224, have_execfd = 0, execfd_creds = 0, secureexec = 0,
  point_of_no_return = 0, comm_from_dentry = 0, executable = 0x0 <fixed_percpu_data>, interpreter = 0x0 <fixed_percpu_data>, file = 0xffff8880032cb180,
  cred = 0x0 <fixed_percpu_data>, unsafe = 0, per_clear = 0, argc = 0, envc = 0, filename = 0xffff888003359020 "/hello", interp = 0xffff888003359020 "/hello",
  fdpath = 0x0 <fixed_percpu_data>, interp_flags = 0, execfd = 0, loader = 0, exec = 0, rlim_stack = {rlim_cur = 8388608, rlim_max = 18446744073709551615},
  buf = '\000' <repeats 255 times>}
(gdb) n
1947            retval = count(envp, MAX_ARG_STRINGS);
(gdb)
1948            if (retval < 0)
(gdb)
1950            bprm->envc = retval;
(gdb)
1952            retval = bprm_stack_limits(bprm);
(gdb)
1953            if (retval < 0)
(gdb)
1956            retval = copy_string_kernel(bprm->filename, bprm);
(gdb)
1957            if (retval < 0)
(gdb)
1959            bprm->exec = bprm->p;
(gdb)
1961            retval = copy_strings(bprm->envc, envp, bprm);
(gdb)
1962            if (retval < 0)
(gdb)
1965            retval = copy_strings(bprm->argc, argv, bprm);
(gdb)
1966            if (retval < 0)
(gdb)
1975            if (bprm->argc == 0) {
(gdb)
1982            retval = bprm_execve(bprm);
(gdb) s
bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1852
1852    {
(gdb) s
1855            retval = prepare_bprm_creds(bprm);
(gdb) n
1851    static int bprm_execve(struct linux_binprm *bprm)
(gdb) list
1846            ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
1847            proc_exec_connector(current);
1848            return 0;
1849    }
1850
1851    static int bprm_execve(struct linux_binprm *bprm)
1852    {
1853            int retval;
1854
1855            retval = prepare_bprm_creds(bprm);
(gdb)
1856            if (retval)
1857                    return retval;
1858
1859            /*
1860             * Check for unsafe execution states before exec_binprm(), which
1861             * will call back into begin_new_exec(), into bprm_creds_from_file(),
1862             * where setuid-ness is evaluated.
1863             */
1864            check_unsafe_exec(bprm);
1865            current->in_execve = 1;
(gdb)
1866            sched_mm_cid_before_execve(current);
1867
1868            sched_exec();
1869
1870            /* Set the unchanging part of bprm->cred */
1871            retval = security_bprm_creds_for_exec(bprm);
1872            if (retval)
1873                    goto out;
1874
1875            retval = exec_binprm(bprm);
(gdb)
1876            if (retval < 0)
1877                    goto out;
1878
1879            sched_mm_cid_after_execve(current);
1880            /* execve succeeded */
1881            current->fs->in_exec = 0;
1882            current->in_execve = 0;
1883            rseq_execve(current);
1884            user_events_execve(current);
1885            acct_update_integrals(current);
(gdb)
1886            task_numa_free(current, false);
1887            return retval;
1888
1889    out:
1890            /*
1891             * If past the point of no return ensure the code never
1892             * returns to the userspace process.  Use an existing fatal
1893             * signal if present otherwise terminate the process with
1894             * SIGSEGV.
1895             */
(gdb)
1896            if (bprm->point_of_no_return && !fatal_signal_pending(current))
1897                    force_fatal_sig(SIGSEGV);
1898
1899            sched_mm_cid_after_execve(current);
1900            current->fs->in_exec = 0;
1901            current->in_execve = 0;
1902
1903            return retval;
1904    }
1905
(gdb)
1906    static int do_execveat_common(int fd, struct filename *filename,
1907                                  struct user_arg_ptr argv,
1908                                  struct user_arg_ptr envp,
1909                                  int flags)
1910    {
1911            struct linux_binprm *bprm;
1912            int retval;
1913
1914            if (IS_ERR(filename))
1915                    return PTR_ERR(filename);
(gdb) advance 1875
exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1812
1812            old_pid = current->pid;
(gdb) list
1807    {
1808            pid_t old_pid, old_vpid;
1809            int ret, depth;
1810
1811            /* Need to fetch pid before load_binary changes it */
1812            old_pid = current->pid;
1813            rcu_read_lock();
1814            old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
1815            rcu_read_unlock();
1816
(gdb)
1817            /* This allows 4 levels of binfmt rewrites before failing hard. */
1818            for (depth = 0;; depth++) {
1819                    struct file *exec;
1820                    if (depth > 5)
1821                            return -ELOOP;
1822
1823                    ret = search_binary_handler(bprm);
1824                    if (ret < 0)
1825                            return ret;
1826                    if (!bprm->interpreter)
(gdb) advance search_binary_handler
search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1765
1765            retval = prepare_binprm(bprm);
(gdb) list
1760    {
1761            bool need_retry = IS_ENABLED(CONFIG_MODULES);
1762            struct linux_binfmt *fmt;
1763            int retval;
1764
1765            retval = prepare_binprm(bprm);
1766            if (retval < 0)
1767                    return retval;
1768
1769            retval = security_bprm_check(bprm);
(gdb)
1770            if (retval)
1771                    return retval;
1772
1773            retval = -ENOENT;
1774     retry:
1775            read_lock(&binfmt_lock);
1776            list_for_each_entry(fmt, &formats, lh) {
1777                    if (!try_module_get(fmt->module))
1778                            continue;
1779                    read_unlock(&binfmt_lock);
(gdb)
1780
1781                    retval = fmt->load_binary(bprm);
1782
1783                    read_lock(&binfmt_lock);
1784                    put_binfmt(fmt);
1785                    if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
1786                            read_unlock(&binfmt_lock);
1787                            return retval;
1788                    }
1789            }
(gdb) b 1781
Breakpoint 2 at 0xffffffff812965dc: file fs/exec.c, line 1781.
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$2 = (struct linux_binfmt *) 0xffffffff82573700 <misc_format>
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$3 = (struct linux_binfmt *) 0xffffffff825737a0 <script_format>
(gdb) c
Continuing.

Breakpoint 2, search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1781
1781                    retval = fmt->load_binary(bprm);
(gdb) print fmt
$4 = (struct linux_binfmt *) 0xffffffff82573800 <elf_format>
(gdb) s
__x86_indirect_thunk_array () at ./arch/x86/include/asm/GEN-for-each-reg.h:6
6       GEN(rax)
(gdb) n
load_elf_binary (bprm=0xffff888003d44200) at fs/binfmt_elf.c:820
820     {
(gdb) list
815
816             return ret == -ENOENT ? 0 : ret;
817     }
818
819     static int load_elf_binary(struct linux_binprm *bprm)
820     {
821             struct file *interpreter = NULL; /* to shut gcc up */
822             unsigned long load_bias = 0, phdr_addr = 0;
823             int first_pt_load = 1;
824             unsigned long error;
(gdb)
825             struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
826             struct elf_phdr *elf_property_phdata = NULL;
827             unsigned long elf_brk;
828             int retval, i;
829             unsigned long elf_entry;
830             unsigned long e_entry;
831             unsigned long interp_load_addr = 0;
832             unsigned long start_code, end_code, start_data, end_data;
833             unsigned long reloc_func_desc __maybe_unused = 0;
834             int executable_stack = EXSTACK_DEFAULT;
(gdb)
835             struct elfhdr *elf_ex = (struct elfhdr *)bprm->buf;
836             struct elfhdr *interp_elf_ex = NULL;
837             struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
838             struct mm_struct *mm;
839             struct pt_regs *regs;
840
841             retval = -ENOEXEC;
842             /* First of all, some simple consistency checks */
843             if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
844                     goto out;
(gdb)
845
846             if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN)
847                     goto out;
848             if (!elf_check_arch(elf_ex))
849                     goto out;
850             if (elf_check_fdpic(elf_ex))
851                     goto out;
852             if (!bprm->file->f_op->mmap)
853                     goto out;
854
(gdb) advance 843
load_elf_binary (bprm=0xffff888003d44200) at fs/binfmt_elf.c:843
843             if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
(gdb) print elf_ex.e_ident
$5 = "\177ELF\002\001\001\003\000\000\000\000\000\000\000"
(gdb) list
838             struct mm_struct *mm;
839             struct pt_regs *regs;
840
841             retval = -ENOEXEC;
842             /* First of all, some simple consistency checks */
843             if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
844                     goto out;
845
846             if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN)
847                     goto out;
(gdb)
848             if (!elf_check_arch(elf_ex))
849                     goto out;
850             if (elf_check_fdpic(elf_ex))
851                     goto out;
852             if (!bprm->file->f_op->mmap)
853                     goto out;
854
855             elf_phdata = load_elf_phdrs(elf_ex, bprm->file);
856             if (!elf_phdata)
857                     goto out;
(gdb)
858
859             elf_ppnt = elf_phdata;
860             for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
861                     char *elf_interpreter;
862
863                     if (elf_ppnt->p_type == PT_GNU_PROPERTY) {
864                             elf_property_phdata = elf_ppnt;
865                             continue;
866                     }
867
(gdb)
868                     if (elf_ppnt->p_type != PT_INTERP)
869                             continue;
870
871                     /*
872                      * This is the program interpreter used for shared libraries -
873                      * for now assume that this is an a.out format binary.
874                      */
875                     retval = -ENOEXEC;
876                     if (elf_ppnt->p_filesz > PATH_MAX || elf_ppnt->p_filesz < 2)
877                             goto out_free_ph;
(gdb)
878
879                     retval = -ENOMEM;
880                     elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
881                     if (!elf_interpreter)
882                             goto out_free_ph;
883
884                     retval = elf_read(bprm->file, elf_interpreter, elf_ppnt->p_filesz,
885                                       elf_ppnt->p_offset);
886                     if (retval < 0)
887                             goto out_free_interp;
(gdb)
888                     /* make sure path is NULL terminated */
889                     retval = -ENOEXEC;
890                     if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
891                             goto out_free_interp;
892
893                     interpreter = open_exec(elf_interpreter);
894                     kfree(elf_interpreter);
895                     retval = PTR_ERR(interpreter);
896                     if (IS_ERR(interpreter))
897                             goto out_free_ph;
(gdb)
898
899                     /*
900                      * If the binary is not readable then enforce mm->dumpable = 0
901                      * regardless of the interpreter's permissions.
902                      */
903                     would_dump(bprm, interpreter);
904
905                     interp_elf_ex = kmalloc(sizeof(*interp_elf_ex), GFP_KERNEL);
906                     if (!interp_elf_ex) {
907                             retval = -ENOMEM;
(gdb)
908                             goto out_free_file;
909                     }
910
911                     /* Get the exec headers */
912                     retval = elf_read(interpreter, interp_elf_ex,
913                                       sizeof(*interp_elf_ex), 0);
914                     if (retval < 0)
915                             goto out_free_dentry;
916
917                     break;
(gdb)
918
919     out_free_interp:
920                     kfree(elf_interpreter);
921                     goto out_free_ph;
922             }
923
924             elf_ppnt = elf_phdata;
925             for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
926                     switch (elf_ppnt->p_type) {
927                     case PT_GNU_STACK:
(gdb)
928                             if (elf_ppnt->p_flags & PF_X)
929                                     executable_stack = EXSTACK_ENABLE_X;
930                             else
931                                     executable_stack = EXSTACK_DISABLE_X;
932                             break;
933
934                     case PT_LOPROC ... PT_HIPROC:
935                             retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
936                                                       bprm->file, false,
937                                                       &arch_state);
(gdb)
938                             if (retval)
939                                     goto out_free_dentry;
940                             break;
941                     }
942
943             /* Some simple consistency checks for the interpreter */
944             if (interpreter) {
945                     retval = -ELIBBAD;
946                     /* Not an ELF interpreter */
947                     if (memcmp(interp_elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
(gdb)
948                             goto out_free_dentry;
949                     /* Verify the interpreter has a valid arch */
950                     if (!elf_check_arch(interp_elf_ex) ||
951                         elf_check_fdpic(interp_elf_ex))
952                             goto out_free_dentry;
953
954                     /* Load the interpreter program headers */
955                     interp_elf_phdata = load_elf_phdrs(interp_elf_ex,
956                                                        interpreter);
957                     if (!interp_elf_phdata)
(gdb)
958                             goto out_free_dentry;
959
960                     /* Pass PT_LOPROC..PT_HIPROC headers to arch code */
961                     elf_property_phdata = NULL;
962                     elf_ppnt = interp_elf_phdata;
963                     for (i = 0; i < interp_elf_ex->e_phnum; i++, elf_ppnt++)
964                             switch (elf_ppnt->p_type) {
965                             case PT_GNU_PROPERTY:
966                                     elf_property_phdata = elf_ppnt;
967                                     break;
(gdb)
968
969                             case PT_LOPROC ... PT_HIPROC:
970                                     retval = arch_elf_pt_proc(interp_elf_ex,
971                                                               elf_ppnt, interpreter,
972                                                               true, &arch_state);
973                                     if (retval)
974                                             goto out_free_dentry;
975                                     break;
976                             }
977             }
(gdb)
978
979             retval = parse_elf_properties(interpreter ?: bprm->file,
980                                           elf_property_phdata, &arch_state);
981             if (retval)
982                     goto out_free_dentry;
983
984             /*
985              * Allow arch code to reject the ELF at this point, whilst it's
986              * still possible to return an error to the code that invoked
987              * the exec syscall.
(gdb)
988              */
989             retval = arch_check_elf(elf_ex,
990                                     !!interpreter, interp_elf_ex,
991                                     &arch_state);
992             if (retval)
993                     goto out_free_dentry;
994
995             /* Flush all traces of the currently running executable */
996             retval = begin_new_exec(bprm);
997             if (retval)
(gdb)
998                     goto out_free_dentry;
999
1000            /* Do this immediately, since STACK_TOP as used in setup_arg_pages
1001               may depend on the personality.  */
1002            SET_PERSONALITY2(*elf_ex, &arch_state);
1003            if (elf_read_implies_exec(*elf_ex, executable_stack))
1004                    current->personality |= READ_IMPLIES_EXEC;
1005
1006            const int snapshot_randomize_va_space = READ_ONCE(randomize_va_space);
1007            if (!(current->personality & ADDR_NO_RANDOMIZE) && snapshot_randomize_va_space)
(gdb)
1008                    current->flags |= PF_RANDOMIZE;
1009
1010            setup_new_exec(bprm);
1011
1012            /* Do this so that we can load the interpreter, if need be.  We will
1013               change some of these later */
1014            retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
1015                                     executable_stack);
1016            if (retval < 0)
1017                    goto out_free_dentry;
(gdb)
1018
1019            elf_brk = 0;
1020
1021            start_code = ~0UL;
1022            end_code = 0;
1023            start_data = 0;
1024            end_data = 0;
1025
1026            /* Now we do a little grungy work by mmapping the ELF image into
1027               the correct location in memory. */
(gdb)
1028            for(i = 0, elf_ppnt = elf_phdata;
1029                i < elf_ex->e_phnum; i++, elf_ppnt++) {
1030                    int elf_prot, elf_flags;
1031                    unsigned long k, vaddr;
1032                    unsigned long total_size = 0;
1033                    unsigned long alignment;
1034
1035                    if (elf_ppnt->p_type != PT_LOAD)
1036                            continue;
1037
(gdb)
1038                    elf_prot = make_prot(elf_ppnt->p_flags, &arch_state,
1039                                         !!interpreter, false);
1040
1041                    elf_flags = MAP_PRIVATE;
1042
1043                    vaddr = elf_ppnt->p_vaddr;
1044                    /*
1045                     * The first time through the loop, first_pt_load is true:
1046                     * layout will be calculated. Once set, use MAP_FIXED since
1047                     * we know we've already safely mapped the entire region with
(gdb)
1048                     * MAP_FIXED_NOREPLACE in the once-per-binary logic following.
1049                     */
1050                    if (!first_pt_load) {
1051                            elf_flags |= MAP_FIXED;
1052                    } else if (elf_ex->e_type == ET_EXEC) {
1053                            /*
1054                             * This logic is run once for the first LOAD Program
1055                             * Header for ET_EXEC binaries. No special handling
1056                             * is needed.
1057                             */
(gdb)
1058                            elf_flags |= MAP_FIXED_NOREPLACE;
1059                    } else if (elf_ex->e_type == ET_DYN) {
1060                            /*
1061                             * This logic is run once for the first LOAD Program
1062                             * Header for ET_DYN binaries to calculate the
1063                             * randomization (load_bias) for all the LOAD
1064                             * Program Headers.
1065                             */
1066
1067                            /*
(gdb)
1068                             * Calculate the entire size of the ELF mapping
1069                             * (total_size), used for the initial mapping,
1070                             * due to load_addr_set which is set to true later
1071                             * once the initial mapping is performed.
1072                             *
1073                             * Note that this is only sensible when the LOAD
1074                             * segments are contiguous (or overlapping). If
1075                             * used for LOADs that are far apart, this would
1076                             * cause the holes between LOADs to be mapped,
1077                             * running the risk of having the mapping fail,
(gdb)
1078                             * as it would be larger than the ELF file itself.
1079                             *
1080                             * As a result, only ET_DYN does this, since
1081                             * some ET_EXEC (e.g. ia64) may have large virtual
1082                             * memory holes between LOADs.
1083                             *
1084                             */
1085                            total_size = total_mapping_size(elf_phdata,
1086                                                            elf_ex->e_phnum);
1087                            if (!total_size) {
(gdb)
1088                                    retval = -EINVAL;
1089                                    goto out_free_dentry;
1090                            }
1091
1092                            /* Calculate any requested alignment. */
1093                            alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
1094
1095                            /*
1096                             * There are effectively two types of ET_DYN
1097                             * binaries: programs (i.e. PIE: ET_DYN with PT_INTERP)
(gdb)
1098                             * and loaders (ET_DYN without PT_INTERP, since they
1099                             * _are_ the ELF interpreter). The loaders must
1100                             * be loaded away from programs since the program
1101                             * may otherwise collide with the loader (especially
1102                             * for ET_EXEC which does not have a randomized
1103                             * position). For example to handle invocations of
1104                             * "./ld.so someprog" to test out a new version of
1105                             * the loader, the subsequent program that the
1106                             * loader loads must avoid the loader itself, so
1107                             * they cannot share the same load range. Sufficient
(gdb)
1108                             * room for the brk must be allocated with the
1109                             * loader as well, since brk must be available with
1110                             * the loader.
1111                             *
1112                             * Therefore, programs are loaded offset from
1113                             * ELF_ET_DYN_BASE and loaders are loaded into the
1114                             * independently randomized mmap region (0 load_bias
1115                             * without MAP_FIXED nor MAP_FIXED_NOREPLACE).
1116                             */
1117                            if (interpreter) {
(gdb)
1118                                    /* On ET_DYN with PT_INTERP, we do the ASLR. */
1119                                    load_bias = ELF_ET_DYN_BASE;
1120                                    if (current->flags & PF_RANDOMIZE)
1121                                            load_bias += arch_mmap_rnd();
1122                                    /* Adjust alignment as requested. */
1123                                    if (alignment)
1124                                            load_bias &= ~(alignment - 1);
1125                                    elf_flags |= MAP_FIXED_NOREPLACE;
1126                            } else {
1127                                    /*
(gdb)
1128                                     * For ET_DYN without PT_INTERP, we rely on
1129                                     * the architectures's (potentially ASLR) mmap
1130                                     * base address (via a load_bias of 0).
1131                                     *
1132                                     * When a large alignment is requested, we
1133                                     * must do the allocation at address "0" right
1134                                     * now to discover where things will load so
1135                                     * that we can adjust the resulting alignment.
1136                                     * In this case (load_bias != 0), we can use
1137                                     * MAP_FIXED_NOREPLACE to make sure the mapping
(gdb)
1138                                     * doesn't collide with anything.
1139                                     */
1140                                    if (alignment > ELF_MIN_ALIGN) {
1141                                            load_bias = elf_load(bprm->file, 0, elf_ppnt,
1142                                                                 elf_prot, elf_flags, total_size);
1143                                            if (BAD_ADDR(load_bias)) {
1144                                                    retval = IS_ERR_VALUE(load_bias) ?
1145                                                             PTR_ERR((void*)load_bias) : -EINVAL;
1146                                                    goto out_free_dentry;
1147                                            }
(gdb)
1148                                            vm_munmap(load_bias, total_size);
1149                                            /* Adjust alignment as requested. */
1150                                            if (alignment)
1151                                                    load_bias &= ~(alignment - 1);
1152                                            elf_flags |= MAP_FIXED_NOREPLACE;
1153                                    } else
1154                                            load_bias = 0;
1155                            }
1156
1157                            /*
(gdb)
1158                             * Since load_bias is used for all subsequent loading
1159                             * calculations, we must lower it by the first vaddr
1160                             * so that the remaining calculations based on the
1161                             * ELF vaddrs will be correctly offset. The result
1162                             * is then page aligned.
1163                             */
1164                            load_bias = ELF_PAGESTART(load_bias - vaddr);
1165                    }
1166
1167                    error = elf_load(bprm->file, load_bias + vaddr, elf_ppnt,
(gdb)
1168                                    elf_prot, elf_flags, total_size);
1169                    if (BAD_ADDR(error)) {
1170                            retval = IS_ERR_VALUE(error) ?
1171                                    PTR_ERR((void*)error) : -EINVAL;
1172                            goto out_free_dentry;
1173                    }
1174
1175                    if (first_pt_load) {
1176                            first_pt_load = 0;
1177                            if (elf_ex->e_type == ET_DYN) {
(gdb)
1178                                    load_bias += error -
1179                                                 ELF_PAGESTART(load_bias + vaddr);
1180                                    reloc_func_desc = load_bias;
1181                            }
1182                    }
1183
1184                    /*
1185                     * Figure out which segment in the file contains the Program
1186                     * Header table, and map to the associated memory address.
1187                     */
(gdb)
1188                    if (elf_ppnt->p_offset <= elf_ex->e_phoff &&
1189                        elf_ex->e_phoff < elf_ppnt->p_offset + elf_ppnt->p_filesz) {
1190                            phdr_addr = elf_ex->e_phoff - elf_ppnt->p_offset +
1191                                        elf_ppnt->p_vaddr;
1192                    }
1193
1194                    k = elf_ppnt->p_vaddr;
1195                    if ((elf_ppnt->p_flags & PF_X) && k < start_code)
1196                            start_code = k;
1197                    if (start_data < k)
(gdb)
1198                            start_data = k;
1199
1200                    /*
1201                     * Check to see if the section's size will overflow the
1202                     * allowed task size. Note that p_filesz must always be
1203                     * <= p_memsz so it is only necessary to check p_memsz.
1204                     */
1205                    if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
1206                        elf_ppnt->p_memsz > TASK_SIZE ||
1207                        TASK_SIZE - elf_ppnt->p_memsz < k) {
(gdb)
1208                            /* set_brk can never work. Avoid overflows. */
1209                            retval = -EINVAL;
1210                            goto out_free_dentry;
1211                    }
1212
1213                    k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
1214
1215                    if ((elf_ppnt->p_flags & PF_X) && end_code < k)
1216                            end_code = k;
1217                    if (end_data < k)
(gdb)
1218                            end_data = k;
1219                    k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
1220                    if (k > elf_brk)
1221                            elf_brk = k;
1222            }
1223
1224            e_entry = elf_ex->e_entry + load_bias;
1225            phdr_addr += load_bias;
1226            elf_brk += load_bias;
1227            start_code += load_bias;
(gdb)
1228            end_code += load_bias;
1229            start_data += load_bias;
1230            end_data += load_bias;
1231
1232            current->mm->start_brk = current->mm->brk = ELF_PAGEALIGN(elf_brk);
1233
1234            if (interpreter) {
1235                    elf_entry = load_elf_interp(interp_elf_ex,
1236                                                interpreter,
1237                                                load_bias, interp_elf_phdata,
(gdb)
1238                                                &arch_state);
1239                    if (!IS_ERR_VALUE(elf_entry)) {
1240                            /*
1241                             * load_elf_interp() returns relocation
1242                             * adjustment
1243                             */
1244                            interp_load_addr = elf_entry;
1245                            elf_entry += interp_elf_ex->e_entry;
1246                    }
1247                    if (BAD_ADDR(elf_entry)) {
(gdb)
1248                            retval = IS_ERR_VALUE(elf_entry) ?
1249                                            (int)elf_entry : -EINVAL;
1250                            goto out_free_dentry;
1251                    }
1252                    reloc_func_desc = interp_load_addr;
1253
1254                    allow_write_access(interpreter);
1255                    fput(interpreter);
1256
1257                    kfree(interp_elf_ex);
(gdb)
1258                    kfree(interp_elf_phdata);
1259            } else {
1260                    elf_entry = e_entry;
1261                    if (BAD_ADDR(elf_entry)) {
1262                            retval = -EINVAL;
1263                            goto out_free_dentry;
1264                    }
1265            }
1266
1267            kfree(elf_phdata);
(gdb)
1268
1269            set_binfmt(&elf_format);
1270
1271    #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
1272            retval = ARCH_SETUP_ADDITIONAL_PAGES(bprm, elf_ex, !!interpreter);
1273            if (retval < 0)
1274                    goto out;
1275    #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
1276
1277            retval = create_elf_tables(bprm, elf_ex, interp_load_addr,
(gdb)
1278                                       e_entry, phdr_addr);
1279            if (retval < 0)
1280                    goto out;
1281
1282            mm = current->mm;
1283            mm->end_code = end_code;
1284            mm->start_code = start_code;
1285            mm->start_data = start_data;
1286            mm->end_data = end_data;
1287            mm->start_stack = bprm->p;
(gdb)
1288
1289            if ((current->flags & PF_RANDOMIZE) && (snapshot_randomize_va_space > 1)) {
1290                    /*
1291                     * For architectures with ELF randomization, when executing
1292                     * a loader directly (i.e. no interpreter listed in ELF
1293                     * headers), move the brk area out of the mmap region
1294                     * (since it grows up, and may collide early with the stack
1295                     * growing down), and into the unused ELF_ET_DYN_BASE region.
1296                     */
1297                    if (IS_ENABLED(CONFIG_ARCH_HAS_ELF_RANDOMIZE) &&
(gdb)
1298                        elf_ex->e_type == ET_DYN && !interpreter) {
1299                            mm->brk = mm->start_brk = ELF_ET_DYN_BASE;
1300                    } else {
1301                            /* Otherwise leave a gap between .bss and brk. */
1302                            mm->brk = mm->start_brk = mm->brk + PAGE_SIZE;
1303                    }
1304
1305                    mm->brk = mm->start_brk = arch_randomize_brk(mm);
1306    #ifdef compat_brk_randomized
1307                    current->brk_randomized = 1;
(gdb)
1308    #endif
1309            }
1310
1311            if (current->personality & MMAP_PAGE_ZERO) {
1312                    /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
1313                       and some applications "depend" upon this behavior.
1314                       Since we do not have the power to recompile these, we
1315                       emulate the SVr4 behavior. Sigh. */
1316                    error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
1317                                    MAP_FIXED | MAP_PRIVATE, 0);
(gdb)
1318
1319                    retval = do_mseal(0, PAGE_SIZE, 0);
1320                    if (retval)
1321                            pr_warn_ratelimited("pid=%d, couldn't seal address 0, ret=%d.\n",
1322                                                task_pid_nr(current), retval);
1323            }
1324
1325            regs = current_pt_regs();
1326    #ifdef ELF_PLAT_INIT
1327            /*
(gdb)
1328             * The ABI may specify that certain registers be set up in special
1329             * ways (on i386 %edx is the address of a DT_FINI function, for
1330             * example.  In addition, it may also specify (eg, PowerPC64 ELF)
1331             * that the e_entry field is the address of the function descriptor
1332             * for the startup routine, rather than the address of the startup
1333             * routine itself.  This macro performs whatever initialization to
1334             * the regs structure is required as well as any relocations to the
1335             * function descriptor entries when executing dynamically links apps.
1336             */
1337            ELF_PLAT_INIT(regs, reloc_func_desc);
(gdb)
1338    #endif
1339
1340            finalize_exec(bprm);
1341            START_THREAD(elf_ex, regs, elf_entry, bprm->p);
1342            retval = 0;
1343    out:
1344            return retval;
1345
1346            /* error cleanup */
1347    out_free_dentry:
(gdb) advance 1341
load_elf_binary (bprm=<optimized out>) at fs/binfmt_elf.c:1341
1341            START_THREAD(elf_ex, regs, elf_entry, bprm->p);
(gdb) s
start_thread (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920) at arch/x86/kernel/process_64.c:583
583     {
(gdb) list
578             regs->flags     = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
579     }
580
581     void
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
583     {
584             start_thread_common(regs, new_ip, new_sp,
585                                 __USER_CS, __USER_DS, 0);
586     }
587     EXPORT_SYMBOL_GPL(start_thread);
(gdb) s
584             start_thread_common(regs, new_ip, new_sp,
(gdb) list
579     }
580
581     void
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
583     {
584             start_thread_common(regs, new_ip, new_sp,
585                                 __USER_CS, __USER_DS, 0);
586     }
587     EXPORT_SYMBOL_GPL(start_thread);
588
(gdb) s
start_thread_common (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920, _cs=_cs@entry=51, _ds=_ds@entry=0, _ss=43)
    at arch/x86/kernel/process_64.c:534
534             WARN_ON_ONCE(regs != current_pt_regs());
(gdb) list
529     static void
530     start_thread_common(struct pt_regs *regs, unsigned long new_ip,
531                         unsigned long new_sp,
532                         u16 _cs, u16 _ss, u16 _ds)
533     {
534             WARN_ON_ONCE(regs != current_pt_regs());
535
536             if (static_cpu_has(X86_BUG_NULL_SEG)) {
537                     /* Loading zero below won't clear the base. */
538                     loadsegment(fs, __USER_DS);
(gdb)
539                     load_gs_index(__USER_DS);
540             }
541
542             reset_thread_features();
543
544             loadsegment(fs, 0);
545             loadsegment(es, _ds);
546             loadsegment(ds, _ds);
547             load_gs_index(0);
548
(gdb)
549             regs->ip        = new_ip;
550             regs->sp        = new_sp;
551             regs->csx       = _cs;
552             regs->ssx       = _ss;
553             /*
554              * Allow single-step trap and NMI when starting a new task, thus
555              * once the new task enters user space, single-step trap and NMI
556              * are both enabled immediately.
557              *
558              * Entering a new task is logically speaking a return from a
(gdb) advance 550
start_thread_common (regs=regs@entry=0xffffc9000015bf58, new_ip=new_ip@entry=4200549, new_sp=140729674415920, _cs=_cs@entry=51, _ds=_ds@entry=0, _ss=43)
    at arch/x86/kernel/process_64.c:550
550             regs->sp        = new_sp;
(gdb) print regs
$6 = (struct pt_regs *) 0xffffc9000015bf58
(gdb) print regs.ip
$7 = 4200549
(gdb) print/x regs.ip
$8 = 0x401865
(gdb) n
551             regs->csx       = _cs;
(gdb)
552             regs->ssx       = _ss;
(gdb)
578             regs->flags     = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
(gdb) list
573             if (cpu_feature_enabled(X86_FEATURE_FRED)) {
574                     regs->fred_ss.swevent   = true;
575                     regs->fred_ss.nmi       = true;
576             }
577
578             regs->flags     = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
579     }
580
581     void
582     start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
(gdb) n
load_elf_binary (bprm=<optimized out>) at fs/binfmt_elf.c:1344
1344            return retval;
(gdb)
1356            goto out;
(gdb)
search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1783
1783                    read_lock(&binfmt_lock);
(gdb) list
1778                            continue;
1779                    read_unlock(&binfmt_lock);
1780
1781                    retval = fmt->load_binary(bprm);
1782
1783                    read_lock(&binfmt_lock);
1784                    put_binfmt(fmt);
1785                    if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
1786                            read_unlock(&binfmt_lock);
1787                            return retval;
(gdb)
1788                    }
1789            }
1790            read_unlock(&binfmt_lock);
1791
1792            if (need_retry) {
1793                    if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
1794                        printable(bprm->buf[2]) && printable(bprm->buf[3]))
1795                            return retval;
1796                    if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
1797                            return retval;
(gdb)
1798                    need_retry = false;
1799                    goto retry;
1800            }
1801
1802            return retval;
1803    }
1804
1805    /* binfmt handlers will call back into begin_new_exec() on success. */
1806    static int exec_binprm(struct linux_binprm *bprm)
1807    {
(gdb) finish
Run till exit from #0  search_binary_handler (bprm=0xffff888003d44200) at fs/exec.c:1783
exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1824
1824                    if (ret < 0)
(gdb) finish
Run till exit from #0  exec_binprm (bprm=0xffff888003d44200) at fs/exec.c:1824
bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1879
1879            sched_mm_cid_after_execve(current);
(gdb) finish
Run till exit from #0  bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1879
bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1903
1903            return retval;
(gdb) finish
Run till exit from #0  bprm_execve (bprm=0xffff888003d44200) at fs/exec.c:1903
0xffffffff81297dba in do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at fs/exec.c:1982
1982            retval = bprm_execve(bprm);
Value returned is $9 = 0
(gdb) finish
Run till exit from #0  0xffffffff81297dba in do_execveat_common (fd=fd@entry=-100, filename=0xffff888003359000, flags=0, envp=..., argv=...) at fs/exec.c:1982
__x64_sys_execve (regs=<optimized out>) at fs/exec.c:2127
2127    SYSCALL_DEFINE3(execve,
Value returned is $10 = 0
(gdb) list
2122                    return;
2123
2124            set_mask_bits(&mm->flags, MMF_DUMPABLE_MASK, value);
2125    }
2126
2127    SYSCALL_DEFINE3(execve,
2128                    const char __user *, filename,
2129                    const char __user *const __user *, argv,
2130                    const char __user *const __user *, envp)
2131    {
(gdb)
2132            return do_execve(getname(filename), argv, envp);
2133    }
2134
2135    SYSCALL_DEFINE5(execveat,
2136                    int, fd, const char __user *, filename,
2137                    const char __user *const __user *, argv,
2138                    const char __user *const __user *, envp,
2139                    int, flags)
2140    {
2141            return do_execveat(fd,
(gdb) n
do_syscall_64 (regs=0xffffc9000015bf58, nr=<optimized out>) at arch/x86/entry/common.c:88
88              instrumentation_end();
(gdb) n
89              syscall_exit_to_user_mode(regs);
(gdb)
102             if (unlikely(regs->cx != regs->ip || regs->r11 != regs->flags))
(gdb)
entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:130
130             ALTERNATIVE "testb %al, %al; jz swapgs_restore_regs_and_return_to_usermode", \
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:561
561             IBRS_EXIT
(gdb)
570             POP_REGS
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:571
571             add     $8, %rsp        /* orig_ax */
(gdb)
common_interrupt_return () at arch/x86/entry/entry_64.S:575
575             swapgs
(gdb)
578             testb   $3, 8(%rsp)
(gdb)
579             jnz     .Lnative_iret
(gdb)
647             testb   $4, (SS-RIP)(%rsp)
(gdb)
648             jnz     native_irq_return_ldt
(gdb)
659             iretq
(gdb) n
0x0000000000401865 in ?? ()
(gdb) x/i $rip
=> 0x401865:    Cannot access memory at address 0x401865
(gdb) ni
asm_exc_page_fault () at ./arch/x86/include/asm/idtentry.h:623
623     DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_PF,     exc_page_fault);
(gdb) advance *0x0000000000401865
0x0000000000401865 in ?? ()
(gdb) x/i $rip
=> 0x401865:    endbr64
(gdb)
   0x401869:    push   %rbp
(gdb)
   0x40186a:    mov    %rsp,%rbp
(gdb)
   0x40186d:    mov    $0x6,%edx
(gdb)
   0x401872:    lea    0x7d797(%rip),%rax        # 0x47f010
(gdb)
   0x401879:    mov    %rax,%rsi
(gdb)
   0x40187c:    mov    $0x1,%edi
(gdb)
   0x401881:    call   0x411310
(gdb)
   0x401886:    mov    $0xa4,%edi
(gdb) x/s 0x47f010
0x47f010:       <error: Cannot access memory at address 0x47f010>
(gdb) advance 0x401886
Function "0x401886" not defined.
(gdb) advance *0x401886
0x0000000000401886 in ?? ()
(gdb) x/s 0x47f010
0x47f010:       "nice!\n"
(gdb) c
Continuing.
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计