Fix global finding logic.

Recently, the maps for an elf in memory might show up looking like:

  f0000-f1000 0 r-- /system/lib/libc.so
  f1000-f2000 0 ---
  f2000-f3000 1000 r-x /system/lib/libc.so
  f3000-f4000 2000 rw- /system/lib/libc.so

That empty map was confusing the logic when looking for a global
variable. Now this case is handled properly.

New unit test added for this case.

Bug: 147910661

Test: Ran unit tests.
Test: Ran original failing test 137-cfi.
Change-Id: Ida2e96d1da5e1bf61f41646949fe5a2d405c0d61
This commit is contained in:
Christopher Ferris 2020-01-21 17:56:25 -08:00
parent a78d0cb735
commit de5cd8ccd4
2 changed files with 44 additions and 21 deletions

View file

@ -70,30 +70,28 @@ void Global::FindAndReadVariable(Maps* maps, const char* var_str) {
// This also works:
// f0000-f2000 0 r-- /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
MapInfo* map_start = nullptr;
// It is also possible to see empty maps after the read-only like so:
// f0000-f1000 0 r-- /system/lib/libc.so
// f1000-f2000 0 ---
// f2000-f3000 1000 r-x /system/lib/libc.so
// f3000-f4000 2000 rw- /system/lib/libc.so
MapInfo* map_zero = nullptr;
for (const auto& info : *maps) {
if (map_start != nullptr && map_start->name == info->name) {
if (info->offset != 0 &&
(info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
Elf* elf = map_start->GetElf(memory_, arch());
uint64_t ptr;
if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
uint64_t offset_end = info->offset + info->end - info->start;
if (ptr >= info->offset && ptr < offset_end) {
ptr = info->start + ptr - info->offset;
if (ReadVariableData(ptr)) {
break;
}
if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
Elf* elf = map_zero->GetElf(memory_, arch());
uint64_t ptr;
if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
uint64_t offset_end = info->offset + info->end - info->start;
if (ptr >= info->offset && ptr < offset_end) {
ptr = info->start + ptr - info->offset;
if (ReadVariableData(ptr)) {
break;
}
}
map_start = nullptr;
}
} else {
map_start = nullptr;
}
if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
Searchable(info->name)) {
map_start = info.get();
} else if (info->offset == 0 && !info->name.empty()) {
map_zero = info.get();
}
}
}

View file

@ -64,7 +64,11 @@ class DexFilesTest : public ::testing::Test {
"f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
"100000-110000 rw-p 00f1000 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"));
"300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
"500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
"501000-502000 ---p 0000000 00:00 0\n"
"503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
"510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
ASSERT_TRUE(maps_->Parse());
// Global variable in a section that is not readable.
@ -81,6 +85,11 @@ class DexFilesTest : public ::testing::Test {
map_info = maps_->Get(kMapGlobal);
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
// Global variable set in this map, but there is an empty map before rw map.
map_info = maps_->Get(kMapGlobalAfterEmpty);
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
}
void SetUp() override {
@ -102,6 +111,8 @@ class DexFilesTest : public ::testing::Test {
static constexpr size_t kMapGlobalRw = 6;
static constexpr size_t kMapDexFileEntries = 7;
static constexpr size_t kMapDexFiles = 8;
static constexpr size_t kMapGlobalAfterEmpty = 9;
static constexpr size_t kMapDexFilesAfterEmpty = 12;
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
@ -328,4 +339,18 @@ TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
EXPECT_EQ(0x123U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_with_empty_map) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
WriteDescriptor32(0x503800, 0x506000);
WriteEntry32(0x506000, 0, 0, 0x510000);
WriteDex(0x510000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
}
} // namespace unwindstack