Add a method to share the process memory object.

New function to create the process memory object. This allows for
a future where different remote process memory objects could be created
depending on the way remote memory can be created. Even different local
memory objects that access memory without doing any checks.

It also allows MemoryRange objects to share one single process memory object
and could help if the process memory object caches data.

Small changes to MapInfo::CreateMemory to when some errors are detected.
- Always check if the map is a device map, instead of only if the name
  is not empty.
- Check if a memory map is readable before creating the memory from process
  memory.

Bug: 23762183

Test: Ran unit tests, unwound on device using the new code.
Change-Id: I12a93c2dc19639689a528ec41c67bfac74d431b3
This commit is contained in:
Christopher Ferris 2017-09-01 11:17:16 -07:00
parent 9638729a9d
commit 5f118519fd
14 changed files with 367 additions and 165 deletions

View file

@ -41,8 +41,7 @@
#include "UnwindStack.h"
#include "UnwindStackMap.h"
static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t pc,
uintptr_t* offset) {
static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr_t* offset) {
*offset = 0;
unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
@ -52,7 +51,8 @@ static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t
return "";
}
unwindstack::Elf* elf = map_info->GetElf(pid, true);
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
std::string name;
uint64_t func_offset;
@ -68,10 +68,10 @@ static bool IsUnwindLibrary(const std::string& map_name) {
return library == "libunwindstack.so" || library == "libbacktrace.so";
}
static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* regs,
BacktraceMap* back_map, std::vector<backtrace_frame_data_t>* frames,
size_t num_ignore_frames) {
unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
unwindstack::Maps* maps = stack_map->stack_maps();
bool adjust_rel_pc = false;
size_t num_frames = 0;
frames->clear();
@ -84,7 +84,7 @@ static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* re
break;
}
unwindstack::Elf* elf = map_info->GetElf(pid, true);
unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
@ -137,7 +137,7 @@ static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* re
break;
}
if (!elf->Step(rel_pc + map_info->elf_offset, regs, memory)) {
if (!elf->Step(rel_pc + map_info->elf_offset, regs, stack_map->process_memory().get())) {
break;
}
}
@ -146,10 +146,10 @@ static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* re
}
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
: BacktraceCurrent(pid, tid, map), memory_(new unwindstack::MemoryLocal) {}
: BacktraceCurrent(pid, tid, map) {}
std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
return ::GetFunctionName(Pid(), GetMap(), pc, offset);
return ::GetFunctionName(GetMap(), pc, offset);
}
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
@ -165,14 +165,14 @@ bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t*
}
error_ = BACKTRACE_UNWIND_NO_ERROR;
return ::Unwind(getpid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
}
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
: BacktracePtrace(pid, tid, map), memory_(new unwindstack::MemoryRemote(pid)) {}
: BacktracePtrace(pid, tid, map) {}
std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
return ::GetFunctionName(Pid(), GetMap(), pc, offset);
return ::GetFunctionName(GetMap(), pc, offset);
}
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
@ -185,7 +185,7 @@ bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
}
error_ = BACKTRACE_UNWIND_NO_ERROR;
return ::Unwind(Pid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
}
Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {

View file

@ -35,9 +35,6 @@ class UnwindStackCurrent : public BacktraceCurrent {
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
private:
std::unique_ptr<unwindstack::Memory> memory_;
};
class UnwindStackPtrace : public BacktracePtrace {
@ -48,9 +45,6 @@ class UnwindStackPtrace : public BacktracePtrace {
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
private:
std::unique_ptr<unwindstack::Memory> memory_;
};
#endif // _LIBBACKTRACE_UNWIND_STACK_H

View file

@ -36,6 +36,9 @@ bool UnwindStackMap::Build() {
stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
}
// Create the process memory object.
process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
if (!stack_maps_->Parse()) {
return false;
}
@ -68,7 +71,7 @@ void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
if (map_info == nullptr) {
return;
}
unwindstack::Elf* elf = map_info->GetElf(pid_, true);
unwindstack::Elf* elf = map_info->GetElf(process_memory_, true);
map->load_bias = elf->GetLoadBias();
}

View file

@ -20,6 +20,8 @@
#include <stdint.h>
#include <sys/types.h>
#include <memory>
#include <backtrace/BacktraceMap.h>
#include <unwindstack/Maps.h>
@ -34,8 +36,11 @@ class UnwindStackMap : public BacktraceMap {
unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
protected:
std::unique_ptr<unwindstack::Maps> stack_maps_;
std::shared_ptr<unwindstack::Memory> process_memory_;
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H

View file

@ -57,6 +57,13 @@ cc_library {
"Symbols.cpp",
],
target: {
// Always disable optimizations for host to make it easier to debug.
linux: {
cflags: ["-O0", "-g"],
},
},
arch: {
x86: {
srcs: ["AsmGetRegsX86.S"],
@ -97,7 +104,6 @@ cc_test {
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/LogFake.cpp",
"tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapsTest.cpp",
"tests/MemoryBufferTest.cpp",

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@ -76,40 +77,39 @@ Memory* MapInfo::GetFileMemory() {
return memory.release();
}
Memory* MapInfo::CreateMemory(pid_t pid) {
Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
if (end <= start) {
return nullptr;
}
elf_offset = 0;
// Fail on device maps.
if (flags & MAPS_FLAGS_DEVICE_MAP) {
return nullptr;
}
// First try and use the file associated with the info.
if (!name.empty()) {
// Fail on device maps.
if (flags & MAPS_FLAGS_DEVICE_MAP) {
return nullptr;
}
Memory* memory = GetFileMemory();
if (memory != nullptr) {
return memory;
}
}
Memory* memory;
if (pid == getpid()) {
memory = new MemoryLocal();
} else {
memory = new MemoryRemote(pid);
// If the map isn't readable, don't bother trying to read from process memory.
if (!(flags & PROT_READ)) {
return nullptr;
}
return new MemoryRange(memory, start, end);
return new MemoryRange(process_memory, start, end);
}
Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
if (elf) {
return elf;
}
elf = new Elf(CreateMemory(pid));
elf = new Elf(CreateMemory(process_memory));
if (elf->Init() && init_gnu_debugdata) {
elf->InitGnuDebugdata();
}

View file

@ -52,6 +52,13 @@ bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
return false;
}
std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
if (pid == getpid()) {
return std::shared_ptr<Memory>(new MemoryLocal());
}
return std::shared_ptr<Memory>(new MemoryRemote(pid));
}
bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
uint64_t last_read_byte;
if (__builtin_add_overflow(size, addr, &last_read_byte)) {
@ -249,7 +256,7 @@ bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
return true;
}
MemoryRange::MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
: memory_(memory), begin_(begin), length_(end - begin) {
CHECK(end > begin);
}

View file

@ -40,10 +40,13 @@ struct MapInfo {
// instead of a portion of the file.
uint64_t elf_offset;
Memory* GetFileMemory();
Memory* CreateMemory(pid_t pid);
// This function guarantees it will never return nullptr.
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
private:
Memory* GetFileMemory();
Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
};
} // namespace unwindstack

View file

@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <vector>
@ -31,6 +32,8 @@ class Memory {
Memory() = default;
virtual ~Memory() = default;
static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
@ -125,13 +128,13 @@ class MemoryLocal : public Memory {
class MemoryRange : public Memory {
public:
MemoryRange(Memory* memory, uint64_t begin, uint64_t end);
virtual ~MemoryRange() { delete memory_; }
MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end);
virtual ~MemoryRange() = default;
bool Read(uint64_t addr, void* dst, size_t size) override;
private:
Memory* memory_;
std::shared_ptr<Memory> memory_;
uint64_t begin_;
uint64_t length_;
};

View file

@ -34,6 +34,8 @@
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
#include "MemoryFake.h"
namespace unwindstack {
class MapInfoCreateMemoryTest : public ::testing::Test {
@ -71,6 +73,14 @@ class MapInfoCreateMemoryTest : public ::testing::Test {
InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
}
void SetUp() override {
memory_ = new MemoryFake;
process_memory_.reset(memory_);
}
MemoryFake* memory_;
std::shared_ptr<Memory> process_memory_;
static TemporaryFile elf_;
static TemporaryFile elf_at_100_;
@ -86,17 +96,16 @@ TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
std::unique_ptr<Memory> memory;
memory.reset(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
info.end = 0xff;
memory.reset(info.CreateMemory(getpid()));
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
// Make sure this test is valid.
info.end = 0x101;
memory.reset(info.CreateMemory(getpid()));
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
}
@ -105,7 +114,7 @@ TEST_F(MapInfoCreateMemoryTest, end_le_start) {
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
@ -126,7 +135,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -149,7 +158,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -165,7 +174,7 @@ TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_e
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@ -187,81 +196,38 @@ TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
info.start = reinterpret_cast<uint64_t>(buffer.data());
info.end = info.start + buffer.size();
info.offset = 0;
std::unique_ptr<Memory> memory;
info.flags = 0x8000;
info.name = "/dev/something";
memory.reset(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, local_memory) {
// Set up some memory for a valid local memory object.
TEST_F(MapInfoCreateMemoryTest, process_memory) {
MapInfo info;
info.start = 0x2000;
info.end = 0x3000;
info.offset = 0;
// Verify that the the process_memory object is used, so seed it
// with memory.
std::vector<uint8_t> buffer(1024);
for (size_t i = 0; i < buffer.size(); i++) {
buffer[i] = i % 256;
}
memory_->SetMemory(info.start, buffer.data(), buffer.size());
MapInfo info;
info.start = reinterpret_cast<uint64_t>(buffer.data());
info.end = info.start + buffer.size();
info.offset = 0;
std::unique_ptr<Memory> memory;
memory.reset(info.CreateMemory(getpid()));
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
std::vector<uint8_t> read_buffer(1024);
ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
for (size_t i = 0; i < read_buffer.size(); i++) {
ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size()));
for (size_t i = 0; i < buffer.size(); i++) {
ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
}
TEST_F(MapInfoCreateMemoryTest, remote_memory) {
std::vector<uint8_t> buffer(1024);
memset(buffer.data(), 0xa, buffer.size());
pid_t pid;
if ((pid = fork()) == 0) {
while (true)
;
exit(1);
}
ASSERT_LT(0, pid);
ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
uint64_t iterations = 0;
siginfo_t si;
while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
usleep(30);
iterations++;
ASSERT_LT(iterations, 500000000ULL);
}
MapInfo info;
info.start = reinterpret_cast<uint64_t>(buffer.data());
info.end = info.start + buffer.size();
info.offset = 0;
std::unique_ptr<Memory> memory;
memory.reset(info.CreateMemory(pid));
ASSERT_TRUE(memory.get() != nullptr);
// Set the local memory to a different value to guarantee we are reading
// from the remote process.
memset(buffer.data(), 0x1, buffer.size());
std::vector<uint8_t> read_buffer(1024);
ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
for (size_t i = 0; i < read_buffer.size(); i++) {
ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
}
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
kill(pid, SIGKILL);
ASSERT_EQ(pid, wait(nullptr));
// Try to read outside of the map size.
ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1));
}
} // namespace unwindstack

View file

@ -32,43 +32,57 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include "ElfTestUtils.h"
#include "MemoryFake.h"
namespace unwindstack {
class MapInfoGetElfTest : public ::testing::Test {
protected:
void SetUp() override {
map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(MAP_FAILED, map_);
uint64_t start = reinterpret_cast<uint64_t>(map_);
info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
memory_ = new MemoryFake;
process_memory_.reset(memory_);
}
void TearDown() override { munmap(map_, kMapSize); }
template <typename Ehdr, typename Shdr>
static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
memset(ehdr, 0, sizeof(*ehdr));
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
ehdr->e_ident[EI_CLASS] = class_type;
ehdr->e_machine = machine_type;
ehdr->e_shoff = sh_offset;
ehdr->e_shentsize = sizeof(Shdr) + 100;
ehdr->e_shnum = 4;
}
const size_t kMapSize = 4096;
void* map_ = nullptr;
std::unique_ptr<MapInfo> info_;
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
TemporaryFile elf_;
};
TEST_F(MapInfoGetElfTest, invalid) {
MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
// The map is empty, but this should still create an invalid elf object.
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid32) {
MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memcpy(map_, &ehdr, sizeof(ehdr));
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@ -76,11 +90,13 @@ TEST_F(MapInfoGetElfTest, valid32) {
}
TEST_F(MapInfoGetElfTest, valid64) {
MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
memcpy(map_, &ehdr, sizeof(ehdr));
memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@ -88,12 +104,14 @@ TEST_F(MapInfoGetElfTest, valid64) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
});
MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x4000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@ -102,12 +120,14 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
});
MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x6000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@ -116,12 +136,14 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
});
MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x2000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@ -130,12 +152,14 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
});
MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x5000 + offset, ptr, size);
});
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@ -143,4 +167,195 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
}
TEST_F(MapInfoGetElfTest, end_le_start) {
MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_FALSE(elf->valid());
info.elf = nullptr;
info.end = 0xfff;
elf.reset(info.GetElf(process_memory_, false));
ASSERT_FALSE(elf->valid());
// Make sure this test is valid.
info.elf = nullptr;
info.end = 0x2000;
elf.reset(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
}
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
MapInfo info{
.start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memcpy(buffer.data(), &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
// Read the entire file.
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size()));
ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1));
}
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{
.start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
// Read the valid part of the file.
ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1));
}
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used. Further verify that if the
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
MapInfo info{
.start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
ehdr.e_shnum = 4;
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
// Verify the memory is a valid elf.
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
// Read past the end of what would normally be the size of the map.
ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
MapInfo info{
.start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
ehdr.e_shnum = 4;
memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
// Verify the memory is a valid elf.
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
// Read past the end of what would normally be the size of the map.
ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
}
TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
// Create valid elf data in process memory only.
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
ehdr.e_shnum = 4;
memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_FALSE(elf->valid());
info.elf = nullptr;
info.flags = PROT_READ;
elf.reset(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
MapInfo info{.start = 0x7000,
.end = 0x8000,
.offset = 0x1000,
.flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
.name = "/dev/something"};
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
ehdr.e_shnum = 4;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_FALSE(elf->valid());
// Set the name to nothing to verify that it still fails.
info.elf = nullptr;
info.name = "";
elf.reset(info.GetElf(process_memory_, false));
ASSERT_FALSE(elf->valid());
// Change the flags and verify the elf is valid now.
info.elf = nullptr;
info.flags = PROT_READ;
elf.reset(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf->valid());
}
} // namespace unwindstack

View file

@ -31,10 +31,11 @@ namespace unwindstack {
TEST(MemoryRangeTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
MemoryFake* memory = new MemoryFake;
memory->SetMemory(9001, src);
MemoryFake* memory_fake = new MemoryFake;
std::shared_ptr<Memory> process_memory(memory_fake);
memory_fake->SetMemory(9001, src);
MemoryRange range(memory, 9001, 9001 + src.size());
MemoryRange range(process_memory, 9001, 9001 + src.size());
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@ -46,10 +47,11 @@ TEST(MemoryRangeTest, read) {
TEST(MemoryRangeTest, read_near_limit) {
std::vector<uint8_t> src(4096);
memset(src.data(), 0x4c, 4096);
MemoryFake* memory = new MemoryFake;
memory->SetMemory(1000, src);
MemoryFake* memory_fake = new MemoryFake;
std::shared_ptr<Memory> process_memory(memory_fake);
memory_fake->SetMemory(1000, src);
MemoryRange range(memory, 1000, 2024);
MemoryRange range(process_memory, 1000, 2024);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@ -69,7 +71,8 @@ TEST(MemoryRangeTest, read_near_limit) {
TEST(MemoryRangeTest, read_overflow) {
std::vector<uint8_t> buffer(100);
std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200));
ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}

View file

@ -85,10 +85,11 @@ static std::string ErrorMsg(const std::vector<const char*>& function_names, size
function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
}
static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
std::vector<const char*>& function_names) {
size_t function_name_index = 0;
auto process_memory = Memory::CreateProcessMemory(pid);
std::stringstream unwind_stream;
unwind_stream << std::hex;
for (size_t frame_num = 0; frame_num < 64; frame_num++) {
@ -96,7 +97,7 @@ static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
MapInfo* map_info = maps->Find(regs->pc());
ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
Elf* elf = map_info->GetElf(pid, true);
Elf* elf = map_info->GetElf(process_memory, true);
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
uint64_t adjusted_rel_pc = rel_pc;
if (frame_num != 0) {
@ -122,7 +123,7 @@ static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
unwind_stream << " " << name;
}
unwind_stream << "\n";
ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get()))
<< ErrorMsg(function_names, function_name_index, unwind_stream);
}
ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
@ -137,9 +138,8 @@ extern "C" void InnerFunction(bool local) {
ASSERT_TRUE(maps.Parse());
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
RegsGetLocal(regs.get());
MemoryLocal memory;
VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder);
VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
} else {
g_ready_for_remote = true;
g_ready = true;
@ -205,11 +205,10 @@ TEST(UnwindTest, remote) {
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
MemoryRemote memory(pid);
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
ASSERT_TRUE(regs.get() != nullptr);
VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder);
VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
@ -254,9 +253,8 @@ TEST(UnwindTest, from_context) {
LocalMaps maps;
ASSERT_TRUE(maps.Parse());
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentMachineType(), ucontext));
MemoryLocal memory;
VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder);
VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
@ -291,11 +289,10 @@ static void RemoteThroughSignal(unsigned int sa_flags) {
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
MemoryRemote memory(pid);
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
ASSERT_TRUE(regs.get() != nullptr);
VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder);
VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));

View file

@ -91,7 +91,7 @@ void DoUnwind(pid_t pid) {
}
printf("\n");
unwindstack::MemoryRemote remote_memory(pid);
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
for (size_t frame_num = 0; frame_num < 64; frame_num++) {
if (regs->pc() == 0) {
break;
@ -102,7 +102,7 @@ void DoUnwind(pid_t pid) {
break;
}
unwindstack::Elf* elf = map_info->GetElf(pid, true);
unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
uint64_t adjusted_rel_pc = rel_pc;
@ -135,7 +135,7 @@ void DoUnwind(pid_t pid) {
}
printf("\n");
if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) {
if (!elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get())) {
break;
}
}