64 位 linux 内核如何从 ELF 启动 32 位进程
How does the 64 bit linux kernel kick off a 32 bit process from an ELF
通过查看内核源代码中的 binfmt_elf.c,我无法弄清楚内核(64 位)在生成 32 位进程与 64 位进程时有何不同。
任何人都可以向我解释我缺少什么吗?
(这个问题与我的另一个问题有关,即在与 64 位指令相同的进程中使用 32 位指令 (),但这可以作为一个单独的问题。)
如果使用execveat系统调用启动新进程,我们先在fs/exec.c中输入内核源代码进入 SYSCALL_DEFINEx(execveat..) 函数。
这个然后调用这些函数:
- do_execveat(..)
- do_execveat_common(..)
- exec_binprm(..)
- search_binary_handler(..)
search_binary_handler 遍历各种二进制处理程序。在 64 位 Linux 内核中,将有一个用于 64 位 ELF 的处理程序和一个用于 32 位 ELF 的处理程序。这两个处理程序最终都是从相同的来源 fs/binfmt_elf.c 构建的。但是,32 位处理程序是通过 fs/compat_binfmt_elf.c 构建的,它在 包括 源文件 [=52] 之前重新定义了一些宏=]binfmt_elf.c本身。
里面binfmt_elf.c,elf_check_arch被调用。这是在 arch/x86/include/asm/elf.h 中定义的宏,在 64 位处理程序和 32 位处理程序中定义不同。对于 64 位,它与 EM_X86_64(62 - 在 include/uapi/ilnux/elf-em.h 中定义)进行比较。对于 32 位,它与 EM_386 (3) 或 EM_486 (6) 进行比较(在同一文件中定义).如果比较失败,二进制处理程序会放弃,所以我们最终只有一个处理程序负责 ELF 解析和执行——取决于 ELF 是 64 位还是 32 位。
因此,在 64 位 Linux 中解析 32 位 ELF 与 64 位 ELF 的所有差异都应在文件 fs/compat_binfmt_elf.c.
主要线索似乎是compat_start_thread。 start_thread 重新定义为 compat_start_thread。此函数定义可在 arch/x86/kernel/process_64.c 中找到。 compat_start_thread 然后使用这些参数调用 start_thread_common:
start_thread_common(regs, new_ip, new_sp,
test_thread_flag(TIF_X32)
? __USER_CS : __USER32_CS,
__USER_DS, __USER_DS);
而正常的 start_thread 函数使用这些参数调用 start_thread_common:
start_thread_common(regs, new_ip, new_sp,
__USER_CS, __USER_DS, 0);
这里我们已经看到架构相关代码对 64 位 ELF 和 32 位 ELF 的 CS 做了不同的事情。
然后我们在arch/x86/include/asm/segment.h:
中有__USER_CS和__USER32_CS的定义
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8 + 3)
#define __USER32_CS (GDT_ENTRY_DEFAULT_USER32_CS*8 + 3)
和:
#define GDT_ENTRY_DEFAULT_USER_CS 6
#define GDT_ENTRY_DEFAULT_USER32_CS 4
所以__USER_CS
是6*8+3=51=0x33
而__USER32_CS
是4*8+3=35=0x23
这些数字与这些示例中用于 CS 的数字相匹配:
由于CPU在实模式下不是运行,段寄存器中填充的不是段本身,而是一个16位的选择器:
来自维基百科 (Protected mode):
In protected mode, the segment_part is replaced by a 16-bit selector, in which the 13 upper bits (bit 3 to bit 15) contain the index of an entry inside a descriptor table. The next bit (bit 2) specifies whether the operation is used with the GDT or the LDT. The lowest two bits (bit 1 and bit 0) of the selector are combined to define the privilege of the request, where the values of 0 and 3 represent the highest and the lowest privilege, respectively.
CS值为0x23时,bit 1和bit 0为3,意思是"lowest privilege"。 bit 2是0,意思是GDT,bit 3到bit 15是4,意思是我们得到index 4 来自全局描述符 table (GDT).
这是我到目前为止能够挖掘到的程度。
通过查看内核源代码中的 binfmt_elf.c,我无法弄清楚内核(64 位)在生成 32 位进程与 64 位进程时有何不同。
任何人都可以向我解释我缺少什么吗?
(这个问题与我的另一个问题有关,即在与 64 位指令相同的进程中使用 32 位指令 (
如果使用execveat系统调用启动新进程,我们先在fs/exec.c中输入内核源代码进入 SYSCALL_DEFINEx(execveat..) 函数。 这个然后调用这些函数:
- do_execveat(..)
- do_execveat_common(..)
- exec_binprm(..)
- search_binary_handler(..)
- exec_binprm(..)
- do_execveat_common(..)
search_binary_handler 遍历各种二进制处理程序。在 64 位 Linux 内核中,将有一个用于 64 位 ELF 的处理程序和一个用于 32 位 ELF 的处理程序。这两个处理程序最终都是从相同的来源 fs/binfmt_elf.c 构建的。但是,32 位处理程序是通过 fs/compat_binfmt_elf.c 构建的,它在 包括 源文件 [=52] 之前重新定义了一些宏=]binfmt_elf.c本身。
里面binfmt_elf.c,elf_check_arch被调用。这是在 arch/x86/include/asm/elf.h 中定义的宏,在 64 位处理程序和 32 位处理程序中定义不同。对于 64 位,它与 EM_X86_64(62 - 在 include/uapi/ilnux/elf-em.h 中定义)进行比较。对于 32 位,它与 EM_386 (3) 或 EM_486 (6) 进行比较(在同一文件中定义).如果比较失败,二进制处理程序会放弃,所以我们最终只有一个处理程序负责 ELF 解析和执行——取决于 ELF 是 64 位还是 32 位。
因此,在 64 位 Linux 中解析 32 位 ELF 与 64 位 ELF 的所有差异都应在文件 fs/compat_binfmt_elf.c.
主要线索似乎是compat_start_thread。 start_thread 重新定义为 compat_start_thread。此函数定义可在 arch/x86/kernel/process_64.c 中找到。 compat_start_thread 然后使用这些参数调用 start_thread_common:
start_thread_common(regs, new_ip, new_sp,
test_thread_flag(TIF_X32)
? __USER_CS : __USER32_CS,
__USER_DS, __USER_DS);
而正常的 start_thread 函数使用这些参数调用 start_thread_common:
start_thread_common(regs, new_ip, new_sp,
__USER_CS, __USER_DS, 0);
这里我们已经看到架构相关代码对 64 位 ELF 和 32 位 ELF 的 CS 做了不同的事情。
然后我们在arch/x86/include/asm/segment.h:
中有__USER_CS和__USER32_CS的定义#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8 + 3)
#define __USER32_CS (GDT_ENTRY_DEFAULT_USER32_CS*8 + 3)
和:
#define GDT_ENTRY_DEFAULT_USER_CS 6
#define GDT_ENTRY_DEFAULT_USER32_CS 4
所以__USER_CS
是6*8+3=51=0x33
而__USER32_CS
是4*8+3=35=0x23
这些数字与这些示例中用于 CS 的数字相匹配:
由于CPU在实模式下不是运行,段寄存器中填充的不是段本身,而是一个16位的选择器:
来自维基百科 (Protected mode):
In protected mode, the segment_part is replaced by a 16-bit selector, in which the 13 upper bits (bit 3 to bit 15) contain the index of an entry inside a descriptor table. The next bit (bit 2) specifies whether the operation is used with the GDT or the LDT. The lowest two bits (bit 1 and bit 0) of the selector are combined to define the privilege of the request, where the values of 0 and 3 represent the highest and the lowest privilege, respectively.
CS值为0x23时,bit 1和bit 0为3,意思是"lowest privilege"。 bit 2是0,意思是GDT,bit 3到bit 15是4,意思是我们得到index 4 来自全局描述符 table (GDT).
这是我到目前为止能够挖掘到的程度。