Skip to content

patchir-decomp: whole-program lift aborts with ClangIR NYI 'emitDeclRefLValue: static local' #278

Description

@kumarak

Description

Lifting a whole statically-linked firmware image to CIR aborts inside vendored ClangIR with:

Diag Error: ClangIR code gen Not Yet Implemented: emitDeclRefLValue: static local
Assertion failed: (isValid()), function getPointer, file Address.h, line 91.

The message is a misnomer: in this ClangIR (clang/lib/CIR/CodeGen/CIRGenExpr.cpp, emitDeclRefLValue) the errorNYI("emitDeclRefLValue: static local") branch is taken for any local variable / parameter DeclRefExpr that is missing from the function's localDeclMap — not only genuine static locals. The errorNYI then returns an invalid Address, and the immediate getPointer() use aborts at Address.h:91.

Key observations:

  • It only reproduces in the whole-program lift. The function implicated in the failure lifts cleanly when decompiled on its own (and together with its direct callers), so it is not a defect in that function's own lifting.
  • The emitted C (-print-tu) for the implicated function shows no genuine function-scope static locals and no duplicate-arity declarations.
  • The result is non-deterministic between runs (see the existing unordered_set iteration-order non-determinism in decomp), so the specific function/variable can vary.

This is independent of CIR emission order: the name-ordered emission added in #276 does not prevent it.

How to reproduce

Using the in-tree statically-linked firmware firmwares/output/ventilator/controller-firmware.elf:

# 1. Serialize the whole program to P-Code JSON
bash scripts/ghidra/decompile-headless.sh \
    --input firmwares/output/ventilator/controller-firmware.elf \
    --output /tmp/controller-firmware.json

# 2. Lift the whole program to CIR -> aborts
patchir-decomp -input /tmp/controller-firmware.json -emit-cir -output /tmp/controller
# Diag Error: ... Not Yet Implemented: emitDeclRefLValue: static local
# Assertion failed: (isValid()), function getPointer, file Address.h, line 91.

Contrast — the same function lifts cleanly in isolation (no abort):

bash scripts/ghidra/decompile-headless.sh \
    --input firmwares/output/ventilator/controller-firmware.elf \
    --function strncmp --output /tmp/strncmp.json
patchir-decomp -input /tmp/strncmp.json -emit-cir -output /tmp/strncmp   # OK

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions