Chromium Code Reviews| 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. |