将 JonesForth 移植到 macOS v10.15 (Catalina)

Posted

技术标签:

【中文标题】将 JonesForth 移植到 macOS v10.15 (Catalina)【英文标题】:Porting JonesForth to macOS v10.15 (Catalina) 【发布时间】:2020-05-19 00:31:45 【问题描述】:

我正在尝试让 JonesForth 开箱即用地在最近的 MacBook 上运行,仅使用 Mac 工具。

我开始将所有内容都转换为 64 位并注意 Mac 汇编器语法。

我有东西要组装,但我立即遇到了一个奇怪的分段错误:


/* NEXT macro. */
        .macro NEXT
        lodsq
        jmpq *(%rax)
        .endm

...

/* Assembler entry point. */
        .text
        .globl start
        .balign 16
start:
        cld
        mov %rsp,var_SZ(%rip)           // Save the initial data stack pointer in FORTH variable S0.
        mov return_stack_top(%rip),%rbp // Initialise the return stack.
        //call set_up_data_segment

        mov cold_start(%rip),%rsi       // Initialise interpreter.
        NEXT                    // Run interpreter!
        .const
cold_start:                     // High-level code without a codeword.
        .quad QUIT

QUIT 通过宏 defword 定义如下:

        .macro defword
        .const_data
        .balign 8
        .globl name_$3
name_$3 :
        .quad $4                // Link
        .byte $2+$1             // Flags + length byte
        .ascii $0               // The name
        .balign 8               // Padding to next four-byte boundary
        .globl $3
$3 :
        .quad DOCOL             // Codeword - the interpreter
        // list of word pointers follow
        .endm

        // QUIT must not return (ie. must not call EXIT).
        defword "QUIT",4,,QUIT,name_TELL
        .quad RZ,RSPSTORE       // R0 RSP!, clear the return stack
        .quad INTERPRET         // Interpret the next word
        .quad BRANCH,-16        // And loop (indefinitely)

...more code

当我运行这个时,我第一次在 NEXT 宏中遇到了分段错误:

(lldb) run
There is a running process, kill it and restart?: [Y/n] y
Process 83000 exited with status = 9 (0x00000009)
Process 83042 launched: '/Users/klapauciusisgreat/jonesforth64/jonesforth' (x86_64)
Process 83042 stopped
* thread #1, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x0000000100000698 jonesforth`start + 24
jonesforth`start:
->  0x100000698 <+24>: jmpq   *(%rax)
    0x10000069a <+26>: nopw   (%rax,%rax)

jonesforth`code_DROP:
    0x1000006a0 <+0>:  popq   %rax
    0x1000006a1 <+1>:  lodsq  (%rsi), %rax
Target 0: (jonesforth) stopped.

rax 确实指向了我认为取消引用的地址,DOCOL:

(lldb) register read
General Purpose Registers:
       rax = 0x0000000100000660  jonesforth`DOCOL

所以一个谜团是:

    为什么 RAX 指向 DOCOL 而不是 QUIT?我的猜测是指令执行到一半,间接的结果存储在 rax 中。有哪些好的文档指南? 为什么会出现分段错误?

我注释掉了原文中调用brk设置数据段的原始段设置代码。另一个 [implementation] 也根本没有调用它,所以我想我也可以忽略它。关于如何在 Catalina 上的 64 位二进制文​​件中使用系统调用设置段权限有什么魔法吗? make 命令几乎是标准的 JonesForth 之一:

jonesforth: jonesforth.S
    gcc -nostdlib -g -static $(BUILD_ID_NONE) -o $@ $<

P.S.:是的,我可以让 JonesForth 在 Docker 图像中完美工作,但这不是重点。我真的希望它开箱即用地在 Catalina 上以 64 位运行。

【问题讨论】:

jmpq *(%rax) 跳转到地址rax 的地址,而不是rax 的地址。你确定这个额外的间接是正确的吗? 这应该是间接跳转吧。下一个宏将 rsi(冷启动)的地址读入 rax。冷启动本身包含 QUIT 的地址(此处未显示,它以 DOCOL 指针开头)。所以这里有两个谜团:1)为什么segfault和为什么rax现在指向DOCOL?如果它指向 QUIT,则意味着 jmp 立即中止,因为 QUIT(在数据段中)是段违规。但看起来 rax 已成功取消引用,然后发生了段错误。 是的,但是如果rax 指向jonesforth`DOCOL,那么jmp *(%rax) 会将jonesforth`DOCOL 的内容解释为地址并跳转到该地址。这似乎不正确。 对不起,我的评论被切断了。我认为 rax 现在指向 DOCOL,因为指令已部分执行。但我错了,我认为我需要单步完成程序集。任何想法如何用lldb做到这一点?在 Catalina 上安装 gdb 很麻烦。 总结一下,我错误地将x64 mov $cold_start,%rsi 替换为mov cold_start(%rip),%rsi ,它取消了对位置的引用。我不知道如何使用苹果汇编程序将立即地址移动到 x64 模式下的寄存器。而且我不知道如何在 Catalina 上加油。我问了一个单独的问题来单独讨论这个问题。再次感谢多一双眼睛! 【参考方案1】:

原来的代码是这样的

mov $cold_start,%rsi

Apple 汇编器抱怨无法在 64 位二进制文​​件中使用 32 位立即寻址。

所以我尝试了

mov $cold_start(%rip),%rsi

但这也行不通。

所以我尝试了

mov cold_start(%rip),%rsi

组装,但当然它取消引用cold start,这不是我需要的。

这样做的正确方法显然是

lea cold_start(%rip),%rsi

这似乎按预期工作。

【讨论】:

以上是关于将 JonesForth 移植到 macOS v10.15 (Catalina)的主要内容,如果未能解决你的问题,请参考以下文章

为折叠屏手机做准备:苹果移植MacOS系统到iPhone上!

将 Psyco 移植到 64 位可能存在哪些缺陷?

将数据序列化代码从 C++ linux/mac 移植到 C++ windows

如何将你的Sprite Kit游戏从iOS移植到Mac OS X平台

从 macOS 应用程序打印 PDF - 将上下文定义为 PDFDocument

Swift macOS Firebase