Skip to content

Commit b0b614f

Browse files
committed
Release v6.5: fix CallsiteClassifier funcmap parser for minified SoF.exe.json
Minified funcmaps since v6.0 only loaded ~23/2611 SoF.exe entries, breaking scaled UI caller detection; scan the full file and restore pretty-printed map.
1 parent 7d51a8f commit b0b614f

5 files changed

Lines changed: 2668 additions & 44 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## v6.5
4+
5+
### CallsiteClassifier — funcmap parser fix
6+
7+
- **Bug:** Since v6.0, `SoF.exe.json` was minified to a single line (~45 KB). `CallsiteClassifier` parsed funcmaps with `fgets` and assumed one function entry per line, so only **~23 of 2611** `SoF.exe` function starts were loaded at runtime.
8+
- **Impact:** Scaled UI/HUD caller detection (`caller_from`, hook callsite classification) failed for almost all `SoF.exe` call sites — wrong scaling paths and crashes on XP and other builds shipping `sof_buddy/funcmaps/`.
9+
- **Fix:** Load the whole funcmap file and scan for every `"rva"` entry regardless of JSON formatting (pretty-printed or minified).
10+
- **Data:** Re-pretty-printed `rsrc/funcmaps/SoF.exe.json` to match `pe_funcmap_gen` output.
11+
312
## v6.4
413

514
### Internal menus — UI Scale round-step layout

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.4
1+
6.5

hdr/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
Increment version using: ./increment_version.sh
88
*/
99

10-
#define SOFBUDDY_VERSION "6.4"
10+
#define SOFBUDDY_VERSION "6.5"

rsrc/funcmaps/SoF.exe.json

Lines changed: 2619 additions & 1 deletion
Large diffs are not rendered by default.

src/debug/callsite_classifier.cpp

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -81,32 +81,36 @@ inline Module identifyModuleFromHandle(HMODULE hmod) {
8181
return Module::Unknown;
8282
}
8383

84-
static inline bool parseUintAfterKey(const char *line, const char *key, uint32_t &out) {
85-
const char *p = strstr(line, key);
86-
if (!p) return false;
87-
p += strlen(key);
88-
// Skip spaces
89-
while (*p == ' ' || *p == '\t') ++p;
90-
// Read number (decimal)
91-
unsigned int val = 0;
92-
if (sscanf(p, "%u", &val) == 1) { out = val; return true; }
93-
// Try hex like 0x...
94-
if (sscanf(p, "0x%x", &val) == 1) { out = val; return true; }
95-
return false;
96-
}
97-
98-
static inline bool parseStringAfterKey(const char *line, const char *key, std::string &out) {
99-
const char *p = strstr(line, key);
100-
if (!p) return false;
101-
p += strlen(key);
102-
while (*p && *p != '"') ++p;
103-
if (*p != '"') return false;
104-
++p;
105-
std::string s;
106-
while (*p && *p != '"') { s.push_back(*p++); }
107-
if (*p != '"') return false;
108-
out = s;
109-
return true;
84+
static void parseFuncmapJson(const char *data, size_t len, ModuleMap &mm) {
85+
const char *p = data;
86+
const char *end = data + len;
87+
while (p < end) {
88+
const char *key = strstr(p, "\"rva\"");
89+
if (!key || key >= end) break;
90+
key = (const char*)memchr(key, ':', (size_t)(end - key));
91+
if (!key || key >= end) break;
92+
++key;
93+
while (key < end && (*key == ' ' || *key == '\t')) ++key;
94+
unsigned int rva = 0;
95+
if (sscanf(key, "%u", &rva) != 1) { p = key + 1; continue; }
96+
const char *objEnd = (const char*)memchr(key, '}', (size_t)(end - key));
97+
if (!objEnd) break;
98+
std::string name;
99+
const char *nameKey = strstr(key, "\"name\"");
100+
if (nameKey && nameKey < objEnd) {
101+
nameKey = (const char*)memchr(nameKey, ':', (size_t)(objEnd - nameKey));
102+
if (nameKey) {
103+
++nameKey;
104+
while (nameKey < objEnd && (*nameKey == ' ' || *nameKey == '\t')) ++nameKey;
105+
if (nameKey < objEnd && *nameKey == '"') {
106+
++nameKey;
107+
while (nameKey < objEnd && *nameKey != '"') name.push_back(*nameKey++);
108+
}
109+
}
110+
}
111+
mm.functions.push_back(Func{ rva, name });
112+
p = objEnd + 1;
113+
}
110114
}
111115

112116
static inline const char* moduleJsonLeaf(Module m) {
@@ -190,22 +194,15 @@ static inline void ensureLoadedFor(Module m, const char *dir) {
190194
f = fopen(path, "rb");
191195
if (!f) return;
192196
}
193-
char line[2048];
194-
uint32_t pendingRva = 0; bool haveRva = false; std::string pendingName;
195-
while (fgets(line, sizeof(line), f)) {
196-
uint32_t val;
197-
if (parseUintAfterKey(line, "\"rva\":", val)) { pendingRva = val; haveRva = true; }
198-
std::string nm;
199-
if (parseStringAfterKey(line, "\"name\":", nm)) { pendingName = nm; }
200-
// End of object '}' can occur on the same line as rva/name (e.g., { "rva": 4096 },)
201-
if (strchr(line, '}')) {
202-
if (haveRva) {
203-
mm.functions.push_back(Func{ pendingRva, pendingName });
204-
}
205-
haveRva = false; pendingRva = 0; pendingName.clear();
206-
}
207-
}
197+
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return; }
198+
long sz = ftell(f);
199+
if (sz <= 0 || sz > 32 * 1024 * 1024) { fclose(f); return; }
200+
if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return; }
201+
std::vector<char> buf((size_t)sz + 1);
202+
if (fread(buf.data(), 1, (size_t)sz, f) != (size_t)sz) { fclose(f); return; }
208203
fclose(f);
204+
buf[(size_t)sz] = '\0';
205+
parseFuncmapJson(buf.data(), (size_t)sz, mm);
209206
std::sort(mm.functions.begin(), mm.functions.end(), [](const Func&a, const Func&b){ return a.rva < b.rva; });
210207
mm.loaded = true;
211208
}

0 commit comments

Comments
 (0)