Skip to content

[lldb] Support changes to TLS on macOS #77988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 17, 2024

Conversation

bulbazord
Copy link
Member

I decided to resubmit #77854 but broken up into multiple commits for better reviewability. I recommend reviewing each commit individually, the overall change is quite large.

rdar://120676969

This is a small re-organization of GetThreadLocalData. This makes it
easier for me to add support for a newer way of getting thread local
data on apple platforms.
I am working on adding support for a more modern method of getting TLS
data on apple platforms. It also involves calling a function in the
inferior, so I'm generalizing the pthread implementation.
The TLS implementation on apple platforms has changed. Instead of
invoking pthread_getspecific with a pthread_key_t, we instead perform a
virtual function call.

Note: Some versions of Apple's new linker do not emit debug symbols for
TLS symbols. This causes the TLS tests to fail because LLDB and dsymutil
expects there to be debug symbols to resolve the relevant TLS block. You
may work around this by switchint to the older linker (ld-classic_ or by
disabling the TLS tests until you have a newer version of the new
linker.
@llvmbot
Copy link
Member

llvmbot commented Jan 12, 2024

@llvm/pr-subscribers-lldb

Author: Alex Langford (bulbazord)

Changes

I decided to resubmit #77854 but broken up into multiple commits for better reviewability. I recommend reviewing each commit individually, the overall change is quite large.

rdar://120676969


Full diff: https://github.com/llvm/llvm-project/pull/77988.diff

1 Files Affected:

  • (modified) lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp (+92-63)
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index 1e3e2e5641ad83..3bfa3778ad8f9c 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -1048,74 +1048,103 @@ DynamicLoaderDarwin::GetThreadLocalData(const lldb::ModuleSP module_sp,
 
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
 
+  lldb_private::Address tls_addr;
+  if (!module_sp->ResolveFileAddress(tls_file_addr, tls_addr))
+    return LLDB_INVALID_ADDRESS;
+
+  Target &target = m_process->GetTarget();
+  TypeSystemClangSP scratch_ts_sp =
+      ScratchTypeSystemClang::GetForTarget(target);
+  if (!scratch_ts_sp)
+    return LLDB_INVALID_ADDRESS;
+
+  CompilerType clang_void_ptr_type =
+      scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+  auto evaluate_tls_address = [this, &thread_sp, &clang_void_ptr_type](
+                                  Address func_ptr,
+                                  llvm::ArrayRef<addr_t> args) -> lldb::addr_t {
+    EvaluateExpressionOptions options;
+
+    lldb::ThreadPlanSP thread_plan_sp(new ThreadPlanCallFunction(
+        *thread_sp, func_ptr, clang_void_ptr_type, args, options));
+
+    DiagnosticManager execution_errors;
+    ExecutionContext exe_ctx(thread_sp);
+    lldb::ExpressionResults results = m_process->RunThreadPlan(
+        exe_ctx, thread_plan_sp, options, execution_errors);
+
+    if (results == lldb::eExpressionCompleted) {
+      if (lldb::ValueObjectSP result_valobj_sp =
+              thread_plan_sp->GetReturnValueObject()) {
+        return result_valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+      }
+    }
+    return LLDB_INVALID_ADDRESS;
+  };
+
+  // On modern apple platforms, there is a small data structure that looks
+  // approximately like this:
+  // struct TLS_Thunk {
+  //  void *(*get_addr)(struct TLS_Thunk *);
+  //  size_t key;
+  //  size_t offset;
+  // }
+  //
+  // The strategy is to take get_addr, call it with the address of the
+  // containing TLS_Thunk structure, and add the offset to the resulting
+  // pointer to get the data block.
+  //
+  // On older apple platforms, the key is treated as a pthread_key_t and passed
+  // to pthread_getspecific. The pointer returned from that call is added to
+  // offset to get the relevant data block.
+
   const uint32_t addr_size = m_process->GetAddressByteSize();
   uint8_t buf[sizeof(lldb::addr_t) * 3];
+  Status error;
+  const size_t tls_data_size = addr_size * 3;
+  const size_t bytes_read = target.ReadMemory(
+      tls_addr, buf, tls_data_size, error, /*force_live_memory = */ true);
+  if (bytes_read != tls_data_size || error.Fail())
+    return LLDB_INVALID_ADDRESS;
 
-  lldb_private::Address tls_addr;
-  if (module_sp->ResolveFileAddress(tls_file_addr, tls_addr)) {
-    Status error;
-    const size_t tsl_data_size = addr_size * 3;
-    Target &target = m_process->GetTarget();
-    if (target.ReadMemory(tls_addr, buf, tsl_data_size, error, true) ==
-        tsl_data_size) {
-      const ByteOrder byte_order = m_process->GetByteOrder();
-      DataExtractor data(buf, sizeof(buf), byte_order, addr_size);
-      lldb::offset_t offset = addr_size; // Skip the first pointer
-      const lldb::addr_t pthread_key = data.GetAddress(&offset);
-      const lldb::addr_t tls_offset = data.GetAddress(&offset);
-      if (pthread_key != 0) {
-        // First check to see if we have already figured out the location of
-        // TLS data for the pthread_key on a specific thread yet. If we have we
-        // can re-use it since its location will not change unless the process
-        // execs.
-        const tid_t tid = thread_sp->GetID();
-        auto tid_pos = m_tid_to_tls_map.find(tid);
-        if (tid_pos != m_tid_to_tls_map.end()) {
-          auto tls_pos = tid_pos->second.find(pthread_key);
-          if (tls_pos != tid_pos->second.end()) {
-            return tls_pos->second + tls_offset;
-          }
-        }
-        StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(0);
-        if (frame_sp) {
-          TypeSystemClangSP scratch_ts_sp =
-              ScratchTypeSystemClang::GetForTarget(target);
-
-          if (!scratch_ts_sp)
-            return LLDB_INVALID_ADDRESS;
-
-          CompilerType clang_void_ptr_type =
-              scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
-          Address pthread_getspecific_addr = GetPthreadSetSpecificAddress();
-          if (pthread_getspecific_addr.IsValid()) {
-            EvaluateExpressionOptions options;
-
-            lldb::ThreadPlanSP thread_plan_sp(new ThreadPlanCallFunction(
-                *thread_sp, pthread_getspecific_addr, clang_void_ptr_type,
-                llvm::ArrayRef<lldb::addr_t>(pthread_key), options));
-
-            DiagnosticManager execution_errors;
-            ExecutionContext exe_ctx(thread_sp);
-            lldb::ExpressionResults results = m_process->RunThreadPlan(
-                exe_ctx, thread_plan_sp, options, execution_errors);
-
-            if (results == lldb::eExpressionCompleted) {
-              lldb::ValueObjectSP result_valobj_sp =
-                  thread_plan_sp->GetReturnValueObject();
-              if (result_valobj_sp) {
-                const lldb::addr_t pthread_key_data =
-                    result_valobj_sp->GetValueAsUnsigned(0);
-                if (pthread_key_data) {
-                  m_tid_to_tls_map[tid].insert(
-                      std::make_pair(pthread_key, pthread_key_data));
-                  return pthread_key_data + tls_offset;
-                }
-              }
-            }
-          }
-        }
+  DataExtractor data(buf, sizeof(buf), m_process->GetByteOrder(), addr_size);
+  lldb::offset_t offset = 0;
+  const lldb::addr_t tls_thunk = data.GetAddress(&offset);
+  const lldb::addr_t key = data.GetAddress(&offset);
+  const lldb::addr_t tls_offset = data.GetAddress(&offset);
+
+  if (tls_thunk != 0) {
+    Address thunk_load_addr;
+    if (target.ResolveLoadAddress(tls_thunk, thunk_load_addr)) {
+      const lldb::addr_t tls_load_addr = tls_addr.GetLoadAddress(&target);
+      const lldb::addr_t tls_data = evaluate_tls_address(
+          thunk_load_addr, llvm::ArrayRef<lldb::addr_t>(tls_load_addr));
+      if (tls_data != LLDB_INVALID_ADDRESS)
+        return tls_data + tls_offset;
+    }
+  }
+
+  if (key != 0) {
+    // First check to see if we have already figured out the location of
+    // TLS data for the pthread_key on a specific thread yet. If we have we
+    // can re-use it since its location will not change unless the process
+    // execs.
+    const tid_t tid = thread_sp->GetID();
+    auto tid_pos = m_tid_to_tls_map.find(tid);
+    if (tid_pos != m_tid_to_tls_map.end()) {
+      auto tls_pos = tid_pos->second.find(key);
+      if (tls_pos != tid_pos->second.end()) {
+        return tls_pos->second + tls_offset;
       }
     }
+    Address pthread_getspecific_addr = GetPthreadSetSpecificAddress();
+    if (pthread_getspecific_addr.IsValid()) {
+      const lldb::addr_t tls_data = evaluate_tls_address(
+          pthread_getspecific_addr, llvm::ArrayRef<lldb::addr_t>(key));
+      if (tls_data != LLDB_INVALID_ADDRESS)
+        return tls_data + tls_offset;
+    }
   }
   return LLDB_INVALID_ADDRESS;
 }

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for breaking this up!

Copy link
Contributor

@felipepiovezan felipepiovezan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link

github-actions bot commented Jan 17, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@bulbazord bulbazord merged commit a451c3b into llvm:main Jan 17, 2024
@bulbazord bulbazord deleted the reorganize-darwin-tls branch January 17, 2024 19:20
bulbazord added a commit to bulbazord/llvm-project that referenced this pull request Jan 17, 2024
The TLS implementation on apple platforms has changed. Instead of
invoking pthread_getspecific with a pthread_key_t, we instead perform a
virtual function call.

Note: Some versions of Apple's new linker do not emit debug symbols for
TLS symbols. This causes the TLS tests to fail because LLDB and dsymutil
expects there to be debug symbols to resolve the relevant TLS block. You
may work around this by switching to the older linker (ld-classic) or by
disabling the TLS tests until you have a newer version of the new
linker.

rdar://120676969
(cherry picked from commit a451c3b)
ampandey-1995 pushed a commit to ampandey-1995/llvm-project that referenced this pull request Jan 19, 2024
The TLS implementation on apple platforms has changed. Instead of
invoking pthread_getspecific with a pthread_key_t, we instead perform a
virtual function call.

Note: Some versions of Apple's new linker do not emit debug symbols for
TLS symbols. This causes the TLS tests to fail because LLDB and dsymutil
expects there to be debug symbols to resolve the relevant TLS block. You
may work around this by switching to the older linker (ld-classic) or by
disabling the TLS tests until you have a newer version of the new
linker.

rdar://120676969
bulbazord added a commit to swiftlang/llvm-project that referenced this pull request Jan 19, 2024
[lldb] Support changes to TLS on macOS (llvm#77988)
jasonmolenda pushed a commit to jasonmolenda/llvm-project that referenced this pull request Jan 23, 2024
The TLS implementation on apple platforms has changed. Instead of
invoking pthread_getspecific with a pthread_key_t, we instead perform a
virtual function call.

Note: Some versions of Apple's new linker do not emit debug symbols for
TLS symbols. This causes the TLS tests to fail because LLDB and dsymutil
expects there to be debug symbols to resolve the relevant TLS block. You
may work around this by switching to the older linker (ld-classic) or by
disabling the TLS tests until you have a newer version of the new
linker.

rdar://120676969
(cherry picked from commit a451c3b)
(cherry picked from commit c0776cc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants