- 
                Notifications
    You must be signed in to change notification settings 
- Fork 15k
[lldb][ElfCore] Improve main executable detection in core files #157170
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
| @llvm/pr-subscribers-clang-format @llvm/pr-subscribers-lldb Author: None (GeorgeHuyubo) ChangesThis change improves how LLDB's ProcessElfCore plugin identifies the main executable when loading ELF core files. Previously, the code would simply use the first entry in the NT_FILE section, which is not guaranteed to be the main executable, also the first entry might not have a valid UUID. 
 Full diff: https://github.com/llvm/llvm-project/pull/157170.diff 2 Files Affected: 
 diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 88eeddf178788..e88daebebfa7e 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -257,12 +257,21 @@ Status ProcessElfCore::DoLoadCore() {
   // the main executable using data we found in the core file notes.
   lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
   if (!exe_module_sp) {
-    // The first entry in the NT_FILE might be our executable
     if (!m_nt_file_entries.empty()) {
+      // The first entry in the NT_FILE might be our executable
+      llvm::StringRef executable_path = m_nt_file_entries[0].path;
+      // Prefer the NT_FILE entry matching m_executable_name as main executable.
+      for (const NT_FILE_Entry &file_entry : m_nt_file_entries)
+        if (llvm::StringRef(file_entry.path)
+                .ends_with("/" + m_executable_name)) {
+          executable_path = file_entry.path;
+          break;
+        }
+
       ModuleSpec exe_module_spec;
       exe_module_spec.GetArchitecture() = arch;
-      exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid;
-      exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path,
+      exe_module_spec.GetUUID() = FindModuleUUID(executable_path);
+      exe_module_spec.GetFileSpec().SetFile(executable_path,
                                             FileSpec::Style::native);
       if (exe_module_spec.GetFileSpec()) {
         exe_module_sp =
@@ -935,6 +944,7 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
         return status.ToError();
       thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname)));
       SetID(prpsinfo.pr_pid);
+      m_executable_name = prpsinfo.pr_fname;
       break;
     }
     case ELF::NT_SIGINFO: {
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
index a91c04a277f60..601c8c4ed1b92 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -152,6 +152,9 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   // NT_FILE entries found from the NOTE segment
   std::vector<NT_FILE_Entry> m_nt_file_entries;
 
+  // Executable name found from the ELF PRPSINFO
+  std::string m_executable_name;
+
   // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment
   llvm::Error ParseThreadContextsFromNoteSegment(
       const elf::ELFProgramHeader &segment_header,
 | 
28869cd    to
    44cb5f6      
    Compare
  
    
      
        
              This comment was marked as outdated.
        
        
      
    
  This comment was marked as outdated.
44cb5f6    to
    056abe4      
    Compare
  
    056abe4    to
    ca95ef4      
    Compare
  
    There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get a test case for this?
f778f24    to
    e3de722      
    Compare
  
    e3de722    to
    ce400ed      
    Compare
  
    | 
 @dmpots I think this file historically lack a good way of testing changes due to difficulties in hand craft a ELF core file using YAML. The ELF support yaml2obj doesn't allow content to be supplied for a program header. It expects section headers to contain data and ELF core files don't have section headers. So it would take some modifications to the ELF obj2yaml and yaml2obj to support this. It would be great if we can add this support at some point if anyone has expertise in the yaml layer as it would be easy to create a core file, reduce it using a different tool, and then obj2yaml it for a test. | 
| 
 I see, thanks for the context. Sounds like something we need to fix, but beyond the scope of this PR. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
llvm#157170 added code that assigned pr_fname to another std::string member. In the lines before, we copy pr_fname using assign with a max length set to either the length of the string in pr_fname, or the size of pr_fname. Which is 16 bytes. struct ELFLinuxPrPsInfo { <...> char pr_fname[16]; The content of pr_fname can fill all 16 bytes, that's why we need the limit. This was not done for m_executable_name where it ended up calling the assignment from char* operator which could read on into the rest of the corefile in some cases. Likely wouldn't crash for reading out of bounds, but you would at least see some strange things in LLDB. Fix this by copying the std::string we already made for thread_data.name.
Since llvm#157170 this test has been flakey on several LLDB buildbots. I suspect it's to do with mutli-threading, there are more details in llvm#159377. Disable parallel loading for now so we are not spamming people making unrelated changes.
This change improves how LLDB's ProcessElfCore plugin identifies the main executable when loading ELF core files. Previously, the code would simply use the first entry in the NT_FILE section, which is not guaranteed to be the main executable, also the first entry might not have a valid UUID.
Storing executable name: Extract the executable name from the ELF NT_PRPSINFO note and store it in
m_executable_namePreferential matching: When selecting the main executable from NT_FILE entries, prefer entries whose path ends with the stored executable name
UUID-based lookup: Call
FindModuleUUID()helper function to properly match modules by path and retrieve a valid UUID