linux-runtime: BUNKER1 boots + renders on Linux

First runtime bring-up on Linux (Ubuntu, gcc-14). -level_09 now renders
past frame 1200 crash-free with a clean shutdown, matching Windows.

- port/src/romdata.c: no-PIE loads the image at 0x400000, so the D34
  'clear low 32 bits of a symbol address' gives 0 -> false 'image base not
  4GiB-aligned' abort. The ANIM_DATA_* lvalues are only ever address-taken
  (never dereferenced), so fall back to a fixed 4GiB-aligned sentinel
  (0x100000000) for g_pc_animdata_base.
- port/src/main.c: the 'image must load at 0x140000000' assertion is
  PE-specific (.refptr base relocations); ELF no-PIE resolves dram_syms.s
  absolute symbols literally regardless of load address. Guard it to
  PLATFORM_WINDOWS.
- port/src/libultra.c: mmap game-thread stacks with MAP_32BIT so they sit
  in the low 2GiB. The decomp truncates stack-buffer pointers to u32 (e.g.
   in texLoad); glibc pthread stacks live above 4GiB and
  those truncations corrupt. One fix covers the whole idiom class. Windows
  pthread stacks are already low.
- src/game/image.c: belt-and-braces #ifdef PORT -- align compbuffer at
  uintptr_t width (identical result on N64).
- CMakeLists.txt: drop the -Ttext-segment experiment (needs -mcmodel=large;
  breaks R_X86_64_32 relocs) -- comment only.

Windows: build 240/241, -level_09 crash-free past frame 1440.
This commit is contained in:
James Dansereau committed 2026-09-02 22:50:56 -04:00
1 parent 875b3c7653
commit fe3d543647
5 files changed
+53 -4

No files matched your search

+4
View File
@@ -283,6 +283,10 @@ elseif(UNIX AND NOT APPLE)
# and link no-PIE so the image keeps its preferred load address.
add_compile_options(-fno-pie)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
# (No -Ttext-segment: pushing the image above 4GiB needs -mcmodel=large and
# breaks the R_X86_64_32 relocs. romdataInit handles the low no-PIE base
# 0x400000 by using a fixed 4GiB-aligned g_pc_animdata_base sentinel — the
# ANIM_DATA_* lvalues are only ever address-taken, never dereferenced.)
endif()
# port/shim first: PC-only shadow headers (bondtypes.h, bondconstants.h,
+31
View File
@@ -334,6 +334,29 @@ void osCreateThread(OSThread *t, OSId id, void (*entry)(void *), void *arg,
t->state = OS_STATE_STOPPED;
}
#if !defined(_WIN32)
#include <sys/mman.h>
/* The decomp aligns/compares stack-buffer pointers by truncating to u32
* (e.g. `(u32)compbuffer` in image.c texLoad) — an N64 assumption. glibc
* puts pthread stacks above 4 GiB, so those truncations corrupt. Force each
* game-thread stack into the low 2 GiB with MAP_32BIT so every such idiom
* works exactly as on the console. Windows pthread stacks are already low. */
static void *portAllocLowStack(size_t sz)
{
#if defined(MAP_32BIT)
void *p = mmap(NULL, sz, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT
#if defined(MAP_STACK)
| MAP_STACK
#endif
, -1, 0);
if (p != MAP_FAILED)
return p;
#endif
return NULL;
}
#endif
void osStartThread(OSThread *t)
{
PortThread *pt = portFind(t);
@@ -344,7 +367,15 @@ void osStartThread(OSThread *t)
pthread_attr_t attr;
int rc;
pthread_attr_init(&attr);
#if !defined(_WIN32)
void *lowStack = portAllocLowStack(PORT_THREAD_STACK);
if (lowStack)
pthread_attr_setstack(&attr, lowStack, PORT_THREAD_STACK);
else
pthread_attr_setstacksize(&attr, PORT_THREAD_STACK);
#else
pthread_attr_setstacksize(&attr, PORT_THREAD_STACK);
#endif
rc = pthread_create(&pt->th, &attr, portThreadWrapper, pt);
pthread_attr_destroy(&attr);
if (rc != 0) {
+6
View File
@@ -115,6 +115,7 @@ int main(int argc, char **argv)
* loader loads at 0x140000000 and those relocations are no-ops; if we
* ever got relocated, every such slot would be silently corrupted.
* Fail loudly instead. */
#if defined(PLATFORM_WINDOWS)
if (sysImageBase() != 0x140000000ul) {
sysLogPrintf(LOG_ERROR,
"image loaded at %p, expected preferred base 0x140000000; "
@@ -122,6 +123,11 @@ int main(int argc, char **argv)
(void *)sysImageBase());
return 1;
}
#else
/* Linux/ELF no-PIE: dram_syms.s absolute symbols resolve to their literal
* values independent of the image base (no .refptr indirection), so the
* load address is not constrained. sysImageBase() is a stub here anyway. */
#endif
/* 2b. Reserve the N64-DRAM region: s32-safe view @ 0x70000000 (cfb_16,
* mempools) + KSEG0 mirror @ 0x80000000 (see port/src/dram.c). */
+5 -4
View File
@@ -157,10 +157,11 @@ int romdataInit(void)
uintptr_t p = (uintptr_t)&probe;
uintptr_t hi = p & ~(uintptr_t)0xFFFFFFFFu;
if (hi == 0) {
sysLogPrintf(LOG_ERROR, "romdataInit: image base 0x%llX is not "
"4GiB-aligned; ANIM_DATA_* offsets would be wrong",
(unsigned long long)p);
return -1;
/* Low image base (Linux no-PIE loads at 0x400000). The ANIM_DATA_*
* lvalues are only ever address-taken — `(s32)&ANIM_DATA_x` yields
* the offset, never a dereference — so g_pc_animdata_base just has
* to be some 4GiB-aligned address whose low 32 bits are zero. */
hi = (uintptr_t)0x100000000ull;
}
g_pc_animdata_base = (u8 *)hi;
}
+7
View File
@@ -2435,7 +2435,14 @@ void texLoad(s32 *updateword, struct texpool *pool)
if (tex == NULL)
{
#ifdef PORT
/* compbuffer is a stack array; (u32) truncates the 64-bit host stack
* address (glibc pthread stacks live above 4GiB). Align at full
* pointer width -- same result on N64, correct on the port. */
alignedcompbuffer = (u8 *) (((uintptr_t)compbuffer + 0xF) & ~(uintptr_t)0xF);
#else
alignedcompbuffer = (u8 *) (((u32)compbuffer + 0xF) >> 4 << 4);
#endif
if (alignedcompbuffer);
if (tex);