DW_OP_piece 0 does not consume the location description on the stack
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
LLDB returns success for `DW_OP_piece 0` before consuming its preceding source location. The stale source remains on the expression stack and can later override the actual composite result.
[DWARF v5](https://dwarfstd.org/doc/DWARF5.pdf) Section 2.6.1.2 describes composite locations as a sequence of simple location descriptions followed by composition operations. LLVM's [location-description stack extension](https://llvm.org/docs/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack.html) similarly defines `DW_OP_piece` as an incremental composite-location operation.
DWARF does not explicitly prohibit a zero-sized `DW_OP_piece`. If LLDB accepts it as a piece that contributes no bytes, it must still treat the operation as a composition boundary rather than leave its source live as an ordinary stack entry.
On x86-64, one compact equivalent case is:
```text
DW_OP_reg0
DW_OP_piece 4
```
and:
```text
DW_OP_reg3
DW_OP_piece 0
DW_OP_reg0
DW_OP_piece 4
```
Here `DW_OP_reg0` is RAX and `DW_OP_reg3` is RBX. The first expression describes the first four bytes of RAX. The second expression adds a zero-sized RBX piece before it. That piece contributes no bytes, so the resulting composite location should still describe the same four-byte RAX fragment.
GDB treats the zero-piece form consistently with the base form. In LLDB, the stale RBX source remains on the expression stack and masks the four-byte composite result assembled from RAX.
## Source Evidence
In [`DWARFExpression.cpp`](https://github.com/llvm/llvm-project/blob/main/lldb/source/Expression/DWARFExpression.cpp), `Evaluate_DW_OP_piece` resets the evaluator's location-description kind and then immediately returns for zero-sized pieces:
```cpp
static llvm::Error Evaluate_DW_OP_piece(EvalContext &eval_ctx,
uint64_t piece_byte_size) {
LocationDescriptionKind piece_locdesc = eval_ctx.loc_desc_kind;
// Reset for the next piece.
eval_ctx.loc_desc_kind = Memory;
if (piece_byte_size == 0)
return llvm::Error::success();
Value curr_piece;
if (eval_ctx.stack.empty()) {
...
} else {
Value curr_piece_source_value(eval_ctx.stack.back());
eval_ctx.stack.pop_back();
UpdateValueTypeFromLocationDescription(eval_ctx, piece_locdesc,
&curr_piece_source_value);
...
}
```
The stack-pop path is below the zero-size early return, so `DW_OP_piece 0` does not consume the current source fragment.
This matters because LLDB returns the composite piece buffer only when the expression stack is empty:
```cpp
if (stack.empty()) {
if (eval_ctx.pieces.GetBuffer().GetByteSize())
return eval_ctx.pieces;
return llvm::createStringError("stack empty after evaluation");
}
UpdateValueTypeFromLocationDescription(eval_ctx, eval_ctx.loc_desc_kind,
&stack.back());
return stack.back();
```
After the final `DW_OP_piece 4` appends the real RAX fragment to `eval_ctx.pieces`, the RBX source left by `DW_OP_piece 0` keeps the stack nonempty. LLDB consequently returns that stale register location instead of returning the completed composite value.
Contributor guide
Research direction
Start in lldb/source/Expression/DWARFExpression.cpp at Evaluate_DW_OP_piece, then trace how eval_ctx.stack and eval_ctx.pieces are handled for the two x86-64 expressions in the report. Reproduce the zero-sized-piece case and verify that the source is consumed and the completed four-byte composite location is returned instead of the stale register location.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100