aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/minidump_writer/linux_dumper.cc
diff options
context:
space:
mode:
authorrmcilroy@chromium.org <rmcilroy@chromium.org>2015-06-19 16:30:42 +0000
committerrmcilroy@chromium.org <rmcilroy@chromium.org>2015-06-19 16:30:42 +0000
commit8785c0cb8fc1ccfb20c143026c5fbb8361354c09 (patch)
treec928423f412adfc0c186dbeb9ce9a742102f305d /src/client/linux/minidump_writer/linux_dumper.cc
parentupdate more ignore files (diff)
downloadbreakpad-8785c0cb8fc1ccfb20c143026c5fbb8361354c09.tar.xz
Update breakpad for Android packed relocations.
Shared libraries containing Android packed relocations have a load bias that differs from the start address in /proc/$$/maps. Current breakpad assumes that the load bias and mapping start address are the same. Fixed by changing the client to detect the presence of Android packed relocations in the address space of a loaded library, and adjusting the stored mapping start address of any that are packed so that it contains the linker's load bias. For this to work properly, it is important that the non-packed library is symbolized for breakpad. Either packed or non-packed libraries may be run on the device; the client detects which has been loaded by the linker. BUG=499747 R=primiano@chromium.org, rmcilroy@chromium.org Review URL: https://codereview.chromium.org/1189823002. Patch from Simon Baldwin <simonb@chromium.org>. git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1459 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/linux/minidump_writer/linux_dumper.cc')
-rw-r--r--src/client/linux/minidump_writer/linux_dumper.cc125
1 files changed, 125 insertions, 0 deletions
diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc
index ebb008d6..00d18189 100644
--- a/src/client/linux/minidump_writer/linux_dumper.cc
+++ b/src/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,13 @@ bool LinuxDumper::Init() {
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
}
+bool LinuxDumper::LateInit() {
+#if defined(__ANDROID__)
+ LatePostprocessMappings();
+#endif
+ return true;
+}
+
bool
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
@@ -395,6 +418,108 @@ bool LinuxDumper::EnumerateMappings() {
return !mappings_.empty();
}
+#if defined(__ANDROID__)
+
+bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
+ CopyFromProcess(ehdr, pid_,
+ reinterpret_cast<const void*>(start_addr),
+ sizeof(*ehdr));
+ return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+}
+
+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) {
+ uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
+
+ const uintptr_t max_addr = UINTPTR_MAX;
+ 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) {
+ 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);
+ }
+
+ *min_vaddr_ptr = min_vaddr;
+ *dyn_vaddr_ptr = dyn_vaddr;
+ *dyn_count_ptr = dyn_count;
+}
+
+bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
+ uintptr_t dyn_vaddr,
+ size_t dyn_count) {
+ uintptr_t dyn_addr = load_bias + dyn_vaddr;
+ 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;
+}
+
+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;
+ 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;
+}
+
+void 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[0] == '/')) {
+ continue;
+ }
+ ElfW(Ehdr) ehdr;
+ if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
+ continue;
+ }
+ 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);
+ }
+ }
+}
+
+#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.