// Pin down the LAST forked-child blocker: an indirect call (function pointer).
// clang routes indirect calls through __jsl_indir, whose target slot is selected
// by a single global self-modifying operand __jsl_indir_op that crt0 patches to
// the MAIN thread's DP+$B8.  A forked child stores its target to its OWN (fresh)
// DP+$B8, but __jsl_indir still reads through the parent's operand -> the child's
// indirect call dispatches through the WRONG direct page.  gFp is volatile so the
// call is a genuine indirect dispatch, not a devirtualized direct call.
//
//   0x025060  0001  child entered
//   0x025062  BEEF  child is ABOUT to make the indirect call (crash localizer)
//   0x025064  0001  gFp(41)==42  (indirect call returned correctly)
//   0x025066  E0DD  child reached end (no crash/wild-jump)
//   0x025068  5050  parent reaped the child
#include <stdint.h>

extern int  fork(void *subr);
extern int  wait(int *status);
extern __attribute__((noreturn)) void _exit(int status);

#define M(a)  (*(volatile uint16_t *)(a))
#define BAD   0xDEAD


static uint16_t addOne(uint16_t x) {
    return x + 1;
}


static uint16_t (*volatile gFp)(uint16_t) = addOne;


static void child(void) {
    uint16_t r;
    M(0x025060UL) = 0x0001;
    M(0x025062UL) = 0xBEEF;              // reached the indirect-call site
    r = gFp(41);                         // indirect dispatch via __jsl_indir
    if (r == 42) {
        M(0x025064UL) = 0x0001;
    }
    M(0x025066UL) = 0xE0DD;              // survived the indirect call
    _exit(0);
}


int main(void) {
    int pid;
    int st;

    M(0x025060UL) = BAD;
    M(0x025062UL) = BAD;
    M(0x025064UL) = BAD;
    M(0x025066UL) = BAD;
    M(0x025068UL) = BAD;

    pid = fork((void *)child);
    if (pid > 0) {
        wait(&st);
        M(0x025068UL) = 0x5050;
    }
    for (volatile unsigned long j = 0; j < 300000UL; j++) {
    }
    return 0;
}
