diff options
-rw-r--r-- | libunwindstack/Android.bp | 1 | ||||
-rw-r--r-- | libunwindstack/DexFiles.cpp | 40 | ||||
-rw-r--r-- | libunwindstack/Global.cpp | 96 | ||||
-rw-r--r-- | libunwindstack/JitDebug.cpp | 40 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/DexFiles.h | 9 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/Global.h | 54 | ||||
-rw-r--r-- | libunwindstack/include/unwindstack/JitDebug.h | 10 | ||||
-rw-r--r-- | libunwindstack/tests/DexFilesTest.cpp | 32 | ||||
-rw-r--r-- | libunwindstack/tests/JitDebugTest.cpp | 23 | ||||
-rw-r--r-- | libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt | 1 | ||||
-rw-r--r-- | libunwindstack/tests/files/offline/jit_debug_arm/maps.txt | 4 | ||||
-rw-r--r-- | libunwindstack/tests/files/offline/jit_debug_x86/maps.txt | 1 |
12 files changed, 214 insertions, 97 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 970e05c..14f82c7 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -58,6 +58,7 @@ cc_library { "Elf.cpp", "ElfInterface.cpp", "ElfInterfaceArm.cpp", + "Global.cpp", "JitDebug.cpp", "Log.cpp", "MapInfo.cpp", diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp index 17e2526..ac55fee 100644 --- a/libunwindstack/DexFiles.cpp +++ b/libunwindstack/DexFiles.cpp @@ -43,10 +43,10 @@ struct DEXFileEntry64 { uint64_t dex_file; }; -DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {} +DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {} DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs) - : memory_(memory), search_libs_(search_libs) {} + : Global(memory, search_libs) {} DexFiles::~DexFiles() { for (auto& entry : files_) { @@ -117,6 +117,11 @@ bool DexFiles::ReadEntry64() { return true; } +bool DexFiles::ReadVariableData(uint64_t ptr_offset) { + entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset); + return entry_addr_ != 0; +} + void DexFiles::Init(Maps* maps) { if (initialized_) { return; @@ -124,36 +129,7 @@ void DexFiles::Init(Maps* maps) { initialized_ = true; entry_addr_ = 0; - const std::string dex_debug_name("__dex_debug_descriptor"); - for (MapInfo* info : *maps) { - if (!(info->flags & PROT_READ) || info->offset != 0) { - continue; - } - - if (!search_libs_.empty()) { - bool found = false; - const char* lib = basename(info->name.c_str()); - for (const std::string& name : search_libs_) { - if (name == lib) { - found = true; - break; - } - } - if (!found) { - continue; - } - } - - Elf* elf = info->GetElf(memory_, true); - uint64_t ptr; - // Find first non-empty list (libart might be loaded multiple times). - if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) { - entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start); - if (entry_addr_ != 0) { - break; - } - } - } + FindAndReadVariable(maps, "__dex_debug_descriptor"); } DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) { diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp new file mode 100644 index 0000000..b449c7e --- /dev/null +++ b/libunwindstack/Global.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 <stdint.h> +#include <sys/mman.h> + +#include <string> +#include <vector> + +#include <unwindstack/Global.h> +#include <unwindstack/MapInfo.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> + +namespace unwindstack { + +Global::Global(std::shared_ptr<Memory>& memory) : memory_(memory) {} +Global::Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs) + : memory_(memory), search_libs_(search_libs) {} + +uint64_t Global::GetVariableOffset(MapInfo* info, const std::string& variable) { + if (!search_libs_.empty()) { + bool found = false; + const char* lib = basename(info->name.c_str()); + for (const std::string& name : search_libs_) { + if (name == lib) { + found = true; + break; + } + } + if (!found) { + return 0; + } + } + + Elf* elf = info->GetElf(memory_, true); + uint64_t ptr; + // Find first non-empty list (libraries might be loaded multiple times). + if (elf->GetGlobalVariable(variable, &ptr) && ptr != 0) { + return ptr + info->start; + } + return 0; +} + +void Global::FindAndReadVariable(Maps* maps, const char* var_str) { + std::string variable(var_str); + // When looking for global variables, do not arbitrarily search every + // readable map. Instead look for a specific pattern that must exist. + // The pattern should be a readable map, followed by a read-write + // map with a non-zero offset. + // For example: + // f0000-f1000 0 r-- /system/lib/libc.so + // f1000-f2000 1000 r-x /system/lib/libc.so + // f2000-f3000 2000 rw- /system/lib/libc.so + // This also works: + // f0000-f2000 0 r-- /system/lib/libc.so + // f2000-f3000 2000 rw- /system/lib/libc.so + MapInfo* map_start = nullptr; + for (MapInfo* info : *maps) { + if (map_start != nullptr) { + if (map_start->name == info->name) { + if (info->offset != 0 && + (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) { + uint64_t ptr = GetVariableOffset(map_start, variable); + if (ptr != 0 && ReadVariableData(ptr)) { + break; + } else { + // Failed to find the global variable, do not bother trying again. + map_start = nullptr; + } + } + } else { + map_start = nullptr; + } + } + if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 && + !info->name.empty()) { + map_start = info; + } + } +} + +} // namespace unwindstack diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp index 821aacf..fe680d7 100644 --- a/libunwindstack/JitDebug.cpp +++ b/libunwindstack/JitDebug.cpp @@ -69,10 +69,10 @@ struct JITDescriptor64 { uint64_t first_entry; }; -JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {} +JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {} JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs) - : memory_(memory), search_libs_(search_libs) {} + : Global(memory, search_libs) {} JitDebug::~JitDebug() { for (auto* elf : elf_list_) { @@ -165,6 +165,11 @@ void JitDebug::SetArch(ArchEnum arch) { } } +bool JitDebug::ReadVariableData(uint64_t ptr) { + entry_addr_ = (this->*read_descriptor_func_)(ptr); + return entry_addr_ != 0; +} + void JitDebug::Init(Maps* maps) { if (initialized_) { return; @@ -172,36 +177,7 @@ void JitDebug::Init(Maps* maps) { // Regardless of what happens below, consider the init finished. initialized_ = true; - const std::string descriptor_name("__jit_debug_descriptor"); - for (MapInfo* info : *maps) { - if (!(info->flags & PROT_READ) || info->offset != 0) { - continue; - } - - if (!search_libs_.empty()) { - bool found = false; - const char* lib = basename(info->name.c_str()); - for (std::string& name : search_libs_) { - if (strcmp(name.c_str(), lib) == 0) { - found = true; - break; - } - } - if (!found) { - continue; - } - } - - Elf* elf = info->GetElf(memory_, true); - uint64_t descriptor_addr; - // Find first non-empty entry (libart might be loaded multiple times). - if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr) && descriptor_addr != 0) { - entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr + info->start); - if (entry_addr_ != 0) { - break; - } - } - } + FindAndReadVariable(maps, "__jit_debug_descriptor"); } Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) { diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h index 26f5d35..c2fde74 100644 --- a/libunwindstack/include/unwindstack/DexFiles.h +++ b/libunwindstack/include/unwindstack/DexFiles.h @@ -25,16 +25,18 @@ #include <unordered_map> #include <vector> +#include <unwindstack/Global.h> +#include <unwindstack/Memory.h> + namespace unwindstack { // Forward declarations. class DexFile; class Maps; struct MapInfo; -class Memory; enum ArchEnum : uint8_t; -class DexFiles { +class DexFiles : public Global { public: explicit DexFiles(std::shared_ptr<Memory>& memory); DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs); @@ -60,8 +62,7 @@ class DexFiles { bool ReadEntry64(); - std::shared_ptr<Memory> memory_; - std::vector<std::string> search_libs_; + bool ReadVariableData(uint64_t ptr_offset) override; std::mutex lock_; bool initialized_ = false; diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h new file mode 100644 index 0000000..70e3ddd --- /dev/null +++ b/libunwindstack/include/unwindstack/Global.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 _LIBUNWINDSTACK_GLOBAL_H +#define _LIBUNWINDSTACK_GLOBAL_H + +#include <stdint.h> + +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +#include <unwindstack/Memory.h> + +namespace unwindstack { + +// Forward declarations. +class Maps; +struct MapInfo; + +class Global { + public: + explicit Global(std::shared_ptr<Memory>& memory); + Global(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs); + virtual ~Global() = default; + + protected: + uint64_t GetVariableOffset(MapInfo* info, const std::string& variable); + void FindAndReadVariable(Maps* maps, const char* variable); + + virtual bool ReadVariableData(uint64_t offset) = 0; + + std::shared_ptr<Memory> memory_; + std::vector<std::string> search_libs_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_GLOBAL_H diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h index 0bcd0b0..ccb473f 100644 --- a/libunwindstack/include/unwindstack/JitDebug.h +++ b/libunwindstack/include/unwindstack/JitDebug.h @@ -24,15 +24,17 @@ #include <string> #include <vector> +#include <unwindstack/Global.h> +#include <unwindstack/Memory.h> + namespace unwindstack { // Forward declarations. class Elf; class Maps; -class Memory; enum ArchEnum : uint8_t; -class JitDebug { +class JitDebug : public Global { public: explicit JitDebug(std::shared_ptr<Memory>& memory); JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs); @@ -45,11 +47,9 @@ class JitDebug { private: void Init(Maps* maps); - std::shared_ptr<Memory> memory_; uint64_t entry_addr_ = 0; bool initialized_ = false; std::vector<Elf*> elf_list_; - std::vector<std::string> search_libs_; std::mutex lock_; @@ -62,6 +62,8 @@ class JitDebug { uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size); uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size); uint64_t ReadEntry64(uint64_t* start, uint64_t* size); + + bool ReadVariableData(uint64_t ptr_offset) override; }; } // namespace unwindstack diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp index c6d7f33..3ac3ca6 100644 --- a/libunwindstack/tests/DexFilesTest.cpp +++ b/libunwindstack/tests/DexFilesTest.cpp @@ -44,15 +44,15 @@ class DexFilesTest : public ::testing::Test { dex_files_->SetArch(ARCH_ARM); maps_.reset( - new BufferMaps("1000-4000 ---s 00000000 00:00 0\n" - "4000-6000 r--s 00000000 00:00 0\n" - "6000-8000 -wxs 00000000 00:00 0\n" - "a000-c000 r--p 00000000 00:00 0\n" - "c000-f000 rw-p 00000000 00:00 0\n" - "f000-11000 r--p 00000000 00:00 0\n" - "100000-110000 rw-p 0000000 00:00 0\n" - "200000-210000 rw-p 0000000 00:00 0\n" - "300000-400000 rw-p 0000000 00:00 0\n")); + new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n" + "4000-6000 r--s 00000000 00:00 0 /fake/elf\n" + "6000-8000 -wxs 00000000 00:00 0 /fake/elf\n" + "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n" + "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n" + "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n" + "100000-110000 rw-p 0001000 00:00 0 /fake/elf3\n" + "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n" + "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n")); ASSERT_TRUE(maps_->Parse()); // Global variable in a section that is not readable. @@ -96,8 +96,9 @@ class DexFilesTest : public ::testing::Test { void WriteDex(uint64_t dex_file); static constexpr size_t kMapGlobalNonReadable = 2; - static constexpr size_t kMapGlobalSetToZero = 4; + static constexpr size_t kMapGlobalSetToZero = 3; static constexpr size_t kMapGlobal = 5; + static constexpr size_t kMapGlobalRw = 6; static constexpr size_t kMapDexFileEntries = 7; static constexpr size_t kMapDexFiles = 8; @@ -256,6 +257,9 @@ TEST_F(DexFilesTest, get_method_information_search_libs) { map_info->name = "/system/lib/libart.so"; dex_files_.reset(new DexFiles(process_memory_, libs)); dex_files_->SetArch(ARCH_ARM); + // Set the rw map to the same name or this will not scan this entry. + map_info = maps_->Get(kMapGlobalRw); + map_info->name = "/system/lib/libart.so"; // Make sure that clearing out copy of the libs doesn't affect the // DexFiles object. libs.clear(); @@ -271,7 +275,7 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) { MapInfo* info = maps_->Get(kMapDexFiles); // First global variable found, but value is zero. - WriteDescriptor32(0xc800, 0); + WriteDescriptor32(0xa800, 0); WriteDescriptor32(0xf800, 0x200000); WriteEntry32(0x200000, 0, 0, 0x300000); @@ -286,7 +290,7 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) { dex_files_->SetArch(ARCH_ARM); method_name = "fail"; method_offset = 0x123; - WriteDescriptor32(0xc800, 0x100000); + WriteDescriptor32(0xa800, 0x100000); dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("fail", method_name); EXPECT_EQ(0x123U, method_offset); @@ -298,7 +302,7 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) { MapInfo* info = maps_->Get(kMapDexFiles); // First global variable found, but value is zero. - WriteDescriptor64(0xc800, 0); + WriteDescriptor64(0xa800, 0); WriteDescriptor64(0xf800, 0x200000); WriteEntry64(0x200000, 0, 0, 0x300000); @@ -314,7 +318,7 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) { dex_files_->SetArch(ARCH_ARM64); method_name = "fail"; method_offset = 0x123; - WriteDescriptor64(0xc800, 0x100000); + WriteDescriptor64(0xa800, 0x100000); dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset); EXPECT_EQ("fail", method_name); EXPECT_EQ(0x123U, method_offset); diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp index 66f0859..4598526 100644 --- a/libunwindstack/tests/JitDebugTest.cpp +++ b/libunwindstack/tests/JitDebugTest.cpp @@ -43,15 +43,16 @@ class JitDebugTest : public ::testing::Test { jit_debug_->SetArch(ARCH_ARM); maps_.reset( - new BufferMaps("1000-4000 ---s 00000000 00:00 0\n" - "4000-6000 r--s 00000000 00:00 0\n" - "6000-8000 -wxs 00000000 00:00 0\n" - "a000-c000 --xp 00000000 00:00 0\n" - "c000-f000 rw-p 00000000 00:00 0\n" - "f000-11000 r--p 00000000 00:00 0\n" - "12000-14000 r--p 00000000 00:00 0\n" - "100000-110000 rw-p 0000000 00:00 0\n" - "200000-210000 rw-p 0000000 00:00 0\n")); + new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n" + "4000-6000 r--s 00000000 00:00 0 /fake/elf1\n" + "6000-8000 -wxs 00000000 00:00 0 /fake/elf1\n" + "a000-c000 --xp 00000000 00:00 0 /fake/elf2\n" + "c000-f000 rw-p 00001000 00:00 0 /fake/elf2\n" + "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n" + "11000-12000 rw-p 00001000 00:00 0 /fake/elf3\n" + "12000-14000 r--p 00000000 00:00 0 /fake/elf4\n" + "100000-110000 rw-p 0001000 00:00 0 /fake/elf4\n" + "200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n")); ASSERT_TRUE(maps_->Parse()); MapInfo* map_info = maps_->Get(3); @@ -74,7 +75,7 @@ class JitDebugTest : public ::testing::Test { interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800); map_info->elf.reset(elf); - map_info = maps_->Get(6); + map_info = maps_->Get(7); ASSERT_TRUE(map_info != nullptr); memory = new MemoryFake; elf = new ElfFake(memory); @@ -397,6 +398,8 @@ TEST_F(JitDebugTest, get_elf_search_libs) { // Change the name of the map that includes the value and verify this works. MapInfo* map_info = maps_->Get(5); map_info->name = "/system/lib/libart.so"; + map_info = maps_->Get(6); + map_info->name = "/system/lib/libart.so"; jit_debug_.reset(new JitDebug(process_memory_, libs)); // Make sure that clearing our copy of the libs doesn't affect the // JitDebug object. diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt index 55aaaf6..5657373 100644 --- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt +++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt @@ -1,3 +1,4 @@ d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000> e466e000-e4ae8000 r-xp 0 00:00 0 libart.so +e4ae8000-e4ae9000 rw-p 1000 00:00 0 libart.so e7d91000-e7e31000 r-xp 0 00:00 0 libc.so diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt index f25c781..3cd9d40 100644 --- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt +++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt @@ -2,7 +2,9 @@ ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32 dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000 -e648e000-e690f000 r-xp 00000000 00:00 0 libart.so +e648e000-e690f000 r-xp 0 00:00 0 libart.so +e690f000-e6910000 rw-p 1000 00:00 0 libart.so ed306000-ed801000 r-xp 0 00:00 0 libartd.so +ed801000-ed802000 rw-p 1000 00:00 0 libartd.so eda88000-edb23000 r-xp 0 00:00 0 libc.so ede4e000-ede50000 r-xp 0 00:00 0 anonymous:ede4e000 diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt index db4f9f7..a8d215c 100644 --- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt +++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt @@ -3,4 +3,5 @@ eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000 f6be1000-f732b000 r-xp 0 00:00 0 libartd.so +f732b000-f732c000 rw-p 1000 00:00 0 libartd.so f734b000-f74fc000 r-xp 0 00:00 0 libc.so |