【问题标题】:forth implementation with JIT write protection?第四次实施 JIT 写保护?
【发布时间】:2022-10-23 17:39:00
【问题描述】:

我相信苹果已经禁止在 ARM64 架构上同时写入和执行内存,请参阅:

mmap() RWX page on MacOS (ARM64 architecture)?

这使得移植像 jonesforth 这样的实现变得困难,它将生成的代码和生成它的代码(如 jonesforth.f 中的内置汇编程序)保持在同一段中。

我想我可以做一些事情,比如将用户空间从开始映射到这里作为'r-x',从这里到结束作为'rw-'。然后我必须在编译新单词时不断地重新映射内存,而我不能去修复以前的单词(我相信 SCODE 会利用它)。

您对如何处理此类限制有任何建议吗?

我想我应该研究在 M1 Mac 上运行的其他第四种实现。

【问题讨论】:

    标签: forth


    【解决方案1】:

    Forth 实现只有在生成应该立即执行的机器代码时才会出现写保护代码段的问题。如果它使用线程代码就没有这样的问题。所以下面假设 Forth 系统必须生成机器码。

    数据空间和代码空间

    1. 显然,您必须将代码空间与data space 分开。数据空间(至少可变区域,包括变量区域和数据字段),以及内部可变内存区域和可能的标题,应该映射到“rw-”段。代码空间应该映射到“r-x”段。

    2. 单词here ( -- addr )返回第一个可用于保留的单元格的地址,该单元格对于程序是可写的,它应该是总是在“rw-”段中。如果需要,您可以有一个内部字 code::here ( -- addr ) 来返回代码空间中的地址。

    3. 执行令牌的决定是速度和实现简单性之间的折衷(“r-x”段与“rw-”)。最简单的情况是,执行令牌由 'rw-' 段中的地址表示,然后execute 进行额外的解引用以获取代码的相应地址。

      代码生成

      在给定的条件下,我们应该将机器代码生成到“rw-”段中,但在执行此代码之前,该段应变为“r-x”。

      可能,最简单的解决方案是为每个新定义分配一个内存块,在完成时调整(最小化)块的大小并使其成为“r-x”。可能的缺点——由于页面大小(例如 4 KiB)和内存碎片造成的损失。

      code::here 开始更改主代码段的保护也意味着由于页面大小粒度造成的损失。

      另一个变体是将定义的创建分为两个阶段:

      1. 在编译定义期间在单独的“rw-”段中生成中间表示 (IR);
      2. 定义完成后,从IR生成主代码段的机器码,丢弃IR码。

        实际上,它也可能是第一阶段的机器码,然后它只是在第二阶段重新定位到另一个地方。

        在写入主代码段之前,您将其(或其部分)更改为“rw-”,然后将其恢复为“r-x”。

        翻译 IR 代码的子例程应该驻留在您不更改的另一个“r-x”段中。

        Forth 与生成代码的格式无关,在一个简单的系统中,只有少数定义“知道”生成什么格式。所以只有这些定义应该被改变来生成 IR 代码。如果您重新定位机器代码,您可能甚至不需要更改这些定义。

    【讨论】:

    • 我不会争辩说这可行。但不想再调用生成的语言了。这将是一项巨大的工作,只是要拥有一种在 FORTH 实际上擅长的方面要逊色得多的新语言。如果这是我能做的最好的,我自己会接受 M1 是一个糟糕的架构,无法在其中实现 FORTH,然后继续前进。我只是觉得很难过,没有办法为真正想要的开发人员关闭此保护方案,风险自负。
    • Forth-2012 标准允许标准 Forth 系统所描述的技术,并且一些系统采用了一些这样的技术(即,单独的代码空间、每个定义的自己的内存块、中间表示、代码重定位)。说他们没有实现 Forth 语言是没有道理的。
    猜你喜欢
    • 2011-07-06
    • 2020-05-30
    • 2014-02-11
    • 1970-01-01
    • 2019-09-28
    • 1970-01-01
    • 2014-04-21
    • 2010-09-08
    • 2012-10-18
    相关资源
    最近更新 更多