在流行的x86架构上,可以使用jmp
类型指令之一以及call
指令来控制执行流程。但这两者都是基本的,还是另一个的“语法糖”?
例如,push
指令是语法糖:push eax
相当于mov [esp], eax
后跟sub esp, 4
。因此mov
是唯一的基本数据操作。
但call
也是如此吗?还有其他一些指令可以实现相同的目标吗?
在伪代码中,如果我有权访问,我可以用jmp
编写函数调用
指令指针:
bar:
push eip ;; pseudo-code! ;; call foo
jmp foo
foo:
pop eax ;; ret
jmp [eax]
这对x86无效,因为无法直接读取指令指针寄存器eip
。到目前为止,我所看到的所有“伎俩”都获得了eip
的全部使用call
的价值。或者call
实际上是规范方式来读取x86上的指令指针?作为一个额外的问题,可以实现C的每个架构是否都需要一种方法来读取指令指针,以便可以实现函数指针?
int main(int argc, char * argv[])
{
int (*p)() = strtoul(argv[1]);
return p(); // how to implement without
// accessing the instruction pointer?
}
[这不是一个深刻的问题;我只是觉得令人感到欣慰的是,知道函数不仅仅是高级编程语言引入的抽象,而且实际上是硬件内在的。]
答案 0 :(得分:9)
机器指令集可能非常简单;看Wolfram's 2-state, 3 symbol Turing machine.问题是这样的机器很难编程,很难快速运行。
您可以使用其他说明完全模拟呼叫返回:
; Simulate "call abc":
push offset next_location
jmp abc
next_location:
; Simulate "ret"
pop eax
jmp eax
您可以使用mov指令模拟推/弹,并通过添加常量来调整寄存器,如您在问题中所述。
人们向指令集添加指令以使公共序列有效。因此,"呼叫"之所以存在是因为它确实是一项非常有用的任务("调用/返回"对可以通过硬件使用返回地址的内部堆栈进行优化,以避免指令管道中断;这是真正的性能获胜)。
所以,是的,您可以使用计算机提供的最小指令集来实现汇编语言。但正确的附加说明确实有助于实践。 (您可以出于好意,添加您认为可能有用的说明。通常他们不会帮助,这是source of the original RISC/CISC debate)。
Goofball minimal machines:
20世纪70年代生产的真正的小型机(" GRI")只有一条指令:MOV LOC1到LOC2;目标位置是神奇的,这是真正的工作是如何完成的。通过将某些东西移动到位置33(组成),副作用是该值被添加到可以从位置32读取的累加器。它有很多位置做了有趣,方便的事情,所以实质上添加了一个新的有趣的寄存器只是添加有用指令的一个奇怪的变体。我怀疑JMP指令包括将常量移动到充当程序计数器的位置。 (如果你觉得这很奇怪,你还没有遇到PDP-11)。
理论机器是单指令" SUB LOC1从LOC2 jmp到LOC3,如果是负#34;这台机器是通用的,但在你第一次玩具问题后编程并不好玩。
值得注意的是,这两个指令集都没有操作码位或寻址模式位。实际上,指令解码器很简单。