Merge "Fix which maps to search for globals."

This commit is contained in:
Christopher Ferris 2018-10-18 01:54:47 +00:00 committed by Gerrit Code Review
commit 7cffd15110
12 changed files with 214 additions and 97 deletions

View file

@ -58,6 +58,7 @@ cc_library {
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Global.cpp",
"JitDebug.cpp",
"Log.cpp",
"MapInfo.cpp",

View file

@ -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) {

96
libunwindstack/Global.cpp Normal file
View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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