LCOV - code coverage report
Current view: top level - toolkit/crashreporter/breakpad-client/linux/minidump_writer - linux_dumper.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 199 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 16 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2010, Google Inc.
       2             : // All rights reserved.
       3             : //
       4             : // Redistribution and use in source and binary forms, with or without
       5             : // modification, are permitted provided that the following conditions are
       6             : // met:
       7             : //
       8             : //     * Redistributions of source code must retain the above copyright
       9             : // notice, this list of conditions and the following disclaimer.
      10             : //     * Redistributions in binary form must reproduce the above
      11             : // copyright notice, this list of conditions and the following disclaimer
      12             : // in the documentation and/or other materials provided with the
      13             : // distribution.
      14             : //     * Neither the name of Google Inc. nor the names of its
      15             : // contributors may be used to endorse or promote products derived from
      16             : // this software without specific prior written permission.
      17             : //
      18             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      22             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      23             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      24             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      28             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29             : 
      30             : // linux_dumper.cc: Implement google_breakpad::LinuxDumper.
      31             : // See linux_dumper.h for details.
      32             : 
      33             : // This code deals with the mechanics of getting information about a crashed
      34             : // process. Since this code may run in a compromised address space, the same
      35             : // rules apply as detailed at the top of minidump_writer.h: no libc calls and
      36             : // use the alternative allocator.
      37             : 
      38             : #include "linux/minidump_writer/linux_dumper.h"
      39             : 
      40             : #include <assert.h>
      41             : #include <elf.h>
      42             : #include <fcntl.h>
      43             : #include <limits.h>
      44             : #include <stddef.h>
      45             : #include <string.h>
      46             : 
      47             : #include "linux/minidump_writer/line_reader.h"
      48             : #include "common/linux/elfutils.h"
      49             : #include "common/linux/file_id.h"
      50             : #include "common/linux/linux_libc_support.h"
      51             : #include "common/linux/memory_mapped_file.h"
      52             : #include "common/linux/safe_readlink.h"
      53             : #include "third_party/lss/linux_syscall_support.h"
      54             : 
      55             : #if defined(__ANDROID__)
      56             : 
      57             : // Android packed relocations definitions are not yet available from the
      58             : // NDK header files, so we have to provide them manually here.
      59             : #ifndef DT_LOOS
      60             : #define DT_LOOS 0x6000000d
      61             : #endif
      62             : #ifndef DT_ANDROID_REL
      63             : static const int DT_ANDROID_REL = DT_LOOS + 2;
      64             : #endif
      65             : #ifndef DT_ANDROID_RELA
      66             : static const int DT_ANDROID_RELA = DT_LOOS + 4;
      67             : #endif
      68             : 
      69             : #endif  // __ANDROID __
      70             : 
      71             : static const char kMappedFileUnsafePrefix[] = "/dev/";
      72             : static const char kDeletedSuffix[] = " (deleted)";
      73             : static const char kReservedFlags[] = " ---p";
      74             : 
      75           0 : inline static bool IsMappedFileOpenUnsafe(
      76             :     const google_breakpad::MappingInfo& mapping) {
      77             :   // It is unsafe to attempt to open a mapped file that lives under /dev,
      78             :   // because the semantics of the open may be driver-specific so we'd risk
      79             :   // hanging the crash dumper. And a file in /dev/ almost certainly has no
      80             :   // ELF file identifier anyways.
      81           0 :   return my_strncmp(mapping.name,
      82             :                     kMappedFileUnsafePrefix,
      83           0 :                     sizeof(kMappedFileUnsafePrefix) - 1) == 0;
      84             : }
      85             : 
      86             : namespace google_breakpad {
      87             : 
      88             : #if defined(__CHROMEOS__)
      89             : 
      90             : namespace {
      91             : 
      92             : // Recover memory mappings before writing dump on ChromeOS
      93             : //
      94             : // On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
      95             : // addresses. ChromeOS' hugepage implementation replaces some segments with
      96             : // anonymous private pages, which is a restriction of current implementation
      97             : // in Linux kernel at the time of writing. Thus, breakpad can no longer
      98             : // symbolize addresses from those text segments replaced with hugepages.
      99             : //
     100             : // This postprocess tries to recover the mappings. Because hugepages are always
     101             : // inserted in between some .text sections, it tries to infer the names and
     102             : // offsets of the segments, by looking at segments immediately precede and
     103             : // succeed them.
     104             : //
     105             : // For example, a text segment before hugepage optimization
     106             : //   02001000-03002000 r-xp /opt/google/chrome/chrome
     107             : //
     108             : // can be broken into
     109             : //   02001000-02200000 r-xp /opt/google/chrome/chrome
     110             : //   02200000-03000000 r-xp
     111             : //   03000000-03002000 r-xp /opt/google/chrome/chrome
     112             : //
     113             : // For more details, see:
     114             : // crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization
     115             : 
     116             : // Copied from CrOS' hugepage implementation, which is unlikely to change.
     117             : // The hugepage size is 2M.
     118             : const unsigned int kHpageShift = 21;
     119             : const size_t kHpageSize = (1 << kHpageShift);
     120             : const size_t kHpageMask = (~(kHpageSize - 1));
     121             : 
     122             : // Find and merge anonymous r-xp segments with surrounding named segments.
     123             : // There are two cases:
     124             : 
     125             : // Case 1: curr, next
     126             : //   curr is anonymous
     127             : //   curr is r-xp
     128             : //   curr.size >= 2M
     129             : //   curr.size is a multiple of 2M.
     130             : //   next is backed by some file.
     131             : //   curr and next are contiguous.
     132             : //   offset(next) == sizeof(curr)
     133             : void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) {
     134             :   // Merged segments are marked with size = 0.
     135             :   if (curr->size == 0 || next->size == 0)
     136             :     return;
     137             : 
     138             :   if (curr->size >= kHpageSize &&
     139             :       curr->exec &&
     140             :       (curr->size & kHpageMask) == curr->size &&
     141             :       (curr->start_addr & kHpageMask) == curr->start_addr &&
     142             :       curr->name[0] == '\0' &&
     143             :       next->name[0] != '\0' &&
     144             :       curr->start_addr + curr->size == next->start_addr &&
     145             :       curr->size == next->offset) {
     146             : 
     147             :     // matched
     148             :     my_strlcpy(curr->name, next->name, NAME_MAX);
     149             :     if (next->exec) {
     150             :       // (curr, next)
     151             :       curr->size += next->size;
     152             :       next->size = 0;
     153             :     }
     154             :   }
     155             : }
     156             : 
     157             : // Case 2: prev, curr, next
     158             : //   curr is anonymous
     159             : //   curr is r-xp
     160             : //   curr.size >= 2M
     161             : //   curr.size is a multiple of 2M.
     162             : //   next and prev are backed by the same file.
     163             : //   prev, curr and next are contiguous.
     164             : //   offset(next) == offset(prev) + sizeof(prev) + sizeof(curr)
     165             : void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr,
     166             :     MappingInfo *next) {
     167             :   // Merged segments are marked with size = 0.
     168             :   if (prev->size == 0 || curr->size == 0 || next->size == 0)
     169             :     return;
     170             : 
     171             :   if (curr->size >= kHpageSize &&
     172             :       curr->exec &&
     173             :       (curr->size & kHpageMask) == curr->size &&
     174             :       (curr->start_addr & kHpageMask) == curr->start_addr &&
     175             :       curr->name[0] == '\0' &&
     176             :       next->name[0] != '\0' &&
     177             :       curr->start_addr + curr->size == next->start_addr &&
     178             :       prev->start_addr + prev->size == curr->start_addr &&
     179             :       my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
     180             :       next->offset == prev->offset + prev->size + curr->size) {
     181             : 
     182             :     // matched
     183             :     my_strlcpy(curr->name, prev->name, NAME_MAX);
     184             :     if (prev->exec) {
     185             :       curr->offset = prev->offset;
     186             :       curr->start_addr = prev->start_addr;
     187             :       if (next->exec) {
     188             :         // (prev, curr, next)
     189             :         curr->size += prev->size + next->size;
     190             :         prev->size = 0;
     191             :         next->size = 0;
     192             :       } else {
     193             :         // (prev, curr), next
     194             :         curr->size += prev->size;
     195             :         prev->size = 0;
     196             :       }
     197             :     } else {
     198             :       curr->offset = prev->offset + prev->size;
     199             :       if (next->exec) {
     200             :         // prev, (curr, next)
     201             :         curr->size += next->size;
     202             :         next->size = 0;
     203             :       } else {
     204             :         // prev, curr, next
     205             :       }
     206             :     }
     207             :   }
     208             : }
     209             : 
     210             : // mappings_ is sorted excepted for the first entry.
     211             : // This function tries to merge segemnts into the first entry,
     212             : // then check for other sorted entries.
     213             : // See LinuxDumper::EnumerateMappings().
     214             : void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
     215             :   // Find the candidate "next" to first segment, which is the only one that
     216             :   // could be out-of-order.
     217             :   size_t l = 1;
     218             :   size_t r = mappings.size();
     219             :   size_t next = mappings.size();
     220             :   while (l < r) {
     221             :     int m = (l + r) / 2;
     222             :     if (mappings[m]->start_addr > mappings[0]->start_addr)
     223             :       r = next = m;
     224             :     else
     225             :       l = m + 1;
     226             :   }
     227             : 
     228             :   // Try to merge segments into the first.
     229             :   if (next < mappings.size()) {
     230             :     TryRecoverMappings(mappings[0], mappings[next]);
     231             :     if (next - 1 > 0)
     232             :       TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
     233             :   }
     234             : 
     235             :   // Iterate through normal, sorted cases.
     236             :   // Normal case 1.
     237             :   for (size_t i = 1; i < mappings.size() - 1; i++)
     238             :     TryRecoverMappings(mappings[i], mappings[i + 1]);
     239             : 
     240             :   // Normal case 2.
     241             :   for (size_t i = 1; i < mappings.size() - 2; i++)
     242             :     TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
     243             : 
     244             :   // Collect merged (size == 0) segments.
     245             :   size_t f, e;
     246             :   for (f = e = 0; e < mappings.size(); e++)
     247             :     if (mappings[e]->size > 0)
     248             :       mappings[f++] = mappings[e];
     249             :   mappings.resize(f);
     250             : }
     251             : 
     252             : }  // namespace
     253             : #endif  // __CHROMEOS__
     254             : 
     255             : // All interesting auvx entry types are below AT_SYSINFO_EHDR
     256             : #define AT_MAX AT_SYSINFO_EHDR
     257             : 
     258           0 : LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
     259             :     : pid_(pid),
     260             :       root_prefix_(root_prefix),
     261             :       crash_address_(0),
     262             :       crash_signal_(0),
     263             :       crash_thread_(pid),
     264             :       threads_(&allocator_, 8),
     265             :       mappings_(&allocator_),
     266           0 :       auxv_(&allocator_, AT_MAX + 1) {
     267           0 :   assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX);
     268             :   // The passed-in size to the constructor (above) is only a hint.
     269             :   // Must call .resize() to do actual initialization of the elements.
     270           0 :   auxv_.resize(AT_MAX + 1);
     271           0 : }
     272             : 
     273           0 : LinuxDumper::~LinuxDumper() {
     274           0 : }
     275             : 
     276           0 : bool LinuxDumper::Init() {
     277           0 :   return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
     278             : }
     279             : 
     280           0 : bool LinuxDumper::LateInit() {
     281             : #if defined(__ANDROID__)
     282             :   LatePostprocessMappings();
     283             : #endif
     284             : 
     285             : #if defined(__CHROMEOS__)
     286             :   CrOSPostProcessMappings(mappings_);
     287             : #endif
     288             : 
     289           0 :   return true;
     290             : }
     291             : 
     292             : bool
     293           0 : LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
     294             :                                          bool member,
     295             :                                          unsigned int mapping_id,
     296             :                                          wasteful_vector<uint8_t>& identifier) {
     297           0 :   assert(!member || mapping_id < mappings_.size());
     298           0 :   if (IsMappedFileOpenUnsafe(mapping))
     299           0 :     return false;
     300             : 
     301             :   // Special-case linux-gate because it's not a real file.
     302           0 :   if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
     303           0 :     void* linux_gate = NULL;
     304           0 :     if (pid_ == sys_getpid()) {
     305           0 :       linux_gate = reinterpret_cast<void*>(mapping.start_addr);
     306             :     } else {
     307           0 :       linux_gate = allocator_.Alloc(mapping.size);
     308           0 :       CopyFromProcess(linux_gate, pid_,
     309           0 :                       reinterpret_cast<const void*>(mapping.start_addr),
     310           0 :                       mapping.size);
     311             :     }
     312           0 :     return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
     313             :   }
     314             : 
     315             :   char filename[PATH_MAX];
     316           0 :   if (!GetMappingAbsolutePath(mapping, filename))
     317           0 :     return false;
     318           0 :   bool filename_modified = HandleDeletedFileInMapping(filename);
     319             : 
     320           0 :   MemoryMappedFile mapped_file(filename, mapping.offset);
     321           0 :   if (!mapped_file.data() || mapped_file.size() < SELFMAG)
     322           0 :     return false;
     323             : 
     324             :   bool success =
     325           0 :       FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
     326           0 :   if (success && member && filename_modified) {
     327           0 :     mappings_[mapping_id]->name[my_strlen(mapping.name) -
     328           0 :                                 sizeof(kDeletedSuffix) + 1] = '\0';
     329             :   }
     330             : 
     331           0 :   return success;
     332             : }
     333             : 
     334           0 : bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
     335             :                                          char path[PATH_MAX]) const {
     336           0 :   return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
     337           0 :          my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX;
     338             : }
     339             : 
     340             : namespace {
     341           0 : bool ElfFileSoNameFromMappedFile(
     342             :     const void* elf_base, char* soname, size_t soname_size) {
     343           0 :   if (!IsValidElf(elf_base)) {
     344             :     // Not ELF
     345           0 :     return false;
     346             :   }
     347             : 
     348             :   const void* segment_start;
     349             :   size_t segment_size;
     350             :   int elf_class;
     351           0 :   if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
     352             :                       &segment_start, &segment_size, &elf_class)) {
     353             :     // No dynamic section
     354           0 :     return false;
     355             :   }
     356             : 
     357             :   const void* dynstr_start;
     358             :   size_t dynstr_size;
     359           0 :   if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
     360             :                       &dynstr_start, &dynstr_size, &elf_class)) {
     361             :     // No dynstr section
     362           0 :     return false;
     363             :   }
     364             : 
     365           0 :   const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
     366           0 :   size_t dcount = segment_size / sizeof(ElfW(Dyn));
     367           0 :   for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
     368           0 :     if (dyn->d_tag == DT_SONAME) {
     369           0 :       const char* dynstr = static_cast<const char*>(dynstr_start);
     370           0 :       if (dyn->d_un.d_val >= dynstr_size) {
     371             :         // Beyond the end of the dynstr section
     372           0 :         return false;
     373             :       }
     374           0 :       const char* str = dynstr + dyn->d_un.d_val;
     375           0 :       const size_t maxsize = dynstr_size - dyn->d_un.d_val;
     376           0 :       my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
     377           0 :       return true;
     378             :     }
     379             :   }
     380             : 
     381             :   // Did not find SONAME
     382           0 :   return false;
     383             : }
     384             : 
     385             : // Find the shared object name (SONAME) by examining the ELF information
     386             : // for |mapping|. If the SONAME is found copy it into the passed buffer
     387             : // |soname| and return true. The size of the buffer is |soname_size|.
     388             : // The SONAME will be truncated if it is too long to fit in the buffer.
     389           0 : bool ElfFileSoName(const LinuxDumper& dumper,
     390             :     const MappingInfo& mapping, char* soname, size_t soname_size) {
     391           0 :   if (IsMappedFileOpenUnsafe(mapping)) {
     392             :     // Not safe
     393           0 :     return false;
     394             :   }
     395             : 
     396             :   char filename[PATH_MAX];
     397           0 :   if (!dumper.GetMappingAbsolutePath(mapping, filename))
     398           0 :     return false;
     399             : 
     400           0 :   MemoryMappedFile mapped_file(filename, mapping.offset);
     401           0 :   if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
     402             :     // mmap failed
     403           0 :     return false;
     404             :   }
     405             : 
     406           0 :   return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
     407             : }
     408             : 
     409             : }  // namespace
     410             : 
     411             : 
     412           0 : void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
     413             :                                                  char* file_path,
     414             :                                                  size_t file_path_size,
     415             :                                                  char* file_name,
     416             :                                                  size_t file_name_size) {
     417           0 :   my_strlcpy(file_path, mapping.name, file_path_size);
     418             : 
     419             :   // If an executable is mapped from a non-zero offset, this is likely because
     420             :   // the executable was loaded directly from inside an archive file (e.g., an
     421             :   // apk on Android). We try to find the name of the shared object (SONAME) by
     422             :   // looking in the file for ELF sections.
     423           0 :   bool mapped_from_archive = false;
     424           0 :   if (mapping.exec && mapping.offset != 0) {
     425             :     mapped_from_archive =
     426           0 :         ElfFileSoName(*this, mapping, file_name, file_name_size);
     427             :   }
     428             : 
     429           0 :   if (mapped_from_archive) {
     430             :     // Some tools (e.g., stackwalk) extract the basename from the pathname. In
     431             :     // this case, we append the file_name to the mapped archive path as follows:
     432             :     //   file_name := libname.so
     433             :     //   file_path := /path/to/ARCHIVE.APK/libname.so
     434           0 :     if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
     435           0 :       my_strlcat(file_path, "/", file_path_size);
     436           0 :       my_strlcat(file_path, file_name, file_path_size);
     437             :     }
     438             :   } else {
     439             :     // Common case:
     440             :     //   file_path := /path/to/libname.so
     441             :     //   file_name := libname.so
     442           0 :     const char* basename = my_strrchr(file_path, '/');
     443           0 :     basename = basename == NULL ? file_path : (basename + 1);
     444           0 :     my_strlcpy(file_name, basename, file_name_size);
     445             :   }
     446           0 : }
     447             : 
     448           0 : bool LinuxDumper::ReadAuxv() {
     449             :   char auxv_path[NAME_MAX];
     450           0 :   if (!BuildProcPath(auxv_path, pid_, "auxv")) {
     451           0 :     return false;
     452             :   }
     453             : 
     454           0 :   int fd = sys_open(auxv_path, O_RDONLY, 0);
     455           0 :   if (fd < 0) {
     456           0 :     return false;
     457             :   }
     458             : 
     459             :   elf_aux_entry one_aux_entry;
     460           0 :   bool res = false;
     461           0 :   while (sys_read(fd,
     462             :                   &one_aux_entry,
     463           0 :                   sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
     464           0 :          one_aux_entry.a_type != AT_NULL) {
     465           0 :     if (one_aux_entry.a_type <= AT_MAX) {
     466           0 :       auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
     467           0 :       res = true;
     468             :     }
     469             :   }
     470           0 :   sys_close(fd);
     471           0 :   return res;
     472             : }
     473             : 
     474           0 : bool LinuxDumper::EnumerateMappings() {
     475             :   char maps_path[NAME_MAX];
     476           0 :   if (!BuildProcPath(maps_path, pid_, "maps"))
     477           0 :     return false;
     478             : 
     479             :   // linux_gate_loc is the beginning of the kernel's mapping of
     480             :   // linux-gate.so in the process.  It doesn't actually show up in the
     481             :   // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
     482             :   // aux vector entry, which gives the information necessary to special
     483             :   // case its entry when creating the list of mappings.
     484             :   // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
     485             :   // information.
     486             :   const void* linux_gate_loc =
     487           0 :       reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
     488             :   // Although the initial executable is usually the first mapping, it's not
     489             :   // guaranteed (see http://crosbug.com/25355); therefore, try to use the
     490             :   // actual entry point to find the mapping.
     491           0 :   const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
     492             : 
     493           0 :   const int fd = sys_open(maps_path, O_RDONLY, 0);
     494           0 :   if (fd < 0)
     495           0 :     return false;
     496           0 :   LineReader* const line_reader = new(allocator_) LineReader(fd);
     497             : 
     498             :   const char* line;
     499             :   unsigned line_len;
     500           0 :   while (line_reader->GetNextLine(&line, &line_len)) {
     501             :     uintptr_t start_addr, end_addr, offset;
     502             : 
     503           0 :     const char* i1 = my_read_hex_ptr(&start_addr, line);
     504           0 :     if (*i1 == '-') {
     505           0 :       const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
     506           0 :       if (*i2 == ' ') {
     507           0 :         bool exec = (*(i2 + 3) == 'x');
     508           0 :         const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
     509           0 :         if (*i3 == ' ') {
     510           0 :           const char* name = NULL;
     511             :           // Only copy name if the name is a valid path name, or if
     512             :           // it's the VDSO image.
     513           0 :           if (((name = my_strchr(line, '/')) == NULL) &&
     514           0 :               linux_gate_loc &&
     515           0 :               reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
     516           0 :             name = kLinuxGateLibraryName;
     517           0 :             offset = 0;
     518             :           }
     519             :           // Merge adjacent mappings with the same name into one module,
     520             :           // assuming they're a single library mapped by the dynamic linker
     521           0 :           if (name && !mappings_.empty()) {
     522           0 :             MappingInfo* module = mappings_.back();
     523           0 :             if ((start_addr == module->start_addr + module->size) &&
     524           0 :                 (my_strlen(name) == my_strlen(module->name)) &&
     525           0 :                 (my_strncmp(name, module->name, my_strlen(name)) == 0)) {
     526           0 :               module->size = end_addr - module->start_addr;
     527           0 :               line_reader->PopLine(line_len);
     528           0 :               continue;
     529             :             }
     530             :           }
     531             :           // Also merge mappings that result from address ranges that the
     532             :           // linker reserved but which a loaded library did not use. These
     533             :           // appear as an anonymous private mapping with no access flags set
     534             :           // and which directly follow an executable mapping.
     535           0 :           if (!name && !mappings_.empty()) {
     536           0 :             MappingInfo* module = mappings_.back();
     537           0 :             if ((start_addr == module->start_addr + module->size) &&
     538           0 :                 module->exec &&
     539           0 :                 module->name[0] == '/' &&
     540           0 :                 offset == 0 && my_strncmp(i2,
     541             :                                           kReservedFlags,
     542             :                                           sizeof(kReservedFlags) - 1) == 0) {
     543           0 :               module->size = end_addr - module->start_addr;
     544           0 :               line_reader->PopLine(line_len);
     545           0 :               continue;
     546             :             }
     547             :           }
     548           0 :           MappingInfo* const module = new(allocator_) MappingInfo;
     549           0 :           my_memset(module, 0, sizeof(MappingInfo));
     550           0 :           module->start_addr = start_addr;
     551           0 :           module->size = end_addr - start_addr;
     552           0 :           module->offset = offset;
     553           0 :           module->exec = exec;
     554           0 :           if (name != NULL) {
     555           0 :             const unsigned l = my_strlen(name);
     556           0 :             if (l < sizeof(module->name))
     557           0 :               my_memcpy(module->name, name, l);
     558             :           }
     559             :           // If this is the entry-point mapping, and it's not already the
     560             :           // first one, then we need to make it be first.  This is because
     561             :           // the minidump format assumes the first module is the one that
     562             :           // corresponds to the main executable (as codified in
     563             :           // processor/minidump.cc:MinidumpModuleList::GetMainModule()).
     564           0 :           if (entry_point_loc &&
     565             :               (entry_point_loc >=
     566           0 :                   reinterpret_cast<void*>(module->start_addr)) &&
     567             :               (entry_point_loc <
     568           0 :                   reinterpret_cast<void*>(module->start_addr+module->size)) &&
     569           0 :               !mappings_.empty()) {
     570             :             // push the module onto the front of the list.
     571           0 :             mappings_.resize(mappings_.size() + 1);
     572           0 :             for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
     573           0 :               mappings_[idx] = mappings_[idx - 1];
     574           0 :             mappings_[0] = module;
     575             :           } else {
     576           0 :             mappings_.push_back(module);
     577             :           }
     578             :         }
     579             :       }
     580             :     }
     581           0 :     line_reader->PopLine(line_len);
     582             :   }
     583             : 
     584           0 :   sys_close(fd);
     585             : 
     586           0 :   return !mappings_.empty();
     587             : }
     588             : 
     589             : #if defined(__ANDROID__)
     590             : 
     591             : bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
     592             :   CopyFromProcess(ehdr, pid_,
     593             :                   reinterpret_cast<const void*>(start_addr),
     594             :                   sizeof(*ehdr));
     595             :   return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
     596             : }
     597             : 
     598             : void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
     599             :                                                uintptr_t start_addr,
     600             :                                                uintptr_t* min_vaddr_ptr,
     601             :                                                uintptr_t* dyn_vaddr_ptr,
     602             :                                                size_t* dyn_count_ptr) {
     603             :   uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
     604             : 
     605             :   const uintptr_t max_addr = UINTPTR_MAX;
     606             :   uintptr_t min_vaddr = max_addr;
     607             :   uintptr_t dyn_vaddr = 0;
     608             :   size_t dyn_count = 0;
     609             : 
     610             :   for (size_t i = 0; i < ehdr->e_phnum; ++i) {
     611             :     ElfW(Phdr) phdr;
     612             :     CopyFromProcess(&phdr, pid_,
     613             :                     reinterpret_cast<const void*>(phdr_addr),
     614             :                     sizeof(phdr));
     615             :     if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) {
     616             :       min_vaddr = phdr.p_vaddr;
     617             :     }
     618             :     if (phdr.p_type == PT_DYNAMIC) {
     619             :       dyn_vaddr = phdr.p_vaddr;
     620             :       dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn));
     621             :     }
     622             :     phdr_addr += sizeof(phdr);
     623             :   }
     624             : 
     625             :   *min_vaddr_ptr = min_vaddr;
     626             :   *dyn_vaddr_ptr = dyn_vaddr;
     627             :   *dyn_count_ptr = dyn_count;
     628             : }
     629             : 
     630             : bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
     631             :                                               uintptr_t dyn_vaddr,
     632             :                                               size_t dyn_count) {
     633             :   uintptr_t dyn_addr = load_bias + dyn_vaddr;
     634             :   for (size_t i = 0; i < dyn_count; ++i) {
     635             :     ElfW(Dyn) dyn;
     636             :     CopyFromProcess(&dyn, pid_,
     637             :                     reinterpret_cast<const void*>(dyn_addr),
     638             :                     sizeof(dyn));
     639             :     if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) {
     640             :       return true;
     641             :     }
     642             :     dyn_addr += sizeof(dyn);
     643             :   }
     644             :   return false;
     645             : }
     646             : 
     647             : uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr,
     648             :                                             uintptr_t start_addr) {
     649             :   uintptr_t min_vaddr = 0;
     650             :   uintptr_t dyn_vaddr = 0;
     651             :   size_t dyn_count = 0;
     652             :   ParseLoadedElfProgramHeaders(ehdr, start_addr,
     653             :                                &min_vaddr, &dyn_vaddr, &dyn_count);
     654             :   // If |min_vaddr| is non-zero and we find Android packed relocation tags,
     655             :   // return the effective load bias.
     656             :   if (min_vaddr != 0) {
     657             :     const uintptr_t load_bias = start_addr - min_vaddr;
     658             :     if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) {
     659             :       return load_bias;
     660             :     }
     661             :   }
     662             :   // Either |min_vaddr| is zero, or it is non-zero but we did not find the
     663             :   // expected Android packed relocations tags.
     664             :   return start_addr;
     665             : }
     666             : 
     667             : void LinuxDumper::LatePostprocessMappings() {
     668             :   for (size_t i = 0; i < mappings_.size(); ++i) {
     669             :     // Only consider exec mappings that indicate a file path was mapped, and
     670             :     // where the ELF header indicates a mapped shared library.
     671             :     MappingInfo* mapping = mappings_[i];
     672             :     if (!(mapping->exec && mapping->name[0] == '/')) {
     673             :       continue;
     674             :     }
     675             :     ElfW(Ehdr) ehdr;
     676             :     if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
     677             :       continue;
     678             :     }
     679             :     if (ehdr.e_type == ET_DYN) {
     680             :       // Compute the effective load bias for this mapped library, and update
     681             :       // the mapping to hold that rather than |start_addr|, at the same time
     682             :       // adjusting |size| to account for the change in |start_addr|. Where
     683             :       // the library does not contain Android packed relocations,
     684             :       // GetEffectiveLoadBias() returns |start_addr| and the mapping entry
     685             :       // is not changed.
     686             :       const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr,
     687             :                                                        mapping->start_addr);
     688             :       mapping->size += mapping->start_addr - load_bias;
     689             :       mapping->start_addr = load_bias;
     690             :     }
     691             :   }
     692             : }
     693             : 
     694             : #endif  // __ANDROID__
     695             : 
     696             : // Get information about the stack, given the stack pointer. We don't try to
     697             : // walk the stack since we might not have all the information needed to do
     698             : // unwind. So we just grab, up to, 32k of stack.
     699           0 : bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
     700             :                                uintptr_t int_stack_pointer) {
     701             :   // Move the stack pointer to the bottom of the page that it's in.
     702           0 :   const uintptr_t page_size = getpagesize();
     703             : 
     704             :   uint8_t* const stack_pointer =
     705           0 :       reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
     706             : 
     707             :   // The number of bytes of stack which we try to capture.
     708             :   static const ptrdiff_t kStackToCapture = 32 * 1024;
     709             : 
     710           0 :   const MappingInfo* mapping = FindMapping(stack_pointer);
     711           0 :   if (!mapping)
     712           0 :     return false;
     713             :   const ptrdiff_t offset = stack_pointer -
     714           0 :       reinterpret_cast<uint8_t*>(mapping->start_addr);
     715             :   const ptrdiff_t distance_to_end =
     716           0 :       static_cast<ptrdiff_t>(mapping->size) - offset;
     717           0 :   *stack_len = distance_to_end > kStackToCapture ?
     718             :       kStackToCapture : distance_to_end;
     719           0 :   *stack = stack_pointer;
     720           0 :   return true;
     721             : }
     722             : 
     723             : // Find the mapping which the given memory address falls in.
     724           0 : const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
     725           0 :   const uintptr_t addr = (uintptr_t) address;
     726             : 
     727           0 :   for (size_t i = 0; i < mappings_.size(); ++i) {
     728           0 :     const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
     729           0 :     if (addr >= start && addr - start < mappings_[i]->size)
     730           0 :       return mappings_[i];
     731             :   }
     732             : 
     733           0 :   return NULL;
     734             : }
     735             : 
     736           0 : bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
     737             :   static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
     738             : 
     739             :   // Check for ' (deleted)' in |path|.
     740             :   // |path| has to be at least as long as "/x (deleted)".
     741           0 :   const size_t path_len = my_strlen(path);
     742           0 :   if (path_len < kDeletedSuffixLen + 2)
     743           0 :     return false;
     744           0 :   if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
     745             :                  kDeletedSuffixLen) != 0) {
     746           0 :     return false;
     747             :   }
     748             : 
     749             :   // Check |path| against the /proc/pid/exe 'symlink'.
     750             :   char exe_link[NAME_MAX];
     751           0 :   if (!BuildProcPath(exe_link, pid_, "exe"))
     752           0 :     return false;
     753           0 :   MappingInfo new_mapping = {0};
     754           0 :   if (!SafeReadLink(exe_link, new_mapping.name))
     755           0 :     return false;
     756             :   char new_path[PATH_MAX];
     757           0 :   if (!GetMappingAbsolutePath(new_mapping, new_path))
     758           0 :     return false;
     759           0 :   if (my_strcmp(path, new_path) != 0)
     760           0 :     return false;
     761             : 
     762             :   // Check to see if someone actually named their executable 'foo (deleted)'.
     763             :   struct kernel_stat exe_stat;
     764             :   struct kernel_stat new_path_stat;
     765           0 :   if (sys_stat(exe_link, &exe_stat) == 0 &&
     766           0 :       sys_stat(new_path, &new_path_stat) == 0 &&
     767           0 :       exe_stat.st_dev == new_path_stat.st_dev &&
     768           0 :       exe_stat.st_ino == new_path_stat.st_ino) {
     769           0 :     return false;
     770             :   }
     771             : 
     772           0 :   my_memcpy(path, exe_link, NAME_MAX);
     773           0 :   return true;
     774             : }
     775             : 
     776             : }  // namespace google_breakpad

Generated by: LCOV version 1.13