将 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上!
将数据序列化代码从 C++ linux/mac 移植到 C++ windows
如何将你的Sprite Kit游戏从iOS移植到Mac OS X平台