On Fri, May 2, 2025 at 2:14 AM Kyle Huey <m...@kylehuey.com> 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.

> 2025-05-01  Kyle Huey  <kh...@kylehuey.com>
>
>         * 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
>

Reply via email to