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 return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && |
| 166 my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX; |
| 167 } |
| 168 |
166 namespace { | 169 namespace { |
167 bool ElfFileSoNameFromMappedFile( | 170 bool ElfFileSoNameFromMappedFile( |
168 const void* elf_base, char* soname, size_t soname_size) { | 171 const void* elf_base, char* soname, size_t soname_size) { |
169 if (!IsValidElf(elf_base)) { | 172 if (!IsValidElf(elf_base)) { |
170 // Not ELF | 173 // Not ELF |
171 return false; | 174 return false; |
172 } | 175 } |
173 | 176 |
174 const void* segment_start; | 177 const void* segment_start; |
175 size_t segment_size; | 178 size_t segment_size; |
(...skipping 29 matching lines...) Expand all Loading... |
205 } | 208 } |
206 | 209 |
207 // Did not find SONAME | 210 // Did not find SONAME |
208 return false; | 211 return false; |
209 } | 212 } |
210 | 213 |
211 // Find the shared object name (SONAME) by examining the ELF information | 214 // 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 | 215 // 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|. | 216 // |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. | 217 // The SONAME will be truncated if it is too long to fit in the buffer. |
215 bool ElfFileSoName( | 218 bool ElfFileSoName(const LinuxDumper& dumper, |
216 const MappingInfo& mapping, char* soname, size_t soname_size) { | 219 const MappingInfo& mapping, char* soname, size_t soname_size) { |
217 if (IsMappedFileOpenUnsafe(mapping)) { | 220 if (IsMappedFileOpenUnsafe(mapping)) { |
218 // Not safe | 221 // Not safe |
219 return false; | 222 return false; |
220 } | 223 } |
221 | 224 |
222 char filename[NAME_MAX]; | 225 char filename[PATH_MAX]; |
223 size_t filename_len = my_strlen(mapping.name); | 226 if (!dumper.GetMappingAbsolutePath(mapping, filename)) |
224 if (filename_len >= NAME_MAX) { | |
225 assert(false); | |
226 // name too long | |
227 return false; | 227 return false; |
228 } | |
229 | |
230 my_memcpy(filename, mapping.name, filename_len); | |
231 filename[filename_len] = '\0'; | |
232 | 228 |
233 MemoryMappedFile mapped_file(filename, mapping.offset); | 229 MemoryMappedFile mapped_file(filename, mapping.offset); |
234 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { | 230 if (!mapped_file.data() || mapped_file.size() < SELFMAG) { |
235 // mmap failed | 231 // mmap failed |
236 return false; | 232 return false; |
237 } | 233 } |
238 | 234 |
239 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); | 235 return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); |
240 } | 236 } |
241 | 237 |
242 } // namespace | 238 } // namespace |
243 | 239 |
244 | 240 |
245 // static | |
246 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, | 241 void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, |
247 char* file_path, | 242 char* file_path, |
248 size_t file_path_size, | 243 size_t file_path_size, |
249 char* file_name, | 244 char* file_name, |
250 size_t file_name_size) { | 245 size_t file_name_size) { |
251 my_strlcpy(file_path, mapping.name, file_path_size); | 246 my_strlcpy(file_path, mapping.name, file_path_size); |
252 | 247 |
253 // If an executable is mapped from a non-zero offset, this is likely because | 248 // 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 | 249 // 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 | 250 // apk on Android). We try to find the name of the shared object (SONAME) by |
256 // looking in the file for ELF sections. | 251 // looking in the file for ELF sections. |
257 bool mapped_from_archive = false; | 252 bool mapped_from_archive = false; |
258 if (mapping.exec && mapping.offset != 0) | 253 if (mapping.exec && mapping.offset != 0) { |
259 mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size); | 254 mapped_from_archive = |
| 255 ElfFileSoName(*this, mapping, file_name, file_name_size); |
| 256 } |
260 | 257 |
261 if (mapped_from_archive) { | 258 if (mapped_from_archive) { |
262 // Some tools (e.g., stackwalk) extract the basename from the pathname. In | 259 // 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: | 260 // this case, we append the file_name to the mapped archive path as follows: |
264 // file_name := libname.so | 261 // file_name := libname.so |
265 // file_path := /path/to/ARCHIVE.APK/libname.so | 262 // file_path := /path/to/ARCHIVE.APK/libname.so |
266 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { | 263 if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { |
267 my_strlcat(file_path, "/", file_path_size); | 264 my_strlcat(file_path, "/", file_path_size); |
268 my_strlcat(file_path, file_name, file_path_size); | 265 my_strlcat(file_path, file_name, file_path_size); |
269 } | 266 } |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 const size_t path_len = my_strlen(path); | 570 const size_t path_len = my_strlen(path); |
574 if (path_len < kDeletedSuffixLen + 2) | 571 if (path_len < kDeletedSuffixLen + 2) |
575 return false; | 572 return false; |
576 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, | 573 if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, |
577 kDeletedSuffixLen) != 0) { | 574 kDeletedSuffixLen) != 0) { |
578 return false; | 575 return false; |
579 } | 576 } |
580 | 577 |
581 // Check |path| against the /proc/pid/exe 'symlink'. | 578 // Check |path| against the /proc/pid/exe 'symlink'. |
582 char exe_link[NAME_MAX]; | 579 char exe_link[NAME_MAX]; |
583 char new_path[NAME_MAX]; | |
584 if (!BuildProcPath(exe_link, pid_, "exe")) | 580 if (!BuildProcPath(exe_link, pid_, "exe")) |
585 return false; | 581 return false; |
586 if (!SafeReadLink(exe_link, new_path)) | 582 MappingInfo new_mapping = {0}; |
| 583 if (!SafeReadLink(exe_link, new_mapping.name)) |
| 584 return false; |
| 585 char new_path[PATH_MAX]; |
| 586 if (!GetMappingAbsolutePath(new_mapping, new_path)) |
587 return false; | 587 return false; |
588 if (my_strcmp(path, new_path) != 0) | 588 if (my_strcmp(path, new_path) != 0) |
589 return false; | 589 return false; |
590 | 590 |
591 // Check to see if someone actually named their executable 'foo (deleted)'. | 591 // Check to see if someone actually named their executable 'foo (deleted)'. |
592 struct kernel_stat exe_stat; | 592 struct kernel_stat exe_stat; |
593 struct kernel_stat new_path_stat; | 593 struct kernel_stat new_path_stat; |
594 if (sys_stat(exe_link, &exe_stat) == 0 && | 594 if (sys_stat(exe_link, &exe_stat) == 0 && |
595 sys_stat(new_path, &new_path_stat) == 0 && | 595 sys_stat(new_path, &new_path_stat) == 0 && |
596 exe_stat.st_dev == new_path_stat.st_dev && | 596 exe_stat.st_dev == new_path_stat.st_dev && |
597 exe_stat.st_ino == new_path_stat.st_ino) { | 597 exe_stat.st_ino == new_path_stat.st_ino) { |
598 return false; | 598 return false; |
599 } | 599 } |
600 | 600 |
601 my_memcpy(path, exe_link, NAME_MAX); | 601 my_memcpy(path, exe_link, NAME_MAX); |
602 return true; | 602 return true; |
603 } | 603 } |
604 | 604 |
605 } // namespace google_breakpad | 605 } // namespace google_breakpad |
OLD | NEW |