机器代码
在计算机编程中,机器代码是由机器语言指令组成的计算机代码,用于控制计算机的中央处理单元(CPU)。尽管十进制计算机曾经很普遍,但当代市场以二元计算机为主。对于这些计算机,机器代码是“计算机程序的二进制表示,该计算机实际上是由计算机读取和解释的。机器代码中的程序由一系列机器说明组成(可能插入了数据)。”
每种指令都会导致CPU执行非常具体的任务,例如负载,商店,跳跃或算术逻辑单元(ALU)操作,对CPU寄存器或内存中的一个或多个数据单位。
早期CPU具有特定的机器代码,可能会在每个发布的新CPU时向后兼容。指令集体系结构(ISA)的概念定义并指定行为和编码系统的记忆,而无需指定其确切的实现。这是一个抽象层,可以在同一CPU家族中兼容,因此根据ISA为家族编写或生成的机器代码将在家庭中的所有CPU上运行,包括未来的CPU。
通常,每个体系结构家族(例如X86 , ARM )都有自己的ISA,因此其特定的机器代码语言。有例外,例如VAX体系结构,其中包括PDP-11指令集的可选支持和IA-64 ,其中包括对IA-32指令集的可选支持。另一个示例是PowerPC 615 ,该处理器旨在同时处理PowerPC和X86指令。
机器代码是一种严格的数值语言,是用于程序员的CPU的最低级别接口。汇编语言提供了数值机器代码和人类可读版本之间的直接映射,其中数值操作编码和操作数被可读字符串替换(例如0x90是x86上的NOP指令)。虽然可以在机器代码中直接编写程序,但要管理单个位并计算数值地址和常数手动既乏味又容易出错。因此,在现代环境中,程序很少直接用机器代码编写,但可以用于低级调试,程序修补(尤其是在汇编器源不可用时)和汇编语言拆卸。
当今的大多数实用程序都是用高级语言编写的。这些程序要幺由编译器翻译成机器代码,要幺由解释器解释,通常在翻译成中间代码(例如字节码)之后,然后将其解释。
根据定义,机器代码是程序员可见的最低编程详细信息级别,但是在内部,许多处理器使用Microcode或优化并将机器代码指令转换为Micro-Ops序列。通常不认为微码和微型op是机器代码;除了某些计算机上,用户无法编写微型码或微型OPS,并且Microcode的操作以及机器代码指令转换为微型OPS,除了性能相关的副作用外,都会透明地向程序员透明地发生。
指令系统
每个处理器或处理器家族都有自己的指导集。说明是与机器命令相对应的位,数字或字符的模式。因此,指令集特定于使用(主要)相同体系结构的一类处理器。后继或衍生处理器设计通常包括前身的说明,并可能添加新的其他说明。有时,后继设计会停止或更改某些指令代码的含义(通常是因为它是为了新目的而需要的),从而影响代码兼容性在某种程度上;对于某些说明,即使是兼容的处理器也可能显示出略有不同的行为,但这很少是一个问题。系统也可能在其他细节上有所不同,例如内存布置,操作系统或外围设备。由于程序通常依赖于此类因素,因此即使使用相同类型的处理器,不同系统通常不会运行相同的机器代码。
处理器的指令集可能具有固定长度或可变长度指令。如何组织模式随特定的体系结构和指令类型而变化。大多数指令都有一个或多个OpCode字段,可以指定基本指令类型(例如算术,逻辑,跳跃等),操作(例如添加或比较)以及其他可能给出操作数类型的字段(S ),寻址模式,地址偏移量或索引或操作数值本身(指令中包含的恒定操作数称为立即称为)。
并非所有机器或个人说明都有明确的操作数。在具有单个蓄能器的机器上,累加器既隐含左操作数,也是大多数算术指令的结果。其他一些架构,例如X86体系结构,具有常见指令的累加器版本,累加器通过更长的说明将累加器视为一般寄存器之一。堆栈机在隐式堆栈上具有大部分或全部操作数。特殊目的说明通常也缺乏明确的操作数;例如,X86体系结构中的CPUID将值写入四个隐式目标寄存器。显式和隐式操作数之间的区别在代码生成器中很重要,尤其是在寄存器分配和实时跟踪零件中。良好的代码优化器可以跟踪隐式以及明确的操作数,这些操作数可能会允许更频繁地恒定传播,持续的寄存器(寄存器分配给寄存器分配的结果是通过通过常数替换来释放的常数表达式的结果)和其他代码增强功能。
程式
计算机程序是可以由中央处理单元(CPU)执行的指令列表。程序的执行是为了使其执行以解决问题并因此完成结果的CPU。虽然简单处理器能够接一个地执行指令,但在某些情况下(当管道已满时)可以同时执行两个或多个指令。例如,从1993年开始的原始英特尔奔腾可以在其管道已满时每次时钟周期最多执行。
程序流可能会受到特殊的“跳跃”指令的影响,这些指令将执行执行到下一个数字顺序地址之外的其他指令(因此是指令)。这些有条件的跳跃是否发生取决于一个条件,例如值大于,小于或等于其他值的情况。
装配语言
一种更具人为友好的机器语言的演绎,称为“汇编语言” ,使用助记符代码来参考机器代码指令,而不是直接使用说明的数字值,并使用符号名称来参考存储位置,有时是寄存器。例如,在Zilog Z80处理器上,机器代码00000101
,这导致CPU减少B
通用登记册,将以汇编语言表示DEC B
.
例子
MIPS体系结构为机器代码提供了一个特定的示例,其指令总是长32位。一般的指令类型由OP (操作)字段(最高6位)给出。 J-Type(跳跃)和I-Type(立即)说明完全由OP指定。 R型(寄存器)指令包括一个附加的字段功能,以确定确切的操作。这些类型中使用的字段是:
6 5 5 5 5 6 bits [ op | rs | rt | rd |shamt| funct] R-type [ op | rs | rt | address/immediate] I-type [ op | target address ] J-type
RS , RT和RD表示注册操作数; Shamt给出了班次;地址或直接字段直接包含操作数。
例如,添加寄存器1和2并将结果放入寄存器6中是编码:
[ op | rs | rt | rd |shamt| funct] 0 1 2 6 0 32 decimal 000000 00001 00010 00110 00000 100000 binary
将一个值加载到寄存器8中,在寄存器3中列出的位置之后,从内存单元68个单元格中加载值。
[ op | rs | rt | address/immediate] 35 3 8 68 decimal 100011 00011 01000 00000 00001 000100 binary
跳到地址1024:
[ op | target address ] 2 1024 decimal 000010 00000 00000 00000 10000 000000 binary
重叠说明
在具有可变长度指令集的处理器体系结构(例如Intel的X86处理器家族)上,在控制流的范围内,它被称为Kruskal计数,有时可以通过OpCode-evel-evellive Opcode-evellive Opcode- evelling级别来安排结果。代码使两个代码路径共享OpCode序列的常见片段。这些被称为重叠指令,重叠的操作编码,重叠的代码,重叠的代码,指令分配或跳入指令的中间,并表示叠加的形式。
在1970年代和1980年代,有时使用重叠的说明来保留记忆空间。一个示例是在Microsoft的Altair Basic中实现错误表的实现,其中交织的指令相互共享其指令字节。该技术今天很少使用,但是仍然需要在字节级上需要对大小进行极端优化的区域,例如在实现启动装载机的实现中,必须适合引导扇区。
该原理还用于脂肪二进制文件的共享代码序列,这些代码必须在多个指令集合的处理器平台上运行。
此属性还用于在现有代码存储库中查找称为小工具的意外说明,并在面向返回的编程中用作代码注入诸如返回LIBC攻击之类的代码注入的替代方案。
与微码的关系
在某些计算机中,体系结构的机器代码是由一个更基本的基础层(称为Microcode)实现的,该层在不同模型的计算机或家族中提供了一个常见的机器语言界面,其基础数据流的基础差异很大。这样做是为了促进不同模型之间的机器语言程序的移植。此用途的一个例子是IBM System/360计算机系列及其后继产品。随着数据流路径的宽度为8位至64位及以后,它们在整个行的机器语言层面上都呈现了一个共同的体系结构。
使用Microcode实现模拟器,使计算机可以展示完全不同的计算机的体系结构。 System/360系列使用它允许从早期的IBM计算机到新计算机系列的移植程序,例如IBM S/360模型40上的IBM 1401/1440/1460模拟器。
与字节码的关系
机器代码通常与字节码(也称为p代码)不同,该字节由解释器执行,或者本身将其编译到机器代码中以更快(直接)执行。一个例外是,当处理器设计用于将特定字节码直接用作其机器代码时,例如Java处理器是这种情况。
当参考语言功能或库的平台相关部分时,机器代码和汇编代码有时称为本机代码。
存储在内存中
从CPU的角度来看,机器代码存储在RAM中,但出于性能原因,通常也将机器代码存储在一组缓存中。根据体系结构,可能有不同的指令和数据缓存。
CPU根据其内部程序计数器知道要执行的机器代码。该程序对抗指向内存地址,并根据可能导致程序化分支的特殊说明进行更改。当CPU首先打开时,程序计数器通常设置为硬编码值,因此将执行任何机器代码恰好在此地址。
同样,即使这不是有效的机器代码,也可以将程序计数器设置为在某些任意地址处执行任何机器代码。这通常会触发特定于体系结构的保护故障。
通常会告诉CPU,按照基于分页的系统的页面权限,如果当前页面实际上通过执行位保存机器代码- 页面具有多个此类许可位(可读,可读,可写等),以实现各种家政功能。例如,在类似于Unix的系统内存页面上可以切换到可执行的mprotect()
系统呼叫和Windows上VirtualProtect()
可用于获得类似的结果。如果尝试在不执行的页面上执行机器代码,则通常会发生架构特定的故障。将数据视为机器代码,或者找到通过各种技术使用现有机器代码的新方法是某些安全漏洞的基础。
同样,在基于段的系统中,段描述符可以指示一个段是否可以包含可执行的代码以及代码可以运行的戒指。
从过程的角度来看,代码空间是其地址空间的一部分,该空间存储了执行中的代码。在多任务系统中,这包括程序的代码段和通常共享的库。在多线程环境中,一个过程共享代码空间的不同线程以及数据空间,这与过程切换相比大大降低了上下文切换的开销。
人类的可读性
帕梅拉·塞缪尔森(Pamela Samuelson)写道,机器代码是如此不可读取,以至于美国版权所能无法确定特定编码程序是否是作者的原始作品;但是,美国版权办公室确实允许对计算机程序进行版权注册,并且有时可以对程序的机器代码进行分解,以便使其功能更容易理解。但是,分解器或拆卸器的输出将缺少注释和符号引用,因此,尽管输出可能比对象代码更易于读取,但它仍然比原始源代码更加困难。对于squoze之类的对象代码格式不存在此问题,其中包含源代码。
认知科学教授道格拉斯·霍夫斯塔特(Douglas Hofstadter)将机器代码与遗传密码进行了比较,他说:“看一项用机器语言编写的程序可与原子观察DNA分子原子相提并论。”