Index: client/linux/minidump_writer/linux_dumper.cc |
diff --git a/client/linux/minidump_writer/linux_dumper.cc b/client/linux/minidump_writer/linux_dumper.cc |
index ebb008d6692e6ffa5d7a788db811c4076cf35d11..da03380c697a73ed3cef32bb591717e014d0cb6f 100644 |
--- a/client/linux/minidump_writer/linux_dumper.cc |
+++ b/client/linux/minidump_writer/linux_dumper.cc |
@@ -52,6 +52,22 @@ |
#include "common/linux/safe_readlink.h" |
#include "third_party/lss/linux_syscall_support.h" |
+#if defined(__ANDROID__) |
+ |
+// Android packed relocations definitions are not yet available from the |
+// NDK header files, so we have to provide them manually here. |
+#ifndef DT_LOOS |
+#define DT_LOOS 0x6000000d |
+#endif |
+#ifndef DT_ANDROID_REL |
+static const int DT_ANDROID_REL = DT_LOOS + 2; |
+#endif |
+#ifndef DT_ANDROID_RELA |
+static const int DT_ANDROID_RELA = DT_LOOS + 4; |
+#endif |
+ |
+#endif // __ANDROID __ |
+ |
static const char kMappedFileUnsafePrefix[] = "/dev/"; |
static const char kDeletedSuffix[] = " (deleted)"; |
static const char kReservedFlags[] = " ---p"; |
@@ -92,6 +108,14 @@ bool LinuxDumper::Init() { |
return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); |
} |
+bool LinuxDumper::LateInit() { |
+#if defined(__ANDROID__) |
+ return LatePostprocessMappings(); |
+#else |
+ return true; |
+#endif |
+} |
+ |
bool |
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, |
bool member, |
@@ -395,6 +419,128 @@ bool LinuxDumper::EnumerateMappings() { |
return !mappings_.empty(); |
} |
+#if defined(__ANDROID__) |
+ |
+// Read the memory at start_addr, expecting to find an ELF header. The first |
+// LOAD segment in an ELF shared library has offset zero, so the ELF file |
+// header is at the start of this map entry, and in already mapped memory. |
+bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) { |
+ CopyFromProcess(ehdr, pid_, |
+ reinterpret_cast<const void*>(start_addr), |
+ sizeof(*ehdr)); |
+ if (my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) != 0) { |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+// Iterate ELF program headers to find the min vaddr of LOAD segments, and |
+// the vaddr and count of entries for the DYNAMIC table. The program header |
+// table is also in already mapped memory. |
+void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, |
+ uintptr_t start_addr, |
+ uintptr_t* min_vaddr_ptr, |
+ uintptr_t* dyn_vaddr_ptr, |
+ size_t* dyn_count_ptr) { |
+ uint8_t* phdr_addr = reinterpret_cast<uint8_t*>(start_addr) + ehdr->e_phoff; |
+ |
+ const uintptr_t max_addr = ~static_cast<uintptr_t>(0); |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
Isn't this just UINTPTR_MAX from stdint.h? Or is t
simonb (inactive)
2015/06/18 16:24:38
Done.
|
+ uintptr_t min_vaddr = max_addr; |
+ uintptr_t dyn_vaddr = 0; |
+ size_t dyn_count = 0; |
+ |
+ for (size_t i = 0; i < ehdr->e_phnum; ++i) { |
+ ElfW(Phdr) phdr; |
+ CopyFromProcess(&phdr, pid_, |
+ reinterpret_cast<const void*>(phdr_addr), |
+ sizeof(phdr)); |
+ if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) { |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
Just doublechecking. I thought you meant to look f
simonb (inactive)
2015/06/18 16:24:38
I do want the min(vaddr). Typically this will be
|
+ min_vaddr = phdr.p_vaddr; |
+ } |
+ if (phdr.p_type == PT_DYNAMIC) { |
+ dyn_vaddr = phdr.p_vaddr; |
+ dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn)); |
+ } |
+ phdr_addr += sizeof(phdr); |
+ } |
+ assert(min_vaddr != max_addr); |
rmcilroy
2015/06/18 09:42:10
Do we want to be a bit more permissive to failures
simonb (inactive)
2015/06/18 10:46:39
I don't think so. We strongly assume that a libra
rmcilroy
2015/06/18 17:13:36
Sure, but we wouldn't see a crash since this would
simonb (inactive)
2015/06/18 17:33:40
Okay, I've removed the assert. If we fail (inexpl
|
+ |
+ *min_vaddr_ptr = min_vaddr; |
+ *dyn_vaddr_ptr = dyn_vaddr; |
+ *dyn_count_ptr = dyn_count; |
+} |
+ |
+// Retrieve and check dynamic tags, checking for the telltale that marks |
rmcilroy
2015/06/18 09:42:10
nit - "Retrieve and check dynamic task, returning
simonb (inactive)
2015/06/18 10:46:39
Done.
|
+// Android packed relocations as present. Dynamic tags are found at dyn_vaddr |
+// past the load_bias. |
+bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias, |
+ uintptr_t dyn_vaddr, |
+ size_t dyn_count) { |
+ uint8_t* dyn_addr = reinterpret_cast<uint8_t*>(load_bias) + dyn_vaddr; |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
I think (dyn_addr) this can stay an uintptr_t with
simonb (inactive)
2015/06/18 16:24:38
Done. Also phdr_addr at 445 above.
|
+ for (size_t i = 0; i < dyn_count; ++i) { |
+ ElfW(Dyn) dyn; |
+ CopyFromProcess(&dyn, pid_, |
+ reinterpret_cast<const void*>(dyn_addr), |
+ sizeof(dyn)); |
+ if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) { |
+ return true; |
+ } |
+ dyn_addr += sizeof(dyn); |
+ } |
+ return false; |
+} |
+ |
+// Return the effective load_bias, used by the system linker (or Chromium |
+// crazy linker) for the ELF shared library mapped at the given start_addr. |
+// The effective load_bias is start_addr adjusted downwards by the min vaddr |
+// in the library LOAD segments. |
+uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, |
+ uintptr_t start_addr) { |
+ uintptr_t min_vaddr = 0; |
+ uintptr_t dyn_vaddr = 0; |
+ size_t dyn_count = 0; |
+ ParseLoadedElfProgramHeaders(ehdr, start_addr, |
+ &min_vaddr, &dyn_vaddr, &dyn_count); |
+ // If min vaddr is non-zero and we find Android packed relocation tags, |
+ // return the effective load_bias. |
+ if (min_vaddr != 0) { |
+ const uintptr_t load_bias = start_addr - min_vaddr; |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
should you also check here that min_vadr < start_a
simonb (inactive)
2015/06/18 16:24:38
Not sure this would offer much protection. If the
|
+ if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) { |
+ return load_bias; |
+ } |
+ } |
+ // Either min vaddr is zero, or it is non-zero but we did not find the |
+ // expected Android packed relocations tags. |
+ return start_addr; |
+} |
+ |
+// Iterate mappings_, and adjust any start_addr fields that are for mapped |
+// libraries that contain Android packed relocations. |
+bool LinuxDumper::LatePostprocessMappings() { |
+ for (size_t i = 0; i < mappings_.size(); ++i) { |
+ // Only consider exec mappings that indicate a file path was mapped, and |
+ // where the ELF header indicates a mapped shared library. |
+ MappingInfo* mapping = mappings_[i]; |
+ if (!(mapping->exec && mapping->name && mapping->name[0] == '/')) { |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
I think you don't really need the middle check (ma
simonb (inactive)
2015/06/18 16:24:38
Done (and thanks, I know from annoying prior exper
|
+ continue; |
+ } |
+ ElfW(Ehdr) ehdr; |
+ if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) { |
+ return false; |
Primiano Tucci (use gerrit)
2015/06/18 10:59:38
Hmm should you really return here or just continue
simonb (inactive)
2015/06/18 16:24:38
Done.
|
+ } |
+ if (ehdr.e_type == ET_DYN) { |
+ // Compute the effective load_bias for this mapped library, and update |
+ // the mapping to hold that rather than start_addr. Where the library |
+ // does not contain Android packed relocations, GetEffectiveLoadBias() |
+ // returns start_addr and the mapping entry is not changed. |
+ mapping->start_addr = GetEffectiveLoadBias(&ehdr, mapping->start_addr); |
+ } |
+ } |
+ return true; |
+} |
+ |
+#endif // __ANDROID__ |
+ |
// Get information about the stack, given the stack pointer. We don't try to |
// walk the stack since we might not have all the information needed to do |
// unwind. So we just grab, up to, 32k of stack. |