diff --git a/compat/BUILD.gn b/compat/BUILD.gn index 2bfe074f..9f247a45 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -75,6 +75,8 @@ compat_target("compat") { if (crashpad_is_android) { sources += [ + "android/android/api-level.cc", + "android/android/api-level.h", "android/dlfcn_internal.cc", "android/dlfcn_internal.h", "android/elf.h", diff --git a/compat/android/android/api-level.cc b/compat/android/android/api-level.cc new file mode 100644 index 00000000..1a553368 --- /dev/null +++ b/compat/android/android/api-level.cc @@ -0,0 +1,50 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include + +#include "dlfcn_internal.h" + +#if __ANDROID_API__ < 29 + +extern "C" { + +int android_get_device_api_level() { + using FuncType = int (*)(); + static const FuncType bionic_get_device_api_level = + reinterpret_cast( + crashpad::internal::Dlsym(RTLD_NEXT, "android_get_device_api_level")); + + if (bionic_get_device_api_level) { + return bionic_get_device_api_level(); + } + + char api_string[PROP_VALUE_MAX]; + int length = __system_property_get("ro.build.version.sdk", api_string); + if (length <= 0) { + return -1; + } + + int api_level = atoi(api_string); + return api_level > 0 ? api_level : -1; +} + +} // extern "C" + +#endif // __ANDROID_API__ < 29 diff --git a/compat/android/android/api-level.h b/compat/android/android/api-level.h new file mode 100644 index 00000000..03794cbd --- /dev/null +++ b/compat/android/android/api-level.h @@ -0,0 +1,38 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_ +#define CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_ + +#include_next + +#include + +#if __ANDROID_API__ < 29 + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns the API level of the device or -1 if it can't be determined. This +// function is provided by Bionic at API 29. +int android_get_device_api_level(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __ANDROID_API__ < 29 + +#endif // CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_ diff --git a/compat/compat.gyp b/compat/compat.gyp index 1229ee0c..6294d1fc 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -20,6 +20,8 @@ { 'target_name': 'crashpad_compat', 'sources': [ + 'android/android/api-level.cc', + 'android/android/api-level.h', 'android/dlfcn_internal.cc', 'android/dlfcn_internal.h', 'android/elf.h', diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index 2cc0faa5..0846ada4 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -100,10 +100,9 @@ void LocateExecutable(PtraceConnection* connection, ASSERT_TRUE(memory_map.Initialize(connection)); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); - std::vector possible_mappings = - memory_map.FindFilePossibleMmapStarts(*phdr_mapping); - ASSERT_EQ(possible_mappings.size(), 1u); - *elf_address = possible_mappings[0]->range.Base(); + auto possible_mappings = memory_map.FindFilePossibleMmapStarts(*phdr_mapping); + ASSERT_EQ(possible_mappings->Count(), 1u); + *elf_address = possible_mappings->Next()->range.Base(); } #endif // OS_FUCHSIA diff --git a/snapshot/linux/debug_rendezvous_test.cc b/snapshot/linux/debug_rendezvous_test.cc index 431e2bbe..be22c903 100644 --- a/snapshot/linux/debug_rendezvous_test.cc +++ b/snapshot/linux/debug_rendezvous_test.cc @@ -37,28 +37,13 @@ #include "util/process/process_memory_range.h" #if defined(OS_ANDROID) -#include +#include #endif namespace crashpad { namespace test { namespace { -#if defined(OS_ANDROID) -int AndroidRuntimeAPI() { - char api_string[PROP_VALUE_MAX]; - int length = __system_property_get("ro.build.version.sdk", api_string); - if (length <= 0) { - return -1; - } - - int api_level; - bool success = - base::StringToInt(base::StringPiece(api_string, length), &api_level); - return success ? api_level : -1; -} -#endif // OS_ANDROID - void TestAgainstTarget(PtraceConnection* connection) { // Use ElfImageReader on the main executable which can tell us the debug // address. glibc declares the symbol _r_debug in link.h which we can use to @@ -74,10 +59,10 @@ void TestAgainstTarget(PtraceConnection* connection) { const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); - std::vector exe_mappings = - mappings.FindFilePossibleMmapStarts(*phdr_mapping); - ASSERT_EQ(exe_mappings.size(), 1u); - LinuxVMAddress elf_address = exe_mappings[0]->range.Base(); + + auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping); + ASSERT_EQ(exe_mappings->Count(), 1u); + LinuxVMAddress elf_address = exe_mappings->Next()->range.Base(); ProcessMemoryLinux memory; ASSERT_TRUE(memory.Initialize(connection->GetProcessID())); @@ -94,7 +79,7 @@ void TestAgainstTarget(PtraceConnection* connection) { ASSERT_TRUE(debug.Initialize(range, debug_address)); #if defined(OS_ANDROID) - const int android_runtime_api = AndroidRuntimeAPI(); + const int android_runtime_api = android_get_device_api_level(); ASSERT_GE(android_runtime_api, 1); EXPECT_NE(debug.Executable()->name.find("crashpad_snapshot_test"), @@ -143,13 +128,13 @@ void TestAgainstTarget(PtraceConnection* connection) { mappings.FindMapping(module.dynamic_array); ASSERT_TRUE(dyn_mapping); - std::vector possible_mappings = - mappings.FindFilePossibleMmapStarts(*dyn_mapping); - ASSERT_GE(possible_mappings.size(), 1u); + auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping); + ASSERT_GE(possible_mappings->Count(), 1u); std::unique_ptr module_reader; const MemoryMap::Mapping* module_mapping = nullptr; - for (const auto mapping : possible_mappings) { + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { auto parsed_module = std::make_unique(); VMAddress dynamic_address; if (parsed_module->Initialize(range, mapping->range.Base()) && diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index 811ddd03..c1d6c40f 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -29,6 +29,10 @@ #include "util/linux/auxiliary_vector.h" #include "util/linux/proc_stat_reader.h" +#if defined(OS_ANDROID) +#include +#endif + namespace crashpad { namespace { @@ -352,17 +356,15 @@ void ProcessReaderLinux::InitializeModules() { return; } - std::vector possible_mappings = + auto possible_mappings = memory_map_.FindFilePossibleMmapStarts(*phdr_mapping); - for (auto riter = possible_mappings.rbegin(); - riter != possible_mappings.rend(); - ++riter) { - auto mapping = *riter; + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { auto parsed_exe = std::make_unique(); if (parsed_exe->Initialize( range, mapping->range.Base(), - /* verbose= */ possible_mappings.size() == 1) && + /* verbose= */ possible_mappings->Count() == 1) && parsed_exe->GetProgramHeaderTableAddress() == phdrs) { exe_mapping = mapping; exe_reader = std::move(parsed_exe); @@ -370,7 +372,8 @@ void ProcessReaderLinux::InitializeModules() { } } if (!exe_mapping) { - LOG(ERROR) << "no exe mappings " << possible_mappings.size(); + LOG(ERROR) << "no exe mappings 0x" << std::hex + << phdr_mapping->range.Base(); return; } } @@ -407,18 +410,30 @@ void ProcessReaderLinux::InitializeModules() { continue; } - std::vector possible_mappings = +#if defined(OS_ANDROID) + // Beginning at API 21, Bionic provides android_dlopen_ext() which allows + // passing a file descriptor with an existing relro segment to the loader. + // This means that the mapping attributes of dyn_mapping may be unrelated + // to the attributes of the other mappings for the module. In this case, + // search all mappings in reverse order from dyn_mapping until a module is + // parsed whose dynamic address matches the value in the debug link. + static int api_level = android_get_device_api_level(); + auto possible_mappings = + (api_level >= 21 || api_level < 0) + ? memory_map_.ReverseIteratorFrom(*dyn_mapping) + : memory_map_.FindFilePossibleMmapStarts(*dyn_mapping); +#else + auto possible_mappings = memory_map_.FindFilePossibleMmapStarts(*dyn_mapping); - for (auto riter = possible_mappings.rbegin(); - riter != possible_mappings.rend(); - ++riter) { - auto mapping = *riter; +#endif + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { auto parsed_module = std::make_unique(); VMAddress dynamic_address; if (parsed_module->Initialize( range, mapping->range.Base(), - /* verbose= */ possible_mappings.size() == 1) && + /* verbose= */ possible_mappings->Count() == 1) && parsed_module->GetDynamicArrayAddress(&dynamic_address) && dynamic_address == entry.dynamic_array) { module_mapping = mapping; @@ -427,7 +442,8 @@ void ProcessReaderLinux::InitializeModules() { } } if (!module_mapping) { - LOG(ERROR) << "no module mappings " << possible_mappings.size(); + LOG(ERROR) << "no module mappings 0x" << std::hex + << dyn_mapping->range.Base(); continue; } } diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index d827f35d..1edc5ab2 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -715,7 +715,7 @@ void ExpectTestModule(ProcessReaderLinux* reader, auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr); auto mappings = reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping); - EXPECT_EQ(mappings.size(), 2u); + EXPECT_EQ(mappings->Count(), 2u); return; } } diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc index 495adf7c..0bcca3fa 100644 --- a/util/linux/memory_map.cc +++ b/util/linux/memory_map.cc @@ -204,6 +204,48 @@ ParseResult ParseMapsLine(DelimitedFileReader* maps_file_reader, return ParseResult::kSuccess; } +class SparseReverseIterator : public MemoryMap::Iterator { + public: + SparseReverseIterator(const std::vector& mappings) + : mappings_(mappings), riter_(mappings_.rbegin()){}; + + SparseReverseIterator() : mappings_(), riter_(mappings_.rend()) {} + + // Iterator: + const MemoryMap::Mapping* Next() override { + return riter_ == mappings_.rend() ? nullptr : *(riter_++); + } + + unsigned int Count() override { return mappings_.rend() - riter_; } + + private: + std::vector mappings_; + std::vector::reverse_iterator riter_; + + DISALLOW_COPY_AND_ASSIGN(SparseReverseIterator); +}; + +class FullReverseIterator : public MemoryMap::Iterator { + public: + FullReverseIterator( + std::vector::const_reverse_iterator rbegin, + std::vector::const_reverse_iterator rend) + : riter_(rbegin), rend_(rend) {} + + // Iterator: + const MemoryMap::Mapping* Next() override { + return riter_ == rend_ ? nullptr : &*riter_++; + } + + unsigned int Count() override { return rend_ - riter_; } + + private: + std::vector::const_reverse_iterator riter_; + std::vector::const_reverse_iterator rend_; + + DISALLOW_COPY_AND_ASSIGN(FullReverseIterator); +}; + } // namespace MemoryMap::Mapping::Mapping() @@ -296,7 +338,7 @@ const MemoryMap::Mapping* MemoryMap::FindMappingWithName( return nullptr; } -std::vector MemoryMap::FindFilePossibleMmapStarts( +std::unique_ptr MemoryMap::FindFilePossibleMmapStarts( const Mapping& mapping) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); @@ -308,12 +350,12 @@ std::vector MemoryMap::FindFilePossibleMmapStarts( for (const auto& candidate : mappings_) { if (mapping.Equals(candidate)) { possible_starts.push_back(&candidate); - return possible_starts; + return std::make_unique(possible_starts); } } LOG(ERROR) << "mapping not found"; - return std::vector(); + return std::make_unique(); } #if defined(OS_ANDROID) @@ -341,7 +383,7 @@ std::vector MemoryMap::FindFilePossibleMmapStarts( possible_starts.push_back(&candidate); } if (mapping.Equals(candidate)) { - return possible_starts; + return std::make_unique(possible_starts); } } } @@ -359,12 +401,23 @@ std::vector MemoryMap::FindFilePossibleMmapStarts( possible_starts.push_back(&candidate); } if (mapping.Equals(candidate)) { - return possible_starts; + return std::make_unique(possible_starts); } } LOG(ERROR) << "mapping not found"; - return std::vector(); + return std::make_unique(); +} + +std::unique_ptr MemoryMap::ReverseIteratorFrom( + const Mapping& target) const { + for (auto riter = mappings_.crbegin(); riter != mappings_.rend(); ++riter) { + if (riter->Equals(target)) { + return std::make_unique(riter, mappings_.rend()); + } + } + return std::make_unique(mappings_.rend(), + mappings_.rend()); } } // namespace crashpad diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h index 28b8901f..a062b560 100644 --- a/util/linux/memory_map.h +++ b/util/linux/memory_map.h @@ -17,6 +17,7 @@ #include +#include #include #include @@ -76,6 +77,24 @@ class MemoryMap { //! it was obtained from. const Mapping* FindMappingWithName(const std::string& name) const; + //! \brief An abstract base class for iterating over ordered sets of mappings + //! in a MemoryMap. + class Iterator { + public: + virtual ~Iterator() = default; + + //! \return the mapping pointed to by the iterator and advance the iterator + //! to the next mapping. If there are no more mappings, this method + //! returns `nullptr` on all subsequent invocations. + virtual const Mapping* Next() = 0; + + //! \return the number of mappings remaining. + virtual unsigned int Count() = 0; + + protected: + Iterator() = default; + }; + //! \brief Find possible initial mappings of files mapped over several //! segments. //! @@ -99,10 +118,15 @@ class MemoryMap { //! map a file, \a mapping is returned in \a possible_starts. //! //! \param[in] mapping A Mapping whose series to find the start of. - //! \return a vector of the possible mapping starts. - std::vector FindFilePossibleMmapStarts( + //! \return a reverse iterator over the possible mapping starts, starting from + //! the mapping with highest base address. + std::unique_ptr FindFilePossibleMmapStarts( const Mapping& mapping) const; + //! \return A reverse iterator over all mappings in the MemoryMap from \a + //! mapping to the start of the MemoryMap. + std::unique_ptr ReverseIteratorFrom(const Mapping& mapping) const; + private: std::vector mappings_; InitializationStateDcheck initialized_; diff --git a/util/linux/memory_map_test.cc b/util/linux/memory_map_test.cc index 77d832b6..2df44b3a 100644 --- a/util/linux/memory_map_test.cc +++ b/util/linux/memory_map_test.cc @@ -373,22 +373,20 @@ void ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start, ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping2, mapping3); - std::vector mappings; - - mappings = map.FindFilePossibleMmapStarts(*mapping1); - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping1); + auto mappings = map.FindFilePossibleMmapStarts(*mapping1); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping2); - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping2); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping2); mappings = map.FindFilePossibleMmapStarts(*mapping3); #if defined(OS_ANDROID) - EXPECT_EQ(mappings.size(), 2u); + EXPECT_EQ(mappings->Count(), 2u); #else - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping1); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); #endif } @@ -432,29 +430,30 @@ TEST(MemoryMap, FindFilePossibleMmapStarts) { ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping2, mapping3); - std::vector mappings; - #if defined(OS_ANDROID) - mappings = map.FindFilePossibleMmapStarts(*mapping1); - EXPECT_EQ(mappings.size(), 1u); + auto mappings = map.FindFilePossibleMmapStarts(*mapping1); + EXPECT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); + EXPECT_EQ(mappings->Next(), nullptr); mappings = map.FindFilePossibleMmapStarts(*mapping2); - EXPECT_EQ(mappings.size(), 2u); + EXPECT_EQ(mappings->Count(), 2u); mappings = map.FindFilePossibleMmapStarts(*mapping3); - EXPECT_EQ(mappings.size(), 3u); + EXPECT_EQ(mappings->Count(), 3u); #else - mappings = map.FindFilePossibleMmapStarts(*mapping1); - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping1); + auto mappings = map.FindFilePossibleMmapStarts(*mapping1); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); + EXPECT_EQ(mappings->Next(), nullptr); mappings = map.FindFilePossibleMmapStarts(*mapping2); - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping1); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping3); - ASSERT_EQ(mappings.size(), 1u); - EXPECT_EQ(mappings[0], mapping1); + ASSERT_EQ(mappings->Count(), 1u); + EXPECT_EQ(mappings->Next(), mapping1); #endif #if defined(ARCH_CPU_64_BITS) @@ -464,7 +463,9 @@ TEST(MemoryMap, FindFilePossibleMmapStarts) { #endif MemoryMap::Mapping bad_mapping; bad_mapping.range.SetRange(is_64_bit, 0, 1); - EXPECT_EQ(map.FindFilePossibleMmapStarts(bad_mapping).size(), 0u); + mappings = map.FindFilePossibleMmapStarts(bad_mapping); + EXPECT_EQ(mappings->Count(), 0u); + EXPECT_EQ(mappings->Next(), nullptr); } // Make the second page an anonymous mapping @@ -578,45 +579,45 @@ TEST(MemoryMap, FindFilePossibleMmapStarts_MultipleStarts) { ASSERT_TRUE(mapping); auto possible_starts = map.FindFilePossibleMmapStarts(*mapping); #if defined(OS_ANDROID) - EXPECT_EQ(possible_starts.size(), 1u); + EXPECT_EQ(possible_starts->Count(), 1u); #else - EXPECT_EQ(possible_starts.size(), 0u); + EXPECT_EQ(possible_starts->Count(), 0u); #endif mapping = map.FindMapping(file_mapping1.addr_as()); ASSERT_TRUE(mapping); possible_starts = map.FindFilePossibleMmapStarts(*mapping); #if defined(OS_ANDROID) - EXPECT_EQ(possible_starts.size(), 2u); + EXPECT_EQ(possible_starts->Count(), 2u); #else - EXPECT_EQ(possible_starts.size(), 1u); + EXPECT_EQ(possible_starts->Count(), 1u); #endif mapping = map.FindMapping(file_mapping2.addr_as()); ASSERT_TRUE(mapping); possible_starts = map.FindFilePossibleMmapStarts(*mapping); #if defined(OS_ANDROID) - EXPECT_EQ(possible_starts.size(), 3u); + EXPECT_EQ(possible_starts->Count(), 3u); #else - EXPECT_EQ(possible_starts.size(), 2u); + EXPECT_EQ(possible_starts->Count(), 2u); #endif mapping = map.FindMapping(file_mapping3.addr_as()); ASSERT_TRUE(mapping); possible_starts = map.FindFilePossibleMmapStarts(*mapping); #if defined(OS_ANDROID) - EXPECT_EQ(possible_starts.size(), 4u); + EXPECT_EQ(possible_starts->Count(), 4u); #else - EXPECT_EQ(possible_starts.size(), 3u); + EXPECT_EQ(possible_starts->Count(), 3u); #endif mapping = map.FindMapping(file_mapping4.addr_as()); ASSERT_TRUE(mapping); possible_starts = map.FindFilePossibleMmapStarts(*mapping); #if defined(OS_ANDROID) - EXPECT_EQ(possible_starts.size(), 5u); + EXPECT_EQ(possible_starts->Count(), 5u); #else - EXPECT_EQ(possible_starts.size(), 4u); + EXPECT_EQ(possible_starts->Count(), 4u); #endif }