https://gcc.gnu.org/g:36dd228f2e96e3cf03ec84d73471cfdc5523de8d

commit r14-11781-g36dd228f2e96e3cf03ec84d73471cfdc5523de8d
Author: Kyle Huey <m...@kylehuey.com>
Date:   Tue May 13 20:26:26 2025 -0700

    dwarf2out: Propagate dtprel into the .debug_addr table in 
resolve_addr_in_expr
    
    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  <m...@kylehuey.com>
    
            * dwarf2out.cc (resolve_addr_in_expr): Propagate dtprel into the 
address
            table when appropriate.
    
    (cherry picked from commit 02e95abdde9742cecd7d1211e572549c1e56b8b1)

Diff:
---
 gcc/dwarf2out.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index e1398c61cd59..c91eb74683b1 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -30916,7 +30916,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:

Reply via email to