Skip to content

[lldb] Revive shell test after updating UnwindTable #86770

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

Conversation

jasonmolenda
Copy link
Collaborator

@jasonmolenda jasonmolenda commented Mar 27, 2024

In
commit 2f63718
Author: Jason Molenda jmolenda@apple.com
Date: Tue Mar 26 09:07:15 2024 -0700

 [lldb] Don't clear a Module's UnwindTable when adding a SymbolFile (#86603)

I stopped clearing a Module's UnwindTable when we add a SymbolFile to avoid the memory management problems with adding a symbol file asynchronously while the UnwindTable is being accessed on another thread. This broke the target-symbols-add-unwind.test shell test on Linux which removes the DWARF debub_frame section from a binary, loads it, then loads the unstripped binary with the DWARF debug_frame section and checks that the UnwindPlans for a function include debug_frame.

I originally decided that I was willing to sacrifice the possiblity of additional unwind sources from a symbol file because we rely on assembly emulation so heavily, they're rarely critical. But there are targets where we we don't have emluation and rely on things like DWARF debug_frame a lot more, so this probably wasn't a good choice.

This patch adds a new UnwindTable::Update method which looks for any new sources of unwind information and adds it to the UnwindTable, and calls that after a new SymbolFile has been added to a Module.

In
  commit 2f63718
  Author: Jason Molenda <jmolenda@apple.com>
  Date:   Tue Mar 26 09:07:15 2024 -0700

    [lldb] Don't clear a Module's UnwindTable when adding a SymbolFile (llvm#86603)

I stopped clearing a Module's UnwindTable when we add a SymbolFile
to avoid the memory management problems with adding a symbol file
asynchronously while the UnwindTable is being accessed on another
thread.  This broke the target-symbols-add-unwind.test shell test
on Linux which removes the DWARF debub_frame section from a binary,
loads it, then loads the unstripped binary with the DWARF debug_frame
section and checks that the UnwindPlans for a function include
debug_frame.

I originally decided that I was willing to sacrifice the possiblity
of additional unwind sources from a symbol file because we rely on
assembly emulation so heavily, they're rarely critical.  But there
are targets where we we don't have emluation and rely on things
like DWARF debug_frame a lot more, so this probably wasn't a good
choice.

This patch adds a new UnwindTable::Update method which looks for any
new sources of unwind information and adds it to the UnwindTable,
and calls that after a new SymbolFile has been added to a Module.
@llvmbot
Copy link
Member

llvmbot commented Mar 27, 2024

@llvm/pr-subscribers-lldb

Author: Jason Molenda (jasonmolenda)

Changes

In
commit 2f63718
Author: Jason Molenda <jmolenda@apple.com>
Date: Tue Mar 26 09:07:15 2024 -0700

[lldb] Don't clear a Module's UnwindTable when adding a SymbolFile (#<!-- -->86603)

I stopped clearing a Module's UnwindTable when we add a SymbolFile to avoid the memory management problems with adding a symbol file asynchronously while the UnwindTable is being accessed on another thread. This broke the target-symbols-add-unwind.test shell test on Linux which removes the DWARF debub_frame section from a binary, loads it, then loads the unstripped binary with the DWARF debug_frame section and checks that the UnwindPlans for a function include debug_frame.

I originally decided that I was willing to sacrifice the possiblity of additional unwind sources from a symbol file because we rely on assembly emulation so heavily, they're rarely critical. But there are targets where we we don't have emluation and rely on things like DWARF debug_frame a lot more, so this probably wasn't a good choice.

This patch adds a new UnwindTable::Update method which looks for any new sources of unwind information and adds it to the UnwindTable, and calls that after a new SymbolFile has been added to a Module.


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

4 Files Affected:

  • (modified) lldb/include/lldb/Symbol/UnwindTable.h (+4)
  • (modified) lldb/source/Core/Module.cpp (+2)
  • (modified) lldb/source/Symbol/UnwindTable.cpp (+45)
  • (added) lldb/test/Shell/SymbolFile/target-symbols-add-unwind.test (+27)
diff --git a/lldb/include/lldb/Symbol/UnwindTable.h b/lldb/include/lldb/Symbol/UnwindTable.h
index f0ce7047de2d1e..26826e5d1b497c 100644
--- a/lldb/include/lldb/Symbol/UnwindTable.h
+++ b/lldb/include/lldb/Symbol/UnwindTable.h
@@ -57,6 +57,10 @@ class UnwindTable {
 
   ArchSpec GetArchitecture();
 
+  /// Called after a SymbolFile has been added to a Module to add any new
+  /// unwind sections that may now be available.
+  void Update();
+
 private:
   void Dump(Stream &s);
 
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index a520523a96521a..9c105b3f0e57a1 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -1009,6 +1009,8 @@ SymbolFile *Module::GetSymbolFile(bool can_create, Stream *feedback_strm) {
         m_symfile_up.reset(
             SymbolVendor::FindPlugin(shared_from_this(), feedback_strm));
         m_did_load_symfile = true;
+        if (m_unwind_table)
+          m_unwind_table->Update();
       }
     }
   }
diff --git a/lldb/source/Symbol/UnwindTable.cpp b/lldb/source/Symbol/UnwindTable.cpp
index 3c1a5187b11054..11bedf3d6052e7 100644
--- a/lldb/source/Symbol/UnwindTable.cpp
+++ b/lldb/source/Symbol/UnwindTable.cpp
@@ -84,6 +84,51 @@ void UnwindTable::Initialize() {
   }
 }
 
+void UnwindTable::Update() {
+  if (!m_initialized)
+    return Initialize();
+
+  std::lock_guard<std::mutex> guard(m_mutex);
+
+  ObjectFile *object_file = m_module.GetObjectFile();
+  if (!object_file)
+    return;
+
+  if (!m_object_file_unwind_up)
+    m_object_file_unwind_up = object_file->CreateCallFrameInfo();
+
+  SectionList *sl = m_module.GetSectionList();
+  if (!sl)
+    return;
+
+  SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
+  if (!m_eh_frame_up && sect) {
+    m_eh_frame_up = std::make_unique<DWARFCallFrameInfo>(
+        *object_file, sect, DWARFCallFrameInfo::EH);
+  }
+
+  sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
+  if (!m_debug_frame_up && sect) {
+    m_debug_frame_up = std::make_unique<DWARFCallFrameInfo>(
+        *object_file, sect, DWARFCallFrameInfo::DWARF);
+  }
+
+  sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
+  if (!m_compact_unwind_up && sect) {
+    m_compact_unwind_up =
+        std::make_unique<CompactUnwindInfo>(*object_file, sect);
+  }
+
+  sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
+  if (!m_arm_unwind_up && sect) {
+    SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
+    if (sect_extab.get()) {
+      m_arm_unwind_up =
+          std::make_unique<ArmUnwindInfo>(*object_file, sect, sect_extab);
+    }
+  }
+}
+
 UnwindTable::~UnwindTable() = default;
 
 std::optional<AddressRange>
diff --git a/lldb/test/Shell/SymbolFile/target-symbols-add-unwind.test b/lldb/test/Shell/SymbolFile/target-symbols-add-unwind.test
new file mode 100644
index 00000000000000..5420213d405e86
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/target-symbols-add-unwind.test
@@ -0,0 +1,27 @@
+# TODO: When it's possible to run "image show-unwind" without a running
+# process, we can remove the unsupported line below, and hard-code an ELF
+# triple in the test.
+# UNSUPPORTED: system-windows, system-darwin
+
+# RUN: cd %T
+# RUN: %clang_host %S/Inputs/target-symbols-add-unwind.c -g \
+# RUN:   -fno-unwind-tables -fno-asynchronous-unwind-tables \
+# RUN:   -o target-symbols-add-unwind.debug
+# RUN: llvm-objcopy --strip-debug target-symbols-add-unwind.debug \
+# RUN:   target-symbols-add-unwind.stripped
+# RUN: %lldb target-symbols-add-unwind.stripped -s %s -o quit | FileCheck %s
+
+process launch --stop-at-entry
+image show-unwind -n main
+# CHECK-LABEL: image show-unwind -n main
+# CHECK-NOT: debug_frame UnwindPlan:
+
+target symbols add -s target-symbols-add-unwind.stripped target-symbols-add-unwind.debug
+# CHECK-LABEL: target symbols add
+# CHECK: symbol file {{.*}} has been added to {{.*}}
+
+image show-unwind -n main
+# CHECK-LABEL: image show-unwind -n main
+# CHECK: debug_frame UnwindPlan:
+# CHECK-NEXT: This UnwindPlan originally sourced from DWARF CFI
+# CHECK-NEXT: This UnwindPlan is sourced from the compiler: yes.

@jasonmolenda
Copy link
Collaborator Author

I developed & tested this on aarch64 linux, the only system I have here that runs this shell test. It's a little trickier to test on macOS, we basically only use compact_unwind and it's in the executable binary, not the dSYM SymbolFile.

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.

Nice!

@jasonmolenda jasonmolenda merged commit 6a0ec8e into llvm:main Mar 27, 2024
@jasonmolenda jasonmolenda deleted the update-unwindtable-after-adding-symbolfile branch March 27, 2024 16:25
jasonmolenda added a commit to jasonmolenda/llvm-project that referenced this pull request Mar 27, 2024
In
     commit 2f63718
     Author: Jason Molenda <jmolenda@apple.com>
     Date:   Tue Mar 26 09:07:15 2024 -0700

[lldb] Don't clear a Module's UnwindTable when adding a SymbolFile
(llvm#86603)

I stopped clearing a Module's UnwindTable when we add a SymbolFile to
avoid the memory management problems with adding a symbol file
asynchronously while the UnwindTable is being accessed on another
thread. This broke the target-symbols-add-unwind.test shell test on
Linux which removes the DWARF debub_frame section from a binary, loads
it, then loads the unstripped binary with the DWARF debug_frame section
and checks that the UnwindPlans for a function include debug_frame.

I originally decided that I was willing to sacrifice the possiblity of
additional unwind sources from a symbol file because we rely on assembly
emulation so heavily, they're rarely critical. But there are targets
where we we don't have emluation and rely on things like DWARF
debug_frame a lot more, so this probably wasn't a good choice.

This patch adds a new UnwindTable::Update method which looks for any new
sources of unwind information and adds it to the UnwindTable, and calls
that after a new SymbolFile has been added to a Module.

(cherry picked from commit 6a0ec8e)
jasonmolenda added a commit to jasonmolenda/llvm-project that referenced this pull request Mar 27, 2024
In
     commit 2f63718
     Author: Jason Molenda <jmolenda@apple.com>
     Date:   Tue Mar 26 09:07:15 2024 -0700

[lldb] Don't clear a Module's UnwindTable when adding a SymbolFile
(llvm#86603)

I stopped clearing a Module's UnwindTable when we add a SymbolFile to
avoid the memory management problems with adding a symbol file
asynchronously while the UnwindTable is being accessed on another
thread. This broke the target-symbols-add-unwind.test shell test on
Linux which removes the DWARF debub_frame section from a binary, loads
it, then loads the unstripped binary with the DWARF debug_frame section
and checks that the UnwindPlans for a function include debug_frame.

I originally decided that I was willing to sacrifice the possiblity of
additional unwind sources from a symbol file because we rely on assembly
emulation so heavily, they're rarely critical. But there are targets
where we we don't have emluation and rely on things like DWARF
debug_frame a lot more, so this probably wasn't a good choice.

This patch adds a new UnwindTable::Update method which looks for any new
sources of unwind information and adds it to the UnwindTable, and calls
that after a new SymbolFile has been added to a Module.

(cherry picked from commit 6a0ec8e)
jasonmolenda added a commit to swiftlang/llvm-project that referenced this pull request Mar 28, 2024
…ate-shell-test

[lldb] Revive shell test after updating UnwindTable (llvm#86770)
labath added a commit to labath/llvm-project that referenced this pull request Feb 5, 2025
PR llvm#86603 broke unwinding in for unwind info added via "target symbols
add". llvm#86770 attempted to fix this, but the fix was only partial -- it
accepted new sources of unwind information, but didn't take into account
that the symbol file can alter what lldb percieves as function
boundaries.

A stripped file will not contain information about private
(non-exported) symbols, which will make the public symbols appear very
large. If lldb tries to unwind from such a function before symbols are
added, then the cached unwind plan will prevent new (correct) unwind
plans from being created.

target-symbols-add-unwind.test might have caught this, were it not for
the fact that the "image show-unwind" command does *not* use cached
unwind information (it recomputes it from scratch).

The changes in this patch come in three pieces:
- Clear cached unwind plans when adding symbols. Since the symbol
  boundaries can change, we cannot trust anything we've computed
  previously.
- Add a flag to "image show-unwind" to display the cached unwind
  information (mainly for the use in the test, but I think it's also
  generally useful).
- Rewrite the test to better and more reliably simulate the real-world
  scenario: I've swapped the running process for a core (minidump) file
  so it can run anywhere; used the caching version of the show-unwind
  command; and swapped C for assembly to better control the placement of
  symbols
labath added a commit that referenced this pull request Feb 7, 2025
PR #86603 broke unwinding in for unwind info added via "target symbols
add". #86770 attempted to fix this, but the fix was only partial -- it
accepted new sources of unwind information, but didn't take into account
that the symbol file can alter what lldb percieves as function
boundaries.

A stripped file will not contain information about private
(non-exported) symbols, which will make the public symbols appear very
large. If lldb tries to unwind from such a function before symbols are
added, then the cached unwind plan will prevent new (correct) unwind
plans from being created.

target-symbols-add-unwind.test might have caught this, were it not for
the fact that the "image show-unwind" command does *not* use cached
unwind information (it recomputes it from scratch).

The changes in this patch come in three pieces:
- Clear cached unwind plans when adding symbols. Since the symbol
boundaries can change, we cannot trust anything we've computed
previously.
- Add a flag to "image show-unwind" to display the cached unwind
information (mainly for the use in the test, but I think it's also
generally useful).
- Rewrite the test to better and more reliably simulate the real-world
scenario: I've swapped the running process for a core (minidump) file so
it can run anywhere; used the caching version of the show-unwind
command; and swapped C for assembly to better control the placement of
symbols
Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
PR llvm#86603 broke unwinding in for unwind info added via "target symbols
add". llvm#86770 attempted to fix this, but the fix was only partial -- it
accepted new sources of unwind information, but didn't take into account
that the symbol file can alter what lldb percieves as function
boundaries.

A stripped file will not contain information about private
(non-exported) symbols, which will make the public symbols appear very
large. If lldb tries to unwind from such a function before symbols are
added, then the cached unwind plan will prevent new (correct) unwind
plans from being created.

target-symbols-add-unwind.test might have caught this, were it not for
the fact that the "image show-unwind" command does *not* use cached
unwind information (it recomputes it from scratch).

The changes in this patch come in three pieces:
- Clear cached unwind plans when adding symbols. Since the symbol
boundaries can change, we cannot trust anything we've computed
previously.
- Add a flag to "image show-unwind" to display the cached unwind
information (mainly for the use in the test, but I think it's also
generally useful).
- Rewrite the test to better and more reliably simulate the real-world
scenario: I've swapped the running process for a core (minidump) file so
it can run anywhere; used the caching version of the show-unwind
command; and swapped C for assembly to better control the placement of
symbols
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.

3 participants