65816-llvm-mos/demos/gnoForkGlobal.c
2026-07-07 21:11:28 -05:00

57 lines
1.7 KiB
C

// Confirm the &global blocker for forked children: forming a 32-bit &global
// pointer reads DP $BE for the bank byte, and a forked child's fresh DP has $BE
// uninitialized (crt0 sets it only for the main thread). So a child that takes
// the address of a global and dereferences it through a real pointer should read
// from the WRONG bank -> garbage (or crash), unless/until a fork thunk sets $BE.
// Passing &gCanary through a noinline fn forces a genuine 32-bit pointer (not a
// near/absolute access the compiler could fold).
//
// 0x025060 0001 child entered
// 0x025062 xxxx value read back through &gCanary (0x1234 == $BE correct)
// 0x025064 E0DD child reached end (no crash)
// 0x025066 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 volatile uint16_t gCanary = 0x1234;
static uint16_t __attribute__((noinline)) deref16(volatile uint16_t *p) {
return *p; // deref a full 32-bit pointer
}
static void child(void) {
uint16_t v;
M(0x025060UL) = 0x0001;
v = deref16((volatile uint16_t *)&gCanary); // &global -> uses $BE bank byte
M(0x025062UL) = v; // 0x1234 if $BE right; else garbage
M(0x025064UL) = 0xE0DD;
_exit(0);
}
int main(void) {
int pid;
int st;
M(0x025060UL) = BAD;
M(0x025062UL) = BAD;
M(0x025064UL) = BAD;
M(0x025066UL) = BAD;
pid = fork((void *)child);
if (pid > 0) {
wait(&st);
M(0x025066UL) = 0x5050;
}
for (volatile unsigned long j = 0; j < 300000UL; j++) {
}
return 0;
}