Porting JonesForth to macOS v10.15 (Catalina)


I'm trying to make JonesForth run on a recent MacBook out of the box, just using Mac tools.

I started to convert everything 64 bits and attend to the Mac assembler syntax.

I got things to assemble, but I immediately run into a curious segmentation fault:

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


/* Assembler entry point. */
        .globl start
        .balign 16
        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!
cold_start:                     // High-level code without a codeword.
        .quad QUIT

QUIT is defined like this via macro defword:

        .macro defword
        .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

        // 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

When I run this, I get a segmentation fault the first time in the NEXT macro:

(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
->  0x100000698 <+24>: jmpq   *(%rax)
    0x10000069a <+26>: nopw   (%rax,%rax)

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

rax does point to what I think is the dereferenced address, DOCOL:

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

So one mystery is:

  1. Why does RAX point to DOCOL instead of QUIT? My guess is that the instruction was halfway executed and the result of the indirection was stored in rax. What are some good pointers to documentation?
  2. Why the segmentation fault?

I commented out the original segment setup code in the original that called brk to set up a data segment. Another [implementation] also did not call it at all, so I thought I could as well ignore this. Is there any magic on how to set up segment permissions with syscalls in a 64-bit binary on Catalina? The make command is pretty much the standard JonesForth one:

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

P.S.: Yes, I can get JonesForth to work perfectly in Docker images, but that's besides the point. I really want it to work in 64 bit on Catalina, out of the box.

asked on Stack Overflow May 19, 2020 by Klapaucius Klapaucius • edited Aug 4, 2020 by Peter Mortensen

1 Answer


The original code had something like

mov $cold_start,%rsi

And the Apple assembler complains about not being able to use 32 immediate addressing in 64-bit binaries.

So I tried

mov $cold_start(%rip),%rsi

but that also doesn't work.

So I tried

mov cold_start(%rip),%rsi

which assembles, but of course it dereferences cold start, which is not something I need.

The correct way of doing this is apparently

lea cold_start(%rip),%rsi

This seems to work as intended.

answered on Stack Overflow May 19, 2020 by Klapaucius Klapaucius • edited Aug 4, 2020 by Peter Mortensen

User contributions licensed under CC BY-SA 3.0