Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Load DWARF info from external file using NT_GNU_BUILD_ID
[simgrid.git] / src / mc / mc_dwarf.cpp
index 38c7d3a..29c7206 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 
 #include <cstdlib>
 #define DW_LANG_Objc DW_LANG_ObjC       /* fix spelling error in older dwarf.h */
@@ -257,11 +258,10 @@ static const char *MC_dwarf_attr_integrate_string(Dwarf_Die * die,
                                                   int attribute)
 {
   Dwarf_Attribute attr;
-  if (!dwarf_attr_integrate(die, attribute, &attr)) {
+  if (!dwarf_attr_integrate(die, attribute, &attr))
     return nullptr;
-  } else {
+  else
     return dwarf_formstring(&attr);
-  }
 }
 
 /** \brief Get the linkage name of a DIE.
@@ -867,9 +867,8 @@ static void MC_dwarf_handle_scope_die(simgrid::mc::ObjectInformation* info, Dwar
   if (low_pc) {
     // DW_AT_high_pc:
     Dwarf_Attribute attr;
-    if (!dwarf_attr_integrate(die, DW_AT_high_pc, &attr)) {
+    if (!dwarf_attr_integrate(die, DW_AT_high_pc, &attr))
       xbt_die("Missing DW_AT_high_pc matching with DW_AT_low_pc");
-    }
 
     Dwarf_Sword offset;
     Dwarf_Addr high_pc;
@@ -941,9 +940,8 @@ static void MC_dwarf_handle_children(simgrid::mc::ObjectInformation* info, Dwarf
   Dwarf_Die child;
   int res;
   for (res = dwarf_child(die, &child); res == 0;
-       res = dwarf_siblingof(&child, &child)) {
+       res = dwarf_siblingof(&child, &child))
     MC_dwarf_handle_die(info, &child, unit, frame, ns);
-  }
 }
 
 static void MC_dwarf_handle_die(simgrid::mc::ObjectInformation* info, Dwarf_Die * die,
@@ -981,9 +979,8 @@ static void MC_dwarf_handle_die(simgrid::mc::ObjectInformation* info, Dwarf_Die
 }
 
 static
-Elf64_Half MC_dwarf_elf_type(Dwarf* dwarf)
+Elf64_Half get_type(Elf* elf)
 {
-  Elf* elf = dwarf_getelf(dwarf);
   Elf64_Ehdr* ehdr64 = elf64_getehdr(elf);
   if (ehdr64)
     return ehdr64->e_type;
@@ -993,28 +990,9 @@ Elf64_Half MC_dwarf_elf_type(Dwarf* dwarf)
   xbt_die("Could not get ELF heeader");
 }
 
-/** \brief Populate the debugging informations of the given ELF object
- *
- *  Read the DWARf information of the EFFL object and populate the
- *  lists of types, variables, functions.
- */
 static
-void MC_dwarf_get_variables(simgrid::mc::ObjectInformation* info)
+void read_dwarf_info(simgrid::mc::ObjectInformation* info, Dwarf* dwarf)
 {
-  int fd = open(info->file_name.c_str(), O_RDONLY);
-  if (fd < 0)
-    xbt_die("Could not open file %s", info->file_name.c_str());
-  Dwarf *dwarf = dwarf_begin(fd, DWARF_C_READ);
-  if (dwarf == nullptr)
-    xbt_die("Missing debugging information in %s\n"
-      "Your program and its dependencies must have debugging information.\n"
-      "You might want to recompile with -g or install the suitable debugging package.\n",
-      info->file_name.c_str());
-
-  Elf64_Half elf_type = MC_dwarf_elf_type(dwarf);
-  if (elf_type == ET_EXEC)
-    info->flags |= simgrid::mc::ObjectInformation::Executable;
-
   // For each compilation unit:
   Dwarf_Off offset = 0;
   Dwarf_Off next_offset = 0;
@@ -1027,9 +1005,168 @@ void MC_dwarf_get_variables(simgrid::mc::ObjectInformation* info)
       MC_dwarf_handle_children(info, &unit_die, &unit_die, nullptr, NULL);
     offset = next_offset;
   }
+}
+
+/** Get the build-id (NT_GNU_BUILD_ID) from the ELF file
+ *
+ *  Return an empty vector is none is found.
+ */
+static
+std::vector<char> get_build_id(Elf* elf)
+{
+  size_t phnum;
+  if (elf_getphdrnum (elf, &phnum) != 0)
+    xbt_die("Could not read program headers");
+  for (size_t i = 0; i < phnum; ++i) {
+    GElf_Phdr phdr_temp;
+    GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_temp);
+    if (phdr->p_type != PT_NOTE)
+      continue;
+    Elf_Data* data = elf_getdata_rawchunk(elf, phdr->p_offset, phdr->p_filesz, ELF_T_NHDR);
+    size_t pos = 0;
+    while (1) {
+      GElf_Nhdr nhdr;
+      size_t name_pos;
+      size_t desc_pos;
+      pos = gelf_getnote(data, pos, &nhdr, &name_pos, &desc_pos);
+      if (nhdr.n_type == NT_GNU_BUILD_ID
+          && nhdr.n_namesz == sizeof("GNU")
+          && memcmp((char*) data->d_buf + name_pos, "GNU", sizeof("GNU")) == 0) {
+        char* start = (char*) data->d_buf + desc_pos;
+        char* end = (char*) start + nhdr.n_descsz;
+        return std::vector<char>(start, end);
+      }
+    }
+  }
+  return std::vector<char>();
+}
 
+static char hexdigits[16] = {
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+  'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+static inline
+std::array<char, 2> to_hex(std::uint8_t byte)
+{
+  return { hexdigits[byte >> 4], hexdigits[byte & 0xF] };
+}
+
+/** Hexadecimal representation of some binary data */
+static
+std::string to_hex(const char* data, std::size_t count)
+{
+  std::string res;
+  res.resize(2*count);
+  for (std::size_t i = 0; i < count; i++) {
+    std::array<char, 2> hex_byte = to_hex(data[i]);
+    for (int j = 0; j < 2; ++j)
+      res[2 * i + j] = hex_byte[j];
+  }
+  return std::move(res);
+}
+
+static
+std::string to_hex(std::vector<char> const& data)
+{
+  return to_hex(data.data(), data.size());
+}
+
+const char* debug_paths[] = {
+  "/usr/lib/debug/",
+  "/usr/local/lib/debug/"
+};
+
+static
+std::string find_by_build_id(std::vector<char> id)
+{
+  std::string filename;
+  for (const char* debug_path : debug_paths) {
+    filename = debug_path;
+    filename += ".build-id/" + to_hex(id.data(), 1) + '/'
+      + to_hex(id.data() + 1, id.size() - 1) + ".debug";
+    XBT_DEBUG("Checking debug file: %s", filename.c_str());
+    if (access(filename.c_str(), F_OK) == 0)
+      return std::move(filename);
+  }
+  return std::string();
+}
+
+/** \brief Populate the debugging informations of the given ELF object
+ *
+ *  Read the DWARf information of the EFFL object and populate the
+ *  lists of types, variables, functions.
+ */
+static
+void MC_dwarf_get_variables(simgrid::mc::ObjectInformation* info)
+{
+  if (elf_version(EV_CURRENT) == EV_NONE)
+    xbt_die("libelf initialization error");
+
+  int fd = open(info->file_name.c_str(), O_RDONLY);
+  if (fd < 0)
+    xbt_die("Could not open file %s", info->file_name.c_str());
+  Elf* elf = elf_begin(fd, ELF_C_READ, nullptr);
+  if (elf == nullptr)
+    xbt_die("Not an ELF file 1");
+  Elf_Kind kind = elf_kind(elf);
+  if (kind != ELF_K_ELF)
+    xbt_die("Not an ELF file 2");
+
+  Elf64_Half type = get_type(elf);
+  if (type == ET_EXEC)
+    info->flags |= simgrid::mc::ObjectInformation::Executable;
+
+  Dwarf* dwarf = dwarf_begin_elf (elf, DWARF_C_READ, nullptr);
+  if (dwarf != nullptr) {
+    // This is the simple case where DWARF is located in the ELF file:
+    read_dwarf_info(info, dwarf);
+    dwarf_end(dwarf);
+    elf_end(elf);
+    close(fd);
+    return;
+  }
   dwarf_end(dwarf);
-  close(fd);
+
+  // Try to find it with NT_GNU_BUILD_ID:
+  std::vector<char> build_id = get_build_id(elf);
+  if (!build_id.empty()) {
+    elf_end(elf);
+    close(fd);
+
+    // Find a debug file using the build id:
+    std::string debug_file = find_by_build_id(build_id);
+    if (debug_file.empty()) {
+      std::string hex = to_hex(build_id);
+      xbt_die(
+        "Missing debug info for %s with build-id %s\n"
+        "You might want to install the suitable debugging package.\n",
+        info->file_name.c_str(), hex.c_str());
+    }
+
+    // Load the DWARF info from this file:
+    XBT_DEBUG("Load DWARF for %s from %s",
+      info->file_name.c_str(), debug_file.c_str());
+    fd = open(debug_file.c_str(), O_RDONLY);
+    if (fd < 0)
+      xbt_die("Could not open file %s", debug_file.c_str());
+    Dwarf* dwarf = dwarf_begin(fd, DWARF_C_READ);
+    if (dwarf == nullptr)
+      xbt_die("No DWARF info in %s for %s",
+        debug_file.c_str(), info->file_name.c_str());
+    read_dwarf_info(info, dwarf);
+    dwarf_end(dwarf);
+    close(fd);
+    return;
+  }
+
+  // TODO, try to find DWARF info using debug-link.
+  // Is this debug-link actually used anywhere?
+
+  // Everything failes, complain loudly:
+  xbt_die("Debugging information not found for %s\n"
+    "Try recompiling with -g\n",
+    info->file_name.c_str());
 }
 
 // ***** Functions index
@@ -1176,7 +1313,7 @@ void MC_post_process_object_info(simgrid::mc::Process* process, simgrid::mc::Obj
         break;
 
     // Resolve full_type:
-    if (!subtype->name.empty() && subtype->byte_size == 0) {
+    if (!subtype->name.empty() && subtype->byte_size == 0)
       for (auto const& object_info : process->object_infos) {
         auto i = object_info->full_types_by_name.find(subtype->name);
         if (i != object_info->full_types_by_name.end()
@@ -1185,7 +1322,7 @@ void MC_post_process_object_info(simgrid::mc::Process* process, simgrid::mc::Obj
           break;
         }
       }
-    else type->full_type = subtype;
+    else type->full_type = subtype;
 
   }
 }