通过查看内核源代码中的 binfmt_elf.c,我无法弄清楚内核(64 位)在生成 32 位进程与 64 位进程时有何不同。
谁能给我解释一下我错过了什么?
(这个问题与我的另一个问题有关,即在与 64 位指令 ( link ) 相同的进程中有 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 构建的,它在 包括 源文件 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 的数字相匹配:
- For going from 64 bit mode to 32 bit in the middle of a process
- For going from 32 bit mode to 64 bit in the middle of a process
由于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,第 1 位和第 0 位是3,意思是“最低权限”。第 2 位是0,表示GDT,第 3 位到第 15 位是 4,表示我们从全局描述符表(GDT)中得到索引 4 ).
这是我到目前为止能够挖掘到的程度。
关于linux - 64 位 linux 内核如何从 ELF 启动 32 位进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48874756/