Merge "Add a method to share the process memory object."

This commit is contained in:
Christopher Ferris 2017-09-07 00:01:10 +00:00 committed by Gerrit Code Review
commit 18149b6764
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

@ -61,6 +61,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"],
@ -101,7 +108,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;
}
}