llvm / llvm/llvm-project

[BOLT] DWARF v5 -O3 GCC --update-debug-sections leaves DW_AT_GNU_locviews stale after rewriting .debug_loclists

Open
#206,552 2 comments 0 reactions 0 assignees View on GitHub
BOLT
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Description

When `llvm-bolt` is run with `--update-debug-sections` on a C++ binary built with GCC, DWARF 5, and `-O3`, it rewrites `.debug_loclists` but does not preserve GNU Location Views (`DW_AT_GNU_locviews`). BOLT rewrites location lists and updates related attributes such as `DW_AT_loclists_base` and `loclistx indices`, yet it neither emits view-pair bytes before the new lists nor remaps `DW_AT_GNU_locviews` — those offsets are left identical to the pre-BOLT binary. The debug metadata is therefore actively incorrect: `DW_AT_GNU_locviews` still references old absolute offsets that no longer contain view-pair data (in some cases overlapping the new offset entry table). Location Views help debuggers distinguish multiple source-level inspection points that may share the same PC after optimization; stale view metadata can degrade stepping and variable inspection under `-O3`, even when the location list expressions themselves are otherwise correct.

`llvm-dwarfdump` reports no issues, but `readelf --debug-dump=loc` reports warnings such as invalid view offsets and broken adjacency between view lists and location lists.

On a large `-O3` binary thousands of similar readelf warnings is observed (`.debug_loclists` holes / invalid view offsets); this minimal repro shows the same root cause.

## Environment
- **BOLT:** `llvm-bolt` (example: `BOLT version: e1aa9d4cf0952b88010c38bae23ea9c651ae08c7`)
- **Target:** `x86_64`
- **Compiler:** `g++ (GCC) 14.2.0 with -gdwarf-5 -O3 -g -gz=none -fno-reorder-blocks-and-partition`
- **Link:** `-Wl,-q -Wl,--emit-relocs -gz=none -fno-reorder-blocks-and-partition`
- **Binutils:** `GNU readelf (GNU Binutils) 2.46`

## Reproduction code
A single translation unit may emit only one `DW_AT_GNU_locviews `and may not trigger warnings. The reproduction requires at lest two object files (`main.cpp + helper.cpp`).

### `main.cpp`
```cpp
volatile int sink;

__attribute__((noinline))
void use(int *p) { sink = *p; }

__attribute__((noinline))
int foo(int n) {
int a = n, b = n + 1, c = n + 2;
use(&a); a = b; use(&a);
use(&b); b = c; use(&b);
return a + b + c;
}

int helper(int n);

int main(int argc, char **) {
return foo(argc) + helper(argc);
}
```

### `helper.cpp`
```cpp
extern volatile int sink;
extern void use(int *);

__attribute__((noinline))
int helper(int n) {
int x = n, y = n * 2, z = n * 3;
use(&x); x = y; use(&x);
use(&y); y = z; use(&y);
return x + y + z;
}
```

### Build

```bash
FLAGS="-gdwarf-5 -O3 -g -gz=none -fno-reorder-blocks-and-partition"

g++ $FLAGS -c main.cpp -o main.o
g++ $FLAGS -c helper.cpp -o helper.o
g++ main.o helper.o -o locview_exe -Wl,-q -Wl,--emit-relocs -gz=none -fno-reorder-blocks-and-partition
```

### Run BOLT

```bash
llvm-bolt locview_exe -o locview_exe.bolt --update-debug-sections
```

### Observe

#### Before BOLT: GNU locviews are present
```bash
[execute] llvm-dwarfdump --debug-info locview_exe | grep GNU_locviews

DW_AT_GNU_locviews (0x0000000c)
DW_AT_GNU_locviews (0x0000002e)
DW_AT_GNU_locviews (0x00000050)
DW_AT_GNU_locviews (0x0000006b)
DW_AT_GNU_locviews (0x00000085)

[execute] readelf --debug-dump=loc locview_exe
Contents of the .debug_loclists section:

Table at Offset 0
Length: 0x5b
DWARF version: 5
Address size: 8
Segment size: 0
Offset entries: 0

Offset Begin End Expression

0000000c v000000000000000 v000000000000000 location view pair
0000000e v000000000000000 v000000000000000 location view pair
00000010 v000000000000000 v000000000000000 location view pair

00000012 0000000000401020 (base address)
0000001b v000000000000000 v000000000000000 views at 0000000c for:
0000000000401020 0000000000401027 (DW_OP_reg5 (rdi))
00000020 v000000000000000 v000000000000000 views at 0000000e for:
0000000000401027 0000000000401030 (DW_OP_reg4 (rsi))
00000025 v000000000000000 v000000000000000 views at 00000010 for:
0000000000401030 0000000000401035 (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
0000002d

0000002e v000000000000000 v000000000000000 location view pair
00000030 v000000000000000 v000000000000000 location view pair
00000032 v000000000000000 v000000000000000 location view pair

00000034 0000000000401140 (base address)
0000003d v000000000000000 v000000000000000 views at 0000002e for:
0000000000401140 0000000000401153 (DW_OP_reg5 (rdi))
00000042 v000000000000000 v000000000000000 views at 00000030 for:
0000000000401153 000000000040115b (DW_OP_breg5 (rdi): 0)
00000048 v000000000000000 v000000000000000 views at 00000032 for:
000000000040115b 0000000000401180 (DW_OP_breg1 (rdx): -2; DW_OP_stack_value)
0000004f

00000050 v000000000000000 v000000000000000 location view pair
00000052 v000000000000000 v000000000000000 views at 00000050 for:
000000000040114a 0000000000401180 (DW_OP_reg1 (rdx))
0000005e

Table at Offset 0x5f
Length: 0x36
DWARF version: 5
Address size: 8
Segment size: 0
Offset entries: 0

Offset Begin End Expression

0000006b v000000000000000 v000000000000000 location view pair
0000006d v000000000000000 v000000000000000 location view pair
0000006f v000000000000000 v000000000000000 location view pair

00000071 v000000000000000 v000000000000000 views at 0000006b for:
0000000000401180 000000000040119b (DW_OP_reg5 (rdi))
00000076 v000000000000000 v000000000000000 views at 0000006d for:
000000000040119b 000000000040119f (DW_OP_breg5 (rdi): 0)
0000007c v000000000000000 v000000000000000 views at 0000006f for:
000000000040119f 00000000004011da (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
00000084

00000085 v000000000000000 v000000000000000 location view pair
00000087 v000000000000000 v000000000000000 location view pair

00000089 v000000000000000 v000000000000000 views at 00000085 for:
0000000000401198 00000000004011d8 (DW_OP_reg3 (rbx))
0000008e v000000000000000 v000000000000000 views at 00000087 for:
00000000004011d8 00000000004011da (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_lit3; DW_OP_mul; DW_OP_stack_value)
00000098
```

#### After BOLT: attribute still present, but section layout is inconsistent

```bash
[execute] llvm-dwarfdump --debug-info locview_exe.bolt | grep GNU_locviews

DW_AT_GNU_locviews (0x0000000c)
DW_AT_GNU_locviews (0x0000002e)
DW_AT_GNU_locviews (0x00000050)
DW_AT_GNU_locviews (0x0000006b)
DW_AT_GNU_locviews (0x00000085)

[execute] readelf --debug-dump=loc locview_exe.bolt
Contents of the .debug_loclists section:


Table at Offset 0
Length: 0x44
DWARF version: 5
Address size: 8
Segment size: 0
Offset entries: 3

Offset Entries starting at 0xc:
[ 0] 0xc
[ 1] 0x21
[ 2] 0x36

Offset Begin End Expression

0000000c v00000000000000c v000000000000000 location view pair
0000000e v000000000000000 v000000000000000 location view pair
00000010 v000000000000021 v000000000000000 location view pair
00000012 v000000000000000 v000000000000000 location view pair
00000014 v000000000000036 v000000000000000 location view pair
00000016 v000000000000000 v000000000000000 location view pair

00000018 0000000000000000 (index into .debug_addr) 000000000080001c (base address)
0000001a v00000000000000c v000000000000000 views at 0000000c for:
000000000080001c 0000000000800023 (DW_OP_reg5 (rdi))
0000001f v000000000000000 v000000000000000 views at 0000000e for:
0000000000800023 000000000080002c (DW_OP_reg4 (rsi))
00000024 v000000000000021 v000000000000000 views at 00000010 for:
000000000080002c 0000000000800031 (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
0000002c

readelf: Warning: Hole and overlap detection requires adjacent view lists and loclists.

0000002d 0000000000000001 (index into .debug_addr) 0000000000800140 (base address)
0000002f v000000000000001 v000000000000004 views at 0000002e for:
0000000000800140 0000000000800153 (DW_OP_reg5 (rdi))
00000034 v000000000000000 v000000000000013 views at 00000030 for:
0000000000800153 000000000080015b (DW_OP_breg5 (rdi): 0)
0000003a v000000000000001 v000000000000055 views at 00000032 for:
000000000080015b 0000000000800180 (DW_OP_breg1 (rdx): -2; DW_OP_stack_value)
00000041

00000042 000000000080014a 0000000000800180 (DW_OP_reg1 (rdx))
00000047

Table at Offset 0x48
Length: 0x38
DWARF version: 5
Address size: 8
Segment size: 0
Offset entries: 2

Offset Entries starting at 0x54:
[ 0] 0x8
[ 1] 0x1e


Offset Begin End Expression

0000005c 0000000000000000 (index into .debug_addr) 0000000000800180 (base address)
0000005e v00000000000005a v000000000000004 views at 0000006b for:
0000000000800180 000000000080019b (DW_OP_reg5 (rdi))
00000063 v0000000000000a3 v000000000000055 views at 0000006d for:
000000000080019b 000000000080019f (DW_OP_breg5 (rdi): 0)
00000069 v00000000000001f v000000000000001 views at 00000070 for:
000000000080019f 00000000008001da (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value)
00000071

readelf: Warning: Hole and overlap detection requires adjacent view lists and loclists.
readelf: Warning: View Offset 0x85 is bigger than .debug_loclists section size.
readelf: Warning: There are 18 unused bytes at the end of section .debug_loclists
```

### Observations:

1. Location lists themselves look valid — relocated PC ranges and DWARF expressions are present (e.g. `DW_OP_reg5`, `DW_OP_entry_value`).
2. Per-CU `.debug_loclists` tables are rewritten — two CU contributions appear at offsets `0x0` and `0x48`.
3. `DW_AT_GNU_locviews` **is not updated** — e.g. still (`0x0000000c`) in `.debug_info` after BOLT, while bytes at offset 0xc are now part of the offset entry table, not view-pair data.
4. Stale view offsets — `readelf` follows stale `DW_AT_GNU_locviews` (e.g. `0x85` past section end) and loclist LLE entries still reference old view offsets (e.g. `0x6b`, `0x6d`) that no longer contain view-pair data.
5. Side-by-side `llvm-dwarfdump --debug-info` shows that BOLT correctly rewrites all five `DW_AT_location` entries to new `loclistx` indices and relocated PC ranges, and adds per-CU `DW_AT_loclists_base` (`0x0c` and `0x54`). However, all five `DW_AT_GNU_locviews` offsets are bit-identical before and after BOLT (`0x0c`, `0x2e`, `0x50`, `0x6b`, `0x85`), while the corresponding location lists moved to `0x18`, `0x2d`, `0x42`, `0x5c`, and `0x72`. In the original binary, each `GNU_locviews` offset pointed 2–6 bytes before its location list (consistent GCC layout). After BOLT, `GNU_locviews` still references the old absolute offsets, including `0x0c` which now coincides with the offset table at `loclists_base`, not view-pair data.

#### Before BOLT usage:

| Variable | GNU_locviews | DW_AT_location| Bytes distance (view -> location) |
| ----------- | ----------- | ----------- | ----------- |
| 1 | 0x0c | 0x12 | +6 |
| 2 | 0x2e| 0x34 | +6 |
| 3 | 0x50 | 0x52 | +2 |
| 4 | 0x6b| 0x71 | +6 |
| 5 | 0x85| 0x89 | +4 |

#### After BOLT usage (no updated GNU_locviews):

| Variable | GNU_locviews | DW_AT_location| Bytes distance (view -> location) |
| ----------- | ----------- | ----------- | ----------- |
| 1 | 0x0c | 0x18 | +12 |
| 2 | 0x2e| 0x2d | -1 |
| 3 | 0x50 | 0x42 | -14 |
| 4 | 0x6b| 0x5c | -15 |
| 5 | 0x85| 0x72 | -19 |


### Expected behavior
After `--update-debug-sections`, BOLT should either:
1. Preserve GNU Location Views: emit view-pair bytes before each rewritten loclist and update every `DW_AT_GNU_locviews` to the new absolute offset,
2. `readelf --debug-dump=loc` should report no view/loclist adjacency warnings

Contributor guide

Open the contributing guide

Research direction

Reproduce the failure using the two-file C++ build, llvm-bolt with --update-debug-sections, and the llvm-dwarfdump and readelf commands shown. Start in the BOLT debug-section rewriting path and trace handling for .debug_loclists, DW_AT_location, and DW_AT_GNU_locviews. Done means rewritten view metadata points to the new view-pair data and readelf reports no invalid offsets, holes, or overlaps.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.