llvm / llvm/llvm-project

[lldb] DW_OP_drop leaves stale location state after dropping register or implicit entries

Open
#209,582 1 comment 0 reactions 0 assignees View on GitHub
lldb
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

LLDB removes the top stack entry for `DW_OP_drop`, but leaves the evaluator-wide location-description state set by that entry instead of restoring the state for the new stack top, or clearing it when the stack becomes empty.

`EvalContext` stores all stack entries in `stack`, but stores only one evaluator-wide `loc_desc_kind`. `DW_OP_reg*` sets this field to `Register`, and `DW_OP_implicit_value` sets it to `Implicit`. However, [`DW_OP_drop`](https://github.com/llvm/llvm-project/blob/main/lldb/source/Expression/DWARFExpression.cpp) only removes the value:

```cpp
case DW_OP_drop:
...
stack.pop_back();
break;
```

LLVM's [location-description stack extension](https://llvm.org/docs/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack.html#a-2-5-4) defines `DW_OP_drop` as discarding the top stack entry. The location state belonging to that entry must therefore stop affecting evaluation, but LLDB leaves it active and applies it to later entries.

## Examples

Let `A` be a valid target-memory address. The first example is:

```text
DW_OP_reg0
DW_OP_drop
DW_OP_addr A
DW_OP_deref_size 2
```

The second example reaches the same problem through an implicit location:

```text
DW_OP_implicit_value 1, [0x00]
DW_OP_drop
DW_OP_addr A
DW_OP_deref_size 2
```

In both expressions, the first two operations are semantically dead. Each expression must therefore behave like:

```text
DW_OP_addr A
DW_OP_deref_size 2
```

However, `DW_OP_addr` pushes a `FileAddress` without changing `loc_desc_kind`. The stale `Register` or `Implicit` state therefore reaches `Evaluate_DW_OP_deref`, which takes this early branch:

```cpp
if (eval_ctx.loc_desc_kind == Register ||
eval_ctx.loc_desc_kind == Implicit) {
eval_ctx.loc_desc_kind = Memory;
...
scalar.TruncOrExtendTo(size * 8, /*sign=*/false);
...
return llvm::Error::success();
}
```

Consequently, LLDB truncates the scalar representation of `A` and returns instead of following the normal `FileAddress` path that reads `size` bytes from target memory. This explains why either dead prefix changes the evaluation.

`DW_OP_deref_size` is not the only affected operation. For example, `Evaluate_DW_OP_piece` also captures the same evaluator-wide state and uses it to interpret the current piece source:

```cpp
LocationDescriptionKind piece_locdesc = eval_ctx.loc_desc_kind;
...
UpdateValueTypeFromLocationDescription(eval_ctx, piece_locdesc,
&curr_piece_source_value);
```

Contributor guide

Open the contributing guide

Research direction

Start in lldb/source/Expression/DWARFExpression.cpp at the DW_OP_drop handling, then trace Evaluate_DW_OP_deref and Evaluate_DW_OP_piece to understand how loc_desc_kind is consumed. Add regression coverage for the register and implicit-value prefixes shown in the issue, and verify they behave like the equivalent expression without the dropped entries.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.