summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2018-10-17 10:57:53 -0700
committerChristopher Ferris <cferris@google.com>2018-10-17 14:23:10 -0700
commit99e9621c82c0baff42d71f90b88ce1b636821b97 (patch)
tree5bcf6f26a85b4159f9bbdc1e82c7c27960efcfbf
parent185f0e4add0820ddcb567da4b02c2f6f0cdbedc5 (diff)
downloadunwinding-99e9621c82c0baff42d71f90b88ce1b636821b97.tar.gz
Fix which maps to search for globals.
If multiple threads are unwinding at the same time, new maps that contain the global variables for dex files and jit information are created. This leads to threads creating more new maps that then get searched, then more maps, then more searching until virtual address space exhaustion. Fix this so that we only search maps that have a corresponding rw map that could contain the global memory. Small refactor to combine the code to search for global variables into one class that both classes inherit from. Modify unit tests for the new pattern checking. Bug: 117761427 Test: Ran unit tests for libunwindstack/libbacktrace/simpleperf. Test: Ran art 004-ThreadStress that used to fail. Change-Id: I837ca6b9d0383100079de090bc7d019598e0cdfe
-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