Files
goldeneye-pc-port/src/sprintf.c
T
James Dansereau 1261389a5c fix(linux): thread a va_list* through _Printf (x86-64 SysV ABI) (#22)
* fix(linux): thread a va_list* through _Printf (x86-64 SysV ABI)

On the x86-64 System V ABI `va_list` is an array type, so a by-value
`va_list` parameter decays to a pointer. `_Printf()` was passing `&args`
(the address of that local pointer, not the argument list) to `_Putfld()`,
which does `va_arg(*args, ...)` — so `_Putfld` walked garbage and
segfaulted. Latent at `-Og`, fatal at `-O2` (PR #9), which is why the
Linux release build crashed at boot:

  bossInitMainthreadData (boss.c:247, the D121 per-level -m token sprintf)
    -> sprintf("-level_%c%c %s", ...) -> _Putfld -> SIGSEGV @ xprintf.c

Same UB class as D187. Windows x64's `va_list` is a scalar (`char*`), so
`&args` happened to work there — the Windows build is unaffected.

Fix: under `#ifdef PORT`, `_Printf` takes `va_list *` and passes it
straight to `_Putfld` (which already expects `va_list *`); `src/sprintf.c`
(the only PC caller — `rmon.c` is excluded from the build) passes `&args`
of a real `va_list` object. N64 build untouched (`#ifdef PORT`, and it
byte-matches the ROM).

Verified:
- Windows: build links, `-level_09` framediff 3/3 within golden threshold.
- Linux -O2: boot crash gone, now advances past bossInitMainthreadData
  into stage load (uncovers a separate pre-existing stan.c stack overflow
  during BUNKER1 prop setup — tracked separately).

* docs: D188 (xprintf va_list ABI) + D189 (stan.c stack-smash) findings + porting-notes D7
2026-09-03 20:12:56 -04:00

31 lines
654 B
C

#include <stdarg.h>
#include <ultra64.h>
/*#include <stddef.h>*/
char *proutSprintf(char *dst, const char *src, size_t count) {
return (char *) memcpy((u8 *) dst, (u8 *) src, count) + count;
}
int sprintf(char *dst, const char *fmt, ...) {
s32 written;
va_list args;
va_start(args, fmt);
#ifdef PORT
/* xprintf.c's _Printf takes a va_list* under PORT — see the comment there
* (x86-64 SysV va_list-is-an-array ABI). */
written = _Printf(proutSprintf, dst, fmt, &args);
#else
written = _Printf(proutSprintf, dst, fmt, args);
#endif
if (written >= 0) {
dst[written] = 0;
}
return written-1;
}