Repository navigation
Firmware Reversing
This page is about the RNS510 head unit's own firmware (the
PowerPC/VxWorks application that reads the map database this wiki
otherwise documents), not the map disc format itself. Source: a real
Continental/VW factory "Software Loading" (SWL) CD, VwSwIndex 5238,
extracted at 5238_ALL, whose APPS\SILVER_1\RNSMIDEC\PROG\FHDD6.FLI
(85,658,916 bytes) is the actual application image flashed onto the
unit. Reader/analysis code: research/swl_5238_reader.py.
FHDD6.FLI splits into 3 regions: 0-24MB (native PowerPC code +
debug strings — this page's focus), 24-76MB (Java classes),
76-85.6MB (VxWorks kernel/BSP — see
Theft Protection and the CAN Gateway
for TpPvVerifyPin, the one thing this project has directly reached
inside it). Compiler: GNU C gcc-2.96 (2.96+ MW/LM) 19990621 AltiVec VxWorks 5.5.
All addresses are file offsets into FHDD6.FLI unless marked VA.
VA = file_offset + 0xf688dcf4 (see below).
The application processor runs VxWorks 5.5 hosting an embedded
Java VM (Insignia Jeode EVM). The touchscreen UI is a Java
application (packages vdo.rns.*, vdo.most.*, vdo.nav.*,
vdo.bsl.* — a Siemens VDO/Continental in-house codebase). The actual
navigation/routing engine is native PowerPC code, internally named
"navicore" (codename "e60" — see the real internal build path
F:\Entwicklung\e60-tools\CodingTest\cp2\PPC603gnu found in
CTEST.OUT's own DWARF debug info, HOST BSP and Vehicle Coding),
running on a Freescale/Motorola MPC5200/5200B SoC, built with a
Wind River GCC 2.96 cross-toolchain. All 3 layers — native navicore
code, the Java class store, and the VxWorks kernel/BSP — are packed
into the one FHDD6.FLI file described above.
A real, complete on-disk module inventory for the live system,
recovered from a real flashed maintenance script (WA/DEL_OUT.WSH,
"Delete out files acc. to CR 54634") that lists every real loadable
.out object it tidies up, by its real full path — the clearest
single real confirmation of how the running system is actually laid
out on /tffs0/: lib/mm/ (multimedia) holds mostgen.out (MOST-bus
protocol generation), madMM.out, WMADEC.out (WMA audio decoder),
MpegAtaSwitcher.out, mm_drives.out, dab.out, util.out,
servicebroker.out, micromedia.out/micromedia_wma.out,
mpeg4_celp_lib.out/mpeg4_celp_micromedia.out (MPEG-4 CELP speech
codec — likely TTS/voice-prompt audio), recorder.out,
msdcontrol.out; lib/pos/pos.out (positioning); lib/arriba/ navcore.out — a SECOND real internal codename for the navigation
engine, "arriba", alongside the "e60"/"navicore" name already known
from DWARF paths below — this one is the real deployed library path,
not a build-tree path; lib/kernel/libDelayImage.out/fileMgr.out;
plus data/SysCfgConfig, a real system-config data file sitting
alongside the code.
2 satellite MCUs identified (checked against real datasheets —
neither has anywhere near enough RAM to run the UI/nav engine itself,
but likely handle sub-systems): ST10F276E (STMicroelectronics,
16-bit, 64MHz, 68KB RAM, dual CAN) — matches the size of
VUCI\...\GATEWAY.FLI/GWBOOTL.FLI; confirmed the real MOST-bus/CAN
gateway controller (fully explored on
Theft Protection and the CAN Gateway,
including a real disassembler for this exact chip). TMS470MF04207
(TI ARM Cortex-M3 automotive safety MCU, 24KB RAM, dual CAN) — likely
tied to the SYS_CODE_SYSTEM_SAFE/immobilizer-style security-code
subsystem seen in the menu tree below; unconfirmed.
The full menu tree, recovered from compiled class names: every
screen/overlay the UI can show is a vdo/rns/ui/Menu_<NAME>Rules or
MenuNet_<NAME>Rules class — 86 unique screens total, spanning
Startup/System, Navigation/Map, Radio/Tuner, Media/Audio, Video,
Phone, Traffic/TMC, Setup, and Test/Service (TESTMODE,
TESTMODE_SYSTEM, TESTMODE_DAB_MONITOR_MAIN, SERVICE — likely
technician diagnostic screens, not investigated further). Notably
absent: no Climate Control or Parking Sensor screen exists anywhere
in the menu tree — those subsystems only have backend status-
integration classes (audio ducking, icons), never a navigable screen,
in this firmware build. A real destination-entry UI package also
exists (vdo/rns/app/nav/std/ctrls/addressentry/ —
AECitySuggestionList, AEStreetSuggestionList, AESpellerType, a
letter-by-letter spelling control) reached through a generic native
search bridge (EdalWrapper*, also used for POI/along-route lookups)
— corroborating, not proving, that a real on-unit search screen like
this wiki's own road/city name-search catalog could plausibly serve;
its exact backing file can't be confirmed (see "romized bytecode"
below).
UI customization — a real, tested wall: most Java class bodies use
a proprietary "romized"/pre-linked constant-pool format from the
embedded JVM's ROM-build toolchain, not standard javac bytecode —
only trivial classes (interfaces with no real method bodies) survive
in standard, decompilable form. This blocks decompiling/patching the
interesting UI logic (ClimateControlAdapter, SystemBundle, etc.)
with normal tools. A separate attempt to reach the same logic via the
native code's own parsing routines also hit a wall at the time (no
load base recoverable for the 0-24MB region) — since resolved for the
1.2MB-9MB sub-range, see "The load base" below, though that doesn't
by itself unlock the romized Java layer. Net effect: UI-level
modding (new buttons/screens) is not achievable with this project's
current tools — this is why the project's own practical focus is map
editing and firmware/coding reverse-engineering, not UI modding.
The Extensions Database (EDB) — a real, limited customization API:
vdo.nav.api.edb.* is a JDBC-like client to an embedded SQLite engine
in the native navicore process (confirmed via literal SQL strings like
INSERT OR IGNORE INTO Tiles VALUES (?1,?2,?3,?4,?5)). Clients can
only call a fixed catalog of precompiled statement templates, not
arbitrary SQL. Confirmed real use: POI databases (.db3 files under
EDB/POI/, matching the map disc's own EDB\POI folder — see
config/create_cd and the Disc-Root Subsystems),
satellite-radio song tagging, weather/traffic tile caching. Schema is
point data only (name/lat/lon) — no road geometry or routing
fields, so this cannot be used to add roads, only custom POIs
("Personal POI" is an explicit, supported list type).
Found while tracing mv_convert_slot_nr
and mv_lefthand_traffic (both call
into a then-unknown CArr_AdasMain class) and confirmed at scale by
searching the full symbol catalog for related names: this codebase
contains a real, substantial Advanced Driver Assistance System
integration, previously entirely unmentioned anywhere on this wiki.
Real, confirmed class/function names (all under 9MB, several hundred
real symbol-table entries, not individually catalogued here):
-
Adas_Most_Dispatcher— a real, dedicated MOST-bus dispatcher class for this subsystem specifically (SendToMOST,SendEvent,DumpMessage,GetLocalDeviceId,GetMostAvailable,GetFBlockAvailable,SetTransmissionMode/GetTransmissionMode,SetDispatcherState,Instance/RemoveInstance— a singleton) — the same general real "FBlock available"/"local device id" vocabulary already confirmed on the realConfigFblockTest.cprotocol documented on RNS510 Serial Console Commands, now shown to be a shared pattern across multiple real MOST FBlocks, not unique to vehicle coding. -
CArr_AdasMain— the real main ADAS task/controller class:SendToMOST/Send,Update,SetState(takes a realADAS_MAIN_STATEenum),Deactivate,RetrieveNavSysInfData,UpdateSettingsDueToDVDChange,initializePartlyDatabase/cleanupPartlyDatabase,CallbackFunction. -
AdasTools— a real utility class for unpacking datagrams received from the ADAS module:UnPackNavGraphDatagram,UnPackNavSysInfDatagram,UnPackNavGraphResyncDatagram,UnPackNavGraphMatchDatagram, plus angle-transform helpers (ReverseTransformAngle,TransformHeadingAngleRelative) and severalDump*diagnostic functions (one,DumpNavGraph, now traced) — strong evidence the real wire format is a family of namedNAV_GRAPH/NAV_SYS_INF/NAV_GRAPH_RESYNC/NAV_GRAPH_MATCHstruct types (seen directly as real parameter types in the symbol table). -
AdasSpeedTime/AdasEntranceExit/AdasRouteObject/AdasMessage/AdasDBAccess(already partly known fromResolveUnsetNodeID) — real, separate helper classes;AdasDBAccess::GetStreetTypeFromDatabaseandAdasEntranceExit::GetStreetTypeForRamptie this subsystem directly into the road-network/street-type data this wiki otherwise documents from the map-disc side. -
A separate, parallel "VAdas" name family —
CfcTypeVadasOpOrTimePt/CfcTypeVAdasTimeDuration/CfcTypeVAdasTimeStart/CfcTypeVAdasSpeedLimit— real classes for time-windowed, direction-aware speed limits (ADAS_SPEED_DIRECTION,PSD_getAdasSpeedLimit, see0x78834c) and day/week/month-scoped validity windows (IsValidWeek/IsValidYear/IsValidHourAndMinute/IsValidExactWeekAndWeekDay) — consistent with real variable/time-restricted speed-limit signage (school zones, time-of-day limits), not just static posted limits. -
ElectronicHorizon— a real, previously-uncatalogued class found viaregisterBlocking(CArr_DYNA_Main::registerBlocking, confirmed callingElectronicHorizon::Draw(const MapPoint&)). "Electronic Horizon" is a genuine, well-known real-world ADAS term — predictive road-ahead data (curvature, speed limits, intersections some distance ahead) feeding things like adaptive cruise control and predictive gear shifting on real vehicles. Only this one method found under 9MB; a real, concrete, named lead for a future session, not yet followed further.
Net read, revised: the RNS510 firmware has a real, substantial,
previously undocumented integration with an external ADAS-related
module communicating over the MOST bus through its own dedicated
FBlock dispatcher, feeding speed-limit and navigation-graph data into
the existing navigation/TMC pipeline (TIMEx_FlowExplorer,
CArr_NavigationMain) confirmed by
DumpNavGraph and
0x78834c, with real dynamic road-blocking
registration (registerBlocking) tying in genuine floating-point
distance math, actual SQLite, and a named ElectronicHorizon concept.
The earlier "plausibly a forward-facing camera unit doing
traffic-sign recognition" guess is retracted — it went beyond what
the evidence supports, and sits poorly against this head unit
generation's known real capabilities: the RNS-510 is a ~2007-2008,
pre-MQB-era VW "Silver" platform unit, well-documented as using static
NAVTEQ-era map data with no onboard camera/radar sensor fusion and no
path to display camera/radar-derived driver-assist data. Two things
point away from live camera input specifically: PSD_getAdasSpeedLimit
takes a direction parameter (the shape of a direction-dependent
map-attribute lookup, not a live sign-detection report), and the
complete 1,573-name whole-file
reloc-confirmed catalog contains no
camera/video-decoder class whatsoever. "Electronic Horizon," as the
term was actually used by this era's real automotive suppliers
(Continental/Siemens VDO — RNS-510's real navigation supplier), was
itself a map-attribute-driven concept (predictive curve/speed data
computed from digital map data, not sensor fusion), so finding this
code here isn't anachronistic at all, as long as it's read as
map-sourced rather than camera-sourced.
Best-supported current model: this is real, genuinely-compiled
supplier platform code (shared across a family of related head
units/trim levels/model years from the same navigation-software
supplier) implementing map-attribute-driven electronic-horizon-style
data distribution — most likely the head unit sending its own
map-derived predictive data out over MOST to some other vehicle system
(e.g. adaptive transmission/headlight controllers on higher-trim
platform siblings), with NAV_GRAPH_RESYNC/NAV_GRAPH_MATCH
datagrams plausibly being synchronization/acknowledgment traffic in
that exchange rather than incoming sensor detections. That the code
exists in this binary does not mean this specific RNS-510 build
exercises it against real hardware — which, per the above, it most
likely doesn't. Still worth a dedicated future session to trace the
actual direction of data flow (who initiates UnPackNavGraphDatagram,
and what triggers it) — only 3 of the many real functions in this
family have been traced so far, and that would settle the
sender-vs-receiver question directly rather than by inference.
Every function this project has named and can say something concrete about, one page each. Status legend:
| Symbol | Meaning |
|---|---|
| 🟢 |
Fully traced — complete disassembly (prologue to blr), real understood behavior |
| 🟡 | Found, reachable, not yet traced — real address confirmed under 9MB, a real function prologue exists nearby, but not disassembled/analyzed beyond that |
| 🔴 | Found, unreachable — real symbol-table record exists, but its address falls in the confirmed 9MB+ dead zone (see below); no code reachable with current technique |
| ⚪ | Name only — found via plain debug-string scanning, never resolved to any address (confirmed structurally unreferenced by executing code) |
| 🟡* |
Partially traced — real disassembly and analysis exist but the page is deliberately not a complete line-by-line trace, either because of a genuine unresolved anomaly (e.g. no locally-visible blr/return) or because the function is too large for a full transcription to be proportionate |
| Function | Real name | Status | Page |
|---|---|---|---|
getId |
CfcMsgStreamedServicePoint::getId |
🟢 | Firmware-Fn-getId |
| (unnamed) | — | 🟢 | Firmware-Fn-0x1f95d0 |
getNodeVidArmMP0 |
RoutePath::getNodeVidArmMP0 |
🟢 | Firmware-Fn-getNodeVidArmMP0 |
ResolveUnsetNodeID |
AdasDBAccess::ResolveUnsetNodeID |
🟢 | Firmware-Fn-ResolveUnsetNodeID |
prepareOutputForMap |
TIMEx_Explorer::prepareOutputForMap |
🟢 | Firmware-Fn-prepareOutputForMap |
getIndexOfVid |
RoutePath::getIndexOfVid |
🟡* | Firmware-Fn-getIndexOfVid |
calculateManeuvers |
ManeuverGeneratorManager::calculateManeuvers |
🟡* | Firmware-Fn-calculateManeuvers |
addSegmentData |
DYNA_SegmentList::addSegmentData |
🟡* | Firmware-Fn-addSegmentData |
filterToggleSeg |
(global) filterToggleSeg
|
🟡* | Firmware-Fn-filterToggleSeg |
getSegProperty |
RoutePath::getSegProperty |
🟡* | Firmware-Fn-getSegProperty |
mv_check_turn |
(global) mv_check_turn
|
🟡* | Firmware-Fn-mv-check-turn |
mv_generate_mnvr_name |
(global) mv_generate_mnvr_name
|
🟡* | Firmware-Fn-mv-generate-mnvr-name |
mv_lefthand_traffic |
(global) mv_lefthand_traffic
|
🟡* | Firmware-Fn-mv-lefthand-traffic |
mv_convert_slot_nr |
(global) mv_convert_slot_nr
|
🟡* | Firmware-Fn-mv-convert-slot-nr |
DumpNavGraph |
AdasTools::DumpNavGraph |
🟡* | Firmware-Fn-DumpNavGraph |
| (unnamed) | — | 🟡* | Firmware-Fn-0x78834c |
TransformHeadingAngleRelative |
AdasTools::TransformHeadingAngleRelative |
🟡* | Firmware-Fn-TransformHeadingAngleRelative |
UpdateSettingsDueToDVDChange |
CArr_AdasMain::UpdateSettingsDueToDVDChange |
🟡* | Firmware-Fn-UpdateSettingsDueToDVDChange |
SetRouteObject |
AdasRouteObject::SetRouteObject |
🟡* | Firmware-Fn-SetRouteObject |
| (unnamed) | — | 🟢 | Firmware-Fn-0x5ba570 |
| (unnamed) | — | 🟡* | Firmware-Fn-0x5ba5d0 |
getCurrentCarPosition |
(global) | 🟢 | Firmware-Fn-getCurrentCarPosition |
registerBlocking |
CArr_DYNA_Main::registerBlocking |
🟡* | Firmware-Fn-registerBlocking |
Draw |
ElectronicHorizon::Draw |
🟡* | Firmware-Fn-Draw-ElectronicHorizon |
getIso2CountryCode |
CfcAspectISO2CountryCodeChangedEvent::getIso2CountryCode |
🟡* | Firmware-Fn-getIso2CountryCode |
setRouteSectionDetourId |
CfcTypeJourneyRouteId::setRouteSectionDetourId |
🟡* | Firmware-Fn-setRouteSectionDetourId |
operator new |
CfcGuidanceSpeedTableFuture::operator new |
🟡* | Firmware-Fn-CfcGuidanceSpeedTableFuture-new |
| (comparison fn + ~18 accessors) | plausibly EHTreeNode (name not confirmed) |
🟢 | Firmware-Fn-EHTreeNode-struct-layout |
readNodeMP0 |
RoutePath::readNodeMP0 |
🔴 | Firmware-Fn-readNodeMP0 |
RoutePath_readNodeMP0 |
RoutePath::readNodeMP0 (2nd overload) |
🔴 | Firmware-Fn-RoutePath-readNodeMP0 |
RoutePath_readNodeArmMP0 |
RoutePath::readNodeArmMP0 |
🔴 | Firmware-Fn-RoutePath-readNodeArmMP0 |
readNodeArmMP0 |
RoutePath::readNodeArmMP0 (2nd overload) |
🔴 | Firmware-Fn-readNodeArmMP0 |
resolveUnsetVNodeId |
LaneCalculation::resolveUnsetVNodeId |
🔴 | Firmware-Fn-resolveUnsetVNodeId |
db_vid_get_map_id_V000 |
(accessor, name only) | ⚪ | Firmware-Fn-db-vid-get-map-id |
db_vid_get_pcl_id_V000 |
(accessor, name only) | ⚪ | Firmware-Fn-db-vid-get-pcl-id |
db_find_node |
(accessor, name only) | ⚪ | Firmware-Fn-db-find-node |
db_page_pcl_V000 |
(accessor, name only) | ⚪ | Firmware-Fn-db-page-pcl |
For years this project could locate real function/string names in
the 0-24MB region (via plain string scanning) but could never turn a
name into working code — there was no known mapping from a byte offset
in the file to the runtime address (VA) the CPU would actually use,
so lis/addi absolute-address instructions couldn't be resolved and
disassembly was blocked.
Recovered this session: for the region's code-dense 1.2MB-9MB sub-range,
VA = file_offset + 0xf688dcf4 (mod 2^32)
How: disassembled that sub-range instruction-by-instruction
(capstone, PPC, big-endian, every 4-byte-aligned offset decoded
independently so one bad instruction can't desync the rest), collected
every lis rX,HI16 + addi rX,rX,LO16 pair (34,652 of them, from
116,383 lis instructions) as a candidate absolute address, then ran a
brute-force sliding-window scan over the sorted target values: find
the window of width 24,000,000 containing the most targets. 95.1%
(32,966/34,652) fall inside one window — 170x the count pure chance
would predict. The window's own left edge gave the base value above.
Verified byte-exact, 3 independent ways: a real runtime log string
("bootMgrLax : Now I'm going to create the image -") landed exactly
where a completely unrelated instruction's own computed address said it
would; a real, fixed-width UI button-label table
("PLAY \0\0SEARCHBACK\0\0SEARCHFOR \0\0STOP \0\0PAUS...")
was hit identically by 4 separate call sites; a broader unbiased
spot-check turned up more coherent real content (C++ mangled names, XML
config fragments) with zero cherry-picking.
Confirmed limit — do not extrapolate past this without re-verifying: this base only works for code whose real address falls under ~9MB. Real prologue density is ~900-1,235/MB from 1-9MB and essentially zero from 9MB on (confirmed: a ±200,000-byte window search around several 9MB+ targets found zero real function starts, every time it was tried). Code beyond 9MB may use a different base, may not be locally present in this file at all, or may need a fundamentally different technique to reach. This is why every 🔴 function above is real (a confirmed record exists) but not reachable.
Real GCC 2.96 PowerPC function prologues in this codebase always begin
stwu r1, -N(r1) ; bytes: 94 21 <N:2 bytes>
mflr r0 ; bytes: 7c 08 02 a6 (byte-invariant)
swl_5238_reader.find_prologues() scans for this exact 8-byte shape at
every 4-byte-aligned offset. Finds 7,757 real, low-false-positive
candidates across the whole 24MB native region. Does not find
every real function — simple leaf functions can validly skip this exact
frame-setup shape.
A recurring pattern: no locally-visible blr — confirmed, via real CPU emulation, to be genuine non-code data
Confirmed on 9 separate functions now (getNodeVidArmMP0,
getIndexOfVid,
filterToggleSeg,
addSegmentData,
TransformHeadingAngleRelative,
UpdateSettingsDueToDVDChange,
SetRouteObject,
mv_lefthand_traffic,
getIso2CountryCode — notably, this
last one is a clean, boundary-unambiguous "gold standard" function,
confirming the anomaly is NOT an artifact of uncertain boundaries
elsewhere on this list) — this is a real,
pervasive architectural pattern across this codebase, not per-function
noise or specific to one subsystem (it shows up in both the original
route-path functions and the newly-found ADAS family equally). In each
case, the function pops its own stack frame (addi r1,r1,N) but the
word(s) immediately after do NOT decode as blr or any other valid
PowerPC instruction (the exact word count varies per instance, 2 or 3
words seen so far). For getNodeVidArmMP0 specifically a real backward
loop branch explained one occurrence of this shape; for the others, no
such explanation has been found, and on
addSegmentData/UpdateSettingsDueToDVDChange specifically, the code
that resumes afterward shows no fresh stwu/mflr prologue either —
a real, distinct variant, not previously seen on the first few
instances.
UPDATE, resolved (the "is it code or data" half of the question):
built a real execution harness using Unicorn Engine (PPC32,
big-endian) — not just reading the bytes, actually running them on an
emulated CPU core. Loaded UpdateSettingsDueToDVDChange's
real bytes at their real VA, set arguments so the one external call is
skipped (keeping the test fully self-contained, no other code needed),
and single-stepped from entry. Confirmed, unambiguously: every real
instruction up to and including the frame-pop (addi r1,r1,0x10)
executes correctly; the very next word (00130019, exactly the first
word capstone already flagged as undecodable) causes a genuine CPU
exception the instant the processor tries to execute it — the same
illegal-instruction trap real hardware would raise. This rules out
"disassembler gap" definitively — Unicorn/QEMU's PPC decoder is a
mature, independent implementation from capstone's, and it agrees the
bytes aren't valid code. The data really is data.
Still open: what the data actually encodes (the EH-descriptor-table
guess remains a guess, not confirmed). Partially advanced: why some
functions show a fresh prologue right after it while
addSegmentData/
UpdateSettingsDueToDVDChange
don't. Re-ran the emulation with the ENTIRE 24MB native region mapped
from the real file (emu_fli_wide.py, same scratchpad) instead of a
narrow window, then manually skipped the 8-byte gap and resumed: 5
further real, valid instructions execute cleanly before the trail goes
cold on an unmapped read — confirming genuine, well-formed machine
code really does resume right after the gap, it just isn't a fresh
function prologue (no stwu — it writes into stack offsets BEYOND the
small frame the preceding function reserved, i.e. it reuses the
just-popped caller frame rather than allocating its own). Reads as a
shared trampoline/stub reached by multiple small functions' tails
rather than each one's own independent continuation — plausibly where
"what the data encodes" and "why no fresh prologue" turn out to be the
same answer (the data might be exactly what tells this stub where to
dispatch to, keyed by which function fell into it). Not confirmed
further; the specific addresses this test computed depend on
synthetic register state and aren't trustworthy without the function's
real caller context. Given the harness now exists and works, this is
a concrete, re-runnable next step rather than a dead end — see
emu_fli_func.py/emu_fli_wide.py in this session's scratchpad (not
yet promoted into research/).
UPDATE, a bigger reframing — tested on filterToggleSeg,
changes how this whole section should be read. filterToggleSeg was
previously documented as a clean, complete, standalone 108-byte leaf
function ending at its own blob. Emulating past that blob (full 24MB
mapped, realistic register values — not synthetic garbage) shows 13
more real instructions execute cleanly, and the operation is
independently verified correct: a test value placed in a synthetic
source record is correctly copied through to the destination record by
the code that "shouldn't" be reachable. Continuing further: a real call
to getOffroadDistanceFromRouteToDestination (already known from 2
other functions), a genuine array-copy loop, a mr r3,r31 ("return
this") idiom, and what looks like a clean, correctly-matching
epilogue — which is ITSELF immediately followed by yet another blob
and still more real code (CfcAspectGuidanceStatusChangedEvent
construction, also already known from 2 other functions). At least 3
separate blobs, each followed by real, meaningful, on-topic logic, all
chained together.
This changes the right interpretation of the whole anomaly. It is very unlikely that 8 unrelated functions all independently "forgot" their own return instruction. Far more likely: this compiler/codebase lays out groups of logically-related functions (or a function plus its exception/call-site metadata) as one contiguous block, where real control flow branches AROUND each inline data blob via jumps this project hasn't mapped yet — and this project's function-at-a-time, linear-disassembly approach has been slicing that contiguous block at each blob and mistaking the pieces for separate, independently-broken functions. Every individual finding on every affected page (the real call targets, the real logic) remains correct and stands — what's revised is the FRAME: these are very likely fragments of a few larger real structures, not N independently-anomalous small functions. Fully mapping this would mean finding the real branch instructions that skip each blob in normal (non-exceptional) execution — a concrete, scoped research question for a future session, now that the execution harness to test hypotheses against exists and works.
UPDATE, immediately following, same session: that specific
hypothesis (a direct static branch jumps around each blob) is
REFUTED. Scanned the entire 24MB native region for any real b/bc-
family branch (direct or conditional, both relative and absolute forms
decoded) whose own encoded target lands exactly on the address right
after 5 different known blobs. Result: zero real branches target 4
of the 5; the one addressed that did get a hit
(0x606f3c, after filterToggleSeg's 3rd blob) turned out to be a
branch already known and accounted for as part of that function's own
ordinary conditional logic (its beq skipping the "then" half of the
record-copy) — not a dedicated "jump around the blob" instruction, just
a coincidence of where that already-understood branch happens to land.
This rules out the simplest explanation. Whatever actually reaches
these post-blob addresses in real, intended execution, it is not a
plain direct or conditional branch anywhere in the 24MB region — which
leaves either an INDIRECT/computed branch (bctr/bclr off a register
or jump table, invisible to this kind of static address scan since the
real target is only known at runtime) or something this project hasn't
considered yet. Narrows the search, doesn't close it.
UPDATE, same session: also checked whether these post-blob addresses
are independently-called real function entry points (the same raw-
data/vtable scan used elsewhere on this wiki, plus the direct
lis/addi scan) — zero hits, for all of them, anywhere in the
whole 85.6MB file. Nothing calls them, nothing branches to them, and
yet real, meaningful, independently-verified-correct code sits there.
Current best synthesis, converging 3 independent negative results
with the project's own original hypothesis: these addresses are most
likely real C++ exception-handling landing pads — real,
compiler-generated cleanup/catch code that genuinely exists and genuinely
works, but is reached only by the C++ runtime's own stack unwinder
during an actual throw, which jumps there by READING the "data blob"
as an EH range-table entry (PC-range → handler-address mapping), not by
executing it or branching to it in the normal instruction stream. This
would cleanly explain every piece of evidence gathered: no static
branch or call targets it (the runtime computes the jump from table
data, not from a branch instruction anywhere in this function's own
bytes); it's genuinely unreachable via ordinary execution (consistent
with "function appears to end here" being literally true for the
NORMAL, non-throwing path); and it's still real, correct, meaningful
compiled code (because it genuinely is real cleanup code, just invoked
through an unusual, data-driven path this project's tooling doesn't
model). Not proven — confirming it would need either the real EH
table format fully decoded, or a from-scratch VxWorks/GCC unwinder
reference to compare against — but it is now the best-supported
explanation this project has had for this anomaly, strengthened rather
than weakened by this session's attempts to find a simpler one.
UPDATE, new session: independently confirmed and quantified at whole-
file scale using IDA Professional 9.3's real PowerPC analyzer, not
just this project's own hand tracing. Loaded FHDD6.FLI's native
0-24,000,000-byte region into IDA as raw 32-bit big-endian PowerPC at
the project's own established load base (0xF688DCF4), ran full
autoanalysis, then scripted (IDAPython) a whole-region scan for every
word-aligned real stwu r1,-N(r1) function-prologue byte pattern (the
same signal IDA's own Patfind prologue matcher uses) and asked IDA to
bind each one into a complete function via add_func:
- 8,015 real prologue candidates found across the 24MB region
- Only 9.4% (754, combining already-recognized plus newly-bound) could be bound into a complete function
- 90.6% (7,261) failed — IDA's own flow analysis could not find a function end for them
This is a professional, independently-implemented PowerPC disassembler
and cross-reference engine hitting essentially the same wall as this
project's manual work, at full-file scale rather than on a handful of
examples. For a real compiled 24MB PowerPC binary, a true function
density of ~9% (vs. the expected near-100%, since stwu r1,-N(r1) is
about as unambiguous a real-function-start signal as PowerPC code
produces) is itself strong evidence something systematic — not
isolated disassembler confusion on a few hand-picked functions — is
going on across the whole native region.
Manually single-stepped IDA's own instruction decoder forward from 15 of the failing prologues (not just checking the end result, but exactly where decoding stops). All 15 stopped at a word whose primary opcode field is 0 — the exact same "universally illegal on real Power ISA" signature independently confirmed via live CPU emulation earlier in this project (see above). 15-for-15 is a strong replication, via a completely different tool and method, of the original emulation finding.
A new structural detail turned up by looking at all 15 stop-words
together: the low 16 bits of each undecodable word form a tight
arithmetic progression — 0x0019, 0x0019, 0x0021, 0x0029, 0x0021, 0x0029, 0x0021, 0x0029, 0x0021, 0x0021, 0x0029, 0x0019, 0x0021, 0x0019, 0x0031 — every single one is 8n + 1 (25, 33, 41, 49...),
across 15 completely unrelated call sites with no other connection to
each other. Raw noise or ad-hoc embedded constants would not be
expected to share this property. This is new, independent evidence
favoring the "structured table" framing over "arbitrary data blob" —
consistent with (though still not proof of) the EH range-table
hypothesis above: a small, fixed-stride field (plausibly a type-index
or table-slot number, scaled by a fixed per-entry byte size) sitting in
the low half-word of each entry, with the high half-word carrying a
per-entry value that varies freely (0x000c, 0x001a, 0x0094, 0x0009, 0x024f, 0x0008, 0x01f0, 0x0115, 0x002b, 0x001c, 0x0009, 0x0018, 0x0007, 0x0162, 0x02ca — no obvious shared pattern, consistent with a
real per-entry payload rather than more index structure).
UPDATE: the "EH landing pad" hypothesis now has real documentary
confirmation, not just inference. This firmware's Cfront-style name
mangling (__vd__, Pv, Ui, RC9msgheader, etc.) points to Wind
River's own compiler (historically branded "Diab", dcc) rather than
GNU GCC — confirmed independently via the real VxWorks 6.2 Kernel
Programmer's Guide (vxworks_kernel_programmers_guide_6.2.pdf,
legitimately obtained, chapter 11 "C++ Development"), which documents
the exact compiler invocation (dcc -tPPC604FH:vxworks61 ... -DTOOL_FAMILY=diab -DTOOL=diab -c hello.cpp) and states the Wind
River Compiler's C++ front end is the Edison Design Group (EDG)
front end, a different lineage from both classic Cfront and GCC. Section
11.5.2 "Exception Handling" of that same manual states, verbatim:
"The Wind River Compiler exception handling model is table driven and requires little run-time overhead if a given exception is not thrown. Exception handling does, however, involve a size increase."
This matches this project's own independently-derived hypothesis on every point: a genuinely table-driven mechanism (not inline branch logic), zero overhead on the non-throwing path (consistent with ordinary execution never reaching or branching to the blob at all), and a size cost attached to functions that use exceptions (consistent with the blob being real, compiler-emitted per-function data). This doesn't yet give us the exact byte-level table layout — that would need the Wind River/Diab Compiler User's Guide itself, or a real Diab install's runtime headers — but it is the first genuine, citable, vendor-authored confirmation that this project's reverse-engineered model of the anomaly (table-driven EH metadata, not broken code) is the right shape, not just the best of several guesses.
UPDATE: the anomaly is now definitively, completely explained — not
just supported — via the actual Wind River Compiler User's Guide
(wr_compiler_users_guide_mips_5.4.pdf,
legitimately obtained and archived in this wiki's own repo for
reference; the MIPS-target edition, but this mechanism is described as
a general compiler/linker feature, not an architecture-specific
appendix, so it applies identically to the PowerPC edition). Section
23.8 ".frame_info sections" states, verbatim:
"The compiler generates
.frame_infosections for C++ programs when exception-handling is enabled. A section is created for any function that might appear on the call stack between a try and a throw; the linker concatenates these into a searchable table that is used for stack-unwinding and object clean-up after an exception occurs. For each function, the table contains a small (8- to 24-byte) record that includes pointers to structures in the.datasection."
And critically, the manual's own example linker script (sample.dld,
25. Linker Command Language) shows exactly where these records get
physically linked:
.text (TEXT) :{
*(.text) *(.rodata) *(.rdata)
*(.frame_info) *(.j_class_table)
*(.init) *(.fini)
.frame_info is placed inside the same .text output section as
real code, not segregated into a separate data region — and sorting
is OFF by default (-Xsort-frame-info-off), so each function's own
record is linked in right where the linker encountered it: immediately
after that function's own compiled code. This is a complete,
compiler-documented explanation for every single thing this project
measured by hand and by emulation: a small (matches our observed
multi-word blobs), non-code (matches real CPU-emulation finding
primary-opcode-0 illegal words), per-function (matches "appears right
after THIS function's own stack-pop, not some other function's"),
table-structured (matches the 8n+1 arithmetic-progression field found
via IDA) record, sitting physically interleaved with code (matches "no
locally-visible blr" — there was never a missing blr; the function
genuinely ends there, and what follows is linker-placed metadata, not
more of that function's own code) because the linker's own default
script puts it there on purpose.
One more real-world detail this explains: -Xframe-info force-generates
.frame_info even for plain C functions, specifically "for mixed C/C++
programs in which C++ exceptions may propagate back through C
functions." Given this firmware's measured ~90% prevalence of the
anomaly across essentially all real function prologues (see the IDA
whole-file scan above) — far more than "only functions between a try
and a throw" would suggest — it's a reasonable inference that this
build used -Xframe-info project-wide, consistent with a large,
C-and-C++-mixed embedded navigation codebase needing exceptions to
propagate safely through older C-style modules.
This closes the central open question of this entire project. What remains open is only the fine-grained internal layout of the record itself (which fields are which within the 8-24 bytes) — a smaller, well-scoped follow-up, not a fundamental unknown anymore.
Attempted, a later session: 2 leading hypotheses for the record's
"word2" field (the small, 8-1576-ish value following the 8n+1-coded
first word) — both refuted at scale. Scanned every function in the
whole-file-recovered database (8,164 checked, 7,169 containing at least
one internal blob) and directly compared each blob's word2 against (a)
the containing function's own total byte size, and (b) the blob's own
byte offset from the function's start — the two most natural
candidates for a small per-function integer in an EH record. Result:
word2 matches the function's size in only 4 of 7,169 cases, and
the offset-from-start in only 61 of 7,169 — both consistent with
coincidental small-integer collisions (plenty of functions and offsets
are small numbers already), not a real systematic relationship. No
simple ratio or other relationship was visible by eye across the raw
samples either. Both hypotheses are refuted, not just unconfirmed
— recorded so a future session doesn't re-attempt the same two ideas
expecting a different result. Word2's real meaning is more likely a
small count/index into some other structure (a catch-type count, an
action-table slot, etc.) than anything directly computable from this
function's own size or layout. Closing this further would most likely
need either the real Diab/Wind River compiler's own runtime headers
(not obtained this project — see the compiler-identification work
elsewhere on this page) or a fundamentally different analytical angle,
not another guess at the same two candidates.
Knowing exactly what the blobs are (and that the linker interleaves
them directly into .text) made it possible to go from "explain the
anomaly" to "recover real function boundaries across the whole 24MB
region" — not just the ~90 functions this wiki had hand-traced one at a
time. Done entirely inside IDA Professional 9.3 (loaded per the setup
documented elsewhere on this wiki: PowerPC big-endian 32-bit, generic
ppc device, load base 0xF688DCF4, 24,000,000-byte native region),
scripted with IDAPython.
Baseline measurement. A whole-region scan for every word-aligned
real stwu r1,-N(r1) function-prologue byte pattern found 8,015
real prologue candidates. IDA's own autoanalysis + add_func could
only bind 754 of them (9.4%) into complete functions — the rest
failed because flow analysis walked straight into a .frame_info blob
and gave up, exactly as this page documents above.
Recovery approach, validated as a read-only dry run before any
mutation: for each unbound candidate, decode forward from its
prologue; on hitting an undecodable (primary-opcode-0) word, treat it
as the start of a .frame_info-shaped blob, and search forward
(word-aligned) for the point where real code resumes — but strictly
bounded by the address of the next already-known real prologue, so
the search can never overshoot into a different, already-correct
function. This dry run found a validated, in-bounds resume point for
99.9% of all candidates, confirming the approach was sound before
touching the live database.
First attempt, 9.4% → 9.7%: barely moved. Patching the first blob
per candidate and calling add_func(ea, BADADDR) (asking IDA to
auto-detect the end via its own control-flow trace) still failed for
nearly everyone. Root cause, found by iterating: a single function
can contain many .frame_info blobs, one per call site that might
throw — not just one trailing blob. Patching only the first blob left
later ones still blocking flow analysis deeper in the same function.
Looping blob-detection-and-patch within each candidate (up to 25 blobs,
still bounded by the next real prologue) got every byte in range
decoding cleanly — but add_func(ea, BADADDR) still refused almost
all of them (recovered: 0 new). ida_funcs.find_func_bounds confirmed
the refusal (FIND_FUNC_UNDEF) without more detail, but the real cause
is BADADDR-mode add_func demands IDA's own control-flow tracer
prove the function's extent (follow every branch, resolve any
computed jump/bctr, etc.) — a stricter bar than "every byte decodes."
The actual fix: stop asking IDA to infer the end; give it the end we
already validated. Switching to add_func(ea, upper_bound) — passing
the next known real prologue as an explicit end address, rather than
BADADDR — bypasses IDA's conservative flow-closure requirement
entirely and just takes our own already-validated boundary. Result:
7,149 additional functions recovered, taking the whole-file bind
rate from 9.4% to 98.9% (7,923 of 8,015 candidates), with only 92
unresolved stragglers left for manual follow-up.
Why this matters beyond a bigger number: real IDA function objects
across virtually the whole 24MB region means Hex-Rays decompilation is
now available broadly — not just for the small set of functions this
project traced by hand over many sessions. This changes the project's
realistic next phase from "trace one function at a time" to "decompile
and read," a fundamentally faster way to cover the rest of this
firmware. (Caveat carried over from elsewhere on this page: IDA's
auto-assigned sub_XXXXXXXX names carry none of this project's
already-confirmed real names — reconciling the two is a separate,
future task.)
Found while tracing mv_check_turn: the
"scan backward for the nearest real prologue" technique (used to find
true function starts once the naive symbol-table address lands
mid-body) finds a nearby prologue, but not necessarily the RIGHT one
— it doesn't check whether the function's own control flow (backward
loop branches especially) actually stays within that boundary.
Confirmed concretely: mv_check_turn's disassembly reaches a real
blr, but its own loop branches backward past its assumed start,
landing before an EARLIER prologue (~900 bytes further back) than the
one the naive scan found. This means at least one page on this wiki
(mv_check_turn) may document a MIDDLE FRAGMENT of a larger enclosing
function, not that symbol's own complete, isolated body — flagged
honestly on that page rather than silently treated as complete. Worth
checking for on any future page built this same way.
UPDATE, resolved: disassembling from that earlier candidate start
confirms it IS mv_check_turn's own real, complete body — a genuine
undershoot, now fixed. See
mv_check_turn for the real preamble this
recovers (a MDEDMDatabase SQL query plus oslibVfs_register, feeding
the already-captured switch statement).
A related but DIFFERENT trap, found on
mv_generate_mnvr_name: a
function using a callee-saved register (r30/r31) before any visible
assignment is NOT automatically evidence of a boundary undershoot.
Checked directly by disassembling the immediately preceding real
function: it ends cleanly and independently, immediately followed by
this function's own fresh, genuine prologue — ruling out undershoot
here. The real explanation: GCC can pass extra arguments to a static/
internal-linkage helper through callee-saved registers instead of the
standard r3-r10 parameter registers, since no external ABI
constrains it — the callee still faithfully saves/restores the
original value for the caller's benefit, it just also reads the
incoming value as a real input first. Net lesson: before concluding
a "used before set" register implies wrong boundaries, always check
whether the immediately preceding function ends cleanly right before
the prologue in question — if it does, the usage is far more likely
an extra hidden argument than evidence of undershoot.
With the whole-file recovery above giving IDA real function objects
covering 98.9% of real prologues, Hex-Rays decompilation (PowerPC
decompiler, hexppc.dll) is now usable broadly rather than
theoretically. Ran it on five already-documented, hand-traced
functions/addresses to directly compare against this wiki's own prior
work: filterToggleSeg,
mv_convert_slot_nr,
registerBlocking, and the two
EHTreeNode-struct-layout
addresses (the "constructor" and the "accessor cluster start").
New confirming evidence that functions in this codebase genuinely
escape our forced boundaries. Every single decompiled function
contains at least one JUMPOUT(0x...) marker — Hex-Rays' own explicit
signal that real control flow branches to an address it could not
incorporate into the function (because it lies outside the bounds we
gave add_func). For mv_convert_slot_nr, one such target
(0xF701F774) is provably past the end address we forced
(0xf701f6ec) — not a decompiler quirk, but real, live confirmation
(independent of, and agreeing with, the earlier CPU-emulation/static-
scan work) that this codebase's functions do not always end where the
next textual prologue starts. This is the same "shared trampoline"
phenomenon already documented on
UpdateSettingsDueToDVDChange,
now independently reproduced by Hex-Rays across multiple unrelated
functions rather than being a one-off. Net effect: treat every
add_func(ea, next_prologue)-bounded function from the whole-file
recovery pass as a close, usable approximation for decompilation, not
a proven-exact boundary — a JUMPOUT in the output is the decompiler
telling you exactly where that approximation broke down for this
particular function.
A real correction to the EHTreeNode struct-layout page. The
"constructor" address this wiki identified at 0xf6ffc760 is not
itself a function entry point — Hex-Rays reports its real containing
function spans 0xf6ffc500-0xf6ffc7c4. Likewise the "accessor
cluster start" at 0xf6ffc934 belongs to a real function spanning
0xf6ffc7c4-0xf6ffca5c. Both addresses remain real, reachable,
correctly-identified code (confirmed via real callers elsewhere) — what
was wrong was describing them as independent function entries, rather
than call-reachable points inside one larger compiled function. This
actually fits, and slightly sharpens, the struct-layout page's own
"~20 tiny leaf accessors back-to-back" description: they are back-to-
back because the compiler emitted them as one single function, not
because the compiler happened to place ~20 separate zero-frame
functions adjacent to each other.
Two new, concrete content findings (independent of the boundary question above):
-
filterToggleSeg's real logic is a virtual function call:(*(vtable + 28))(a1 + *(int16*)(vtable + 24)), i.e. a genuine C++ vtable dispatch at a specific, now-known offset — more precise than this wiki's earlier "record-copy operation" framing, and worth folding into that page. -
registerBlockinguses the classic int-to-double bit-trick conversion idiom: XOR a raw 32-bit value with0x80000000, pair it with the magic high word0x43300000(2^52in double representation), and subtract2^52as a double — the standard software technique for converting an unsigned integer to a double without a hardware int→float instruction. Consistent with the e300 core's real floating-point capabilities, and plausibly how this function turns raw fixed-point position/coordinate data into a realdoublebefore thesqlite3-adjacent work already documented on that page.
Standing caveat carried over from setup: this IDA database treats
the whole 24MB region as one read-only "ROM" segment, so Hex-Rays
inlines every global's ROM-image value as a constant — including ones
that are actually mutable at runtime (copied to RAM at startup, per
standard embedded .data initialization). A decompiled "constant"
global should be read as "this was its value in the flashed image," not
necessarily "this value never changes" — relevant context for any
future reading of Hex-Rays output from this database, not a defect to
fix.
Hex-Rays at scale: 20 more functions decompiled — the JUMPOUT pattern is universal, plus 3 real new findings
Extended the single-function pilot above to 20 more already-documented addresses in one pass. Results:
The JUMPOUT pattern is not occasional — it is universal. All 19
addresses that successfully decompiled (one, mv_check_turn, still
isn't bound to any function at all — a follow-up item) show at least
one JUMPOUT marker. Several (CfcGuidanceSpeedTableFuture's
constructor-or-operator new, Draw,
mv_lefthand_traffic, setRouteSectionDetourId) decompile to almost
nothing BUT an immediate JUMPOUT — meaning the forced
next-known-prologue boundary cut off nearly the entire real function
body, not just a trailing portion. This is now the clearest evidence
yet that the "shared trampoline"/frame-reuse phenomenon first
documented on
UpdateSettingsDueToDVDChange
is pervasive across this codebase, not an isolated case.
Two more boundary corrections (same pattern as EHTreeNode, now
applied to their own pages):
Draw/ElectronicHorizon::Draw
and getCurrentCarPosition are
both real, reachable code, but NOT independent function entries — their
real containing functions start earlier (0xf70abaf4 and 0xf702468c
respectively). (One extraction false-positive avoided:
mv_generate_mnvr_name already correctly documented its own true start
— no correction needed there.)
A real, recurring call pattern — UPDATE: checked, and it's not a
diagnostic logger, it's almost certainly real EH/throw runtime
machinery. The same call shape, unk_F6F24CE8(id, &unk_F7886E04, number, ...), appears in both
mv_convert_slot_nr and
SetRouteObject, with the exact same
second-argument pointer (&unk_F7886E04) at both call sites. The
original hypothesis here was a __FILE__-style diagnostic/assert
macro. Checked directly by reading the real bytes at
unk_F7886E04, and that hypothesis is wrong — it's not a string at
all. The raw bytes there (002c0099 00000008, 002c0059 00000008,
002c0019 00000010, 002c0031 00000008, ...) are unmistakably MORE
.frame_info-shaped records — the same 8n+1 low-halfword pattern
plus a small size field, identical in shape to the per-function EH
records documented earlier on this page. This is very likely a direct
pointer into the real, concatenated, searchable .frame_info
table itself (per the compiler manual's own description — "the
linker concatenates these into a searchable table"), not a filename.
Being the exact same address across two unrelated functions fits this
reading well: a single, stable, shared table/table-entry reference,
not a per-function string.
This reframed unk_F6F24CE8 as likely EH/throw runtime machinery
rather than a logger. UPDATE, same session, immediately following:
that reframing was too hasty — checked 486 total callers (found via
DataRefsTo, since every call site loads the address indirectly via
lis/addi rather than a direct branch, which is why an earlier xref
search using only CodeRefsTo found zero) and decompiled a fresh,
unrelated sample of them. Several show unk_F6F24CE8 receiving
real, legible C strings as arguments — including two more
RTTI-shaped class-name fragments, confirming 2 more real,
previously-uncatalogued classes: "...ZP17CfcTypeCoordinate"
(17CfcTypeCoordinate → CfcTypeCoordinate) and
"...erP8VpTurtle" (P8VpTurtle → VpTurtle, joining
VpSimEventSetPosition as a second confirmed Vp-prefixed class — a
real naming convention in this codebase, not a one-off). One call site
(sub_F6C484C4) fires right after a null-check on a malloc-shaped
call, with first argument 118 — the same 118 value also seen
returned/passed in 2 of the other new samples, consistent with it being
a specific error/category code (plausibly "allocation failure" or
"null pointer").
RESOLVED: did the concrete next step (grouped all 486 callers by
their first-argument ID, 344 successfully decompiled) — both pieces of
evidence are explained by the SAME single mechanism, not two competing
ones. Across every ID bucket, the overwhelming majority of
second-argument values are non-string pointers (&unk_XXXXXXXX); real
"strings" are a small minority, and critically, every single one of
those "strings" is a garbled mid-record fragment, not a real message
— e.g. "ngsationing" (plainly the tail of something like
...PositioningDiagnosisImpl) recurs byte-identically across 5
unrelated call sites, and "ilFixedArray1ZP17CfcTypeCoordinate" /
"...P8VpTurtle" are mangled-name tails, not English sentences a
programmer would write for a log message.
These are not diagnostic strings at all — they are
the symbol/EH-descriptor table this
wiki already extensively documented: a 26,541-record table where
each record is [3-byte marker][NUL-terminated mangled C++ name][header fields][ordinal][5 trailing fields]. Hex-Rays' string
auto-detector fires on whatever readable ASCII run it finds starting
near the given address — which is almost never record-aligned, hence
the "garbled fragment" appearance — while the .frame_info-shaped
non-string pointers seen at most other call sites are simply addresses
elsewhere within the same broader table structure (consistent with
one compiler-generated table serving both EH range data and RTTI/name
data, exactly as the Wind River compiler manual documents table-driven
mechanisms for both).
Conclusion: unk_F6F24CE8(category_id, &descriptor_table_entry, number, ...) is almost certainly the real, single, compiled
implementation of this codebase's C++ throw / exception-dispatch
runtime entry point — its second argument is a pointer into this
project's own already-documented descriptor table, used (plausibly)
to identify the thrown type for RTTI-based catch matching. This also
opens a genuinely valuable new methodology: these 486 call sites are
real, compiler-emitted, direct references into the descriptor
table — a potentially more reliable cross-reference source for
validating/discovering table entries than the already-known-unreliable
"position-4 resolution" heuristic this project has relied on
elsewhere, since each one is a live, executing reference rather than a
cold catalog lookup.
Tried this immediately, same session, using the project's own
research/swl_5238_reader.py marker-scan logic directly against the
real FHDD6.FLI bytes (not Hex-Rays' text output): built the full
26,320-record table (by byte range, not the 9MB-limited "field-4
code address" resolution this project uses elsewhere), then checked
every distinct call-site address recovered from the Hex-Rays sample
against it. Only a small sample was available to check this pass (69
distinct addresses — the earlier classification script only kept up to
3 examples per ID bucket, not the full 486), and only 3 landed inside a
real record's byte range — but all 3 are solid, call-site-confirmed,
previously-uncatalogued real names:
-
__dl__Q220MDCacheWeatherShapes19ProductInfoReceiverPv— a realoperator deletefor a brand-new class,MDCacheWeatherShapes:: ProductInfoReceiver— a previously-undocumented weather-data- caching subsystem. Confirmed as the literal argument to the ID-118throw-site insub_F6C484C4, the same one that fires right after a failedmalloc-shaped call — consistent with118meaning something like an allocation-failure category. -
getMapRenderer2D__C16MapRendererProxy— confirms a new real class,MapRendererProxy, with agetMapRenderer2D()method. -
__23CfcLocationManagerProxyiP23CfcMsgEndPointInterface— a constructor for a new real class,CfcLocationManagerProxy(int, CfcMsgEndPointInterface*), tying back to the already-documentedLocationManagerImplfamily.
This directly confirms the cross-reference mechanism works (argument 2 really does resolve to real table entries when checked against actual file bytes, not just Hex-Rays' guesses) and yields 3 solid new real names in the first, small, incomplete sample.
Done immediately, same session: re-ran keeping every call-site address (508 raw rows, 487 resolvable) instead of 3 examples per ID bucket. Yield: 12 hits, 8 distinct real names — a ~2.5% hit rate against the full table, but every hit is solid and several reveal entirely new real subsystems:
-
sqlite3_update_hook— a real SQLite C API function (update- notification callback registration), independently confirming real SQLite usage in this firmware (already suspected fromsqlite3_versiononregisterBlocking). -
createStatement__Q213SDARSVWDATASV26PredefinedStatementFactoryP22CfcConnectionInterfaceRC9CfcString— reveals a brand-new real subsystem,SDARSVWDATASV:: PredefinedStatementFactory. SDARS is the real automotive industry term for satellite digital audio radio (XM/Sirius) — this is VW's satellite-radio data service, previously entirely undocumented on this wiki. -
_$_30CfcTypeRoadClassStatisticEntry— a destructor for a new class, sibling to the already-knownCfcTypeRoadClassStatisticfamily (seen viagetOffroadDistanceFromRouteToDestinationon several pages). -
getPosition_accuracy__36CfcTypeDeadReckoningDetailed...— a new class tied to dead-reckoning navigation (the real automotive term for GPS-loss position estimation via wheel-speed/heading sensors); trailing byte garbled by a parser edge case, real name is plausiblyCfcTypeDeadReckoningDetailed. -
AU_Preprocessing_StringCompare__FPCvT0— a new real global function. - Plus 3 already-documented above, now further corroborated from
additional independent call sites:
MDCacheWeatherShapes:: ProductInfoReceiver,MapRendererProxy::getMapRenderer2D, andCfcLocationManagerProxy.
Net assessment: the cross-reference method is real and works, but
at this hit rate (~2.5%) it's a low-density prospecting tool, not a
bulk solution — most argument-2 addresses point to byte ranges this
particular marker-scan doesn't recognize as a clean record (plausibly
.frame_info-shaped sub-records, or table regions this parser's
span < 20 or span > 4000 heuristic rejects). Still clearly worth
running against the FULL set of throw-site references project-wide
(not just these 508 from one function's callers) as a future session's
project — likely still has many more genuine finds at this same
quality level, just sparse.
A real, recurring vtable-dispatch idiom. The exact same compiled
shape — (*(vtable + N))(this + *(int16*)(vtable + N-4)) — appears in
both filterToggleSeg (N=28) and
SetRouteObject (N=76). Seeing the
identical pattern at two different vtable offsets, in two independently
-traced functions, confirms this is a real, repeated compiler idiom for
a specific kind of virtual call — plausibly a virtual accessor that
returns a this-relative sub-object pointer — not a one-off
coincidence in either page's original trace.
A new, independently-reliable way to find real class names: RTTI
type-info strings. getIndexOfVid's decompilation surfaces a literal
string reference, "15CfcMapViewProxy" — a classic length-prefixed C++
RTTI type-info name (15 = character count, CfcMapViewProxy = the
real class name). This confirms a real, previously-uncatalogued class,
CfcMapViewProxy, and more importantly points to a reliable new
research method: this firmware was compiled with RTTI enabled (per the
VxWorks Kernel Programmer's Guide, section 11.5.3, and confirmed
locally since IDA's "Enable RTTI analysis" option is on by default) —
scanning .rodata-like regions for this exact
<decimal-length><name> byte pattern could surface many more real
class names directly from the binary, independent of (and more
reliable than) the already-documented-unreliable broader symbol-table
catalog. A concrete, scoped idea for a future session, not yet
attempted at scale.
getIso2CountryCode's real algorithm is now characterized — see
the update on that page: real
bounding-box/point-interpolation geometry (point-against-boundary
classification), not a generic sorted-table lookup as the callee name
alone had suggested.
The mirror-image mistake to the undershoot above, confirmed twice while
re-examining calculateManeuvers and
getSegProperty: scanning a long way
past a function's own body to collect lis/addi call targets can
silently run through a real blr belonging to a DIFFERENT, textually-
adjacent function and keep going, picking up that neighbor's (and even
a further neighbor's) own call targets as if they belonged to the
function being traced. A blr appearing is NOT sufficient evidence of
"this is where my function ends" — the only reliable test is whether
the epilogue immediately before it (addi r1,r1,N, or the equivalent
frame-chain restore) pops EXACTLY the same frame size N the function's
own prologue allocated. On both functions above, a blr was found
whose preceding frame-pop size did NOT match, and a genuinely different
function's own fresh prologue started right after — the real fix in
both cases was scanning the suspect range specifically for ANY stwu
at all (not just the expected one) to find exactly where the
contamination began, then discarding everything past that point. Both
pages were corrected after this check; apply it to any future long scan
before trusting its output.
UPDATE, immediately following, same session: built and validated a
real call-target cross-reference tool — confirms the "no discoverable
caller" finding is systematic, and gives one new, real data point for
mv_check_turn. Collected every lis/addi → mtlr → blrl
computed-call chain across the full 24MB region (a first attempt had a
real bug — too-tight a proximity threshold between mtlr and blrl —
caught by testing it against getId's own already-known-real first
call, which it wrongly missed; fixed and re-validated against that
same known-good target before trusting any of its other output).
Confirmed: none of the 13 functions traced/partially-traced on this
wiki are themselves a target of any discoverable computed call
anywhere in the 24MB region — extending, with a properly-validated
tool this time, the same "caller not found" result each fully-traced
function's own page already reported individually. For
mv_check_turn specifically: one real,
confirmed call target (0x587224) exists between the loop's backward-
branch target and the originally-assumed start — not itself a function
prologue, but genuinely, independently call-reachable code inside the
same larger enclosing routine, consistent with a shared case/label
reached via computed call from elsewhere (a real GCC pattern for
inlined or duplicated code paths). Doesn't fully resolve the boundary,
but is real, validated new structural evidence, not a guess.
A large, real, previously-undocumented table exists in the file, discovered by tracing a literal-pointer reference from a known function. Record format (empirically derived):
[3-byte-fixed/1-byte-variable marker: 00 10 05 <var>]
[NUL-terminated mangled C++ name]
[variable-length header fields, role not decoded]
[4-byte ordinal counter -- confirmed monotonically increasing across
real consecutive records, e.g. 0x6fe6 -> 0x6fe7 -> 0x6fe8]
[5 trailing 4-byte fields, ending right before the next record's marker]
Field index 4 (the last of the 5) is the function's own real code
address, for functions whose code lives under 9MB — confirmed at scale:
of all fields across 26,323 parsed records, position 4 matches a known
prologue 35 times vs. 3-6 for positions 0-3 (all far above the ~0.05-hit
chance baseline for that sample size). A plain marker search already
finds 26,541 occurrences; parse_symbol_table() (in
research/swl_5238_reader.py) parses 26,243 named records, of
which 8,041 resolve to a plausible in-range (<9MB) address and
35 additionally match a known-good strict prologue exactly. The
naive field-4 address often lands a little INSIDE a real function
rather than exactly at its start (several pages below show this) — the
true start is found by scanning backward for the nearest real prologue.
import swl_5238_reader as swl
with open("FHDD6.FLI", "rb") as f:
fli = f.read()
prologues = swl.find_prologues(fli[:24_000_000])
catalog = swl.parse_symbol_table(fli, require_prologue=False) # 8,041 entries, <9MB
full_catalog = swl.parse_symbol_table(fli, max_code_offset=24_000_000) # includes 9MB+ (unreachable) entries tooImportant, evidenced caveat — a function's own string references are
NOT its identity. Cross-checking strict symbol-table entries against
a separate catalog (functions whose own code computes the address of
some readable string) found 4 overlaps, and for every one, the symbol
table's real name had nothing to do with the string the function merely
referenced (getId, below, is the clearest example — it was earlier
mis-called "the findSegIndex-referencing function"). Only the symbol
table's own name field is authoritative; "references string X" is not
evidence of identity.
Further confirmed, 2-for-2, this session: picked 2 entries from
the broader (non-"gold") catalog whose field-4 address happened to
land exactly on a real prologue (errorString__9CfcRegExp,
parseAtom__15CfcRegExpEngine...) and disassembled both — neither
one's real content has anything to do with regular expressions (one is
a CfcMdlAspModelObjectChangedMessage-building event handler, the
other touches CfcCtrlDestroyCompleteFuture/CfcTypeRoadwayBlockingStatus).
Both silently discarded rather than published under the wrong name.
Practical lesson: landing on a real prologue is NOT sufficient
evidence the resolved address is that symbol's own — always check that
the disassembled content is at least thematically plausible for the
claimed name before trusting a position-4 resolution outside the
already-validated 35-entry "gold" set. A much more reliable source
of new targets going forward: addresses confirmed via a real,
already-disassembled caller's own lis/addi reference (as used for
getCurrentCarPosition and
registerBlocking), not a cold
symbol-table lookup.
A 3rd bad resolution, same session, via the SAME broader catalog:
createLocation__19LocationManagerImplP17CfcTypeCoordinate's resolved
address landed on pure readable string/debug data (literal ASCII like
"GetD...", "N:/s..." misdecoded as instructions with suspicious
immediate values), not code at all — discarded, not published.
4th and 5th bad resolutions, caught a different way — not from a
cold catalog lookup this time, but from 2 names that were already
published on registerBlocking's
own page (cleanupMemory__9MapLoader, __vn__11MapJobQueueUi). Their
call ADDRESSES were real (confirmed relocations in already-verified
code), but once Draw's own
byte range was independently established, both addresses turned out to
land mid-body of Draw's own tag-scanning logic — i.e.
registerBlocking really does call those 2 addresses, but the NAMES
attached to them (from the same broader catalog) were wrong; both were
really just registerBlocking jumping into Draw's shared code a 2nd
and 3rd time, not 2 separate classes. Corrected on that page rather
than left standing.
This project now has 5 confirmed-bad name resolutions from the
broader catalog this session, against 2 confirmed-good targets
from real caller references
(getCurrentCarPosition,
Draw) — a stark, now
well-evidenced contrast favoring caller-reference-based targets, and a
concrete reminder to re-check EVERY call-target name against any
newly-established byte ranges, not just trust it once at the moment
it's first resolved.
UPDATE, caught and corrected within the same session: an earlier
version of this note claimed the 0x81d000-0x820000 neighborhood
(33 broader-catalog names in ~36KB) was a specific, unusually dense
"low-trust zone." Checked properly against the catalog's own overall
density and that claim doesn't hold up: the 8,041-entry broader
catalog spans only 0x56ffd8-0x895130 (3.3MB) — an average spacing
of just ~410 bytes between names, meaning a RANDOM 36KB window should
contain ~88 names on average. 33 in 36KB is, if anything, BELOW
average density, not an outlier. There is no evidence this specific
neighborhood is worse than anywhere else in the broader catalog —
the correct lesson is simply the one already established: the
entire broader catalog is this unreliable everywhere, confirmed now
6 times this session (5 bad resolutions plus this density check
itself turning out not to isolate anything special). No specific
sub-region should be treated as worse (or better) than any other
outside the strictly-validated ~35-90 "gold" entries; verify content
independently every time, everywhere, full stop. Left this correction
in place of the original claim rather than deleting the mistake
silently — a real example of exactly the kind of overclaim this
project tries to catch.
Final tally, this session, for the "resolve a name from the broader
catalog, then find its nearest preceding prologue" method specifically
(as opposed to a name confirmed via a real caller reference): attempted
on errorString__9CfcRegExp, parseAtom__15CfcRegExpEngine...,
createLocation__19LocationManagerImplP17CfcTypeCoordinate,
cleanupMemory__9MapLoader, __vn__11MapJobQueueUi,
SetDispatcherState__20Adas_Most_DispatcherUc,
oslibVfs_register__Fi (as a standalone target, not as evidence inside
mv_check_turn's own already-verified body), and
CallbackFunction__13CArr_AdasMainP12DispMqStruct — 8 attempts, 8
content mismatches, 0 confirmed hits. Every single one, once
disassembled, turned out thematically unrelated to its claimed name
(string/token comparison code claiming to be a VFS register call, EDB
stack-storage and route-tree math claiming to be a MOST-bus callback,
etc.). Conclusion: this specific method should be considered
effectively unusable going forward without independent content
verification making the name itself irrelevant to trust — at that
point the "name" is just a label on whatever was actually found by
reading the code, not a confirmed identification. The handful of
earlier pages this session built the same way
(DumpNavGraph,
TransformHeadingAngleRelative,
UpdateSettingsDueToDVDChange,
SetRouteObject) survive only because
their own content was separately, individually argued to match their
claimed names at the time — not because the method that found their
addresses was reliable. A future session should re-examine even those
with this harsher, now better-calibrated standard before leaning on
them further.
UPDATE, a later session, tally now 11-for-11 and a real silver lining
found: re-checking mv_convert_slot_nr's
own previously-published call targets (not a fresh cold lookup — these
were ALREADY on the wiki) turned up 3 more confirmed-wrong names:
IsValid__10EHTreeNodel, writeProtocolHeader__19CfcNetMemoryChannel,
and close__21CfcIoFileOutputStream. The real content at the IsValid/
writeProtocolHeader address, though, turned out to be something
genuinely valuable: a complete, unambiguous cluster of ~20 tiny
getter/setter leaf functions plus one object-comparison function,
together confirming a real, high-confidence in-memory struct layout —
see the dedicated page. A
correction cascaded to every other page that had cited these same
addresses as confirmed: SetRouteObject
(same 2 names, same address, same fix) and
0x78834c (whose own CfcNetMemoryChannel
claim leaned on mv_convert_slot_nr's now-retracted one — spot-checked
independently too, also not confirmed). Practical lesson, sharpened
further: a wrong name can still mark a real, valuable discovery — the
struct-layout page exists only because the "IsValid" call address was
followed up on despite the name being wrong. Don't just discard a
bad-name call target; read what's actually there before writing it off.
The single most valuable capability to come out of this page: cross- referencing the symbol table (real names) against the validated call-target set (real, confirmed callers) directly. 960 functions across the whole 24MB region have BOTH — a real compiler-assigned name AND at least one confirmed real call site referencing them, the strongest evidence this project can currently produce for any given function's identity and liveness. 455 of those 960 are in the reachable <9MB range.
gold = [(name, off) for name, off in catalog.items() if off in call_targets]
reachable_gold = [(n, o) for n, o in gold if o < 9_000_000]Real, directly relevant examples from this set (all reachable, all
doubly-evidenced): IsValid__15EHDatagramGraph, GetFirstSegment__ 10EHTreeNode, Advance__10EHTreeNodeP10EHTreeNodeRP10EHTreeNode,
findStageId__C8MapRouteiPi, initAllocMemoryRCResult__9RoutePath P8RCResult, getInstance__14SegmentBlockerPP14SegmentBlockerP13RBP_ JR_Client. Caveat, confirmed concretely: being in this "gold" set
does NOT mean the naive symbol-table address is a clean, unambiguous
function start — most still land mid-body the same way already
documented elsewhere on this page (GetFirstSegment itself: naive
address 0x85400c isn't the scanned-backward prologue at 0x853d88,
and isn't itself a clean instruction stream either — tried and not
pursued further for that reason). Only 4 of the 455 reachable gold
functions are ALSO an exact, clean strict-prologue match with no
boundary ambiguity at all: getIso2CountryCode
(now traced — confirmed bounded, 1,332 bytes, ends in the 9th instance
of the central blr anomaly), CfcGuidanceSpeedTableFuture's own
operator new (corrected from an earlier claim this 4th slot was a
destructor — re-checked directly this session: the real destructor
isn't actually a clean match, operator new for the same class is;
see its page),
setRouteSectionDetourId
(partially traced — real early logic confirmed, but no matching
epilogue found within 768 bytes, so not fully bounded), and
prepareOutputForMap (the 5th
fully-traced function on this wiki, added from this list). All 4 now
have some real documentation — this specific, highest-confidence
list is fully worked through.
Triaged every other directly-relevant reachable gold name
(IsValid__15EHDatagramGraph, GetFirstSegment__10EHTreeNode,
Advance__10EHTreeNode..., findStageId__C8MapRoute,
initAllocMemoryRCResult__9RoutePath, and 10 more graph/tree/route
names) — none are boundary-clean. Every one lands mid-body of a
larger enclosing routine the same way already documented, confirming
this is a pervasive limitation of the current toolset, not something
fixable one function at a time. A direct search of the full 455-entry
reachable gold set for Vid/VNode/Arm/vseg found zero
matches — that terminology genuinely only exists in the confirmed-
unreachable 9MB+ region, consistent with everything already
established.
Tested and refuted a hypothesis for fixing the boundary problem
systematically: the symbol table's other 4 trailing fields (0-3,
never used before — only field 4 was) might encode the function's real
start/end range or size. Checked directly against the 2 functions with
a fully-known exact start AND end
(getId, prepareOutputForMap):
fields 0-3 do NOT bracket the function — they consistently point to a
completely different, shared location (~23.4-23.7MB, near the very end
of the 24MB region) for BOTH functions, not anywhere near the function
itself. Refuted, not just unconfirmed. The tight clustering is itself
a real, separate observation (most likely a shared RTTI/type-info or
personality-routine table referenced by many functions' EH records) —
a possible lead for future RTTI-focused work, but not for the boundary
problem. No obvious size field was found in the record's other header
bytes either.
Honest overall assessment: with the current toolset (load base, prologue finder, symbol table, call-target cross-reference, gold- catalog intersection), the boundary-ambiguity problem for functions whose naive/confirmed address lands mid-body appears to be a genuine, structural limitation, not a solvable per-function puzzle. Resolving it systematically would need either new ground truth (a real symbol map, hardware access) or a substantially larger tooling investment (a real control-flow-graph reconstruction pass, not a quick fix) — recorded here so a future session doesn't re-attempt the same already-tried ideas expecting a different result.
UPDATE, a later session: that "substantially larger tooling investment" now exists (the whole-file 98.9% function recovery above), and was used to re-test this at full scale with real ground truth instead of heuristics — with a self-correction along the way, recorded honestly rather than silently fixed.
First attempt, checked immediately after the whole-file recovery pass:
only 39 of 8,041 broad-catalog entries landed inside any real function,
8,002 landed inside none at all — seemingly a clean, strong
confirmation that the broad catalog's field-4 addresses mostly don't
correspond to real code at all, not merely imprecise ones. That
number was wrong, caught by re-checking with a fresh, isolated,
read-only script: the real, reproducible count is 7,851 of 8,041
(97.6%) DO fall inside a real recognized function — only 190 don't,
and of those 7,851, nearly all (7,815) land inside but not exactly at
the function's start (only 36 are an exact start match), consistent
with this page's own original, long-standing characterization of the
problem ("naive symbol-table address often lands mid-body"), not a
refutation of it. The most likely explanation for the earlier wrong
reading: real function coverage kept growing across this session's
many subsequent script runs after the initial 98.9% measurement (each
script's own auto_wait() lets IDA's background analysis queue
continue converging), so the first check simply ran before coverage
had caught up to where it is now — not a sign the method itself was
unsound.
Practical result, corrected: applied real names to IDA across
several honestly-labeled confidence tiers — 17 wiki-hand-verified +
28 gold-standard (45 total, clean names, no prefix); 202 more via a
rebuilt "real name + confirmed real call target" cross-check
(lis/addi forming an address actually used in a mtlr+blrl or
mtctr+bctrl call, not just a self-described address — marked
callconfirmed_); and 1,498 more from the broad catalog itself,
attached via containment to whatever real function the address falls
inside (marked unverified_, since a containment hit still isn't the
same as individually content-verified — this wiki has shown, more than
once, that a reloc-confirmed ADDRESS can still carry the WRONG name).
UPDATE, a later session: the callconfirmed_ tier is weaker than its
name suggests — downgrade it mentally to the same trust level as
unverified_. Deep-tracing GetFirstSegment__10EHTreeNode and
Advance__10EHTreeNodeP10EHTreeNodeRP10EHTreeNode — both genuinely in
the validated call-target set, not the raw broad catalog — found both
resolve to completely unrelated real functions
(setErrorCode__23PredefinedStatementImpl... and
updateWeatherLocation__Q213SDARSVWDATASV... respectively), the same
wrong-name pattern already documented many times elsewhere on this page
for the UNVALIDATED catalog. Only a 3rd validated name checked the same
way, SetSlope__15EHDatagramGraph, showed plausible content. Proving
some real caller references an address is not the same as proving the
catalog's name for that address is correct — both are real,
independent facts, but they don't imply each other, and this session's
own callconfirmed_ IDA labels should be read with exactly the same
skepticism as unverified_ ones until individually checked, not
treated as meaningfully more trustworthy just because the tier name
sounds stronger.
Follow-up, same session: found GetFirstSegment's real callers
directly (5 distinct ones) to see if the address's actual BEHAVIOR,
independent of any catalog name, supports the "EHTreeNode" theme. It
does, modestly: all 5 call it the identical way —
(&byte_F70E1C70[144])(), zero arguments, return value immediately
used as a context/handle for a following call — a consistent,
independently-corroborated behavioral signature across unrelated
functions. One caller's real logic (itself mislabeled, same
unreliability) shows a classic spatial-grid pattern: walk a linked
list (v6 = *v6), check each node's bounding coordinates against a
query range, store matches into a grid-indexed lookup array —
thematically consistent with tree/segment data, though not proof of
the specific name. Advance__10EHTreeNode...'s target address had
zero callers found by this same method — inconclusive (the scan
has known blind spots, e.g. r2/r13-relative addressing), not evidence
it doesn't exist. Net for this specific sub-thread: the real
EHTreeNode::GetFirstSegment/Advance tree-traversal algorithm
remains unrecovered — getting it would need the same slow, careful,
per-function manual verification this wiki's ~20 fully-traced pages
already use, not a catalog shortcut.
Follow-up, tried a 2nd class the same way, same session: RouteCalculation's
core search-algorithm methods (calculateRoute, successors,
penalty_cost, prune — the real shape of a graph-search routing
engine: cost function, successor generation, search-space pruning).
All 4 field-4 addresses land inside forced-boundary pseudo-functions,
2 of them (penalty_cost, prune) inside the exact same 6.77MB
desert already characterized above (descriptor-table-and-Java-content
region) — and none of the 4 have any real caller found by this
project's current reference-scanning method. This extends the
9MB+-wall finding: it isn't limited to the handful of originally-named
targets (readNodeMP0 etc.) — real methods of a completely different,
unrelated class are affected too, consistent with this being a
systematic problem (plausibly affecting an entire translation unit's
worth of symbol-table entries at once) rather than scattered
per-symbol corruption.
Pattern across 3 consecutive attempts this session (the original
9MB-wall targets, EHTreeNode's methods, now RouteCalculation's):
picking an exciting-sounding catalog name and trying to deep-trace it
directly is a low-yield approach for this codebase — it either lands
on wrong content or on no reachable content at all, consistently.
Getting real algorithmic content for named classes reliably still
means the same slow approach already used for this wiki's ~20
fully-traced pages: start from a real, independently-confirmed call
site or data structure and work outward, never trust a catalog address
in isolation.
Net: IDA's live database went from 45 to roughly 1,745 named functions this session, with the confidence level of each one legible directly from its name prefix (or lack of one) rather than hidden in a separate document.
The long-standing "9MB+ wall" mystery — substantially resolved using the whole-file function recovery
Background (the original mystery, documented in research/ swl_5238_reader.py's own docstring, pre-dating this wiki's shift to
wiki-only documentation): several real, named, high-value symbol-table
entries — readNodeMP0__9RoutePathUiR5VNodeH,
RoutePath_readNodeMP0__FP9RoutePathUiP5VNoded,
findNode__14MDCacheTIRTreeRC7MapRectPP18,
RoutePath_getNodeVidArmMP0__FP9RoutePathUiPUlPi — have symbol-table
field-4 "code addresses" implying file offsets around 11.9-17.3MB.
Static prologue scanning (find_prologues()) never found ANY real
stwu/mflr prologues anywhere near those addresses, confirmed dead
across 5 independent disc builds (EU and North America). Previous
sessions characterized this as code living beyond a structural "9MB+
wall," unreachable with the tools available at the time.
Re-tested with this session's whole-file IDA function recovery
(98.9% of all real prologues bound into functions across the complete
24MB region) — this gives much stronger ground truth than prologue-
pattern heuristics alone. All 4 target addresses land inside the
exact same forced-boundary pseudo-function IDA's recovery created:
0xf7383630-0xf79f7c38, a 6.77MB span (file offset
0xaf593c-0x1169f44) — not a real function at all, just the gap
between the nearest real prologues before and after a genuine,
enormous "prologue desert." This precisely bounds, for the first time,
exactly how large that desert is.
Characterized the desert's actual content directly (byte-level sampling + cross-checking against the already-known descriptor table) rather than leaving it as an unexplained void:
-
97% of it (25,530 of this project's 26,320 total known descriptor-
table records) is simply a long, uninterrupted run of the
already-documented symbol/EH-descriptor table (file offset
0xaf593c-0xd5e7f4, ~2.9MB) — explaining why no prologues exist there: it's real compiler-emitted metadata, not a code gap at all. 3 of the 4 mystery targets (readNodeMP0,RoutePath_readNodeMP0,RoutePath_getNodeVidArmMP0) land in this sub-range — their field-4 "code addresses" are simply wrong, pointing into the MIDDLE OF A DIFFERENT DESCRIPTOR-TABLE RECORD'S OWN BYTES rather than any real code (directly confirmed:RoutePath_readNodeMP0's own target address decodes as literal ASCII text,"nextAlternative__ 15PositioningImpl..."— unmistakably another record's mangled name, not an instruction). -
The remaining ~4.24MB (file offset
0xd5e7f4-0x1169f44, wherefindNode__14MDCacheTIRTreelands) is real embedded Java VM content — independently re-confirming and precisely extending a finding from an earlier NAR-disc session (research/ swl_5238_reader.py: "the 10-15MB strings are Java-class content, not native code"). Direct, unambiguous evidence sampled this session: a plain-ASCII Java classpath string,"classpath /tffs0/lib/vm/li[b]"(a real VxWorks TrueFFS flash path feeding a Java VM's own library path), plus multiple UTF-16LE (wide-character) strings — the standard encoding for Java's internalStringrepresentation — spelling recognizable fragments like"...lnk/LinkA...","...aBroadcast.*","Listen","Exceptio[n]","M_SETTINGS/A...". This extends the previously-measured ~10-15MB Java-content boundary out to at least ~18.3MB. UPDATE, a later session: this was only a narrow slice of something far bigger — see The Embedded Java Application Layer for a real, substantial (2,806+ class) embedded Java application platform spanning tens of megabytes across the whole file, including the real UI, navigation display logic, Bluetooth, climate control, and speech/voice recognition — none of which live in the native PowerPC C++ code this page otherwise documents.
Net resolution: the "9MB+ wall" was never a single, uniform phenomenon — it's two completely different, now-identified real structures sitting back-to-back (a dense run of known descriptor-table metadata, then a large embedded Java VM data/resource region), neither of which is "missing" or "unreachable" native PowerPC code at all. What remains genuinely unresolved (and likely unresolvable without new ground truth, e.g. the actual build/symbol-generation toolchain) is why these 4 specific entries' field-4 values point into this region rather than wherever their real compiled implementation actually lives — consistent with this project's existing, separately-confirmed finding that field-4 resolution is fundamentally unreliable beyond a narrow validated range, now with a concrete, physical explanation for where the "wrong" values actually land instead of just that they're wrong.
A "second load base" investigation: 3 more code regions found, 2 resolved to a different, real subsystem
A full find_prologues() sweep across the ENTIRE 85.7MB file (not just
the known 0-9MB window) found 3 dense prologue clusters beyond the
already-known native blob: ~25MB (374 prologues), ~30MB (362
prologues), and 76-82MB (6,130 prologues — this last one is the
already-known VxWorks kernel/BSP tail, not new). Candidate load bases
recovered per region with the same lis/addi clustering technique
gave 88.8%/88.2%/99.8% respectively — well above chance, but NONE of
the already-known unreachable targets (readNodeMP0,
findNode__14MDCacheTIRTree, db_seg_rank_V004, etc.) resolve into
any of them — this is a separate set of compilation units, not a hidden
continuation of the native map/navicore blob.
Whether 25MB/30MB were real code or find_prologues() false positives
inside the "24-76MB Java classes" territory was genuinely ambiguous at
first (zero VxWorks-identifying strings nearby, yet a clean 31-
instruction blr-terminated function on the first try). The
"does it reach a clean blr" test turned out to be UNDISCRIMINATING —
even the KNOWN-real 0-9MB region only hits a clean exit in ~10% of
sampled prologues within a 4000-byte window, same ballpark as the
candidates; don't trust this metric alone for this question again.
The decisive test was mangled C++ symbol names physically adjacent to
the code, using the same "name string precedes its function's
prologue by ~100-300 bytes" layout as this page's own symbol table.
25MB's names are all Q33vdo3svc*/Q33vdo4util* (a vdo::svc/
vdo::util namespace — Service/Dispatcher/String plumbing, matching
DVD/AV-app Java strings found nearby — a genuinely separate multimedia-
subsystem module). 30MB's names (only 3 in the entire 85.7MB file,
tightly clustered) are GEO_PntPnt_UCangle, GEO_RpntPnt_NCpnt,
GON_YXL_Atan — real geo-coordinate math type descriptors — but full
disassembly of the adjacent prologue shows a large register-save frame
and a repeated lwz->mtlr->blrl virtual-dispatch-through-a-service-
object idiom, i.e. a command/event dispatch REGISTRATION routine
(the same vdo::svc::dispatcher::Dispatcher role as 25MB), not the
geo-math leaf functions themselves — their own bodies remain
unidentified (their names appear nowhere else in the file). Confirmed
real, newly-identified architecture (a service dispatcher spanning
25-30MB across multiple unrelated subsystems, including telephony/PTT
names found in the same table) — but not a solution to this page's own
open readNodeMP0/R-tree questions.
Applying this page's own lis/addi reloc-confirmation technique
exhaustively across the whole 24MB region (rather than one call site
at a time) surfaced three entire previously-undocumented real
subsystems — SDARS satellite radio data services (sports scores, song
tagging, fuel prices), real RDS-TMC traffic processing, and satellite
weather radar overlay — plus extensive SQLite/zlib confirmation. Full
writeup, method, and the archived 1,573-entry catalog:
Firmware Reloc-Confirmed Names.
import sys
sys.path.insert(0, "research")
import swl_5238_reader as swl
import capstone
with open("5238_ALL/APPS/SILVER_1/RNSMIDEC/PROG/FHDD6.FLI", "rb") as f:
fli = f.read()
prologues = swl.find_prologues(fli[:24_000_000]) # 7,757 real function starts
catalog = swl.parse_symbol_table(fli) # 8,041 name -> file_offset
cs = capstone.Cs(capstone.CS_ARCH_PPC,
capstone.CS_MODE_BIG_ENDIAN | capstone.CS_MODE_32)
off = catalog["getId__C26CfcMsgStreamedServicePoint"]
va = off + swl.FHDD6_LOAD_BASE
for insn in cs.disasm(fli[off:off + 400], va):
print(hex(insn.address - swl.FHDD6_LOAD_BASE), insn.mnemonic, insn.op_str)
if insn.mnemonic == "blr":
breakFull narrative, every validation number, and every dead end tried (so
they aren't repeated): research/swl_5238_reader.py's own module
docstring — search for "load base WAS recovered", "symbol/EH-
descriptor table", and the function names above.
Road network
- eeu.rd
- eeu.il
- eeu.iof
- eeu.typ
- rt / rl / prl
- eeu.si (segment classification)
- eeu.tmc (TMC traffic data)
Places
- eeu.ctr
- cty / cl / ct
- eeu.cny (counties)
- eeu.stt (states/regions)
- eeu.ti (time-info/DST)
- pmc / pmm (postal-code merge)
- pol / pot / pmp (postal-code, empty)
Language / TTS support
- eeu.abc (alphabet)
- eeu.aff (affix)
- eeu.cal (country names)
- eeu.cat (country bboxes)
- prd (road phonetics)
- pct (city phonetics)
- pcl (phoneme cluster list)
- pca (country phonemes)
Map tiles
Container formats
Cross-cutting
- eeu.mod (schema dictionary)
- Road naming join
- Header trailer fields
- Map viewer tool
- Road editor tool
- Unexamined files
- Disc-root build script & subsystems
Firmware reversing