2015-02-24 67 views
2

我正在开发玩具CPU(TR3200 cpu)的仿真器。其实我有一个纯粹的解释器核心,但我正在研究开发一个追踪JIT cpu核心。 我对如何追踪被追踪的源机器代码是否被仿真代码修改(自修改代码或类似将操作系统加载到RAM的操作系统) 我在考虑使用段或间隔为此,但我找不到任何信息或实例如何处理。JIT仿真和跟踪脏内存块

换句话说......我知道JIT(我在考虑如何使用asmjit),我应该在使用jitted块的开始地址作为索引的地图上存储jitted代码;我有一些想法如何处理循环计数和设备与jitted代码同步。但是当guest程序写入一个jitted内存块时,我不清楚如何处理。

例如:我们有一个简单的操作系统,它在地址0x100-0x500处执行了一个程序,并且它正确返回。 JIT cpu核心会(乐观地)生成代表这些代码块的本地机器代码。现在,如果操作系统加载另一个程序并将其放置在0x200-0x300地址块上,则不应使覆盖后的0x100-0x500的旧jitted块失效。 或者是最糟糕的情况,一个自我重写的程序,会导致jitted块无效。 如何检测?

+1

嘿!为什么要投票? – Zardoz89 2015-02-24 16:53:28

+0

可能因为没有明确的问题。 :-) – 2015-02-24 19:35:51

+0

谢谢你为什么反对投票的答案。我会尝试重写。我在一天的漫长的一天之前在医院的一间候诊室里用电话写了一封...... – Zardoz89 2015-02-24 19:43:36

回答

3

即使使用适度复杂的数据结构来跟踪访客内存更改的问题是,它必须能够从jitted代码中查询和更新它。为了避免代码大小的荒谬性,您可能需要在调用代码执行存储指令时插入对某个写入间隔树的函数的调用(而不是内联)。性能很可能会受到影响,您可能已经将代码解释为开头。此外,必须格外小心,以免模拟器的其他部分在不更新数据结构的情况下写入相应的页面。

有一个稍微不便携的方法,涉及利用主机操作系统的虚拟内存设施。当您的JIT为一大块访客内存发出代码时,将相应的虚拟内存页面标记为只读。任何后续写入尝试都会触发可由您的主机程序捕获的异常(例如SIGSEGV)。收到此异常后,您的主机程序会立即使从客户内存中生成的与错误页面重叠的所有jitter代码无效,然后重新启用对该页面的写入访问权限,以便商店可以完成。

虽然此解决方案可以应付大多数写入,但它并不处理一段跳动代码修改紧随其后的指令的情况。为了明白为什么,考虑会发生什么:

  1. jitted代码存储到guest虚拟机内存的一部分,从这个guest虚拟机内存中紧接着一条指令被激发。
  2. 主机程序引发并处理异常。来自错误页面的所有跳转代码(包括商店首先触发异常的代码)都会失效。写访问再次启用。
  3. 异常处理程序返回,但在哪里?为什么,对于错误的指令,当然是,这是失效代码的一部分!呃 - 哦!

据我所知,有两种选择。要么忽略这个问题,要么有效地使修改其附近的指令的代码行为未定义,要么在异常处理程序中设置某种无效标志,并在每个存储区之后对其进行测试,如果测试失败,则释放到解释器中。

快速搜索建议QEMU might be using a similar mechanism来处理来宾存储器的修改,但我无法验证它。