通过查看内核源代码中的binfmt_elf.c,我无法了解在生成32位进程与64位进程时,内核(64位)有什么不同。
有谁能向我解释我缺少什么吗?
(这个问题与我的另一个问题有关,即32位指令与64位指令(link)在同一进程中,但这是一个独立的问题。)
最佳答案
如果execveat系统调用用于启动新进程,我们首先在内核源代码中将fs/exec.c输入syscall_definex(execveat..)函数。
这个函数调用以下函数:
执行(…)
是否执行常见(…)
执行官
搜索二进制处理程序(…)
search_binary_处理程序在各种二进制处理程序上迭代。在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。启动线程被重新定义为兼容启动线程。这个函数定义可以在arch/x86/kernel/process_64.c中找到。compat_start_thread然后使用以下参数调用start_thread:
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中定义了“用户”和“用户32”:
#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):
在保护模式下,段部分由16位选择器代替,其中13个高位(第3位至第15位)包含描述符表内的项的索引。下一位(位2)指定操作是与gdt还是ldt一起使用。选择器的最低两位(位1和位0)组合起来定义请求的特权,其中0和3的值分别表示最高和最低特权。
对于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/