【问题标题】:Is it possible to detect the CPU architecture from machine code?是否可以从机器代码中检测 CPU 架构?
【发布时间】:2016-06-27 13:53:08
【问题描述】:

假设有 2 种可能的架构,ARM 和 x86。 有没有办法检测代码运行在哪个系统上,从汇编/机器代码中实现类似的功能?

if (isArm)
    jmp to arm machine code
if (isX86)
    jmp to x86 machine code

我知道 ARM 机器代码与 x86 机器代码有很大不同。我在想的是一些精心设计的汇编指令,它们会产生相同的二进制机器代码。

【问题讨论】:

  • 对于初学者来说,即使是二进制程序格式也不同(ELF 标头中的架构字段),因此除非它在适当的架构上,否则您甚至无法运行该程序。理论上是可以写出这样的机器码的,但是你需要一种特殊的方式来运行它。
  • 是的,那么就可以了。
  • 神奇的谷歌术语是“多语言”:google.co.uk/webhp?#q=arm+x86+polyglot
  • 如果您的代码不知道它在哪台机器上运行,ISA 几乎是您最不用担心的;不知道 RAM 在哪里、存在哪些外围设备以及它们在哪里等的代码不会取得太大的成就。 x86 PC 上可能有一些相当标准化的固件接口(注意;一般不是 x86),但祝您在各种 ARM 机器上好运;)
  • 您假设基于 ARM 的计算机的启动方式与 x86 PC 相同,它们将磁盘的第一个扇区读入内存并跳转到它。但是,据我所知,没有基于 ARM 的计算机可以做到这一点。甚至不是所有的 x86 PC 都这样做了,因为有些人专门使用完全不同的 EFI 引导过程。

标签: assembly x86 arm x86-64


【解决方案1】:

假设您已经处理了所有其他差异1,并且您还剩下编写一个小的多语言蹦床,您可以使用这些操作码:

EB 02 00 EA

当放置在地址 0 时,对于 ARM(非拇指),转换为:

00000000: b 0xbb4
00000004: ...

但对于 x86(实模式),转换为:

0000:0000 jmp 04h
0000:0002 add dl, ch
0000:0004 ...

然后您可以将更复杂的 x86 代码放在地址 04h 上,并将 ARM 代码放在地址 0bb4h 上。

当然,在重定位基地址时,一定要重定位跳转目标。


1 例如,ARM starts at address 0 而 x86 从地址 0fffffff0h 开始,因此您需要特定的硬件/固件支持来抽象引导地址。

【讨论】:

  • 如果两个体系结构从不同的地址开始,则无需制作兼容的二进制文件。只需在 ROM 地址 0 处加载 ARM 二进制文件,在适当的 ROM 地址处加载 x86 二进制文件。如果 OP 想从拇指驱动器启动,这将无济于事。
  • @slebetman 是的,这是真的。我把这个问题当作“我们可以让相同的二进制代码成为有效的 ARM 和 x86 代码吗?”。我不知道 OP 将要做什么或需要什么。只是给他所要求的。
  • 有趣的事实:在 ARM 中,32/64 位多语言入口点非常简单,因为 A32 b 看起来像 A64 adds(是的,我已经编写了代码(ab )使用这个事实...)
【解决方案2】:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0363g/Beijdcef.html

https://electronics.stackexchange.com/a/232934

How to setup ARM interrupt vector table branches in C or inline assembly?

http://osnet.cs.nchu.edu.tw/powpoint/Embedded94_1/Chapter%207%20ARM%20Exceptions.pdf

ARM Undefined Instruction error

ARM 汇编不是我的专业领域,但我在 x86 汇编中编写了很多程序。我记得我在大学时也有过与家庭作业相同的问题。我找到的解决方案是中断 06h(http://webpages.charter.net/danrollins/techhelp/0103.HTMhttps://es.wikipedia.org/wiki/Llamada_de_interrupci%C3%B3n_del_BIOS#Tabla_de_interrupciones)。每次微处理器尝试执行未知指令(“无效操作码”)时都会触发此中断。

8086在发现无效操作码时卡住,因为IP(指令指针)返回到相同的无效指令,它试图重新执行它,这个循环卡住了程序的执行。

从 80286 开始中断 06h 被触发,因此程序员可以处理无效的操作码情况。

interrupt 06h 有助于检测 CPU 架构,通过简单地尝试执行 x64 操作码,如果 interrrupt 06h 被触发,CPU 没有识别它,所以它是 x86,否则它是 x64。

这种技术也可以用来检测微处理器的类型:

  • 尝试执行 80286 指令,如果没有触发中断 06h,则 CPU 至少为 8286。
  • 尝试执行 80386 指令,如果不触发中断 06h,则 CPU 至少为 8386。
  • 等等……

http://mtech.dk/thomsen/program/ioe.php

https://software.intel.com/en-us/articles/introduction-to-x64-assembly

【讨论】:

  • 问题是关于 x86 与 ARM,而不是关于识别特定芯片(我建议 thisthis 以及 x64 与 x86 的英特尔手册)。使用“#UD 技术”是一个鸡蛋问题:您需要识别架构来设置 ISR 来识别架构。
  • @MargaretBloom 和 Jose:检测长模式与检测单一模式中支持的扩展有着根本的不同。由于它不向后兼容,只需编写解码方式不同的代码,e.g. use REX jz vs inc eax/jz 以区分 long 模式和 legacy/compat(甚至 16 位)模式。
【解决方案3】:

这在汇编或机器代码中是不可能的,因为机器代码将取决于架构。因此,您的if 语句必须首先编译为 ARM 或 x86。如果编译为 ARM,则无法在没有模拟器的情况下在 x86 上运行;如果编译为 x86,则无法在没有模拟器的情况下在 ARM 上运行。

如果您确实在模拟器中运行代码,那么代码基本上是在为其编译的 CPU 的虚拟版本中运行的。根据模拟器的不同,您可能会也可能无法检测到您正在模拟器上运行。并且取决于模拟器,如果模拟器允许您的代码检测到您正在模拟器上运行,您可能无法检测到底层 CPU 和/或操作系统(例如,您可能无法检测 x86 模拟器是否在 x86 或 ARM 上运行)。

现在,如果您非常幸运,您可能会发现两种 CPU 架构,其中一种架构的条件分支或条件 goto 指令要么在您的代码中执行一些有用的操作,要么在另一种架构中不执行任何操作,反之亦然。因此,如果是这种情况,您可以构建一个可以在两种不同 CPU 架构上运行的二进制可执行文件。


多架构二进制文件在现实生活中的工作原理。

在现实生活中,多架构二进制文件实际上是两个具有共享资源(图标、图像等)的完整程序,程序二进制格式包括一个标头或序言,用于告诉操作系统支持哪些 CPU 以及在哪里可以找到 @每个 CPU 的 987654323@ 函数。

我能想到的最好的历史例子之一就是 Mac OS。 Mac 更换了两次 CPU:首先从 68k 到 PowerPC,然后从 PowerPC 到 x86。在每个阶段,他们都必须提出一种文件格式,其中包含两种 CPU 架构的二进制可执行文件。


关于实际可执行文件的说明

现实生活中的程序几乎从来都不是原始的二进制可执行文件。二进制代码始终包含在另一种包含元数据和资源的格式中。例如,Windows 使用 PE 格式,Linux 使用 ELF。但是一些操作系统支持不止一种类型的可执行容器(尽管实际的二进制机器代码可以是相同的)。例如,Linux 传统上支持 ELF、COFF 和 ECOFF。

【讨论】:

  • 另一种表述你的第一段的方式是目标架构是一个编译时常量,所以你可以使用 CPP 宏作为你的分支条件。
  • 您的第三段与您的第一段直接矛盾:/(更不用说后者明显不正确;大多数汇编程序很乐意让您在指令流中插入您喜欢的任意值,例如the .inst directive in GAS )。
猜你喜欢
  • 2013-02-20
  • 2019-11-05
  • 1970-01-01
  • 2013-11-04
  • 1970-01-01
  • 2022-01-19
  • 2010-09-14
  • 2015-01-01
相关资源
最近更新 更多