mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
List the compound physics shape's children in the crash report for the 1.2.1 fault after the material lookup (#705)
On 1.2.1 the game asks a compound physics shape for its first child's material at main+0xe056d0 and reads the result without a check. At the reported crash the shape had none of its 125 children left, although every child is filled when the shape is built. The crash handler now prints that shape's live bits and each record's shape pointer and name, which tells removed children from switched-off ones and names the object. It runs only after the crash. --probe-kernel builds a fake shape in a crashing child and checks the listing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3d386fe37e
commit
4c4bd8c67a
2 files changed
+79
No files matched your search
@@ -10,6 +10,7 @@
|
||||
#include "cfg.h"
|
||||
#include "host.h"
|
||||
#include "kernel.h"
|
||||
#include "layout.h"
|
||||
#include "platform/platform.h"
|
||||
|
||||
#define STACK 0x10000
|
||||
@@ -744,7 +745,36 @@ static void test_platform(void) {
|
||||
static uint64_t crash_host_words[6] = {0, 0, 0x5ca1ab1e0c0de001ull, 0, 0, 0};
|
||||
static uint64_t crash_guest_words[6] = {0, 0, 0x5ca1ab1e0c0de002ull, 0, 0, 0};
|
||||
|
||||
// The compound shape note (main.c crash_compound_shape, 1.2.1): a shape of 3 records whose child 0 has no shape, child
|
||||
// 1 has one but its live bit is clear, child 2 is live and filled.
|
||||
static uint64_t crash_shape[0x1a0 / 8], crash_records[3 * 0x78 / 8], crash_child_shape;
|
||||
|
||||
static void crash_shape_fill(Cpu *c) {
|
||||
uint8_t *s = (uint8_t *)crash_shape, *r = (uint8_t *)crash_records;
|
||||
static const char *const names[3] = {"PartA", "PartB", "PartC"};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint64_t name = (uint64_t)(uintptr_t)(r + i * 0x78 + 0x14);
|
||||
memcpy(r + i * 0x78 + 8, &name, 8);
|
||||
memcpy(r + i * 0x78 + 0x14, names[i], strlen(names[i]) + 1);
|
||||
}
|
||||
uint64_t child = (uint64_t)(uintptr_t)&crash_child_shape, vt = MOD_MAIN_BASE + 0x45e1218;
|
||||
uint64_t rec = (uint64_t)(uintptr_t)crash_records;
|
||||
uint32_t n = 3, bits = 0x5;
|
||||
memcpy(r + 0x78, &child, 8);
|
||||
memcpy(r + 2 * 0x78, &child, 8);
|
||||
memcpy(s, &vt, 8);
|
||||
memcpy(s + 0x78, &n, 4);
|
||||
memcpy(s + 0x80, &rec, 8);
|
||||
memcpy(s + 0x88, &bits, 4);
|
||||
memcpy(s + 0x190, &n, 4);
|
||||
c->x[20] = (uint64_t)(uintptr_t)crash_shape;
|
||||
c->x[21] = 0x1000038;
|
||||
c->x[30] = MOD_MAIN_BASE + 0xe056d4;
|
||||
}
|
||||
|
||||
static void crash_child_main(Cpu *c) {
|
||||
const char *mode = getenv(CRASH_CHILD_ENV);
|
||||
if (mode && !strcmp(mode, "shape")) crash_shape_fill(c);
|
||||
c->x[19] = (uint64_t)(uintptr_t)&crash_guest_words[2];
|
||||
c->sp -= 0x100; // a frame on the guest stack, as a crashing function has
|
||||
__asm__ volatile("movq %0, %%rbx\n\tmovq 0x10, %%rax" : : "r"(&crash_host_words[2]) : "rax", "rbx", "memory");
|
||||
@@ -779,6 +809,18 @@ static void test_crash_report(void) {
|
||||
"crash report: no memory commit line");
|
||||
printf("crash report: exit code %d, host registers, code bytes, memory at rbx and x19, guest stack, commit %s\n", code,
|
||||
failures ? "checked with failures" : "present");
|
||||
if (rt_game_version != 121) return;
|
||||
code = plat_run_self(CRASH_CHILD_ENV, "shape", out, sizeof out, 30000);
|
||||
CHECK(report_line(out, " compound shape ", line, sizeof line) && strstr(line, "3 children, 3 records") &&
|
||||
strstr(line, "live bits: 00000005"),
|
||||
"crash report: no compound shape line (%s)", strstr(out, "compound shape") ? line : "none");
|
||||
CHECK(report_line(out, "child 0:", line, sizeof line) && strstr(line, "shape 0 \"PartA\""),
|
||||
"crash report: child 0 should have no shape (%s)", line);
|
||||
CHECK(report_line(out, "child 1:", line, sizeof line) && strstr(line, "(off) \"PartB\""),
|
||||
"crash report: child 1 should be off (%s)", line);
|
||||
CHECK(report_line(out, "child 2:", line, sizeof line) && !strstr(line, "(off)") && strstr(line, "\"PartC\""),
|
||||
"crash report: child 2 should be live (%s)", line);
|
||||
printf("crash report, compound shape: exit code %d, %s\n", code, failures ? "checked with failures" : "children listed");
|
||||
}
|
||||
|
||||
int probe_kernel(void) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "cfg.h"
|
||||
#include "host.h"
|
||||
#include "layout.h"
|
||||
#include "platform/platform.h"
|
||||
#include "sha256.h"
|
||||
|
||||
@@ -284,6 +285,41 @@ static void crash_memory(const PlatCrash *x, const Cpu *c) {
|
||||
(unsigned long long)s[i + 1], (unsigned long long)s[i + 2], (unsigned long long)s[i + 3]);
|
||||
}
|
||||
|
||||
// 1.2.1: a fault right after a compound physics shape was asked for its first child's material (the call returning to
|
||||
// main+0xe056d4 reads the result without a check). The shape (vtable main+0x45e1218, built by main+0x7359fc) is in x20
|
||||
// there. It returns null when no child slot is both live (bitmap at +0x88) and filled (shape pointer at +0 of the
|
||||
// 0x78-byte records at +0x80). Every child is filled when the shape is built, so the list tells children removed later
|
||||
// (null shape) from children switched off (clear bit), and the names name the object. Runs only in the crash handler.
|
||||
static void crash_compound_shape(const Cpu *c) {
|
||||
if (!c || rt_game_version != 121 || c->x[30] != MOD_MAIN_BASE + 0xe056d4) return;
|
||||
uint64_t shape = c->x[20];
|
||||
if (!readable(shape, 0x1a0) || LD64(shape) != MOD_MAIN_BASE + 0x45e1218) {
|
||||
rt_logf(" compound shape: x20 %#llx is not one\n", (unsigned long long)shape);
|
||||
return;
|
||||
}
|
||||
uint32_t children = LD32(shape + 0x190), records = LD32(shape + 0x78);
|
||||
uint64_t rec = LD64(shape + 0x80);
|
||||
rt_logf(" compound shape %#llx (holder %#llx): %u children, %u records at %#llx, live bits:",
|
||||
(unsigned long long)shape, (unsigned long long)(c->x[21] - 0x38), children, records,
|
||||
(unsigned long long)rec);
|
||||
uint32_t n = children > 0x400 ? 0x400 : children; // the bitmap holds 0x400 bits
|
||||
for (uint32_t w = 0; w < (n + 31) / 32; w++) rt_logf(" %08x", LD32(shape + 0x88 + 4 * w));
|
||||
rt_log_raw("\n", 1);
|
||||
if (records > 0x400 || !readable(rec, (uint64_t)records * 0x78)) return;
|
||||
for (uint32_t i = 0; i < records; i++) {
|
||||
uint64_t r = rec + (uint64_t)i * 0x78, child = LD64(r), s = LD64(r + 8);
|
||||
char name[33];
|
||||
size_t k = 0;
|
||||
for (; k < 32 && readable(s + k, 1); k++) {
|
||||
name[k] = *(const char *)(uintptr_t)(s + k);
|
||||
if (!name[k]) break;
|
||||
}
|
||||
name[k] = 0;
|
||||
int live = LD32(shape + 0x88 + 4 * (i >> 5)) >> (i & 31) & 1;
|
||||
rt_logf(" child %3u: shape %#llx%s \"%s\"\n", i, (unsigned long long)child, live ? "" : " (off)", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void crash_handler(const PlatCrash *x) {
|
||||
static volatile int32_t busy;
|
||||
if (plat_xchg32(&busy, 1)) plat_exit(71);
|
||||
@@ -349,6 +385,7 @@ static void crash_handler(const PlatCrash *x) {
|
||||
rt_logf(" not on a guest thread\n");
|
||||
}
|
||||
crash_memory(x, c);
|
||||
crash_compound_shape(c);
|
||||
kern_free_cores_note();
|
||||
fflush(stderr);
|
||||
plat_exit(71);
|
||||
|
||||
Reference in new issue
Block a user