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