+static
+void read_dwarf_info(simgrid::mc::ObjectInformation* info, Dwarf* dwarf)
+{
+ // For each compilation unit:
+ Dwarf_Off offset = 0;
+ Dwarf_Off next_offset = 0;
+ size_t length;
+
+ while (dwarf_nextcu(dwarf, offset, &next_offset, &length, nullptr, nullptr, nullptr) ==
+ 0) {
+ Dwarf_Die unit_die;
+ if (dwarf_offdie(dwarf, offset + length, &unit_die) != nullptr)
+ MC_dwarf_handle_children(info, &unit_die, &unit_die, nullptr, nullptr);
+ offset = next_offset;
+ }
+}
+
+/** Get the build-id (NT_GNU_BUILD_ID) from the ELF file
+ *
+ * This build-id may is used to locate an external debug (DWARF) file
+ * for this ELF file.
+ *
+ * @param elf libelf handle for an ELF file
+ * @return build-id for this ELF file (or an empty vector if none is found)
+ */
+static
+std::vector<char> get_build_id(Elf* elf)
+{
+ // Summary: the GNU build ID is stored in a ("GNU, NT_GNU_BUILD_ID) note
+ // found in a PT_NOTE entry in the program header table.
+
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ xbt_die("Could not read program headers");
+
+ // Iterate over the program headers and find the PT_NOTE ones:
+ 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);
+
+ // Iterate over the notes and find the NT_GNU_BUILD_ID one:
+ size_t pos = 0;
+ while (pos < data->d_size) {
+ GElf_Nhdr nhdr;
+ // Location of the name within Elf_Data:
+ size_t name_pos;
+ size_t desc_pos;
+ pos = gelf_getnote(data, pos, &nhdr, &name_pos, &desc_pos);
+ // A build ID note is identified by the pair ("GNU", NT_GNU_BUILD_ID)
+ // (a namespace and a type within this namespace):
+ if (nhdr.n_type == NT_GNU_BUILD_ID
+ && nhdr.n_namesz == sizeof("GNU")
+ && memcmp((char*) data->d_buf + name_pos, "GNU", sizeof("GNU")) == 0) {
+ XBT_DEBUG("Found GNU/NT_GNU_BUILD_ID note");
+ 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'
+};
+
+/** Binary data to hexadecimal */
+static inline
+std::array<char, 2> to_hex(std::uint8_t byte)
+{
+ // Horrid double braces!
+ // Apparently, this is needed in C++11 (not in C++14).
+ return { { hexdigits[byte >> 4], hexdigits[byte & 0xF] } };
+}
+
+/** Binary data to hexadecimal */
+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 res;
+}
+
+/** Binary data to hexadecimal */
+static
+std::string to_hex(std::vector<char> const& data)
+{
+ return to_hex(data.data(), data.size());
+}
+
+/** Base directories for external debug files */
+static
+const char* debug_paths[] = {
+ "/usr/lib/debug/",
+ "/usr/local/lib/debug/",
+};
+
+/** Locate an external debug file from the NT_GNU_BUILD_ID
+ *
+ * This is one of the mechanisms used for
+ * [separate debug files](https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html).
+ */
+// Example:
+// /usr/lib/debug/.build-id/0b/dc77f1c29aea2b14ff5acd9a19ab3175ffdeae.debug
+static
+std::string find_by_build_id(std::vector<char> id)
+{
+ std::string filename;
+ std::string hex = to_hex(id);
+ for (const char* debug_path : debug_paths) {
+ // Example:
+ filename = std::string(debug_path) + ".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) {
+ XBT_DEBUG("Found debug file: %s\n", hex.c_str());
+ return filename;
+ }
+ }
+ XBT_DEBUG("Not debuf info found for build ID %s\n", hex.data());
+ return std::string();
+}
+