[Lldb-commits] [lldb] [lldb][NativePDB] Add DW_OP_form_tls_address for thread locals (PR #228220)

via lldb-commits lldb-commits at lists.llvm.org
Thu Oct 1 12:49:54 PDT 2026


https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/228220

Thread locals used the same location expression as globals. This is wrong, because they need to be relative to the thread local slice of their image. That slice is specific to a thread, so the location expression uses `DW_OP_form_tls_address` to signal that it needs to be resolved per thread. This is not yet implemented for Windows, so I only added tests for the location expression.

>From ed64ee9eccc5cabc2138f727a62e2d8b8b87c842 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Thu, 1 Oct 2026 21:44:09 +0200
Subject: [PATCH] [lldb][NativePDB] Add DW_OP_form_tls_address for thread
 locals

---
 .../NativePDB/DWARFLocationExpression.cpp     | 13 +++++++++++
 .../NativePDB/DWARFLocationExpression.h       |  3 +++
 .../NativePDB/SymbolFileNativePDB.cpp         | 17 ++++++--------
 .../SymbolFile/NativePDB/thread-locals.cpp    | 22 +++++++++++++++++++
 4 files changed, 45 insertions(+), 10 deletions(-)
 create mode 100644 lldb/test/Shell/SymbolFile/NativePDB/thread-locals.cpp

diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
index b0333079812a1..bc797bdd6760f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
@@ -279,6 +279,19 @@ DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression(
       });
 }
 
+DWARFExpression lldb_private::npdb::MakeGlobalThreadLocalLocationExpression(
+    uint32_t offset, const ModuleSP &module) {
+  return MakeLocationExpressionInternal(
+      module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+        const ArchSpec &arch = module->GetArchitecture();
+
+        stream.PutHex8(llvm::dwarf::DW_OP_const4u);
+        stream.PutHex32(offset, arch.GetByteOrder());
+        stream.PutHex8(llvm::dwarf::DW_OP_form_tls_address);
+        return true;
+      });
+}
+
 llvm::Expected<DWARFExpression>
 lldb_private::npdb::MakeConstantLocationExpression(TypeIndex underlying_ti,
                                                    TpiStream &tpi,
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
index e1478db6ce9f6..bc018220b4105 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
@@ -53,6 +53,9 @@ MakeVFrameRelIndirLocationExpression(llvm::StringRef fpo_program,
                                      lldb::ModuleSP module);
 DWARFExpression MakeGlobalLocationExpression(uint16_t section, uint32_t offset,
                                              lldb::ModuleSP module);
+DWARFExpression
+MakeGlobalThreadLocalLocationExpression(uint32_t offset,
+                                        const lldb::ModuleSP &module);
 llvm::Expected<DWARFExpression> MakeConstantLocationExpression(
     llvm::codeview::TypeIndex underlying_ti, llvm::pdb::TpiStream &tpi,
     const llvm::APSInt &constant, lldb::ModuleSP module);
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index ca82d6ff36faa..72cd18b65875b 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1046,13 +1046,13 @@ VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
   if (sym.kind() == S_CONSTANT)
     return CreateConstantSymbol(var_id, sym);
 
+  ModuleSP module_sp = GetObjectFile()->GetModule();
   lldb::ValueType scope = eValueTypeInvalid;
   TypeIndex ti;
   llvm::StringRef name;
   lldb::addr_t addr = 0;
-  uint16_t section = 0;
-  uint32_t offset = 0;
   bool is_external = false;
+  DWARFExpression location_expr;
   switch (sym.kind()) {
   case S_GDATA32:
     is_external = true;
@@ -1068,9 +1068,9 @@ VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
     scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal
                                       : eValueTypeVariableStatic;
     name = ds.Name;
-    section = ds.Segment;
-    offset = ds.DataOffset;
     addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset);
+    location_expr =
+        MakeGlobalLocationExpression(ds.Segment, ds.DataOffset, module_sp);
     break;
   }
   case S_GTHREAD32:
@@ -1086,10 +1086,10 @@ VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
     }
     ti = tlds.Type;
     name = tlds.Name;
-    section = tlds.Segment;
-    offset = tlds.DataOffset;
     addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset);
     scope = eValueTypeVariableThreadLocal;
+    location_expr =
+        MakeGlobalThreadLocalLocationExpression(tlds.DataOffset, module_sp);
     break;
   }
   default:
@@ -1119,10 +1119,7 @@ VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) {
       ast_builder->EnsureVariable(var_id);
   }
 
-  ModuleSP module_sp = GetObjectFile()->GetModule();
-  DWARFExpressionList location(
-      module_sp, MakeGlobalLocationExpression(section, offset, module_sp),
-      nullptr);
+  DWARFExpressionList location(module_sp, location_expr, nullptr);
 
   std::string global_name("::");
   global_name += name;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/thread-locals.cpp b/lldb/test/Shell/SymbolFile/NativePDB/thread-locals.cpp
new file mode 100644
index 0000000000000..06d4b3964fdee
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/thread-locals.cpp
@@ -0,0 +1,22 @@
+// clang-format off
+// REQUIRES: lld, x86, target-windows
+
+// Test that we can display tag types.
+// RUN: %clang_cl --target=x86_64-windows-msvc -MDd -Od -Z7 -c /Fo%t.obj -- %s
+// RUN: lld-link -debug:full -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
+// RUN: lldb-test symbols %t.exe | FileCheck %s
+
+// CHECK: CompileUnit{{.*}}, language = "c++", file = '{{.*}}thread-locals.cpp'
+// Can't test for the specific address, but we can check that they're all less than 256 (at most two hex digits).
+// CHECK-DAG: Variable{{.*}}, name = "tls1", type = {{.*}} (int), scope = thread local, location = DW_OP_const4u 0x{{[0-9a-f][0-9a-f]?}}, DW_OP_form_tls_address, external
+// CHECK-DAG: Variable{{.*}}, name = "tls2", type = {{.*}} (int), scope = thread local, location = DW_OP_const4u 0x{{[0-9a-f][0-9a-f]?}}, DW_OP_form_tls_address, external
+// CHECK-DAG: Variable{{.*}}, name = "tls3", type = {{.*}} (long long), scope = thread local, location = DW_OP_const4u 0x{{[0-9a-f][0-9a-f]?}}, DW_OP_form_tls_address
+// CHECK: CompileUnit{{.*}}
+
+__declspec(thread) int tls1 = 1;
+__declspec(thread) int tls2 = 2;
+static __declspec(thread) long long tls3 = 3;
+
+int main() {
+  return tls3;
+}



More information about the lldb-commits mailing list