diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index 3b2f38e08..61812abb5 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -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(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(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* frames, - size_t num_ignore_frames) { - unwindstack::Maps* maps = reinterpret_cast(back_map)->stack_maps(); +static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, + std::vector* frames, size_t num_ignore_frames) { + UnwindStackMap* stack_map = reinterpret_cast(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) { diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h index 32d1f51ab..be9ef6313 100644 --- a/libbacktrace/UnwindStack.h +++ b/libbacktrace/UnwindStack.h @@ -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 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 memory_; }; #endif // _LIBBACKTRACE_UNWIND_STACK_H diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index ba9fd87d7..d4a2444dc 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -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(); } diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h index 7885b7428..b93b3403c 100644 --- a/libbacktrace/UnwindStackMap.h +++ b/libbacktrace/UnwindStackMap.h @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -34,8 +36,11 @@ class UnwindStackMap : public BacktraceMap { unwindstack::Maps* stack_maps() { return stack_maps_.get(); } + const std::shared_ptr& process_memory() { return process_memory_; } + protected: std::unique_ptr stack_maps_; + std::shared_ptr process_memory_; }; #endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index b971a9eda..78f674c9e 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -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", diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 32722154a..96f2cb42b 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include @@ -76,40 +77,39 @@ Memory* MapInfo::GetFileMemory() { return memory.release(); } -Memory* MapInfo::CreateMemory(pid_t pid) { +Memory* MapInfo::CreateMemory(const std::shared_ptr& 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& 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(); } diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 8c36055c4..32753df6f 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -52,6 +52,13 @@ bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) { return false; } +std::shared_ptr Memory::CreateProcessMemory(pid_t pid) { + if (pid == getpid()) { + return std::shared_ptr(new MemoryLocal()); + } + return std::shared_ptr(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, uint64_t begin, uint64_t end) : memory_(memory), begin_(begin), length_(end - begin) { CHECK(end > begin); } diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index b8ba92576..f108766cf 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h @@ -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& process_memory, bool init_gnu_debugdata = false); + + private: + Memory* GetFileMemory(); + + Memory* CreateMemory(const std::shared_ptr& process_memory); }; } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h index 0c0526616..183b8993a 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -31,6 +32,8 @@ class Memory { Memory() = default; virtual ~Memory() = default; + static std::shared_ptr 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, 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_; uint64_t begin_; uint64_t length_; }; diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp index 2aab9c652..d2aad49fa 100644 --- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -34,6 +34,8 @@ #include #include +#include "MemoryFake.h" + namespace unwindstack { class MapInfoCreateMemoryTest : public ::testing::Test { @@ -71,6 +73,14 @@ class MapInfoCreateMemoryTest : public ::testing::Test { InitElf(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64); } + void SetUp() override { + memory_ = new MemoryFake; + process_memory_.reset(memory_); + } + + MemoryFake* memory_; + std::shared_ptr 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.reset(info.CreateMemory(getpid())); + std::unique_ptr 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(info.CreateMemory(getpid())); + std::unique_ptr 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(info.CreateMemory(getpid())); + std::unique_ptr 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(info.CreateMemory(getpid())); + std::unique_ptr 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(info.CreateMemory(getpid())); + std::unique_ptr 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(buffer.data()); info.end = info.start + buffer.size(); info.offset = 0; - std::unique_ptr memory; info.flags = 0x8000; info.name = "/dev/something"; - memory.reset(info.CreateMemory(getpid())); + std::unique_ptr 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 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(buffer.data()); - info.end = info.start + buffer.size(); - info.offset = 0; - - std::unique_ptr memory; - memory.reset(info.CreateMemory(getpid())); + std::unique_ptr memory(info.CreateMemory(process_memory_)); ASSERT_TRUE(memory.get() != nullptr); - std::vector 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 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(buffer.data()); - info.end = info.start + buffer.size(); - info.offset = 0; - - std::unique_ptr 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 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 diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp index abfa172aa..0b70d1337 100644 --- a/libunwindstack/tests/MapInfoGetElfTest.cpp +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp @@ -32,43 +32,57 @@ #include #include +#include #include #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(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 + 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 info_; + std::shared_ptr 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(info_->GetElf(getpid(), false)); + std::unique_ptr 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(&ehdr, ELFCLASS32, EM_ARM); - memcpy(map_, &ehdr, sizeof(ehdr)); + memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr)); - std::unique_ptr elf(info_->GetElf(getpid(), false)); + std::unique_ptr elf(info.GetElf(process_memory_, false)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(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(&ehdr, ELFCLASS64, EM_AARCH64); - memcpy(map_, &ehdr, sizeof(ehdr)); + memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr)); - std::unique_ptr elf(info_->GetElf(getpid(), false)); + std::unique_ptr elf(info.GetElf(process_memory_, false)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(EM_AARCH64), elf->machine_type()); @@ -88,12 +104,14 @@ TEST_F(MapInfoGetElfTest, valid64) { } TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) { - TestInitGnuDebugdata( - ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) { - memcpy(&reinterpret_cast(map_)[offset], ptr, size); - }); + MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""}; - std::unique_ptr elf(info_->GetElf(getpid(), false)); + TestInitGnuDebugdata(ELFCLASS32, EM_ARM, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(0x4000 + offset, ptr, size); + }); + + std::unique_ptr elf(info.GetElf(process_memory_, false)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(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( - ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) { - memcpy(&reinterpret_cast(map_)[offset], ptr, size); - }); + MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""}; - std::unique_ptr elf(info_->GetElf(getpid(), false)); + TestInitGnuDebugdata(ELFCLASS64, EM_AARCH64, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(0x6000 + offset, ptr, size); + }); + + std::unique_ptr elf(info.GetElf(process_memory_, false)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(EM_AARCH64), elf->machine_type()); @@ -116,12 +136,14 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) { } TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) { - TestInitGnuDebugdata( - ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) { - memcpy(&reinterpret_cast(map_)[offset], ptr, size); - }); + MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""}; - std::unique_ptr elf(info_->GetElf(getpid(), true)); + TestInitGnuDebugdata(ELFCLASS32, EM_ARM, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(0x2000 + offset, ptr, size); + }); + + std::unique_ptr elf(info.GetElf(process_memory_, true)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(EM_ARM), elf->machine_type()); @@ -130,12 +152,14 @@ TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) { } TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) { - TestInitGnuDebugdata( - ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) { - memcpy(&reinterpret_cast(map_)[offset], ptr, size); - }); + MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""}; - std::unique_ptr elf(info_->GetElf(getpid(), true)); + TestInitGnuDebugdata(ELFCLASS64, EM_AARCH64, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(0x5000 + offset, ptr, size); + }); + + std::unique_ptr elf(info.GetElf(process_memory_, true)); ASSERT_TRUE(elf.get() != nullptr); ASSERT_TRUE(elf->valid()); EXPECT_EQ(static_cast(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(&ehdr, ELFCLASS32, EM_ARM); + ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr))); + + std::unique_ptr 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 buffer(0x1000); + memset(buffer.data(), 0, buffer.size()); + Elf32_Ehdr ehdr; + TestInitEhdr(&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(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 buffer(0x4000); + memset(buffer.data(), 0, buffer.size()); + Elf32_Ehdr ehdr; + TestInitEhdr(&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(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 buffer(0x4000); + memset(buffer.data(), 0, buffer.size()); + Elf32_Ehdr ehdr; + TestInitEhdr(&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(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 buffer(0x4000); + memset(buffer.data(), 0, buffer.size()); + Elf64_Ehdr ehdr; + TestInitEhdr(&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(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(&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(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(&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(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 diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp index 6d1366c84..680fae9c1 100644 --- a/libunwindstack/tests/MemoryRangeTest.cpp +++ b/libunwindstack/tests/MemoryRangeTest.cpp @@ -31,10 +31,11 @@ namespace unwindstack { TEST(MemoryRangeTest, read) { std::vector src(1024); memset(src.data(), 0x4c, 1024); - MemoryFake* memory = new MemoryFake; - memory->SetMemory(9001, src); + MemoryFake* memory_fake = new MemoryFake; + std::shared_ptr 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 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 src(4096); memset(src.data(), 0x4c, 4096); - MemoryFake* memory = new MemoryFake; - memory->SetMemory(1000, src); + MemoryFake* memory_fake = new MemoryFake; + std::shared_ptr process_memory(memory_fake); + memory_fake->SetMemory(1000, src); - MemoryRange range(memory, 1000, 2024); + MemoryRange range(process_memory, 1000, 2024); std::vector 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 buffer(100); - std::unique_ptr overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200)); + std::shared_ptr process_memory(new MemoryFakeAlwaysReadZero); + std::unique_ptr overflow(new MemoryRange(process_memory, 100, 200)); ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100)); } diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 2fc3a3880..a0c45bdf3 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -85,10 +85,11 @@ static std::string ErrorMsg(const std::vector& 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& 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::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::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::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::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)); diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index c1077f8b6..3614198eb 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -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; } }