NationalSecurityAgency/ghidra · error · RuntimeError

Expression {expression} does not have an address

Error message

Expression {expression} does not have an address

What it means

Thrown by ghidra_trace_putval when the evaluated value's SBValue.addr is not valid. putval only makes sense for values that live in memory (their bytes can be copied into the trace); pure registers and compile-time constants have no address, so the command refuses them.

Source

Thrown at Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py:775

    args = shlex.split(command)
    if len(args) == 1:
        expression = args[0]
        pages = True
    elif len(args) == 2:
        expression = args[0]
        pages = (util.get_eval(args[2]).unsigned != 0)
    else:
        raise RuntimeError("Usage: ghidra trace putval EXPRESSION [PAGES]")

    STATE.require_tx()
    try:
        value = util.get_eval(expression)
        address = value.addr
    except BaseException as e:
        raise RuntimeError(f"Could not evaluate {expression}: {e}")
    if not address.IsValid():
        raise RuntimeError(f"Expression {expression} does not have an address")
    start = int(address)
    end = start + start + value.size
    return put_bytes(start, end, result, pages)


def putmem_state(address: str, length: str, state: str,
                 pages: bool = True) -> None:
    trace = STATE.require_trace()
    trace.validate_state(state)
    start, end = eval_range(address, length)
    if start is None or end is None:
        return
    if pages:
        start, end = quantize_pages(start, end)
    proc = util.get_process()
    base, addr = trace.extra.require_mm().map(proc, start)
    if base != addr.space:
        trace.create_overlay_space(base, addr.space)

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Use a memory-resident expression: a global variable, a dereferenced pointer, or a stack object.
  2. To record registers, use 'ghidra trace putreg' instead of putval.
  3. Take the address explicitly if you meant the storage: 'putval &myvar'.

Example fix

// before
ghidra trace putval 0xdeadbeef
// after
ghidra trace putval global_buffer
Defensive patterns

Strategy: type-guard

Validate before calling

from ghidralldb import util

def has_memory_address(expr: str) -> bool:
    try:
        return util.get_eval(expr).addr.IsValid()
    except BaseException:
        return False

Type guard

def is_memory_resident(value: 'lldb.SBValue') -> bool:
    return bool(value.addr.IsValid())

Try / catch

value = util.get_eval(expression)
if not value.addr.IsValid():
    # fall back to recording via putreg if it is a register
    ghidra_trace_putreg(debugger, '<group>', result, internal_dict)

Prevention

When it happens

Trigger: Passing an immediate/constant expression (e.g. 'putval 42'), a register-only value, or an rvalue whose address LLDB cannot materialize. value.addr.IsValid() returns false for these.

Common situations: User treats putval as 'record any value' rather than 'record a memory-resident value'; trying to record a register (use putreg instead); passing a literal number.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/0dde18ac01d39143. Report an issue: GitHub.