summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libunwindstack/Android.bp1
-rw-r--r--libunwindstack/DexFiles.cpp40
-rw-r--r--libunwindstack/Global.cpp96
-rw-r--r--libunwindstack/JitDebug.cpp40
-rw-r--r--libunwindstack/include/unwindstack/DexFiles.h9
-rw-r--r--libunwindstack/include/unwindstack/Global.h54
-rw-r--r--libunwindstack/include/unwindstack/JitDebug.h10
-rw-r--r--libunwindstack/tests/DexFilesTest.cpp32
-rw-r--r--libunwindstack/tests/JitDebugTest.cpp23
-rw-r--r--libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt1
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_arm/maps.txt4
-rw-r--r--libunwindstack/tests/files/offline/jit_debug_x86/maps.txt1
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