[gcc r13-9675] dwarf2out: Propagate dtprel into the .debug_addr table in resolve_addr_in_expr

Richard Biener rguenth@gcc.gnu.org
Mon May 26 10:36:37 GMT 2025


https://gcc.gnu.org/g:95e204dbc28f479b6b4116e5efacacde6c81ad75

commit r13-9675-g95e204dbc28f479b6b4116e5efacacde6c81ad75
Author: Kyle Huey <me@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  <me@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 b1a846c55a3e..5d1f75c6d0f6 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -30797,7 +30797,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:


More information about the Gcc-cvs mailing list