forked from Ports/goldeneye-pc-port
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
This commit is contained in:
1 parent
bde5e0bdfa
commit
1261389a5c
4 files changed
+55
No files matched your search
@@ -35,8 +35,23 @@ static char spaces[] = " ";
|
||||
static char zeroes[] = "00000000000000000000000000000000";
|
||||
|
||||
static void _Putfld(printf_struct *pf, va_list *pap, char code, char *ac);
|
||||
#ifdef PORT
|
||||
/* x86-64 System V makes va_list an array type, so a by-value va_list
|
||||
* parameter decays to a pointer: `&args` below would be the address of
|
||||
* _Printf's local pointer slot, not the argument list, and _Putfld()'s
|
||||
* `va_arg(*args, ...)` then walks garbage (crash seen at
|
||||
* bossInitMainthreadData -> sprintf -> _Putfld; same UB class as D187,
|
||||
* latent at -Og, fatal at -O2). Thread a real va_list* through instead.
|
||||
* The only PC caller is src/sprintf.c. The N64 build is semantically
|
||||
* unaffected (scalar va_list) but this file byte-matches the ROM, so the
|
||||
* change is guarded. */
|
||||
int _Printf(outfun prout, char *arg, const char *fmt, va_list *argsp)
|
||||
{
|
||||
#define args (*argsp)
|
||||
#else
|
||||
int _Printf(outfun prout, char *arg, const char *fmt, va_list args)
|
||||
{
|
||||
#endif
|
||||
printf_struct x;
|
||||
const char *s;
|
||||
char c;
|
||||
@@ -96,7 +111,11 @@ int _Printf(outfun prout, char *arg, const char *fmt, va_list args)
|
||||
x.length = 'L';
|
||||
++s;
|
||||
}
|
||||
#ifdef PORT
|
||||
_Putfld(&x, argsp, *s, ac);
|
||||
#else
|
||||
_Putfld(&x, &args, *s, ac);
|
||||
#endif
|
||||
x.width -= x.n0 + x.num_leading_zeros + x.part2_len + x.num_mid_zeros + x.part3_len + x.num_trailing_zeros;
|
||||
if (!(x.flags & FLAGS_MINUS))
|
||||
PAD(spaces, x.width);
|
||||
@@ -112,6 +131,9 @@ int _Printf(outfun prout, char *arg, const char *fmt, va_list args)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifdef PORT
|
||||
#undef args
|
||||
#endif
|
||||
|
||||
static void _Putfld(printf_struct *x, va_list *args, char type, char *buff)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,13 @@ 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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user