Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / breakpad / src / client / linux / minidump_writer / linux_dumper.cc
index e09da91..d565c74 100644 (file)
 #include "client/linux/minidump_writer/linux_dumper.h"
 
 #include <assert.h>
+#include <elf.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stddef.h>
 #include <string.h>
 
 #include "client/linux/minidump_writer/line_reader.h"
+#include "common/linux/elfutils.h"
 #include "common/linux/file_id.h"
 #include "common/linux/linux_libc_support.h"
 #include "common/linux/memory_mapped_file.h"
@@ -52,6 +54,7 @@
 
 static const char kMappedFileUnsafePrefix[] = "/dev/";
 static const char kDeletedSuffix[] = " (deleted)";
+static const char kReservedFlags[] = " ---p";
 
 inline static bool IsMappedFileOpenUnsafe(
     const google_breakpad::MappingInfo& mapping) {
@@ -115,15 +118,16 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
 
   char filename[NAME_MAX];
   size_t filename_len = my_strlen(mapping.name);
-  assert(filename_len < NAME_MAX);
-  if (filename_len >= NAME_MAX)
+  if (filename_len >= NAME_MAX) {
+    assert(false);
     return false;
+  }
   my_memcpy(filename, mapping.name, filename_len);
   filename[filename_len] = '\0';
   bool filename_modified = HandleDeletedFileInMapping(filename);
 
-  MemoryMappedFile mapped_file(filename);
-  if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)?
+  MemoryMappedFile mapped_file(filename, mapping.offset);
+  if (!mapped_file.data() || mapped_file.size() < SELFMAG)
     return false;
 
   bool success =
@@ -136,6 +140,80 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
   return success;
 }
 
+namespace {
+bool ElfFileSoNameFromMappedFile(
+    const void* elf_base, char* soname, size_t soname_size) {
+  if (!IsValidElf(elf_base)) {
+    // Not ELF
+    return false;
+  }
+
+  const void* segment_start;
+  size_t segment_size;
+  int elf_class;
+  if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
+                      &segment_start, &segment_size, &elf_class)) {
+    // No dynamic section
+    return false;
+  }
+
+  const void* dynstr_start;
+  size_t dynstr_size;
+  if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
+                      &dynstr_start, &dynstr_size, &elf_class)) {
+    // No dynstr section
+    return false;
+  }
+
+  const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
+  size_t dcount = segment_size / sizeof(ElfW(Dyn));
+  for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
+    if (dyn->d_tag == DT_SONAME) {
+      const char* dynstr = static_cast<const char*>(dynstr_start);
+      if (dyn->d_un.d_val >= dynstr_size) {
+        // Beyond the end of the dynstr section
+        return false;
+      }
+      const char* str = dynstr + dyn->d_un.d_val;
+      const size_t maxsize = dynstr_size - dyn->d_un.d_val;
+      my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
+      return true;
+    }
+  }
+
+  // Did not find SONAME
+  return false;
+}
+}  // namespace
+
+// static
+bool LinuxDumper::ElfFileSoName(
+    const MappingInfo& mapping, char* soname, size_t soname_size) {
+  if (IsMappedFileOpenUnsafe(mapping)) {
+    // Not safe
+    return false;
+  }
+
+  char filename[NAME_MAX];
+  size_t filename_len = my_strlen(mapping.name);
+  if (filename_len >= NAME_MAX) {
+    assert(false);
+    // name too long
+    return false;
+  }
+
+  my_memcpy(filename, mapping.name, filename_len);
+  filename[filename_len] = '\0';
+
+  MemoryMappedFile mapped_file(filename, mapping.offset);
+  if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
+    // mmap failed
+    return false;
+  }
+
+  return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
+}
+
 bool LinuxDumper::ReadAuxv() {
   char auxv_path[NAME_MAX];
   if (!BuildProcPath(auxv_path, pid_, "auxv")) {
@@ -195,6 +273,7 @@ bool LinuxDumper::EnumerateMappings() {
     if (*i1 == '-') {
       const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
       if (*i2 == ' ') {
+        bool exec = (*(i2 + 3) == 'x');
         const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
         if (*i3 == ' ') {
           const char* name = NULL;
@@ -218,11 +297,29 @@ bool LinuxDumper::EnumerateMappings() {
               continue;
             }
           }
+          // Also merge mappings that result from address ranges that the
+          // linker reserved but which a loaded library did not use. These
+          // appear as an anonymous private mapping with no access flags set
+          // and which directly follow an executable mapping.
+          if (!name && !mappings_.empty()) {
+            MappingInfo* module = mappings_.back();
+            if ((start_addr == module->start_addr + module->size) &&
+                module->exec &&
+                module->name[0] == '/' &&
+                offset == 0 && my_strncmp(i2,
+                                          kReservedFlags,
+                                          sizeof(kReservedFlags) - 1) == 0) {
+              module->size = end_addr - module->start_addr;
+              line_reader->PopLine(line_len);
+              continue;
+            }
+          }
           MappingInfo* const module = new(allocator_) MappingInfo;
           my_memset(module, 0, sizeof(MappingInfo));
           module->start_addr = start_addr;
           module->size = end_addr - start_addr;
           module->offset = offset;
+          module->exec = exec;
           if (name != NULL) {
             const unsigned l = my_strlen(name);
             if (l < sizeof(module->name))