Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010, Google Inc. | 1 // Copyright (c) 2010, Google Inc. |
| 2 // All rights reserved. | 2 // All rights reserved. |
| 3 // | 3 // |
| 4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
| 6 // met: | 6 // met: |
| 7 // | 7 // |
| 8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 81 return my_strncmp(mapping.name, | 81 return my_strncmp(mapping.name, |
| 82 kMappedFileUnsafePrefix, | 82 kMappedFileUnsafePrefix, |
| 83 sizeof(kMappedFileUnsafePrefix) - 1) == 0; | 83 sizeof(kMappedFileUnsafePrefix) - 1) == 0; |
| 84 } | 84 } |
| 85 | 85 |
| 86 namespace google_breakpad { | 86 namespace google_breakpad { |
| 87 | 87 |
| 88 // All interesting auvx entry types are below AT_SYSINFO_EHDR | 88 // All interesting auvx entry types are below AT_SYSINFO_EHDR |
| 89 #define AT_MAX AT_SYSINFO_EHDR | 89 #define AT_MAX AT_SYSINFO_EHDR |
| 90 | 90 |
| 91 LinuxDumper::LinuxDumper(pid_t pid) | 91 LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) |
| 92 : pid_(pid), | 92 : pid_(pid), |
| 93 root_prefix_(root_prefix), | |
| 93 crash_address_(0), | 94 crash_address_(0), |
| 94 crash_signal_(0), | 95 crash_signal_(0), |
| 95 crash_thread_(pid), | 96 crash_thread_(pid), |
| 96 threads_(&allocator_, 8), | 97 threads_(&allocator_, 8), |
| 97 mappings_(&allocator_), | 98 mappings_(&allocator_), |
| 98 auxv_(&allocator_, AT_MAX + 1) { | 99 auxv_(&allocator_, AT_MAX + 1) { |
| 100 assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX); | |
| 99 // The passed-in size to the constructor (above) is only a hint. | 101 // The passed-in size to the constructor (above) is only a hint. |
| 100 // Must call .resize() to do actual initialization of the elements. | 102 // Must call .resize() to do actual initialization of the elements. |
| 101 auxv_.resize(AT_MAX + 1); | 103 auxv_.resize(AT_MAX + 1); |
| 102 } | 104 } |
| 103 | 105 |
| 104 LinuxDumper::~LinuxDumper() { | 106 LinuxDumper::~LinuxDumper() { |
| 105 } | 107 } |
| 106 | 108 |
| 107 bool LinuxDumper::Init() { | 109 bool LinuxDumper::Init() { |
| 108 return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); | 110 return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 132 linux_gate = reinterpret_cast<void*>(mapping.start_addr); | 134 linux_gate = reinterpret_cast<void*>(mapping.start_addr); |
| 133 } else { | 135 } else { |
| 134 linux_gate = allocator_.Alloc(mapping.size); | 136 linux_gate = allocator_.Alloc(mapping.size); |
| 135 CopyFromProcess(linux_gate, pid_, | 137 CopyFromProcess(linux_gate, pid_, |
| 136 reinterpret_cast<const void*>(mapping.start_addr), | 138 reinterpret_cast<const void*>(mapping.start_addr), |
| 137 mapping.size); | 139 mapping.size); |
| 138 } | 140 } |
| 139 return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); | 141 return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); |
| 140 } | 142 } |
| 141 | 143 |
| 142 char filename[NAME_MAX]; | 144 char filename[PATH_MAX]; |
| 143 size_t filename_len = my_strlen(mapping.name); | 145 if (!GetMappingAbsolutePath(mapping, filename)) |
| 144 if (filename_len >= NAME_MAX) { | |
| 145 assert(false); | |
| 146 return false; | 146 return false; |
| 147 } | |
| 148 my_memcpy(filename, mapping.name, filename_len); | |
| 149 filename[filename_len] = '\0'; | |
| 150 bool filename_modified = HandleDeletedFileInMapping(filename); | 147 bool filename_modified = HandleDeletedFileInMapping(filename); |
| 151 | 148 |
| 152 MemoryMappedFile mapped_file(filename, mapping.offset); | 149 MemoryMappedFile mapped_file(filename, mapping.offset); |
| 153 if (!mapped_file.data() || mapped_file.size() < SELFMAG) | 150 if (!mapped_file.data() || mapped_file.size() < SELFMAG) |
| 154 return false; | 151 return false; |
| 155 | 152 |
| 156 bool success = | 153 bool success = |
| 157 FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); | 154 FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); |
| 158 if (success && member && filename_modified) { | 155 if (success && member && filename_modified) { |
| 159 mappings_[mapping_id]->name[filename_len - | 156 mappings_[mapping_id]->name[my_strlen(mapping.name) - |
| 160 sizeof(kDeletedSuffix) + 1] = '\0'; | 157 sizeof(kDeletedSuffix) + 1] = '\0'; |
| 161 } | 158 } |
| 162 | 159 |
| 163 return success; | 160 return success; |
| 164 } | 161 } |
| 165 | 162 |
| 163 bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, | |
| 164 char path[PATH_MAX]) const { | |
| 165 assert(my_strlen(mapping.name) < NAME_MAX); | |
|
vapier
2016/03/07 16:32:37
why bother ? why does this matter at all ?
Dominik Laskowski
2016/03/07 18:36:21
It was just to preserve the assert, but I agree it
| |
| 166 return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && | |
| 167 my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX && | |
| 168 (assert(my_strlen(path) < PATH_MAX), true); | |
|
vapier
2016/03/07 16:32:36
i don't see the point in this assert. we're alrea
Dominik Laskowski
2016/03/07 18:36:21
Removed. No real point other than paranoia about t
| |
| 169 } | |
| 170 | |
| 166 namespace { | 171 namespace { |
| 167 bool ElfFileSoNameFromMappedFile( | 172 bool ElfFileSoNameFromMappedFile( |
| 168 const void* elf_base, char* soname, size_t soname_size) { | 173 const void* elf_base, char* soname, size_t soname_size) { |
| 169 if (!IsValidElf(elf_base)) { | 174 if (!IsValidElf(elf_base)) { |
| 170 // Not ELF | 175 // Not ELF |
| 171 return false; | 176 return false; |
| 172 } | 177 } |
| 173 | 178 |
| 174 const void* segment_start; | 179 const void* segment_start; |
| 175 size_t segment_size; | 180 size_t segment_size; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 205 } | 210 } |
| 206 | 211 |
| 207 // Did not find SONAME | 212 // Did not find SONAME |
| 208 return false; | 213 return false; |
| 209 } | 214 } |
| 210 | 215 |
| 211 // Find the shared object name (SONAME) by examining the ELF information | 216 // Find the shared object name (SONAME) by examining the ELF information |
| 212 // for |mapping|. If the SONAME is found copy it into the passed buffer | 217 // for |mapping|. If the SONAME is found copy it into the passed buffer |
| 213 // |soname| and return true. The size of the buffer is |soname_size|. | 218 // |soname| and return true. The size of the buffer is |soname_size|. |
| 214 // The SONAME will be truncated if it is too long to fit in the buffer. | 219 // The SONAME will be truncated if it is too long to fit in the buffer. |
| 215 bool ElfFileSoName( | 220 bool ElfFileSoName(const LinuxDumper& dumper, |
| 216 const MappingInfo& mapping, char* soname, size_t soname_size) { | 221 const MappingInfo& mapping, char* soname, size_t soname_size) { |
| 217 if (IsMappedFileOpenUnsafe(mapping)) { | 222 if (IsMappedFileOpenUnsafe(mapping)) { |
| 218 // Not safe | 223 // Not safe |
| 219 return false; | 224 return false; |
| 220 } | 225 } |
| 221 | 226 |
| 222 char filename[NAME_MAX]; | 227 char filename[PATH_MAX]; |
| 223 size_t filename_len = my_strlen(mapping.name); | 228 if (!dumper.GetMappingAbsolutePath(mapping, filename)) |
| 224 if (filename_len >= NAME_MAX) { | |
| 225 assert(false); | |
| 226 // name too long | |
| 227 return false; | 229 return false; |
| 228 } | |
| 229 | |
| 230 my_memcpy(filename, mapping.name, filename_len); | |
| 231 filename[filename_len] = '\0'; | |
| 232 | 230 |
| 233 MemoryMappedFile mapped_file(filename, mapping.offset); | 231 MemoryMappedFile mapped_file(filename, mapping.offset); |
| 234 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { | 232 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { |
| 235 // mmap failed | 233 // mmap failed |
| 236 return false; | 234 return false; |
| 237 } | 235 } |
| 238 | 236 |
| 239 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); | 237 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); |
| 240 } | 238 } |
| 241 | 239 |
| 242 } // namespace | 240 } // namespace |
| 243 | 241 |
| 244 | 242 |
| 245 // static | |
| 246 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, | 243 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, |
| 247 char* file_path, | 244 char* file_path, |
| 248 size_t file_path_size, | 245 size_t file_path_size, |
| 249 char* file_name, | 246 char* file_name, |
| 250 size_t file_name_size) { | 247 size_t file_name_size) { |
| 251 my_strlcpy(file_path, mapping.name, file_path_size); | 248 my_strlcpy(file_path, mapping.name, file_path_size); |
| 252 | 249 |
| 253 // If an executable is mapped from a non-zero offset, this is likely because | 250 // If an executable is mapped from a non-zero offset, this is likely because |
| 254 // the executable was loaded directly from inside an archive file (e.g., an | 251 // the executable was loaded directly from inside an archive file (e.g., an |
| 255 // apk on Android). We try to find the name of the shared object (SONAME) by | 252 // apk on Android). We try to find the name of the shared object (SONAME) by |
| 256 // looking in the file for ELF sections. | 253 // looking in the file for ELF sections. |
| 257 bool mapped_from_archive = false; | 254 bool mapped_from_archive = false; |
| 258 if (mapping.exec && mapping.offset != 0) | 255 if (mapping.exec && mapping.offset != 0) { |
|
vapier
2016/03/07 16:32:37
these braces aren't needed
Dominik Laskowski
2016/03/07 18:36:21
True, but this file uses braces in most cases. See
| |
| 259 mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size); | 256 mapped_from_archive = |
| 257 ElfFileSoName(*this, mapping, file_name, file_name_size); | |
| 258 } | |
| 260 | 259 |
| 261 if (mapped_from_archive) { | 260 if (mapped_from_archive) { |
| 262 // Some tools (e.g., stackwalk) extract the basename from the pathname. In | 261 // Some tools (e.g., stackwalk) extract the basename from the pathname. In |
| 263 // this case, we append the file_name to the mapped archive path as follows: | 262 // this case, we append the file_name to the mapped archive path as follows: |
| 264 // file_name := libname.so | 263 // file_name := libname.so |
| 265 // file_path := /path/to/ARCHIVE.APK/libname.so | 264 // file_path := /path/to/ARCHIVE.APK/libname.so |
| 266 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { | 265 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { |
| 267 my_strlcat(file_path, "/", file_path_size); | 266 my_strlcat(file_path, "/", file_path_size); |
| 268 my_strlcat(file_path, file_name, file_path_size); | 267 my_strlcat(file_path, file_name, file_path_size); |
| 269 } | 268 } |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 const size_t path_len = my_strlen(path); | 572 const size_t path_len = my_strlen(path); |
| 574 if (path_len < kDeletedSuffixLen + 2) | 573 if (path_len < kDeletedSuffixLen + 2) |
| 575 return false; | 574 return false; |
| 576 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, | 575 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, |
| 577 kDeletedSuffixLen) != 0) { | 576 kDeletedSuffixLen) != 0) { |
| 578 return false; | 577 return false; |
| 579 } | 578 } |
| 580 | 579 |
| 581 // Check |path| against the /proc/pid/exe 'symlink'. | 580 // Check |path| against the /proc/pid/exe 'symlink'. |
| 582 char exe_link[NAME_MAX]; | 581 char exe_link[NAME_MAX]; |
| 583 char new_path[NAME_MAX]; | |
| 584 if (!BuildProcPath(exe_link, pid_, "exe")) | 582 if (!BuildProcPath(exe_link, pid_, "exe")) |
| 585 return false; | 583 return false; |
| 586 if (!SafeReadLink(exe_link, new_path)) | 584 MappingInfo new_mapping = {0}; |
| 585 if (!SafeReadLink(exe_link, new_mapping.name)) | |
| 586 return false; | |
| 587 char new_path[PATH_MAX]; | |
| 588 if (!GetMappingAbsolutePath(new_mapping, new_path)) | |
| 587 return false; | 589 return false; |
| 588 if (my_strcmp(path, new_path) != 0) | 590 if (my_strcmp(path, new_path) != 0) |
| 589 return false; | 591 return false; |
| 590 | 592 |
| 591 // Check to see if someone actually named their executable 'foo (deleted)'. | 593 // Check to see if someone actually named their executable 'foo (deleted)'. |
| 592 struct kernel_stat exe_stat; | 594 struct kernel_stat exe_stat; |
| 593 struct kernel_stat new_path_stat; | 595 struct kernel_stat new_path_stat; |
| 594 if (sys_stat(exe_link, &exe_stat) == 0 && | 596 if (sys_stat(exe_link, &exe_stat) == 0 && |
| 595 sys_stat(new_path, &new_path_stat) == 0 && | 597 sys_stat(new_path, &new_path_stat) == 0 && |
| 596 exe_stat.st_dev == new_path_stat.st_dev && | 598 exe_stat.st_dev == new_path_stat.st_dev && |
| 597 exe_stat.st_ino == new_path_stat.st_ino) { | 599 exe_stat.st_ino == new_path_stat.st_ino) { |
| 598 return false; | 600 return false; |
| 599 } | 601 } |
| 600 | 602 |
| 601 my_memcpy(path, exe_link, NAME_MAX); | 603 my_memcpy(path, exe_link, NAME_MAX); |
| 602 return true; | 604 return true; |
| 603 } | 605 } |
| 604 | 606 |
| 605 } // namespace google_breakpad | 607 } // namespace google_breakpad |
| OLD | NEW |