//go:build linux package main import "testing" // TestModuleSpanExtendsThroughAnonymousWineMapping is a regression test for // what running against a real Proton build surfaced: Wine's PE loader maps // only the PE header as a real file-backed mapping, and the rest of the // module — .text/.rdata/.data, everything the AOB signatures live in — // comes right after as one large anonymous mapping with no path. Without // the extension, findModuleBase would hand the scanner a few KB of PE // header and nothing else, and every signature scan would fail forever. func TestModuleSpanExtendsThroughAnonymousWineMapping(t *testing.T) { lines := []mapLine{ {start: 0x10000, end: 0x12000, path: ""}, // unrelated anonymous mapping before it {start: 0x140000000, end: 0x140001000, path: "/games/ELDEN RING/Game/eldenring.exe"}, // PE header, file-backed {start: 0x140001000, end: 0x145e0e000, path: ""}, // the actual module body, anonymous {start: 0x555591e6c000, end: 0x5555975f4000, path: "[heap]"}, } base, end, path, ok := moduleSpan(lines, "eldenring.exe") if !ok { t.Fatal("expected a match") } if base != 0x140000000 { t.Errorf("base = 0x%X, want 0x140000000", base) } if end != 0x145e0e000 { t.Errorf("end = 0x%X, want 0x145e0e000 (the header alone would give 0x140001000)", end) } if path != "/games/ELDEN RING/Game/eldenring.exe" { t.Errorf("path = %q, want the header's own path", path) } } // TestModuleSpanDoesNotSwallowAFollowingNamedMapping guards the boundary // of the fix above: the extension must stop at the first mapping that has // its own path, even if it's perfectly contiguous, so it can never merge // a genuinely different module into the span. func TestModuleSpanDoesNotSwallowAFollowingNamedMapping(t *testing.T) { lines := []mapLine{ {start: 0x140000000, end: 0x140001000, path: "/games/ELDEN RING/Game/eldenring.exe"}, {start: 0x140001000, end: 0x140003000, path: ""}, // module body, anonymous {start: 0x140003000, end: 0x140010000, path: "/games/ELDEN RING/Game/d3d12.dll"}, // a different, unrelated module } _, end, _, ok := moduleSpan(lines, "eldenring.exe") if !ok { t.Fatal("expected a match") } if end != 0x140003000 { t.Errorf("end = 0x%X, want 0x140003000 (must stop before d3d12.dll)", end) } } // TestModuleSpanCoversMultipleNamedSegments keeps the ELF-style layout // (several file-backed segments sharing the module's own name) working // too, in case a future Wine/Proton build maps it that way instead. func TestModuleSpanCoversMultipleNamedSegments(t *testing.T) { lines := []mapLine{ {start: 0x140000000, end: 0x140001000, path: "/games/ELDEN RING/Game/eldenring.exe"}, {start: 0x140001000, end: 0x140002000, path: "/games/ELDEN RING/Game/eldenring.exe"}, {start: 0x140002000, end: 0x140003000, path: "/games/ELDEN RING/Game/eldenring.exe"}, } base, end, _, ok := moduleSpan(lines, "eldenring.exe") if !ok || base != 0x140000000 || end != 0x140003000 { t.Fatalf("got base=0x%X end=0x%X ok=%v, want 0x140000000-0x140003000", base, end, ok) } } func TestModuleSpanNotFound(t *testing.T) { lines := []mapLine{ {start: 0x1000, end: 0x2000, path: "/games/somethingelse.exe"}, } if _, _, _, ok := moduleSpan(lines, "eldenring.exe"); ok { t.Fatal("expected no match") } }