Joh*_*ite 2 assembly return calling-convention retro-computing
我一直在查看 1950 年代“Pegasus”计算机的模拟,并遇到了“自我修改链接”一词。这是如何运作的?
我们首先要注意的是:
Pegasus 计算机通过传递两条指令(在一个寄存器中)关于被调用者应该如何返回——作为数据,即作为参数来支持子程序/函数调用。然后被调用者将获取该参数,将其存储到自己的本地内存中,并将控制权转移给它——从而执行由调用者作为数据传递的两条指令。
调用者会这样做:
load instructions from L1: into X1 register
... pass other parameters ...
load callee's first block into U0
transfer control to U0 at start
L1: load instructions from main block back into U0
transfer control to U0 at desired caller resume point (e.g. L2)
L2: ...caller resumes...
Run Code Online (Sandbox Code Playgroud)
调用者会这样做:
... compute something ...
store X1 into L4 # writes the two instruction pair in X1 into L4
transfer control to L4 # may or may not be present depending on location of L4
L4: # starts empty but contains the two instructions that came from L1
# so control is transferred back to the caller
Run Code Online (Sandbox Code Playgroud)
L1 处的序列永远不会直接执行,而是作为指令传递给被调用者的参数的模板,被调用者将该模板存储到自己的本地内存中,然后执行该副本。
他们使用术语“提示”来表示调用(子程序调用,例如将控制权转移到被调用者)和“链接”来表示返回(将控制权从子程序转移回正确的调用者)(以及术语“命令”表示机器代码指令) .
因此,当他们谈到“自修改链接”时,他们的意思是子例程返回到其调用者的机制使用自修改代码,即在执行时更改自己的指令的代码,即编写的代码进入指令存储器以供即将执行。这里是将 X1(L1 处的代码副本)写入 L4,在写入后立即执行。
如今,自修改代码通常不受欢迎,因为它有几个负面影响:指令缓存需要与自修改保持同步,这会影响性能,并且指令存储器必须是可写的,这会影响安全性.
自修改经常用于旧处理器,因为我们今天可能认为指令集中缺少指令——例如,PDP-8 有 I/O 指令,但 I/O 端口被硬编码到指令中——那里没有间接 I/O 端口访问。因此,为了让子程序访问作为参数的 I/O 端口,使用自修改代码来构造到该端口的 I/O 指令。(也可以使用大的 switch 语句,但会占用更多的代码。)这些较旧的处理器没有指令缓存,程序员和开发工具也没有强制将指令部分与数据部分分开(即他们没有尝试保护指令)。
在 Pegasus 的情况下,没有间接分支,阻碍了返回到调用者提供的地址的能力。事实证明,调用者的代码实际上可能也被覆盖了——传递两条指令让被调用者执行的能力允许同时恢复调用者代码的寄存器块和该块内的跳转。
(与其他自修改替代方案相比,将完整格式的指令作为参数传递导致返回链接的指令更少:动态构造给定地址参数的跳转指令——这将需要更多的指令来进行位域操作。)
https://en.wikipedia.org/wiki/Ferranti_Pegasus http://bitsavers.org/pdf/ferranti/pegasus/PegasusProgrammingMan_1962.pdf