我的意思是:这个版本(包括 x86 和 ARM 架构驱动程序)可以在 x86 计算机上运行,也可以在其他 ARM 设备上运行。
我知道现在没有这样的 CPU 支持这两种指令集。但理论上,它会成真吗?上电后,CPU可以检查程序在哪个指令集中?
我的意思是:这个版本(包括 x86 和 ARM 架构驱动程序)可以在 x86 计算机上运行,也可以在其他 ARM 设备上运行。
我知道现在没有这样的 CPU 支持这两种指令集。但理论上,它会成真吗?上电后,CPU可以检查程序在哪个指令集中?
有可能的。
除了 dave 和其他发帖人的回答之外,我还编写了一个 python 脚本,该脚本将生成可在 x86 和 ARM 上运行的代码技巧,根据引导的不同,它们将采用不同的路径。
我对 x86 不熟悉,如果这不起作用,请原谅我。
import sys
import struct
import math
armClobberReg = 0
armShift = 31
if (armClobberReg>15):
sys.stderr.write("Error! ARM clobber register is invalid\n")
sys.exit(-1)
if (armClobberReg == 15):
sys.stderr.write("Warning: r15 will be clobbered on ARM\n")
if (armShift & 1 != 1 or armShift > 31):
sys.stderr.write("Warning! ARM shift is invalid. Using 31 instead\n")
armShift = 31
jmpOffset = (armClobberReg<<4)|(armShift>>1)
jmpAndEqNopInstruction = struct.pack("<BBBBI",0xE9, jmpOffset,0,0,0xE1A00000)
armNoOp = struct.pack("<I",0xE1A00000)
x86NoOp = struct.pack("c","\x90")
asmOut = jmpAndEqNopInstruction
asmArmTxtOut = "{0:08x}: andeq r{1}, r0, r9, ror #{2}\n".format(0, armClobberReg, armShift)
asmArmTxtOut+= "{0:08x}: nop\n".format(4)
asmx86TxtOut = "{0:08x}: jmp {1:x}\n".format(0, jmpOffset)
asmx86TxtOut+= " ... (ARM instructions)\n"
i = math.floor((jmpOffset + 5)/4) - 2
armPc = 8
while i>0:
asmOut += armNoOp
asmArmTxtOut+= "{0:08x}: nop <replace with your arm instructions>\n".format(armPc)
armPc+=4
i-=1
asmOut = asmOut.ljust(jmpOffset + 5, chr(0xff))
asmOut += x86NoOp
asmx86TxtOut += "{0:08x}: nop <replace with your x86 instructions>\n".format(jmpOffset + 5)
asmArmTxtOut += " ... (x86 instructions)\n"
sys.stderr.write("x86 disassembly: \n"+asmx86TxtOut+"\n")
sys.stderr.write("ARM disassembly: \n"+asmArmTxtOut+"\n")
sys.stderr.write("ARM code length limited to {0} instructions\n".format(int(math.floor((jmpOffset + 5)/4) - 2)))
sys.stdout.write(asmOut)
sys.stderr.write("Wrote opcodes to stdout\n\n")
在 ARM 上,第一条和第二条指令被解释为andeq
andnop
指令,而在 x86 上,它被解释为一条jmp
指令。
ARM拆解:
x86 反汇编:
您可以在其中附加您自己的操作码,这些操作码将分支到相应的内核。
让我们假设它可以完成。需要做什么?
好吧,您需要将整个内核编译两次,一次使用 x86,一次使用 ARM 操作码(组装后的汇编程序)。好吧,我们只需要一个小程序来查询我们在哪个 CPU 上,然后分支到 x86 版本或 ARM 版本。
糟糕,有一个引导捆绑问题。您将使用这两个操作码中的哪一个来确定要使用哪个版本?ARM 或 x86 - 请记住它需要在两者上运行。
这可能是可能的。也许分支指令的编码是 ARM 上的 nop 或其他东西。所以现在我可以让 x86 和 ARM 的第一条指令不同——其中一个分支很短,我现在知道它是 x86 并从那里跳转到我的 x86 内核。而另一个不会采用分支,而是会进入下面的代码,然后可以分支到 ARM 版本(反之亦然)。
那将是一个很棒的代码技巧。
但是你为什么要这样做呢?您将有一半的内核在内存中未被使用(也许您可以将其换出到磁盘)。你也可以从一开始就为正确的目标进行编译。(此外,我不确定是否有任何工具可以让您编译两个内核并将它们放在一个映像中,但是如果您破解了自己的链接器,则可以完成)。
这主要是引导加载程序中的支持问题,以及操作系统加载后会做什么。这不仅在理论上可行,/mach_kernel
版本 10.6.x-10.7.x 的 OSX 内核 ( ) 以通用 i386/x86_64 二进制文件的形式出现:
$ file /Volumes/KernelDebugKit/mach_kernel
/Volumes/KernelDebugKit/mach_kernel: Mach-O universal binary with 2 architectures
/Volumes/KernelDebugKit/mach_kernel (for architecture x86_64): Mach-O 64-bit executable x86_64
/Volumes/KernelDebugKit/mach_kernel (for architecture i386): Mach-O executable i386
10.5.x 内核甚至以 i386/PowerPC 通用二进制文件的形式出现:
$file /Volumes/KernelDebugKit/mach_kernel
/Volumes/KernelDebugKit/mach_kernel: Mach-O universal binary with 2 architectures
/Volumes/KernelDebugKit/mach_kernel (for architecture i386): Mach-O executable i386
/Volumes/KernelDebugKit/mach_kernel (for architecture ppc): Mach-O executable ppc
引导加载程序(Apple 的 EFI 固件实现 (x86) 或 OSX 的 OpenFirmware (ppc))将需要支持“胖”二进制格式,并能够决定并运行正确的格式。当然,一旦内核被加载,它需要操作系统其余部分的整个支持基础设施以兼容的格式提供,以便做任何有用的事情。
请注意,旧版本的 OSX 甚至附带“Rosetta”,这是一个运行时二进制翻译系统,用于在 x86/x86-64 CPU 上运行 PowerPC 二进制文件。
需要明确的是,内核的 x86 和 ppc 版本存储在同一个文件中是完全不相关的。
我不这么认为,有可能在两个指令集中编写一个内核,并有一个特殊的启动代码在两种架构上以不同的方式执行,从而为架构启动适当的内核。
好吧,在我看来,这不会实现。我的理由是: - 编译一个程序的两个版本更容易,并且在安装期间它可以决定它是在 ARM 还是 x86 平台上。
如果您正在查看 32 位命令。它们中的很多都被使用了,您需要将指令集加倍,这会降低 CPU 的效率。如果你不走运,32 位指令集将不再满足你的需求,所以你将不得不更改为 33 位,这将非常尴尬。
真的,我不明白你为什么要这样做。只会导致效率低下