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 |