Firmware Fn mv generate mnvr name - krcenov/RNS510-Map-Viewer GitHub Wiki

mv_generate_mnvr_name

Part of Firmware Reversing.

  • Naive symbol-table address: 0x5b54f8 (VA 0xf6e431ec)
  • True, confirmed start: 0x5b5314 (VA 0xf6e43008) — the original "boundary ambiguity" concern below is now resolved: this genuinely IS the function's own complete, standalone start.
  • Status: 🟡* Partially traced — real logic confirmed throughout (including the real explanation for the r31 puzzle below); not transcribed past the first ~200 bytes for length.
  • Full mangled name: mv_generate_mnvr_name__FibP14Mv_Info_Struct (global function, int, bool, Mv_Info_Struct*)

Part of the maneuver-generator (mv_*) internals — see calculateManeuvers for the subsystem's real entry point.

The r31 puzzle — resolved, not a boundary problem

At 0x5b5344/0x5b5348, the code does stw r31,0(r31) / stw r31,4(r31) — using r31 as BOTH the value stored and the target address, with no visible assignment to r31 beforehand (only a save of its INCOMING value to the stack at 0x5b5324, standard callee-save preamble). The same is true of r30, used as a live pointer from 0x5b5350 on with no assignment either. Originally flagged as a possible repeat of the real boundary-undershoot bug confirmed on mv_check_turn.

Checked directly and ruled out. Disassembling the immediately preceding real function (prologue at 0x5b526c, unnamed in the catalog) shows it ending completely independently: builds a real doubly-linked-list node (calling getOffroadDistanceFromRouteToDestination__25CfcTypeRoadClassStatistic and getCertainty_total__23CfcTypePossibleLocation — both already seen elsewhere on this wiki — then linking the new node into a list via r25/r26/r27/r29), pops its own stack frame, and ends in the same "no locally-visible blr" shape documented centrally on Firmware Reversing — immediately followed by mv_generate_mnvr_name's own clean, fresh stwu/mflr prologue at 0x5b5314. This is a real, independent function boundary, not a continuation — so 0x5b5314 genuinely is this function's own complete start.

The real explanation: r30 and r31 are real, additional arguments passed in via callee-saved registers rather than the standard r3-r10 parameter registers — a legitimate, if non-obvious, GCC calling-convention choice available for a static/ internal-linkage helper like this one, where no external ABI contract constrains register assignment. The function still faithfully SAVES the caller's original r30/r31 to the stack and restores them before returning (so the caller's own values are preserved across the call, exactly as the callee-saved contract requires) — it just also reads their INCOMING values as real inputs before doing so, which is what looked like "uninitialized use" at first glance. Net effect: this function's REAL effective signature carries (at least) 2 more pointer-shaped inputs than its mangled name declares — not unusual for a compiler-internal helper that's always called from known, compiler-visible call sites.

What was disassembled

0x5b5314: stwu     r1, -0xa0(r1)
0x5b5318: mflr     r0
0x5b531c: stw      r29, 0x94(r1)
0x5b5320: stw      r30, 0x98(r1)
0x5b5324: stw      r31, 0x9c(r1)
0x5b5328: stw      r0, 0xa4(r1)
0x5b5334: mr       r29, r4                  ; arg2
0x5b5340: li       r10, 0
0x5b5344: stw      r31, 0(r31)              ; r31 = real incoming arg, see above
0x5b5348: stw      r31, 4(r31)
0x5b534c: lwz      r9, 4(r29)
0x5b5350: lwz      r11, 4(r30)
0x5b5354: lwz      r0, 0(r9)
0x5b5358: stw      r10, 8(r30)
0x5b535c: cmpw     r0, r9
0x5b5360: stw      r11, 0x30(r1)
0x5b5364: stw      r0, 0x10(r1)
0x5b5368: stw      r9, 0x20(r1)
0x5b536c: beq      0x5b53bc
0x5b5370: lis      r9, -0x8f2
0x5b5374: addi     r31, r9, 0x74
0x5b5378: lwz      r9, 0x10(r1)             ; <- loop-back target
0x5b537c: addi     r3, r1, 0x40
0x5b5380: lwz      r0, 0x30(r1)
0x5b5384: mr       r4, r30
0x5b5388: addi     r6, r9, 8
0x5b538c: mtlr     r31
0x5b5390: addi     r5, r1, 0x50
0x5b5394: stw      r9, 0x78(r1)
0x5b5398: stw      r0, 0x50(r1)
0x5b539c: lwz      r0, 0(r9)
0x5b53a0: stw      r0, 0x10(r1)
0x5b53a4: stw      r9, 0x60(r1)
0x5b53a8: blrl                              ; virtual call, inside a real loop
0x5b53ac: lwz      r9, 0x10(r1)
0x5b53b0: lwz      r0, 0x20(r1)
0x5b53b4: cmpw     r9, r0
0x5b53b8: bne      0x5b5378                 ; loop -- this one stays within bounds
0x5b53bc: mr       r3, r30
0x5b53c0: lwz      r0, 0xa4(r1)
0x5b53c4: mtlr     r0
0x5b53c8: lwz      r29, 0x94(r1)
0x5b53cc: lwz      r30, 0x98(r1)
0x5b53d0: lwz      r31, 0x9c(r1)
0x5b53d4: addi     r1, r1, 0xa0

(reaches a real blr further on, at 0x5b5578, after more virtual calls not reproduced here for length — none of them loop backward past the confirmed start.)

Analysis

A real linked-list-style loop (0x5b5378-0x5b53b8: walk while lwz r9,4(r29) chain doesn't return to its own head, calling a virtual method each iteration) over the Mv_Info_Struct*-shaped data passed in via r30. Consistent with the function's own name — generating a maneuver name/description likely involves walking a list of info records (street names, distances, etc.) to compose the final string.

Callees

Not traced beyond the virtual-call sites shown above (none resolved to named symbols — all genuine virtual/indirect calls through computed vtable-style pointers, not direct lis/addi references to named functions).