Re: [PATCH] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

2025-05-05 Thread Kyle Huey
On Thu, May 1, 2025 at 11:22 PM Richard Biener
 wrote:
>
> On Fri, May 2, 2025 at 2:14 AM Kyle Huey  wrote:
> >
> > For a debugger to display statically-allocated[0] TLS variables the compiler
> > must communicate information[1] that can be used in conjunction with 
> > knowledge
> > of the runtime enviroment[2] to calculate a location for the variable for
> > each thread. That need gives rise to dw_loc_dtprel in dwarf2out, a flag 
> > tracking
> > whether the location description is dtprel, or relative to the
> > "dynamic thread pointer". Location descriptions in the .debug_info section 
> > for
> > TLS variables need to be relocated by the static linker accordingly, and
> > dw_loc_dtprel controls emission of the needed relocations.
> >
> > This is further complicated by -gsplit-dwarf. -gsplit-dwarf is designed to 
> > allow
> > as much debugging information as possible to bypass the static linker to 
> > improve
> > linking performance. One of the ways that is done is by introducing a layer 
> > of
> > indirection for relocatable values[3]. That gives rise to addr_index_table 
> > which
> > ultimately results in the .debug_addr section.
> >
> > While the code handling addr_index_table clearly contemplates the existence 
> > of
> > dtprel entries[4] resolve_addr_in_expr does not, and the result is that when
> > using -gsplit-dwarf the DWARF for TLS variables contains an address[5] 
> > rather
> > than an offset, and debuggers can't work with that.
> >
> > This is visible on a trivial example. Compile
> >
> > ```
> > static __thread int tls_var;
> >
> > int main(void) {
> >   tls_var = 42;
> >   return 0;
> > }
> > ```
> >
> > with -g and -g -gsplit-dwarf. Run the program under gdb. When examining the
> > value of tls_var before and after the assignment, -g behaves as one would
> > expect but -g -gsplit-dwarf does not. If the user is lucky and the 
> > miscalculated
> > address is not mapped, gdb will print "Cannot access memory at address ...".
> > If the user is unlucky and the miscalculated address is mapped, gdb will 
> > simply
> > give the wrong value. You can further confirm that the issue is the address
> > calculation by asking gdb for the address of tls_var and comparing that to 
> > what
> > one would expect.[6]
> >
> > Thankfully this is trivial to fix by modifying resolve_addr_in_expr to 
> > propagate
> > the dtprel character of the location where necessary. gdb begins working as
> > expected and the diff in the generated assembly is clear.
> >
> > ```
> > .section.debug_addr,"",@progbits
> > .long   0x14
> > .value  0x5
> > .byte   0x8
> > .byte   0
> >  .Ldebug_addr0:
> > -   .quad   tls_var
> > +   .long   tls_var@dtpoff, 0
> > .quad   .LFB0
> > ```
> >
> > [0] Referring to e.g. __thread as statically-allocated vs. e.g. a
> > dynamically-allocated pthread_key_create() call.
> > [1] Generally an offset in a TLS block.
> > [2] With glibc, provided by libthread_db.so.
> > [3] Relocatable values are moved to a table in the .debug_addr section, 
> > those
> > values in .debug_info are replaced with special values that look up 
> > indexes
> > in that table, and then the static linker elsewhere assigns a single 
> > per-CU
> > starting index in the .debug_addr section, allowing those special 
> > values to
> > remain permanently fixed and the resulting data to be ignored by the 
> > linker.
> > [4] ate_kind_rtx_dtprel exists, after all, and new_addr_loc_descr does 
> > produce
> > it where appropriate.
> > [5] e.g. an address in the .tbss/.tdata section.
> > [6] e.g. on x86-64 by examining %fsbase and the offset in the assembly
>
> OK.  Feel free to backport as appropriate.
>
> Thanks,
> Richard.

Thanks. I don't have write access so it would be great if you or
someone else could commit this for me.

I'll send a patch that's rebased for 13/14/15 later today.

- Kyle

> > 2025-05-01  Kyle Huey  
> >
> > * dwarf2out.cc (resolve_addr_in_expr): Propagate dtprel into the 
> > address
> > table when appropriate.
> > ---
> >  gcc/dwarf2out.cc | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> > index 34ffeed86ff..9aecdb9fd5a 100644
> > --- a/gcc/dwarf2out.cc
> > +++ b/gcc/dwarf2out.cc
> > @@ -31068,7 +31068,8 @@ resolve_addr_in_expr (dw_attr_node *a, 
> > dw_loc_descr_ref loc)
> >return false;
> >  remove_addr_table_entry (loc->dw_loc_oprnd1.val_entry);
> > loc->dw_loc_oprnd1.val_entry
> > - = add_addr_table_entry (rtl, ate_kind_rtx);
> > + = add_addr_table_entry (rtl, loc->dw_loc_dtprel
> > + ? ate_kind_rtx_dtprel : ate_kind_rtx);
> >}
> > break;
> >case DW_OP_const4u:
> > --
> > 2.43.0
> >


Re: [PATCH] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

2025-05-05 Thread Kyle Huey
On Mon, May 5, 2025 at 6:45 AM Richard Biener
 wrote:
>
> On Mon, May 5, 2025 at 3:37 PM Kyle Huey  wrote:
> >
> > On Thu, May 1, 2025 at 11:22 PM Richard Biener
> >  wrote:
> > >
> > > On Fri, May 2, 2025 at 2:14 AM Kyle Huey  wrote:
> > > >
> > > > For a debugger to display statically-allocated[0] TLS variables the 
> > > > compiler
> > > > must communicate information[1] that can be used in conjunction with 
> > > > knowledge
> > > > of the runtime enviroment[2] to calculate a location for the variable 
> > > > for
> > > > each thread. That need gives rise to dw_loc_dtprel in dwarf2out, a flag 
> > > > tracking
> > > > whether the location description is dtprel, or relative to the
> > > > "dynamic thread pointer". Location descriptions in the .debug_info 
> > > > section for
> > > > TLS variables need to be relocated by the static linker accordingly, and
> > > > dw_loc_dtprel controls emission of the needed relocations.
> > > >
> > > > This is further complicated by -gsplit-dwarf. -gsplit-dwarf is designed 
> > > > to allow
> > > > as much debugging information as possible to bypass the static linker 
> > > > to improve
> > > > linking performance. One of the ways that is done is by introducing a 
> > > > layer of
> > > > indirection for relocatable values[3]. That gives rise to 
> > > > addr_index_table which
> > > > ultimately results in the .debug_addr section.
> > > >
> > > > While the code handling addr_index_table clearly contemplates the 
> > > > existence of
> > > > dtprel entries[4] resolve_addr_in_expr does not, and the result is that 
> > > > when
> > > > using -gsplit-dwarf the DWARF for TLS variables contains an address[5] 
> > > > rather
> > > > than an offset, and debuggers can't work with that.
> > > >
> > > > This is visible on a trivial example. Compile
> > > >
> > > > ```
> > > > static __thread int tls_var;
> > > >
> > > > int main(void) {
> > > >   tls_var = 42;
> > > >   return 0;
> > > > }
> > > > ```
> > > >
> > > > with -g and -g -gsplit-dwarf. Run the program under gdb. When examining 
> > > > the
> > > > value of tls_var before and after the assignment, -g behaves as one 
> > > > would
> > > > expect but -g -gsplit-dwarf does not. If the user is lucky and the 
> > > > miscalculated
> > > > address is not mapped, gdb will print "Cannot access memory at address 
> > > > ...".
> > > > If the user is unlucky and the miscalculated address is mapped, gdb 
> > > > will simply
> > > > give the wrong value. You can further confirm that the issue is the 
> > > > address
> > > > calculation by asking gdb for the address of tls_var and comparing that 
> > > > to what
> > > > one would expect.[6]
> > > >
> > > > Thankfully this is trivial to fix by modifying resolve_addr_in_expr to 
> > > > propagate
> > > > the dtprel character of the location where necessary. gdb begins 
> > > > working as
> > > > expected and the diff in the generated assembly is clear.
> > > >
> > > > ```
> > > > .section.debug_addr,"",@progbits
> > > > .long   0x14
> > > > .value  0x5
> > > > .byte   0x8
> > > > .byte   0
> > > >  .Ldebug_addr0:
> > > > -   .quad   tls_var
> > > > +   .long   tls_var@dtpoff, 0
> > > > .quad   .LFB0
> > > > ```
> > > >
> > > > [0] Referring to e.g. __thread as statically-allocated vs. e.g. a
> > > > dynamically-allocated pthread_key_create() call.
> > > > [1] Generally an offset in a TLS block.
> > > > [2] With glibc, provided by libthread_db.so.
> > > > [3] Relocatable values are moved to a table in the .debug_addr section, 
> > > > those
> > > > values in .debug_info are replaced with special values that look up 
> > > > indexes
> > > > in that table, and then the static linker elsewhere assigns a 
> > > > single per-CU
> > > > starting index in the .debug_addr section, allowing t

[PATCH] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

2025-05-01 Thread Kyle Huey
For a debugger to display statically-allocated[0] TLS variables the compiler
must communicate information[1] that can be used in conjunction with knowledge
of the runtime enviroment[2] to calculate a location for the variable for
each thread. That need gives rise to dw_loc_dtprel in dwarf2out, a flag tracking
whether the location description is dtprel, or relative to the
"dynamic thread pointer". Location descriptions in the .debug_info section for
TLS variables need to be relocated by the static linker accordingly, and
dw_loc_dtprel controls emission of the needed relocations.

This is further complicated by -gsplit-dwarf. -gsplit-dwarf is designed to allow
as much debugging information as possible to bypass the static linker to improve
linking performance. One of the ways that is done is by introducing a layer of
indirection for relocatable values[3]. That gives rise to addr_index_table which
ultimately results in the .debug_addr section.

While the code handling addr_index_table clearly contemplates the existence of
dtprel entries[4] resolve_addr_in_expr does not, and the result is that when
using -gsplit-dwarf the DWARF for TLS variables contains an address[5] rather
than an offset, and debuggers can't work with that.

This is visible on a trivial example. Compile

```
static __thread int tls_var;

int main(void) {
  tls_var = 42;
  return 0;
}
```

with -g and -g -gsplit-dwarf. Run the program under gdb. When examining the
value of tls_var before and after the assignment, -g behaves as one would
expect but -g -gsplit-dwarf does not. If the user is lucky and the miscalculated
address is not mapped, gdb will print "Cannot access memory at address ...".
If the user is unlucky and the miscalculated address is mapped, gdb will simply
give the wrong value. You can further confirm that the issue is the address
calculation by asking gdb for the address of tls_var and comparing that to what
one would expect.[6]

Thankfully this is trivial to fix by modifying resolve_addr_in_expr to propagate
the dtprel character of the location where necessary. gdb begins working as
expected and the diff in the generated assembly is clear.

```
.section.debug_addr,"",@progbits
.long   0x14
.value  0x5
.byte   0x8
.byte   0
 .Ldebug_addr0:
-   .quad   tls_var
+   .long   tls_var@dtpoff, 0
.quad   .LFB0
```

[0] Referring to e.g. __thread as statically-allocated vs. e.g. a
dynamically-allocated pthread_key_create() call.
[1] Generally an offset in a TLS block.
[2] With glibc, provided by libthread_db.so.
[3] Relocatable values are moved to a table in the .debug_addr section, those
values in .debug_info are replaced with special values that look up indexes
in that table, and then the static linker elsewhere assigns a single per-CU
starting index in the .debug_addr section, allowing those special values to
remain permanently fixed and the resulting data to be ignored by the linker.
[4] ate_kind_rtx_dtprel exists, after all, and new_addr_loc_descr does produce
it where appropriate.
[5] e.g. an address in the .tbss/.tdata section.
[6] e.g. on x86-64 by examining %fsbase and the offset in the assembly

2025-05-01  Kyle Huey  

* dwarf2out.cc (resolve_addr_in_expr): Propagate dtprel into the address
table when appropriate.
---
 gcc/dwarf2out.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 34ffeed86ff..9aecdb9fd5a 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -31068,7 +31068,8 @@ resolve_addr_in_expr (dw_attr_node *a, dw_loc_descr_ref 
loc)
   return false;
 remove_addr_table_entry (loc->dw_loc_oprnd1.val_entry);
loc->dw_loc_oprnd1.val_entry
- = add_addr_table_entry (rtl, ate_kind_rtx);
+ = add_addr_table_entry (rtl, loc->dw_loc_dtprel
+ ? ate_kind_rtx_dtprel : ate_kind_rtx);
   }
break;
   case DW_OP_const4u:
-- 
2.43.0



[PATCH][GCC15/14/13/12] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

2025-05-13 Thread Kyle Huey
For a debugger to display statically-allocated[0] TLS variables the compiler
must communicate information[1] that can be used in conjunction with knowledge
of the runtime enviroment[2] to calculate a location for the variable for
each thread. That need gives rise to dw_loc_dtprel in dwarf2out, a flag tracking
whether the location description is dtprel, or relative to the
"dynamic thread pointer". Location descriptions in the .debug_info section for
TLS variables need to be relocated by the static linker accordingly, and
dw_loc_dtprel controls emission of the needed relocations.

This is further complicated by -gsplit-dwarf. -gsplit-dwarf is designed to allow
as much debugging information as possible to bypass the static linker to improve
linking performance. One of the ways that is done is by introducing a layer of
indirection for relocatable values[3]. That gives rise to addr_index_table which
ultimately results in the .debug_addr section.

While the code handling addr_index_table clearly contemplates the existence of
dtprel entries[4] resolve_addr_in_expr does not, and the result is that when
using -gsplit-dwarf the DWARF for TLS variables contains an address[5] rather
than an offset, and debuggers can't work with that.

This is visible on a trivial example. Compile

```
static __thread int tls_var;

int main(void) {
  tls_var = 42;
  return 0;
}
```

with -g and -g -gsplit-dwarf. Run the program under gdb. When examining the
value of tls_var before and after the assignment, -g behaves as one would
expect but -g -gsplit-dwarf does not. If the user is lucky and the miscalculated
address is not mapped, gdb will print "Cannot access memory at address ...".
If the user is unlucky and the miscalculated address is mapped, gdb will simply
give the wrong value. You can further confirm that the issue is the address
calculation by asking gdb for the address of tls_var and comparing that to what
one would expect.[6]

Thankfully this is trivial to fix by modifying resolve_addr_in_expr to propagate
the dtprel character of the location where necessary. gdb begins working as
expected and the diff in the generated assembly is clear.

```
.section.debug_addr,"",@progbits
.long   0x14
.value  0x5
.byte   0x8
.byte   0
 .Ldebug_addr0:
-   .quad   tls_var
+   .long   tls_var@dtpoff, 0
.quad   .LFB0
```

[0] Referring to e.g. __thread as statically-allocated vs. e.g. a
dynamically-allocated pthread_key_create() call.
[1] Generally an offset in a TLS block.
[2] With glibc, provided by libthread_db.so.
[3] Relocatable values are moved to a table in the .debug_addr section, those
values in .debug_info are replaced with special values that look up indexes
in that table, and then the static linker elsewhere assigns a single per-CU
starting index in the .debug_addr section, allowing those special values to
remain permanently fixed and the resulting data to be ignored by the linker.
[4] ate_kind_rtx_dtprel exists, after all, and new_addr_loc_descr does produce
it where appropriate.
[5] e.g. an address in the .tbss/.tdata section.
[6] e.g. on x86-64 by examining %fsbase and the offset in the assembly

2025-05-01  Kyle Huey  

* dwarf2out.cc (resolve_addr_in_expr): Propagate dtprel into the address
table when appropriate.
---
 gcc/dwarf2out.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 69e9d775d0d..2437610d48d 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -31068,7 +31068,8 @@ resolve_addr_in_expr (dw_attr_node *a, dw_loc_descr_ref 
loc)
   return false;
 remove_addr_table_entry (loc->dw_loc_oprnd1.val_entry);
loc->dw_loc_oprnd1.val_entry
- = add_addr_table_entry (rtl, ate_kind_rtx);
+ = add_addr_table_entry (rtl, loc->dtprel
+ ? ate_kind_rtx_dtprel : ate_kind_rtx);
   }
break;
   case DW_OP_const4u:
-- 
2.43.0



Re: [PATCH][GCC15/14/13/12] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

2025-05-14 Thread Kyle Huey
On Wed, May 14, 2025 at 9:26 AM Richard Biener
 wrote:
>
> On Wed, May 14, 2025 at 5:25 AM Kyle Huey  wrote:
> >
> > For a debugger to display statically-allocated[0] TLS variables the compiler
> > must communicate information[1] that can be used in conjunction with 
> > knowledge
> > of the runtime enviroment[2] to calculate a location for the variable for
> > each thread. That need gives rise to dw_loc_dtprel in dwarf2out, a flag 
> > tracking
> > whether the location description is dtprel, or relative to the
> > "dynamic thread pointer". Location descriptions in the .debug_info section 
> > for
> > TLS variables need to be relocated by the static linker accordingly, and
> > dw_loc_dtprel controls emission of the needed relocations.
> >
> > This is further complicated by -gsplit-dwarf. -gsplit-dwarf is designed to 
> > allow
> > as much debugging information as possible to bypass the static linker to 
> > improve
> > linking performance. One of the ways that is done is by introducing a layer 
> > of
> > indirection for relocatable values[3]. That gives rise to addr_index_table 
> > which
> > ultimately results in the .debug_addr section.
> >
> > While the code handling addr_index_table clearly contemplates the existence 
> > of
> > dtprel entries[4] resolve_addr_in_expr does not, and the result is that when
> > using -gsplit-dwarf the DWARF for TLS variables contains an address[5] 
> > rather
> > than an offset, and debuggers can't work with that.
> >
> > This is visible on a trivial example. Compile
> >
> > ```
> > static __thread int tls_var;
> >
> > int main(void) {
> >   tls_var = 42;
> >   return 0;
> > }
> > ```
> >
> > with -g and -g -gsplit-dwarf. Run the program under gdb. When examining the
> > value of tls_var before and after the assignment, -g behaves as one would
> > expect but -g -gsplit-dwarf does not. If the user is lucky and the 
> > miscalculated
> > address is not mapped, gdb will print "Cannot access memory at address ...".
> > If the user is unlucky and the miscalculated address is mapped, gdb will 
> > simply
> > give the wrong value. You can further confirm that the issue is the address
> > calculation by asking gdb for the address of tls_var and comparing that to 
> > what
> > one would expect.[6]
> >
> > Thankfully this is trivial to fix by modifying resolve_addr_in_expr to 
> > propagate
> > the dtprel character of the location where necessary. gdb begins working as
> > expected and the diff in the generated assembly is clear.
> >
> > ```
> > .section.debug_addr,"",@progbits
> > .long   0x14
> > .value  0x5
> > .byte   0x8
> > .byte   0
> >  .Ldebug_addr0:
> > -   .quad   tls_var
> > +   .long   tls_var@dtpoff, 0
> > .quad   .LFB0
> > ```
> >
> > [0] Referring to e.g. __thread as statically-allocated vs. e.g. a
> > dynamically-allocated pthread_key_create() call.
> > [1] Generally an offset in a TLS block.
> > [2] With glibc, provided by libthread_db.so.
> > [3] Relocatable values are moved to a table in the .debug_addr section, 
> > those
> > values in .debug_info are replaced with special values that look up 
> > indexes
> > in that table, and then the static linker elsewhere assigns a single 
> > per-CU
> > starting index in the .debug_addr section, allowing those special 
> > values to
> > remain permanently fixed and the resulting data to be ignored by the 
> > linker.
> > [4] ate_kind_rtx_dtprel exists, after all, and new_addr_loc_descr does 
> > produce
> > it where appropriate.
> > [5] e.g. an address in the .tbss/.tdata section.
> > [6] e.g. on x86-64 by examining %fsbase and the offset in the assembly
>
> I have bootstrapped/tested this on x86_64-unknown-linux-gnu on the
> 15 and 14 branches and pushed there.
>
> Richard.

Thank you.

- Kyle

> > 2025-05-01  Kyle Huey  
> >
> > * dwarf2out.cc (resolve_addr_in_expr): Propagate dtprel into the 
> > address
> > table when appropriate.
> > ---
> >  gcc/dwarf2out.cc | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> > index 69e9d775d0d..2437610d48d 100644
> > --- a/gcc/dwarf2out.cc
> > +++ b/gcc/dwarf2out.cc
> > @@ -31068,7 +31068,8 @@ resolve_addr_in_expr (dw_attr_node *a, 
> > dw_loc_descr_ref loc)
> >return false;
> >  remove_addr_table_entry (loc->dw_loc_oprnd1.val_entry);
> > loc->dw_loc_oprnd1.val_entry
> > - = add_addr_table_entry (rtl, ate_kind_rtx);
> > + = add_addr_table_entry (rtl, loc->dtprel
> > + ? ate_kind_rtx_dtprel : ate_kind_rtx);
> >}
> > break;
> >case DW_OP_const4u:
> > --
> > 2.43.0
> >