Tritwise 向右旋转和 Tritwise 疯狂操作抛出分段错误

Tritwise rotate right and tritwise crazy operation throws a segmentation fault

tritwise 操作、向右旋转和 crazy 操作无法正常运行并在 Malbolge 中抛出分段错误 compiler/interpreter。

在看到关于 Coding Challenges 和 Code Golf 的惊人答案后,我决定开始使用 Malbolge 编程,并且还要学习使用硬编程语言进行编程。

当我尝试输出固定字符时,我注意到 *p(在 Normalized Malbolge 中)在我尝试使用它们的大部分时间都抛出分段错误。

我尝试使用 Internet 查找字符串(在 Google 上)“'Malbolge' 疯狂操作 'segfaults'”和“'Malbolge' 向右旋转 'segfaults'”。我还尝试在不同的上下文中使用这些命令,发现如果没有输入(这不是我想要的).

我正在使用由 tio.run 托管的 online interpreter 或在线试用。

我尝试使用的代码:

归一化马尔博格:/*<

马尔博格:u&a

Try it online!

归一化马尔博格:/p*<v

马尔博格:u=%`M

Try it online!

归一化马尔博格:/pp<v

马尔博格:u=<`M

Try it online!

我希望 u&au=%`Mu=<`M 的输出不会抛出任何错误,但实际输出是一个分段错误。

确切错误: /srv/wrappers/malbolge: line 3: 21992 Segmentation fault (core dumped) /opt/malbolge/malbolge .code.tio < .input.tio 其中 21992 可以是任何数字(很可能是几千到一万)

我一直在调试器中单步执行 Malbolge 解释器,以便诊断这里发生了什么。我会说 "congratulations, you've discovered a bug in the Malbolge interpreter",但考虑到规范和解释器在其他方面不匹配(权威版本通常被视为解释器),而且这是 Malbolge,据我所知这是预期的行为。 (好吧,这可能实际上不是预期的行为,但是其他许多已被视为重要编程技术的功能也不是。)

Malbolge 将所有内容存储在一个大数组中,包括代码和数据。命令旨在在 运行ning 之后修改("encrypt",在 Malbolge 术语中)自身,但解释器并没有完全正确地实现它:它实际上做的是 运行命令,然后查看代码指针指向的地址并对其进行加密。这就是为什么跳转会加密跳转目标之前的指令,而不是跳转指令本身。

如果您 运行ning 的命令超出了 33 到 126(含)的范围,则该命令不是 运行(实际上,在我拥有的 Malbolge 解释器版本中,代码和数据指针也没有增加,这似乎不可避免地会导致无限循环;也许还有其他版本可以解决该问题)。这是一项重要的检查,因为加密例程只是通过索引查找 table 来工作; 33 到 126 范围之外的值将最终从数组之前或之后读取一些任意字节的内存。

不幸的是,因为代码和数据一起存储在一个大数组中,一个命令最终可能会在 运行s 中修改自身:它可能在 [=43 之前在 33 到 126 的范围内=]ning(从而导致安全检查成功),但在 运行ning 之后,它超出了范围,然后加密最终将执行查找的越界索引 table。 Malbolge 解释器是用 C 编写的,对于越界读取具有未定义的行为,但对于距离很远的读取,可能(但不保证)出现分段错误行为。

让我们看看代码 u&a:

会发生什么
Command   A     C     D   memory
  start   0     0     0   117,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
      / input   0     0   117,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt input   1     1   111,    38, 97, 29432, 98, 29431, 98, 29432, 97, ...
      * 39378   1     1   111, 39378, 97, 29432, 98, 29431, 98, 29432, 97, ...
encrypt 39378   2     2   111,   ???, 97, 29432, 98, 29431, 98, 29432, 97, ...

如您所见,您实际上并没有旋转加载到 A 中的输入;旋转操作从 D 指向的地址读取(而不是从 A),因此您正在旋转 38(内存位置 1 中 * 命令的内存表示)生成 39378。该值同时存储在 A 和内存位置 1 中。不幸的是,内存位置 1 是当前正在执行的命令,因此当需要对其进行加密时,解释器会执行一个错误的操作bounds read of the lookup table(试图在table中找到对应于39378的位置,它只覆盖了从33到126的范围),如果你幸运的话,这会产生一个分段错误。

这种行为在 "simple" Malbolge 程序中很可能发生,因为 C 和 D 以相同的值开始并以相同的速率增加。如果您希望旋转指令影响当前 运行ning 命令以外的其他内容,则必须以某种方式使它们不同步。最简单的方法通常是 j 命令(注意:使用结果数据指针可能不是特别容易,但至少它可能在代码指针以外的其他地方)。

顺便说一句,实际旋转用户输入比在您提供的示例程序中花费更多的精力。您必须先将其存储到内存中,唯一能够根据 A 的值写入内存的操作是 p,这需要所讨论的单元格已经具有适当的值(以避免丢失信息,这需要是 29524,这个值你必须通过 Malbolge 的算法产生,因为它不能作为原始程序的一部分输入,即使这样你最终也会在输入值中交换 0 个 trits 和 1 个 trits ).然后,您必须将数据指针发送回写入输入的单元格,以便您可以 运行 * 对其进行旋转。