2

基本上,我想知道的是 x86-64 操作系统如何运行为 x86 机器编译的代码。我知道当第一个 x64 系统被引入时,这不是其中任何一个的特性。在那之后,他们以某种方式设法做到了这一点。

请注意,我知道 x86 汇编语言是 x86-64 汇编语言的子集,并且 ISA 的设计方式使其可以支持向后兼容性。但是这里让我感到困惑的是堆栈调用约定。这些约定因架构而异。例如,在 x86 中,为了备份帧指针,进程会在它指向堆栈(RAM)的位置推送,并在完成后弹出。另一方面,在 x86-64 中,进程根本不需要更新帧指针,因为所有引用都是通过堆栈指针给出的。其次,虽然在 x86 架构中函数的参数是通过 x86-64 中的堆栈传递的,但寄存器用于此目的。

也许 x86-64 和 x64 体系结构的堆栈调用约定之间的这种差异可能不会影响程序堆栈的增长方式,只要不同时使用不同的约定,这主要是因为 x32 函数被其他 x32 调用并且相同对于 x64。但是,在某一时刻,一个函数(可能是一个系统函数)将调用一个函数,该函数的代码是为带有一些参数的 x86-64 机器编译的,此时,我很好奇操作系统(或其他一些控制单元)如何处理让这个功能工作。

提前致谢。

4

2 回答 2

3

i386/x86-64 架构设计的部分方式是CS和其他段寄存器引用GDT中的条目。GDT 条目除了描述当前运行任务的操作模式和特权级别的基数和限制外,还有一些特殊位。

如果 CS 寄存器指的是 32 位代码段,则处理器将以 i386 兼容模式运行。同样,64 位代码需要一个 64 位代码段。

所以,把这一切放在一起。

当操作系统要运行一个 32 位任务时,在任务切换到它的过程中,它会将一个值加载到 CS 中,该值指的是一个 32 位代码段。中断处理程序也有与之关联的段寄存器,因此当发生系统调用或发生中断时,处理程序将切换回操作系统的 64 位代码段,(允许 64 位操作系统代码正确运行)和操作系统然后可以完成它的工作并继续安排新任务。

作为关于调用约定的跟进。i386 或 x86-64 都不需要使用帧指针。代码可以随意做。事实上,许多编译器(gcc、clang、VS)都提供了在没有帧指针的情况下编译 32 位代码的能力。重要的调用约定的实现是一致的。如果所有代码都希望参数在堆栈上传递,那很好,但被调用的代码最好同意这一点。同样,通过寄存器传递也可以,只是每个人都必须同意(至少在库接口级别,内部函数通常可以随心所欲)。

除此之外,请记住,两者之间的差异并不是真正的问题,因为每个进程都有自己的私有内存视图。一个副作用是 32 位应用程序无法加载 64 位 dll,而 64 位应用程序无法加载 32 位 dll,因为进程要么有 32 位代码段,要么有 64 位代码部分。不可能两者兼而有之。

于 2012-08-04T23:50:37.177 回答
1

处理器进入传统模式,但这要求当时执行的所有内容都是 32 位代码。此切换由操作系统处理。

Windows:它使用WoW64。WoW64负责改变处理器模式,它还提供了兼容的dll和注册表功能。

Linux:直到最近,Linux 过去(如 Windows)在开始执行 32 位代码时转向以传统模式运行处理器,您需要安装所有 32 位 glibc 库,如果它试图与 64 位代码一起工作,它会中断。现在正在实施X32 ABI,它应该使一切运行得更顺畅,并允许 32 位应用程序访问 x64 功能,如增加 no。的寄存器。请参阅有关 x32 abi 的这篇文章

PS:我对事情的细节不是很确定,但它应该给你一个开始。

此外,这个答案与 Evan Teran 的答案相结合,可能会大致了解正在发生的一切。

于 2012-08-04T23:49:54.150 回答