Skip to content

Panic: index out of bounds in ARM arch process_code on ELF with no STT_FUNC symbols in .text #352

@cntrl-alt-lenny

Description

@cntrl-alt-lenny

Summary

objdiff-cli report generate panics with index out of bounds: the len is 0 but the index is 18446744073709551615 at objdiff-core/src/arch/arm.rs:130:29 when an ARM ELF object has a non-empty .text section but no STT_FUNC symbol covering it — i.e. either zero symbols pointing into the .text section, or only STT_OBJECT/STT_SECTION symbols there.

The 18446744073709551615 is usize::MAX, which suggests a 0_usize - 1 underflow when the code-symbol iteration yields an empty list.

Reproduction

Hit reliably while running objdiff-cli report generate on the Yu-Gi-Oh! GX Spirit Caller decomp project (Nintendo DS, ARM, mwccarm 2.0/sp1p5 toolchain via ds-decomp). Brief 187 ([PR #pending]) ships a workaround filter in the project's own tooling. The relevant tool is objdiff-cli v2.7.1.

Minimal panic command:

RUST_BACKTRACE=1 ./objdiff-cli report generate -p . -o /tmp/r.json -d

(-d forces single-threaded so the panic surfaces deterministically.)

Failure modes that trigger the panic

We bisected to find every crashing unit and grouped them by ELF shape:

  1. .text exists with content but no symbol covers it. Example: a delinked TU with .text carrying real ARM instruction bytes (size 0x86 in our case) but only STT_SECTION (type 3) section symbols and external (undefined) function references. No STT_FUNC symbol with non-zero st_size and st_shndx == .text.

  2. .text exists with content but only STT_OBJECT symbols cover it. Example: a BuildInfo data blob the linker placed in .text instead of .rodata (size 0xcc, single STT_OBJECT symbol). The ARM arch processor tries to disassemble it as code → crashes.

  3. Empty .text section. Often combined with (1) — zero useful symbols.

Backtrace

thread '<unnamed>' panicked at objdiff-core/src/arch/arm.rs:130:29:
index out of bounds: the len is 0 but the index is 18446744073709551615
stack backtrace:
   0: _rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic_bounds_check
   3: <objdiff_core::arch::arm::ObjArchArm as objdiff_core::arch::ObjArch>::process_code
   4: objdiff_core::diff::code::process_code_symbol
   5: objdiff_core::diff::diff_objs
   6: objdiff_cli::cmd::report::report_object
   ...

Minimal reproducer

Tiny ARM ELF (1020 bytes) that triggers the panic — has .text size 0x86 but only STT_SECTION + external-reference symbols (no STT_FUNC in .text).

Base64-encoded `repro.o`
f0VMRgEBAQAAAAAAAAAAAAEAKAABAAAAAAAAAAAAAADkAgAAAAAAADQAAAAAACgABwAGAP/e/+f/3v/n/97/5//en9uRPDQp6rfavPMZToUu5GvkIJkkiAaBdvlJWIXX5U8uiEcvduaXm2EkY8+44A7gB0JWZ693X7or5yjBkmMf+PkC/TYZvt89ylv7Rc9obFfNWkVPlcq1L+ScC8dR4cF6oLYS9ZGe9a20XMlApUhsM7kufxvm10q+AAAAAAwAAAAYAAAA/////w4AAAD/////DwAAAP////8QAAAA/////wUAAAD/////CAAAAP////8HAAAA/////wkAAAD/////CgAAAP////8LAAAA/////wIAAAD/////EgAAAP////8TAAAA/////xQAAAD/////FQAAAP////8XAAAA/////xkAAAD/////GgAAAP////8lAAAA/////yEAAAD/////HAAAAP////8dAAAA/////x8AAAD/////IAAAAP////8bAAAA/////wQAAAD/////IgAAAP////8jAAAA/////0xDMDgAAAAATEMxMAAAAABMQzEyAAAAAExDMTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwABAAAAAAAAAAAAAAAAAAMAAgAdAAAAAAAAAAAAAAAgAAAAOQAAAAAAAADoAAAAEQACAA8AAADoAAAACAAAABEAAgBHAAAA8AAAAAgAAAARAAIAAQAAAPgAAAAIAAAAEQACACsAAAAAAQAACAAAABEAAgAAZGF0YV8wMjBjM2NiOABkYXRhXzAyMGMzY2E4AGZ1bmNfMDIwMDBjNDQAZGF0YV8wMjBjM2NjMABkYXRhXzAyMGMzYmMwAGRhdGFfMDIwYzNjYjAAAAAAAAAAAAIDAAAAAAAAAC50ZXh0AC5zaHN0cnRhYgAuc3RydGFiAC5zeW10YWIALnJlbGEuZGF0YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAGAAAAAAAAADQAAACGAAAAAAAAAAAAAAACAAAAAAAAACYAAAABAAAAAwAAAAAAAAC6AAAACAEAAAAAAAAAAAAAAQAAAAAAAAAhAAAABAAAAEAAAAAAAAAArAIAAAwAAAAEAAAAAgAAAAQAAAAMAAAAGQAAAAIAAAAAAAAAAAAAAMQBAACQAAAABQAAAAQAAAAEAAAAEAAAABEAAAADAAAAAAAAAAAAAABUAgAAVQAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAAwAAAAAAAAAAAAAAuAIAACwAAAAAAAAAAAAAAAEAAAAAAAAA

To repro:

base64 -d > repro.o <<EOF_B64
[paste base64 from above]
EOF_B64

cat > objdiff.json <<EOF_JSON
{
  "min_version": "2.0",
  "custom_make": "ninja",
  "target_dir": "",
  "base_dir": "",
  "units": [
    {"name": "repro", "target_path": "repro.o", "metadata": {}}
  ]
}
EOF_JSON

objdiff-cli report generate -p . -o /tmp/r.json -d

Expected: report.json generated. Actual: panic at arm.rs:130.

Suggested fix direction

process_code in objdiff-core/src/arch/arm.rs likely iterates over symbols pointing into the code section and then derives a span end via something like symbols[idx + 1].address or symbols.last().address + symbols.last().size — when symbols is empty, the index/range math underflows. Guarding the iteration with if symbols.is_empty() { return Ok(()); } (or equivalent) at the function head should make the panic into a clean no-op for code-less / data-only .text sections.

(Same shape applies whether all symbols are STT_OBJECT or simply absent — happy to test a fix branch against our project's full 3330-unit corpus if helpful.)

Project context

This panic blocks objdiff-verified code-match metrics for the entire decomp — build/<region>/report.json never gets written, so we can't measure function-level matching progress without a workaround. Brief 187 ships a project-local filter (tools/objdiff_filter_panic_units.py) that drops the ~20 affected units before invoking objdiff-cli report generate, but the right fix is upstream.

Filed from the spirit-caller project's brief 187 PR — happy to share more diagnostics or test patches.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions