From 8785c0cb8fc1ccfb20c143026c5fbb8361354c09 Mon Sep 17 00:00:00 2001 From: "rmcilroy@chromium.org" Date: Fri, 19 Jun 2015 16:30:42 +0000 Subject: [PATCH] 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 . git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1459 4c0a9323-5329-0410-9bdc-e9ce6186880e --- .../microdump_writer/microdump_writer.cc | 2 +- .../linux/minidump_writer/linux_dumper.cc | 125 ++++++++++++++++++ .../linux/minidump_writer/linux_dumper.h | 65 +++++++++ .../linux/minidump_writer/minidump_writer.cc | 2 +- src/common/linux/dump_symbols.cc | 31 +++++ 5 files changed, 223 insertions(+), 2 deletions(-) diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc index f45925fe..247387e2 100644 --- a/src/client/linux/microdump_writer/microdump_writer.cc +++ b/src/client/linux/microdump_writer/microdump_writer.cc @@ -86,7 +86,7 @@ class MicrodumpWriter { // try to not crash. if (!dumper_->Init() || !log_line_) return false; - return dumper_->ThreadsSuspend(); + return dumper_->ThreadsSuspend() && dumper_->LateInit(); } bool Dump() { 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(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(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(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. diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 87dfadb4..6a3a100f 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -39,6 +39,9 @@ #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ #include +#if defined(__ANDROID__) +#include +#endif #include #include #include @@ -76,6 +79,12 @@ class LinuxDumper { // Parse the data for |threads| and |mappings|. virtual bool Init(); + // Take any actions that could not be taken in Init(). LateInit() is + // called after all other caller's initialization is complete, and in + // particular after it has called ThreadsSuspend(), so that ptrace is + // available. + virtual bool LateInit(); + // Return true if the dumper performs a post-mortem dump. virtual bool IsPostMortem() const = 0; @@ -182,6 +191,62 @@ class LinuxDumper { // Info from /proc//auxv wasteful_vector auxv_; + +#if defined(__ANDROID__) + private: + // Android M and later support packed ELF relocations in shared libraries. + // Packing relocations changes the vaddr of the LOAD segments, such that + // the effective load bias is no longer the same as the start address of + // the memory mapping containing the executable parts of the library. The + // packing is applied to the stripped library run on the target, but not to + // any other library, and in particular not to the library used to generate + // breakpad symbols. As a result, we need to adjust the |start_addr| for + // any mapping that results from a shared library that contains Android + // packed relocations, so that it properly represents the effective library + // load bias. The following functions support this adjustment. + + // Check that a given mapping at |start_addr| is for an ELF shared library. + // If it is, place the ELF header in |ehdr| and return true. + // 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 GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr); + + // For the ELF file mapped at |start_addr|, iterate ELF program headers to + // find the min vaddr of all program header LOAD segments, the vaddr for + // the DYNAMIC segment, and a count of DYNAMIC entries. Return values in + // |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|. + // The program header table is also in already mapped memory. + void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, + uintptr_t start_addr, + uintptr_t* min_vaddr_ptr, + uintptr_t* dyn_vaddr_ptr, + size_t* dyn_count_ptr); + + // Search the DYNAMIC tags for the ELF file with the given |load_bias|, and + // return true if the tags indicate that the file contains Android packed + // relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|. + bool HasAndroidPackedRelocations(uintptr_t load_bias, + uintptr_t dyn_vaddr, + size_t dyn_count); + + // If the ELF file mapped at |start_addr| contained Android packed + // relocations, return the load bias that the system linker (or Chromium + // crazy linker) will have used. If the file did not contain Android + // packed relocations, returns |start_addr|, indicating that no adjustment + // is necessary. + // The effective load bias is |start_addr| adjusted downwards by the + // min vaddr in the library LOAD segments. + uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr); + + // Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr| + // field of any that represent ELF shared libraries with Android packed + // relocations, so that |start_addr| is the load bias that the system linker + // (or Chromium crazy linker) used. This value matches the addresses produced + // when the non-relocation-packed library is used for breakpad symbol + // generation. + void LatePostprocessMappings(); +#endif // __ANDROID__ }; } // namespace google_breakpad diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index 0414bb72..9c1db99b 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -154,7 +154,7 @@ class MinidumpWriter { else if (!minidump_writer_.Open(path_)) return false; - return dumper_->ThreadsSuspend(); + return dumper_->ThreadsSuspend() && dumper_->LateInit(); } ~MinidumpWriter() { diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 0bcc18ab..1e96ca6a 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -95,6 +95,15 @@ using google_breakpad::scoped_ptr; #define EM_AARCH64 183 #endif +// Define SHT_ANDROID_REL and SHT_ANDROID_RELA if not defined by the host. +// Sections with this type contain Android packed relocations. +#ifndef SHT_ANDROID_REL +#define SHT_ANDROID_REL (SHT_LOOS + 1) +#endif +#ifndef SHT_ANDROID_RELA +#define SHT_ANDROID_RELA (SHT_LOOS + 2) +#endif + // // FDWrapper // @@ -611,6 +620,28 @@ bool LoadSymbols(const string& obj_file, bool found_debug_info_section = false; bool found_usable_info = false; + // Reject files that contain Android packed relocations. The pre-packed + // version of the file should be symbolized; the packed version is only + // intended for use on the target system. + if (FindElfSectionByName(".rel.dyn", SHT_ANDROID_REL, + sections, names, + names_end, elf_header->e_shnum)) { + fprintf(stderr, "%s: file contains a \".rel.dyn\" section " + "with type SHT_ANDROID_REL\n", obj_file.c_str()); + fprintf(stderr, "Files containing Android packed relocations " + "may not be symbolized.\n"); + return false; + } + if (FindElfSectionByName(".rela.dyn", SHT_ANDROID_RELA, + sections, names, + names_end, elf_header->e_shnum)) { + fprintf(stderr, "%s: file contains a \".rela.dyn\" section " + "with type SHT_ANDROID_RELA\n", obj_file.c_str()); + fprintf(stderr, "Files containing Android packed relocations " + "may not be symbolized.\n"); + return false; + } + if (options.symbol_data != ONLY_CFI) { #ifndef NO_STABS_SUPPORT // Look for STABS debugging information, and load it if present.