From 3958f8060ac0adccd977c0fab7a53d45f3fce58d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 1 Feb 2017 15:44:40 -0800 Subject: [PATCH] Elf interface for new unwinder. This cl includes the code to read arm unwind information from a shared library. Bug: 23762183 Test: Passes all unit tests. I can dump the arm unwind information Test: for an arm shared library. Change-Id: I43501ea2eab843b81de8bd5128401dd1971af8d3 --- libunwindstack/Android.bp | 55 +- libunwindstack/ArmExidx.cpp | 12 +- libunwindstack/ArmExidx.h | 15 +- libunwindstack/Elf.cpp | 113 ++++ libunwindstack/Elf.h | 78 +++ libunwindstack/ElfInterface.cpp | 223 ++++++++ libunwindstack/ElfInterface.h | 152 +++++ libunwindstack/ElfInterfaceArm.cpp | 127 ++++ libunwindstack/ElfInterfaceArm.h | 90 +++ libunwindstack/Machine.h | 2 - libunwindstack/MapInfo.h | 44 ++ libunwindstack/Memory.cpp | 38 +- libunwindstack/Memory.h | 26 +- libunwindstack/Regs.cpp | 237 ++++++++ libunwindstack/Regs.h | 98 +++- libunwindstack/User.h | 96 ++++ libunwindstack/tests/ArmExidxDecodeTest.cpp | 204 +++++-- libunwindstack/tests/ArmExidxExtractTest.cpp | 150 ++--- libunwindstack/tests/ElfInterfaceArmTest.cpp | 372 ++++++++++++ libunwindstack/tests/ElfInterfaceTest.cpp | 573 +++++++++++++++++++ libunwindstack/tests/ElfTest.cpp | 210 +++++++ libunwindstack/tests/MapsTest.cpp | 219 ------- libunwindstack/tests/MemoryFake.h | 14 +- libunwindstack/tests/MemoryFileTest.cpp | 113 +++- libunwindstack/tests/MemoryLocalTest.cpp | 15 +- libunwindstack/tests/MemoryRangeTest.cpp | 2 - libunwindstack/tests/MemoryRemoteTest.cpp | 6 - libunwindstack/tests/RegsTest.cpp | 249 +++++++- libunwindstack/unwind_info.cpp | 121 ++++ 29 files changed, 3210 insertions(+), 444 deletions(-) create mode 100644 libunwindstack/Elf.cpp create mode 100644 libunwindstack/Elf.h create mode 100644 libunwindstack/ElfInterface.cpp create mode 100644 libunwindstack/ElfInterface.h create mode 100644 libunwindstack/ElfInterfaceArm.cpp create mode 100644 libunwindstack/ElfInterfaceArm.h create mode 100644 libunwindstack/MapInfo.h create mode 100644 libunwindstack/Regs.cpp create mode 100644 libunwindstack/User.h create mode 100644 libunwindstack/tests/ElfInterfaceArmTest.cpp create mode 100644 libunwindstack/tests/ElfInterfaceTest.cpp create mode 100644 libunwindstack/tests/ElfTest.cpp delete mode 100644 libunwindstack/tests/MapsTest.cpp create mode 100644 libunwindstack/unwind_info.cpp diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 9bb13046e..ece623b07 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -30,6 +30,15 @@ cc_defaults { enabled: false, }, }, + + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, } cc_defaults { @@ -38,8 +47,12 @@ cc_defaults { srcs: [ "ArmExidx.cpp", - "Memory.cpp", + "Elf.cpp", + "ElfInterface.cpp", + "ElfInterfaceArm.cpp", "Log.cpp", + "Regs.cpp", + "Memory.cpp", ], shared_libs: [ @@ -74,6 +87,9 @@ cc_defaults { srcs: [ "tests/ArmExidxDecodeTest.cpp", "tests/ArmExidxExtractTest.cpp", + "tests/ElfInterfaceArmTest.cpp", + "tests/ElfInterfaceTest.cpp", + "tests/ElfTest.cpp", "tests/LogFake.cpp", "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", @@ -93,15 +109,6 @@ cc_defaults { "liblog", ], - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, - target: { linux: { host_ldlibs: [ @@ -130,3 +137,31 @@ cc_test { "libunwindstack_debug", ], } + +//------------------------------------------------------------------------- +// Utility Executables +//------------------------------------------------------------------------- +cc_defaults { + name: "libunwindstack_executables", + defaults: ["libunwindstack_flags"], + + shared_libs: [ + "libunwindstack", + "libbase", + ], + + static_libs: [ + "liblog", + ], + + compile_multilib: "both", +} + +cc_binary { + name: "unwind_info", + defaults: ["libunwindstack_executables"], + + srcs: [ + "unwind_info.cpp", + ], +} diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp index 3b78918c0..12adf57dc 100644 --- a/libunwindstack/ArmExidx.cpp +++ b/libunwindstack/ArmExidx.cpp @@ -25,6 +25,8 @@ #include "ArmExidx.h" #include "Log.h" #include "Machine.h" +#include "Memory.h" +#include "Regs.h" void ArmExidx::LogRawData() { std::string log_str("Raw Data:"); @@ -216,10 +218,16 @@ inline bool ArmExidx::DecodePrefix_10_00(uint8_t byte) { cfa_ += 4; } } + // If the sp register is modified, change the cfa value. if (registers & (1 << ARM_REG_SP)) { cfa_ = (*regs_)[ARM_REG_SP]; } + + // Indicate if the pc register was set. + if (registers & (1 << ARM_REG_PC)) { + pc_set_ = true; + } return true; } @@ -296,9 +304,6 @@ inline bool ArmExidx::DecodePrefix_10_11_0000() { return false; } } - if (!(*regs_)[ARM_REG_PC]) { - (*regs_)[ARM_REG_PC] = (*regs_)[ARM_REG_LR]; - } status_ = ARM_STATUS_FINISH; return false; } @@ -675,6 +680,7 @@ bool ArmExidx::Decode() { } bool ArmExidx::Eval() { + pc_set_ = false; while (Decode()); return status_ == ARM_STATUS_FINISH; } diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h index a92caef39..8c7f15a5b 100644 --- a/libunwindstack/ArmExidx.h +++ b/libunwindstack/ArmExidx.h @@ -21,8 +21,9 @@ #include -#include "Memory.h" -#include "Regs.h" +// Forward declarations. +class Memory; +class RegsArm; enum ArmStatus : size_t { ARM_STATUS_NONE = 0, @@ -43,7 +44,7 @@ enum ArmOp : uint8_t { class ArmExidx { public: - ArmExidx(Regs32* regs, Memory* elf_memory, Memory* process_memory) + ArmExidx(RegsArm* regs, Memory* elf_memory, Memory* process_memory) : regs_(regs), elf_memory_(elf_memory), process_memory_(process_memory) {} virtual ~ArmExidx() {} @@ -59,11 +60,14 @@ class ArmExidx { ArmStatus status() { return status_; } - Regs32* regs() { return regs_; } + RegsArm* regs() { return regs_; } uint32_t cfa() { return cfa_; } void set_cfa(uint32_t cfa) { cfa_ = cfa; } + bool pc_set() { return pc_set_; } + void set_pc_set(bool pc_set) { pc_set_ = pc_set; } + void set_log(bool log) { log_ = log; } void set_log_skip_execution(bool skip_execution) { log_skip_execution_ = skip_execution; } void set_log_indent(uint8_t indent) { log_indent_ = indent; } @@ -87,7 +91,7 @@ class ArmExidx { bool DecodePrefix_11_010(uint8_t byte); bool DecodePrefix_11(uint8_t byte); - Regs32* regs_ = nullptr; + RegsArm* regs_ = nullptr; uint32_t cfa_ = 0; std::deque data_; ArmStatus status_ = ARM_STATUS_NONE; @@ -98,6 +102,7 @@ class ArmExidx { bool log_ = false; uint8_t log_indent_ = 0; bool log_skip_execution_ = false; + bool pc_set_ = false; }; #endif // _LIBUNWINDSTACK_ARM_EXIDX_H diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp new file mode 100644 index 000000000..272b5f029 --- /dev/null +++ b/libunwindstack/Elf.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#define LOG_TAG "unwind" +#include + +#include "Elf.h" +#include "ElfInterface.h" +#include "ElfInterfaceArm.h" +#include "Machine.h" +#include "Memory.h" +#include "Regs.h" + +bool Elf::Init() { + if (!memory_) { + return false; + } + + interface_.reset(CreateInterfaceFromMemory(memory_.get())); + if (!interface_) { + return false; + } + + valid_ = interface_->Init(); + if (valid_) { + interface_->InitHeaders(); + } else { + interface_.reset(nullptr); + } + return valid_; +} + +bool Elf::IsValidElf(Memory* memory) { + if (memory == nullptr) { + return false; + } + + // Verify that this is a valid elf file. + uint8_t e_ident[SELFMAG + 1]; + if (!memory->Read(0, e_ident, SELFMAG)) { + return false; + } + + if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { + return false; + } + return true; +} + +ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) { + if (!IsValidElf(memory)) { + return nullptr; + } + + std::unique_ptr interface; + if (!memory->Read(EI_CLASS, &class_type_, 1)) { + return nullptr; + } + if (class_type_ == ELFCLASS32) { + Elf32_Half e_machine; + if (!memory->Read(EI_NIDENT + sizeof(Elf32_Half), &e_machine, sizeof(e_machine))) { + return nullptr; + } + + if (e_machine != EM_ARM && e_machine != EM_386) { + // Unsupported. + ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine); + return nullptr; + } + + machine_type_ = e_machine; + if (e_machine == EM_ARM) { + interface.reset(new ElfInterfaceArm(memory)); + } else { + interface.reset(new ElfInterface32(memory)); + } + } else if (class_type_ == ELFCLASS64) { + Elf64_Half e_machine; + if (!memory->Read(EI_NIDENT + sizeof(Elf64_Half), &e_machine, sizeof(e_machine))) { + return nullptr; + } + + if (e_machine != EM_AARCH64 && e_machine != EM_X86_64) { + // Unsupported. + ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine); + return nullptr; + } + + machine_type_ = e_machine; + interface.reset(new ElfInterface64(memory)); + } + + return interface.release(); +} diff --git a/libunwindstack/Elf.h b/libunwindstack/Elf.h new file mode 100644 index 000000000..7bf45b842 --- /dev/null +++ b/libunwindstack/Elf.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_ELF_H +#define _LIBUNWINDSTACK_ELF_H + +#include + +#include +#include + +#include "ElfInterface.h" +#include "Memory.h" + +#if !defined(EM_AARCH64) +#define EM_AARCH64 183 +#endif + +// Forward declaration. +class Regs; + +class Elf { + public: + Elf(Memory* memory) : memory_(memory) {} + virtual ~Elf() = default; + + bool Init(); + + void InitGnuDebugdata(); + + bool GetSoname(std::string* name) { + return valid_ && interface_->GetSoname(name); + } + + bool GetFunctionName(uint64_t, std::string*, uint64_t*) { + return false; + } + + bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) { + return valid_ && interface_->Step(rel_pc, regs, process_memory); + } + + ElfInterface* CreateInterfaceFromMemory(Memory* memory); + + bool valid() { return valid_; } + + uint32_t machine_type() { return machine_type_; } + + uint8_t class_type() { return class_type_; } + + Memory* memory() { return memory_.get(); } + + ElfInterface* interface() { return interface_.get(); } + + static bool IsValidElf(Memory* memory); + + protected: + bool valid_ = false; + std::unique_ptr interface_; + std::unique_ptr memory_; + uint32_t machine_type_; + uint8_t class_type_; +}; + +#endif // _LIBUNWINDSTACK_ELF_H diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp new file mode 100644 index 000000000..d59e9d8e3 --- /dev/null +++ b/libunwindstack/ElfInterface.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include "ElfInterface.h" +#include "Memory.h" +#include "Regs.h" + +template +bool ElfInterface::ReadAllHeaders() { + EhdrType ehdr; + if (!memory_->Read(0, &ehdr, sizeof(ehdr))) { + return false; + } + + if (!ReadProgramHeaders(ehdr)) { + return false; + } + return ReadSectionHeaders(ehdr); +} + +template +bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) { + uint64_t offset = ehdr.e_phoff; + for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) { + PhdrType phdr; + if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) { + return false; + } + + if (HandleType(offset, phdr.p_type)) { + continue; + } + + switch (phdr.p_type) { + case PT_LOAD: + { + // Get the flags first, if this isn't an executable header, ignore it. + if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) { + return false; + } + if ((phdr.p_flags & PF_X) == 0) { + continue; + } + + if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { + return false; + } + if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { + return false; + } + if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { + return false; + } + pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr, + static_cast(phdr.p_memsz)}; + if (phdr.p_offset == 0) { + load_bias_ = phdr.p_vaddr; + } + break; + } + + case PT_GNU_EH_FRAME: + if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { + return false; + } + eh_frame_offset_ = phdr.p_offset; + if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { + return false; + } + eh_frame_size_ = phdr.p_memsz; + break; + + case PT_DYNAMIC: + if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) { + return false; + } + dynamic_offset_ = phdr.p_offset; + if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { + return false; + } + dynamic_size_ = phdr.p_memsz; + break; + } + } + return true; +} + +template +bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { + uint64_t offset = ehdr.e_shoff; + uint64_t sec_offset = 0; + uint64_t sec_size = 0; + + // Get the location of the section header names. + // If something is malformed in the header table data, we aren't going + // to terminate, we'll simply ignore this part. + ShdrType shdr; + if (ehdr.e_shstrndx < ehdr.e_shnum) { + uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize; + if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) + && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { + sec_offset = shdr.sh_offset; + sec_size = shdr.sh_size; + } + } + + // Skip the first header, it's always going to be NULL. + for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) { + if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) { + return false; + } + + if (shdr.sh_type == SHT_PROGBITS) { + // Look for the .debug_frame and .gnu_debugdata. + if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) { + return false; + } + if (shdr.sh_name < sec_size) { + std::string name; + if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) { + if (name == ".debug_frame") { + if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) + && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { + debug_frame_offset_ = shdr.sh_offset; + debug_frame_size_ = shdr.sh_size; + } + } else if (name == ".gnu_debugdata") { + if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) + && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) { + gnu_debugdata_offset_ = shdr.sh_offset; + gnu_debugdata_size_ = shdr.sh_size; + } + } + } + } + } + } + return true; +} + +template +bool ElfInterface::GetSonameWithTemplate(std::string* soname) { + if (soname_type_ == SONAME_INVALID) { + return false; + } + if (soname_type_ == SONAME_VALID) { + *soname = soname_; + return true; + } + + soname_type_ = SONAME_INVALID; + + uint64_t soname_offset = 0; + uint64_t strtab_offset = 0; + uint64_t strtab_size = 0; + + // Find the soname location from the dynamic headers section. + DynType dyn; + uint64_t offset = dynamic_offset_; + uint64_t max_offset = offset + dynamic_size_; + for (uint64_t offset = dynamic_offset_; offset < max_offset; offset += sizeof(DynType)) { + if (!memory_->Read(offset, &dyn, sizeof(dyn))) { + return false; + } + + if (dyn.d_tag == DT_STRTAB) { + strtab_offset = dyn.d_un.d_ptr; + } else if (dyn.d_tag == DT_STRSZ) { + strtab_size = dyn.d_un.d_val; + } else if (dyn.d_tag == DT_SONAME) { + soname_offset = dyn.d_un.d_val; + } else if (dyn.d_tag == DT_NULL) { + break; + } + } + + soname_offset += strtab_offset; + if (soname_offset >= strtab_offset + strtab_size) { + return false; + } + if (!memory_->ReadString(soname_offset, &soname_)) { + return false; + } + soname_type_ = SONAME_VALID; + *soname = soname_; + return true; +} + +bool ElfInterface::Step(uint64_t, Regs*, Memory*) { + return false; +} + +// Instantiate all of the needed template functions. +template bool ElfInterface::ReadAllHeaders(); +template bool ElfInterface::ReadAllHeaders(); + +template bool ElfInterface::ReadProgramHeaders(const Elf32_Ehdr&); +template bool ElfInterface::ReadProgramHeaders(const Elf64_Ehdr&); + +template bool ElfInterface::ReadSectionHeaders(const Elf32_Ehdr&); +template bool ElfInterface::ReadSectionHeaders(const Elf64_Ehdr&); + +template bool ElfInterface::GetSonameWithTemplate(std::string*); +template bool ElfInterface::GetSonameWithTemplate(std::string*); diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h new file mode 100644 index 000000000..944146cdb --- /dev/null +++ b/libunwindstack/ElfInterface.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_H +#define _LIBUNWINDSTACK_ELF_INTERFACE_H + +#include +#include + +#include +#include +#include +#include + +// Forward declarations. +class Memory; +class Regs; + +struct LoadInfo { + uint64_t offset; + uint64_t table_offset; + size_t table_size; +}; + +enum : uint8_t { + SONAME_UNKNOWN = 0, + SONAME_VALID, + SONAME_INVALID, +}; + +class ElfInterface { + public: + ElfInterface(Memory* memory) : memory_(memory) {} + virtual ~ElfInterface() = default; + + virtual bool Init() = 0; + + virtual void InitHeaders() = 0; + + virtual bool GetSoname(std::string* name) = 0; + + virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0; + + virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory); + + Memory* CreateGnuDebugdataMemory(); + + Memory* memory() { return memory_; } + + const std::unordered_map& pt_loads() { return pt_loads_; } + uint64_t load_bias() { return load_bias_; } + void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; } + + uint64_t dynamic_offset() { return dynamic_offset_; } + uint64_t dynamic_size() { return dynamic_size_; } + uint64_t eh_frame_offset() { return eh_frame_offset_; } + uint64_t eh_frame_size() { return eh_frame_size_; } + uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; } + uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; } + + protected: + template + bool ReadAllHeaders(); + + template + bool ReadProgramHeaders(const EhdrType& ehdr); + + template + bool ReadSectionHeaders(const EhdrType& ehdr); + + template + bool GetSonameWithTemplate(std::string* soname); + + virtual bool HandleType(uint64_t, uint32_t) { return false; } + + Memory* memory_; + std::unordered_map pt_loads_; + uint64_t load_bias_ = 0; + + // Stored elf data. + uint64_t dynamic_offset_ = 0; + uint64_t dynamic_size_ = 0; + + uint64_t eh_frame_offset_ = 0; + uint64_t eh_frame_size_ = 0; + + uint64_t debug_frame_offset_ = 0; + uint64_t debug_frame_size_ = 0; + + uint64_t gnu_debugdata_offset_ = 0; + uint64_t gnu_debugdata_size_ = 0; + + uint8_t soname_type_ = SONAME_UNKNOWN; + std::string soname_; +}; + +class ElfInterface32 : public ElfInterface { + public: + ElfInterface32(Memory* memory) : ElfInterface(memory) {} + virtual ~ElfInterface32() = default; + + bool Init() override { + return ElfInterface::ReadAllHeaders(); + } + + void InitHeaders() override { + } + + bool GetSoname(std::string* soname) override { + return ElfInterface::GetSonameWithTemplate(soname); + } + + bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { + return false; + } +}; + +class ElfInterface64 : public ElfInterface { + public: + ElfInterface64(Memory* memory) : ElfInterface(memory) {} + virtual ~ElfInterface64() = default; + + bool Init() override { + return ElfInterface::ReadAllHeaders(); + } + + void InitHeaders() override { + } + + bool GetSoname(std::string* soname) override { + return ElfInterface::GetSonameWithTemplate(soname); + } + + bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { + return false; + } +}; + +#endif // _LIBUNWINDSTACK_ELF_INTERFACE_H diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp new file mode 100644 index 000000000..e15732073 --- /dev/null +++ b/libunwindstack/ElfInterfaceArm.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "ArmExidx.h" +#include "ElfInterface.h" +#include "ElfInterfaceArm.h" +#include "Machine.h" +#include "Memory.h" +#include "Regs.h" + +bool ElfInterfaceArm::FindEntry(uint32_t pc, uint64_t* entry_offset) { + if (start_offset_ == 0 || total_entries_ == 0) { + return false; + } + + // Need to subtract the load_bias from the pc. + if (pc < load_bias_) { + return false; + } + pc -= load_bias_; + + size_t first = 0; + size_t last = total_entries_; + while (first < last) { + size_t current = (first + last) / 2; + uint32_t addr = addrs_[current]; + if (addr == 0) { + if (!GetPrel31Addr(start_offset_ + current * 8, &addr)) { + return false; + } + addrs_[current] = addr; + } + if (pc == addr) { + *entry_offset = start_offset_ + current * 8; + return true; + } + if (pc < addr) { + last = current; + } else { + first = current + 1; + } + } + if (last != 0) { + *entry_offset = start_offset_ + (last - 1) * 8; + return true; + } + return false; +} + +bool ElfInterfaceArm::GetPrel31Addr(uint32_t offset, uint32_t* addr) { + uint32_t data; + if (!memory_->Read32(offset, &data)) { + return false; + } + + // Sign extend the value if necessary. + int32_t value = (static_cast(data) << 1) >> 1; + *addr = offset + value; + return true; +} + +#if !defined(PT_ARM_EXIDX) +#define PT_ARM_EXIDX 0x70000001 +#endif + +bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) { + if (type != PT_ARM_EXIDX) { + return false; + } + + Elf32_Phdr phdr; + if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) { + return true; + } + if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) { + return true; + } + // The load_bias_ should always be set by this time. + start_offset_ = phdr.p_vaddr - load_bias_; + total_entries_ = phdr.p_memsz / 8; + return true; +} + +bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) { + return StepExidx(pc, regs, process_memory) || + ElfInterface32::Step(pc, regs, process_memory); +} + +bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) { + RegsArm* regs_arm = reinterpret_cast(regs); + // First try arm, then try dwarf. + uint64_t entry_offset; + if (!FindEntry(pc, &entry_offset)) { + return false; + } + ArmExidx arm(regs_arm, memory_, process_memory); + arm.set_cfa(regs_arm->sp()); + if (arm.ExtractEntryData(entry_offset) && arm.Eval()) { + // If the pc was not set, then use the LR registers for the PC. + if (!arm.pc_set()) { + regs_arm->set_pc((*regs_arm)[ARM_REG_LR]); + (*regs_arm)[ARM_REG_PC] = regs_arm->pc(); + } else { + regs_arm->set_pc((*regs_arm)[ARM_REG_PC]); + } + regs_arm->set_sp(arm.cfa()); + (*regs_arm)[ARM_REG_SP] = regs_arm->sp(); + return true; + } + return false; +} diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h new file mode 100644 index 000000000..ece694f76 --- /dev/null +++ b/libunwindstack/ElfInterfaceArm.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H +#define _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H + +#include +#include + +#include +#include + +#include "ElfInterface.h" +#include "Memory.h" + +class ElfInterfaceArm : public ElfInterface32 { + public: + ElfInterfaceArm(Memory* memory) : ElfInterface32(memory) {} + virtual ~ElfInterfaceArm() = default; + + class iterator : public std::iterator { + public: + iterator(ElfInterfaceArm* interface, size_t index) : interface_(interface), index_(index) { } + + iterator& operator++() { index_++; return *this; } + iterator& operator++(int increment) { index_ += increment; return *this; } + iterator& operator--() { index_--; return *this; } + iterator& operator--(int decrement) { index_ -= decrement; return *this; } + + bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; } + bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; } + + uint32_t operator*() { + uint32_t addr = interface_->addrs_[index_]; + if (addr == 0) { + if (!interface_->GetPrel31Addr(interface_->start_offset_ + index_ * 8, &addr)) { + return 0; + } + interface_->addrs_[index_] = addr; + } + return addr; + } + + private: + ElfInterfaceArm* interface_ = nullptr; + size_t index_ = 0; + }; + + iterator begin() { return iterator(this, 0); } + iterator end() { return iterator(this, total_entries_); } + + bool GetPrel31Addr(uint32_t offset, uint32_t* addr); + + bool FindEntry(uint32_t pc, uint64_t* entry_offset); + + bool HandleType(uint64_t offset, uint32_t type) override; + + bool Step(uint64_t pc, Regs* regs, Memory* process_memory) override; + + bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory); + + uint64_t start_offset() { return start_offset_; } + + void set_start_offset(uint64_t start_offset) { start_offset_ = start_offset; } + + size_t total_entries() { return total_entries_; } + + void set_total_entries(size_t total_entries) { total_entries_ = total_entries; } + + private: + uint64_t start_offset_ = 0; + size_t total_entries_ = 0; + + std::unordered_map addrs_; +}; + +#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h index db84271dd..323ce8023 100644 --- a/libunwindstack/Machine.h +++ b/libunwindstack/Machine.h @@ -19,8 +19,6 @@ #include -class Regs; - enum ArmReg : uint16_t { ARM_REG_R0 = 0, ARM_REG_R1, diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h new file mode 100644 index 000000000..834290403 --- /dev/null +++ b/libunwindstack/MapInfo.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBUNWINDSTACK_MAP_INFO_H +#define _LIBUNWINDSTACK_MAP_INFO_H + +#include + +#include + +// Forward declarations. +class Elf; +class Memory; + +struct MapInfo { + uint64_t start; + uint64_t end; + uint64_t offset; + uint16_t flags; + std::string name; + Elf* elf = nullptr; + // This value is only non-zero if the offset is non-zero but there is + // no elf signature found at that offset. This indicates that the + // entire file is represented by the Memory object returned by CreateMemory, + // instead of a portion of the file. + uint64_t elf_offset; + + Memory* CreateMemory(pid_t pid); +}; + +#endif // _LIBUNWINDSTACK_MAP_INFO_H diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp index 336e4fe32..1fcf84280 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -48,14 +48,40 @@ bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) { return false; } +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)) { + return false; + } + if (last_read_byte > raw_.size()) { + return false; + } + memcpy(dst, &raw_[addr], size); + return true; +} + +uint8_t* MemoryBuffer::GetPtr(size_t offset) { + if (offset < raw_.size()) { + return &raw_[offset]; + } + return nullptr; +} + MemoryFileAtOffset::~MemoryFileAtOffset() { + Clear(); +} + +void MemoryFileAtOffset::Clear() { if (data_) { munmap(&data_[-offset_], size_ + offset_); data_ = nullptr; } } -bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset) { +bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t size) { + // Clear out any previous data if it exists. + Clear(); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC))); if (fd == -1) { return false; @@ -71,6 +97,10 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset) { offset_ = offset & (getpagesize() - 1); uint64_t aligned_offset = offset & ~(getpagesize() - 1); size_ = buf.st_size - aligned_offset; + if (size < (UINT64_MAX - offset_) && size + offset_ < size_) { + // Truncate the mapped size. + size_ = size + offset_; + } void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset); if (map == MAP_FAILED) { return false; @@ -91,6 +121,12 @@ bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) { } static bool PtraceRead(pid_t pid, uint64_t addr, long* value) { +#if !defined(__LP64__) + // Cannot read an address greater than 32 bits. + if (addr > UINT32_MAX) { + return false; + } +#endif // ptrace() returns -1 and sets errno when the operation fails. // To disambiguate -1 from a valid result, we clear errno beforehand. errno = 0; diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h index 5ab031dd0..c5316a1d0 100644 --- a/libunwindstack/Memory.h +++ b/libunwindstack/Memory.h @@ -22,10 +22,7 @@ #include #include - -#include - -constexpr bool kMemoryStatsEnabled = true; +#include class Memory { public: @@ -50,15 +47,34 @@ class Memory { } }; +class MemoryBuffer : public Memory { + public: + MemoryBuffer() = default; + virtual ~MemoryBuffer() = default; + + bool Read(uint64_t addr, void* dst, size_t size) override; + + uint8_t* GetPtr(size_t offset); + + void Resize(size_t size) { raw_.resize(size); } + + uint64_t Size() { return raw_.size(); } + + private: + std::vector raw_; +}; + class MemoryFileAtOffset : public Memory { public: MemoryFileAtOffset() = default; virtual ~MemoryFileAtOffset(); - bool Init(const std::string& file, uint64_t offset); + bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX); bool Read(uint64_t addr, void* dst, size_t size) override; + void Clear(); + protected: size_t size_ = 0; size_t offset_ = 0; diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp new file mode 100644 index 000000000..adb652224 --- /dev/null +++ b/libunwindstack/Regs.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include "Elf.h" +#include "ElfInterface.h" +#include "Machine.h" +#include "MapInfo.h" +#include "Regs.h" +#include "User.h" + +template +uint64_t RegsTmpl::GetRelPc(Elf* elf, const MapInfo* map_info) { + uint64_t load_bias = 0; + if (elf->valid()) { + load_bias = elf->interface()->load_bias(); + } + + return pc_ - map_info->start + load_bias + map_info->elf_offset; +} + +template +bool RegsTmpl::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) { + switch (return_loc_.type) { + case LOCATION_REGISTER: + assert(return_loc_.value < total_regs_); + *value = regs_[return_loc_.value]; + return true; + case LOCATION_SP_OFFSET: + AddressType return_value; + if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) { + return false; + } + *value = return_value; + return true; + case LOCATION_UNKNOWN: + default: + return false; + } +} + +RegsArm::RegsArm() : RegsTmpl(ARM_REG_LAST, ARM_REG_SP, + Location(LOCATION_REGISTER, ARM_REG_LR)) { +} + +uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + uint64_t load_bias = elf->interface()->load_bias(); + if (rel_pc < load_bias) { + return rel_pc; + } + uint64_t adjusted_rel_pc = rel_pc - load_bias; + + if (adjusted_rel_pc < 5) { + return rel_pc; + } + + if (adjusted_rel_pc & 1) { + // This is a thumb instruction, it could be 2 or 4 bytes. + uint32_t value; + if (rel_pc < 5 || !elf->memory()->Read(adjusted_rel_pc - 5, &value, sizeof(value)) || + (value & 0xe000f000) != 0xe000f000) { + return rel_pc - 2; + } + } + return rel_pc - 4; +} + +RegsArm64::RegsArm64() : RegsTmpl(ARM64_REG_LAST, ARM64_REG_SP, + Location(LOCATION_REGISTER, ARM64_REG_LR)) { +} + +uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + if (rel_pc < 4) { + return rel_pc; + } + return rel_pc - 4; +} + +RegsX86::RegsX86() : RegsTmpl(X86_REG_LAST, X86_REG_SP, + Location(LOCATION_SP_OFFSET, -4)) { +} + +uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + if (rel_pc == 0) { + return 0; + } + return rel_pc - 1; +} + +RegsX86_64::RegsX86_64() : RegsTmpl(X86_64_REG_LAST, X86_64_REG_SP, + Location(LOCATION_SP_OFFSET, -8)) { +} + +uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) { + if (!elf->valid()) { + return rel_pc; + } + + if (rel_pc == 0) { + return 0; + } + + return rel_pc - 1; +} + +static Regs* ReadArm(void* remote_data) { + arm_user_regs* user = reinterpret_cast(remote_data); + + RegsArm* regs = new RegsArm(); + memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t)); + + regs->set_pc(user->regs[ARM_REG_PC]); + regs->set_sp(user->regs[ARM_REG_SP]); + + return regs; +} + +static Regs* ReadArm64(void* remote_data) { + arm64_user_regs* user = reinterpret_cast(remote_data); + + RegsArm64* regs = new RegsArm64(); + memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t)); + regs->set_pc(user->pc); + regs->set_sp(user->sp); + + return regs; +} + +static Regs* ReadX86(void* remote_data) { + x86_user_regs* user = reinterpret_cast(remote_data); + + RegsX86* regs = new RegsX86(); + (*regs)[X86_REG_EAX] = user->eax; + (*regs)[X86_REG_EBX] = user->ebx; + (*regs)[X86_REG_ECX] = user->ecx; + (*regs)[X86_REG_EDX] = user->edx; + (*regs)[X86_REG_EBP] = user->ebp; + (*regs)[X86_REG_EDI] = user->edi; + (*regs)[X86_REG_ESI] = user->esi; + (*regs)[X86_REG_ESP] = user->esp; + (*regs)[X86_REG_EIP] = user->eip; + + regs->set_pc(user->eip); + regs->set_sp(user->esp); + + return regs; +} + +static Regs* ReadX86_64(void* remote_data) { + x86_64_user_regs* user = reinterpret_cast(remote_data); + + RegsX86_64* regs = new RegsX86_64(); + (*regs)[X86_64_REG_RAX] = user->rax; + (*regs)[X86_64_REG_RBX] = user->rbx; + (*regs)[X86_64_REG_RCX] = user->rcx; + (*regs)[X86_64_REG_RDX] = user->rdx; + (*regs)[X86_64_REG_R8] = user->r8; + (*regs)[X86_64_REG_R9] = user->r9; + (*regs)[X86_64_REG_R10] = user->r10; + (*regs)[X86_64_REG_R11] = user->r11; + (*regs)[X86_64_REG_R12] = user->r12; + (*regs)[X86_64_REG_R13] = user->r13; + (*regs)[X86_64_REG_R14] = user->r14; + (*regs)[X86_64_REG_R15] = user->r15; + (*regs)[X86_64_REG_RDI] = user->rdi; + (*regs)[X86_64_REG_RSI] = user->rsi; + (*regs)[X86_64_REG_RBP] = user->rbp; + (*regs)[X86_64_REG_RSP] = user->rsp; + (*regs)[X86_64_REG_RIP] = user->rip; + + regs->set_pc(user->rip); + regs->set_sp(user->rsp); + + return regs; +} + +// This function assumes that reg_data is already aligned to a 64 bit value. +// If not this could crash with an unaligned access. +Regs* Regs::RemoteGet(pid_t pid, uint32_t* machine_type) { + // Make the buffer large enough to contain the largest registers type. + std::vector buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t)); + struct iovec io; + io.iov_base = buffer.data(); + io.iov_len = buffer.size() * sizeof(uint64_t); + + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, reinterpret_cast(&io)) == -1) { + return nullptr; + } + + switch (io.iov_len) { + case sizeof(x86_user_regs): + *machine_type = EM_386; + return ReadX86(buffer.data()); + case sizeof(x86_64_user_regs): + *machine_type = EM_X86_64; + return ReadX86_64(buffer.data()); + case sizeof(arm_user_regs): + *machine_type = EM_ARM; + return ReadArm(buffer.data()); + case sizeof(arm64_user_regs): + *machine_type = EM_AARCH64; + return ReadArm64(buffer.data()); + } + return nullptr; +} diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h index 2766c6f40..718fc8581 100644 --- a/libunwindstack/Regs.h +++ b/libunwindstack/Regs.h @@ -21,57 +21,107 @@ #include +// Forward declarations. +class Elf; +struct MapInfo; + class Regs { public: - Regs(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs) - : pc_reg_(pc_reg), sp_reg_(sp_reg), total_regs_(total_regs) { - } + enum LocationEnum : uint8_t { + LOCATION_UNKNOWN = 0, + LOCATION_REGISTER, + LOCATION_SP_OFFSET, + }; + + struct Location { + Location(LocationEnum type, int16_t value) : type(type), value(value) {} + + LocationEnum type; + int16_t value; + }; + + Regs(uint16_t total_regs, uint16_t sp_reg, const Location& return_loc) + : total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {} virtual ~Regs() = default; - uint16_t pc_reg() { return pc_reg_; } - uint16_t sp_reg() { return sp_reg_; } - uint16_t total_regs() { return total_regs_; } - - virtual void* raw_data() = 0; + virtual void* RawData() = 0; virtual uint64_t pc() = 0; virtual uint64_t sp() = 0; + virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0; + + virtual uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) = 0; + + virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0; + + uint16_t sp_reg() { return sp_reg_; } + uint16_t total_regs() { return total_regs_; } + + static Regs* RemoteGet(pid_t pid, uint32_t* machine_type); + protected: - uint16_t pc_reg_; - uint16_t sp_reg_; uint16_t total_regs_; + uint16_t sp_reg_; + Location return_loc_; }; template class RegsTmpl : public Regs { public: - RegsTmpl(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs) - : Regs(pc_reg, sp_reg, total_regs), regs_(total_regs) {} + RegsTmpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc) + : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {} virtual ~RegsTmpl() = default; - uint64_t pc() override { return regs_[pc_reg_]; } - uint64_t sp() override { return regs_[sp_reg_]; } + uint64_t GetRelPc(Elf* elf, const MapInfo* map_info) override; + + bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override; + + uint64_t pc() override { return pc_; } + uint64_t sp() override { return sp_; } + + void set_pc(AddressType pc) { pc_ = pc; } + void set_sp(AddressType sp) { sp_ = sp; } inline AddressType& operator[](size_t reg) { return regs_[reg]; } - void* raw_data() override { return regs_.data(); } + void* RawData() override { return regs_.data(); } - private: + protected: + AddressType pc_; + AddressType sp_; std::vector regs_; }; -class Regs32 : public RegsTmpl { +class RegsArm : public RegsTmpl { public: - Regs32(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs) - : RegsTmpl(pc_reg, sp_reg, total_regs) {} - virtual ~Regs32() = default; + RegsArm(); + virtual ~RegsArm() = default; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; }; -class Regs64 : public RegsTmpl { +class RegsArm64 : public RegsTmpl { public: - Regs64(uint16_t pc_reg, uint16_t sp_reg, uint16_t total_regs) - : RegsTmpl(pc_reg, sp_reg, total_regs) {} - virtual ~Regs64() = default; + RegsArm64(); + virtual ~RegsArm64() = default; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; +}; + +class RegsX86 : public RegsTmpl { + public: + RegsX86(); + virtual ~RegsX86() = default; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; +}; + +class RegsX86_64 : public RegsTmpl { + public: + RegsX86_64(); + virtual ~RegsX86_64() = default; + + uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override; }; #endif // _LIBUNWINDSTACK_REGS_H diff --git a/libunwindstack/User.h b/libunwindstack/User.h new file mode 100644 index 000000000..a69546716 --- /dev/null +++ b/libunwindstack/User.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBUNWINDSTACK_USER_H +#define _LIBUNWINDSTACK_USER_H + +struct x86_user_regs { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t xds; + uint32_t xes; + uint32_t xfs; + uint32_t xgs; + uint32_t orig_eax; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t esp; + uint32_t xss; +}; + +struct x86_64_user_regs { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t rbp; + uint64_t rbx; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t orig_rax; + uint64_t rip; + uint64_t cs; + uint64_t eflags; + uint64_t rsp; + uint64_t ss; + uint64_t fs_base; + uint64_t gs_base; + uint64_t ds; + uint64_t es; + uint64_t fs; + uint64_t gs; +}; + +struct arm_user_regs { + uint32_t regs[18]; +}; + +struct arm64_user_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +// The largest user structure. +constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10; + +#endif // _LIBUNWINDSTACK_USER_H diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp index 9ea917aac..4fff48ec1 100644 --- a/libunwindstack/tests/ArmExidxDecodeTest.cpp +++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp @@ -24,6 +24,7 @@ #include #include "ArmExidx.h" +#include "Regs.h" #include "Log.h" #include "LogFake.h" @@ -38,12 +39,14 @@ class ArmExidxDecodeTest : public ::testing::TestWithParam { process_memory = &process_memory_; } - regs32_.reset(new Regs32(0, 1, 32)); - for (size_t i = 0; i < 32; i++) { - (*regs32_)[i] = 0; + regs_arm_.reset(new RegsArm()); + for (size_t i = 0; i < regs_arm_->total_regs(); i++) { + (*regs_arm_)[i] = 0; } + regs_arm_->set_pc(0); + regs_arm_->set_sp(0); - exidx_.reset(new ArmExidx(regs32_.get(), &elf_memory_, process_memory)); + exidx_.reset(new ArmExidx(regs_arm_.get(), &elf_memory_, process_memory)); if (log_) { exidx_->set_log(true); exidx_->set_log_indent(0); @@ -66,7 +69,7 @@ class ArmExidxDecodeTest : public ::testing::TestWithParam { } std::unique_ptr exidx_; - std::unique_ptr regs32_; + std::unique_ptr regs_arm_; std::deque* data_; MemoryFake elf_memory_; @@ -78,6 +81,7 @@ TEST_P(ArmExidxDecodeTest, vsp_incr) { // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4 data_->push_back(0x00); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 4\n", GetFakeLogPrint()); @@ -90,6 +94,7 @@ TEST_P(ArmExidxDecodeTest, vsp_incr) { data_->clear(); data_->push_back(0x01); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 8\n", GetFakeLogPrint()); @@ -102,6 +107,7 @@ TEST_P(ArmExidxDecodeTest, vsp_incr) { data_->clear(); data_->push_back(0x3f); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 256\n", GetFakeLogPrint()); @@ -115,6 +121,7 @@ TEST_P(ArmExidxDecodeTest, vsp_decr) { // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4 data_->push_back(0x40); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp - 4\n", GetFakeLogPrint()); @@ -127,6 +134,7 @@ TEST_P(ArmExidxDecodeTest, vsp_decr) { data_->clear(); data_->push_back(0x41); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp - 8\n", GetFakeLogPrint()); @@ -139,6 +147,7 @@ TEST_P(ArmExidxDecodeTest, vsp_decr) { data_->clear(); data_->push_back(0x7f); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp - 256\n", GetFakeLogPrint()); @@ -164,26 +173,29 @@ TEST_P(ArmExidxDecodeTest, refuse_unwind) { TEST_P(ArmExidxDecodeTest, pop_up_to_12) { // 1000iiii iiiiiiii: Pop up to 12 integer registers - data_->push_back(0x80); - data_->push_back(0x01); - process_memory_.SetData(0x10000, 0x10); + data_->push_back(0x88); + data_->push_back(0x00); + process_memory_.SetData32(0x10000, 0x10); ASSERT_TRUE(exidx_->Decode()); + ASSERT_TRUE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { - ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint()); + ASSERT_EQ("4 unwind pop {r15}\n", GetFakeLogPrint()); } else { ASSERT_EQ("", GetFakeLogPrint()); } ASSERT_EQ(0x10004U, exidx_->cfa()); - ASSERT_EQ(0x10U, (*exidx_->regs())[4]); + ASSERT_EQ(0x10U, (*exidx_->regs())[15]); ResetLogs(); data_->push_back(0x8f); data_->push_back(0xff); for (size_t i = 0; i < 12; i++) { - process_memory_.SetData(0x10004 + i * 4, i + 0x20); + process_memory_.SetData32(0x10004 + i * 4, i + 0x20); } + exidx_->set_pc_set(false); ASSERT_TRUE(exidx_->Decode()); + ASSERT_TRUE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}\n", @@ -211,10 +223,12 @@ TEST_P(ArmExidxDecodeTest, pop_up_to_12) { exidx_->set_cfa(0x10034); data_->push_back(0x81); data_->push_back(0x28); - process_memory_.SetData(0x10034, 0x11); - process_memory_.SetData(0x10038, 0x22); - process_memory_.SetData(0x1003c, 0x33); + process_memory_.SetData32(0x10034, 0x11); + process_memory_.SetData32(0x10038, 0x22); + process_memory_.SetData32(0x1003c, 0x33); + exidx_->set_pc_set(false); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r7, r9, r12}\n", GetFakeLogPrint()); @@ -231,11 +245,12 @@ TEST_P(ArmExidxDecodeTest, set_vsp_from_register) { // 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15) exidx_->set_cfa(0x100); for (size_t i = 0; i < 15; i++) { - (*regs32_)[i] = i + 1; + (*regs_arm_)[i] = i + 1; } data_->push_back(0x90); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = r0\n", GetFakeLogPrint()); @@ -247,6 +262,7 @@ TEST_P(ArmExidxDecodeTest, set_vsp_from_register) { ResetLogs(); data_->push_back(0x93); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = r3\n", GetFakeLogPrint()); @@ -258,6 +274,7 @@ TEST_P(ArmExidxDecodeTest, set_vsp_from_register) { ResetLogs(); data_->push_back(0x9e); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = r14\n", GetFakeLogPrint()); @@ -295,8 +312,9 @@ TEST_P(ArmExidxDecodeTest, reserved_prefix) { TEST_P(ArmExidxDecodeTest, pop_registers) { // 10100nnn: Pop r4-r[4+nnn] data_->push_back(0xa0); - process_memory_.SetData(0x10000, 0x14); + process_memory_.SetData32(0x10000, 0x14); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4}\n", GetFakeLogPrint()); @@ -308,11 +326,12 @@ TEST_P(ArmExidxDecodeTest, pop_registers) { ResetLogs(); data_->push_back(0xa3); - process_memory_.SetData(0x10004, 0x20); - process_memory_.SetData(0x10008, 0x30); - process_memory_.SetData(0x1000c, 0x40); - process_memory_.SetData(0x10010, 0x50); + process_memory_.SetData32(0x10004, 0x20); + process_memory_.SetData32(0x10008, 0x30); + process_memory_.SetData32(0x1000c, 0x40); + process_memory_.SetData32(0x10010, 0x50); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4-r7}\n", GetFakeLogPrint()); @@ -327,15 +346,16 @@ TEST_P(ArmExidxDecodeTest, pop_registers) { ResetLogs(); data_->push_back(0xa7); - process_memory_.SetData(0x10014, 0x41); - process_memory_.SetData(0x10018, 0x51); - process_memory_.SetData(0x1001c, 0x61); - process_memory_.SetData(0x10020, 0x71); - process_memory_.SetData(0x10024, 0x81); - process_memory_.SetData(0x10028, 0x91); - process_memory_.SetData(0x1002c, 0xa1); - process_memory_.SetData(0x10030, 0xb1); + process_memory_.SetData32(0x10014, 0x41); + process_memory_.SetData32(0x10018, 0x51); + process_memory_.SetData32(0x1001c, 0x61); + process_memory_.SetData32(0x10020, 0x71); + process_memory_.SetData32(0x10024, 0x81); + process_memory_.SetData32(0x10028, 0x91); + process_memory_.SetData32(0x1002c, 0xa1); + process_memory_.SetData32(0x10030, 0xb1); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4-r11}\n", GetFakeLogPrint()); @@ -356,9 +376,10 @@ TEST_P(ArmExidxDecodeTest, pop_registers) { TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) { // 10101nnn: Pop r4-r[4+nnn], r14 data_->push_back(0xa8); - process_memory_.SetData(0x10000, 0x12); - process_memory_.SetData(0x10004, 0x22); + process_memory_.SetData32(0x10000, 0x12); + process_memory_.SetData32(0x10004, 0x22); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4, r14}\n", GetFakeLogPrint()); @@ -371,12 +392,13 @@ TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) { ResetLogs(); data_->push_back(0xab); - process_memory_.SetData(0x10008, 0x1); - process_memory_.SetData(0x1000c, 0x2); - process_memory_.SetData(0x10010, 0x3); - process_memory_.SetData(0x10014, 0x4); - process_memory_.SetData(0x10018, 0x5); + process_memory_.SetData32(0x10008, 0x1); + process_memory_.SetData32(0x1000c, 0x2); + process_memory_.SetData32(0x10010, 0x3); + process_memory_.SetData32(0x10014, 0x4); + process_memory_.SetData32(0x10018, 0x5); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4-r7, r14}\n", GetFakeLogPrint()); @@ -392,16 +414,17 @@ TEST_P(ArmExidxDecodeTest, pop_registers_with_r14) { ResetLogs(); data_->push_back(0xaf); - process_memory_.SetData(0x1001c, 0x1a); - process_memory_.SetData(0x10020, 0x2a); - process_memory_.SetData(0x10024, 0x3a); - process_memory_.SetData(0x10028, 0x4a); - process_memory_.SetData(0x1002c, 0x5a); - process_memory_.SetData(0x10030, 0x6a); - process_memory_.SetData(0x10034, 0x7a); - process_memory_.SetData(0x10038, 0x8a); - process_memory_.SetData(0x1003c, 0x9a); + process_memory_.SetData32(0x1001c, 0x1a); + process_memory_.SetData32(0x10020, 0x2a); + process_memory_.SetData32(0x10024, 0x3a); + process_memory_.SetData32(0x10028, 0x4a); + process_memory_.SetData32(0x1002c, 0x5a); + process_memory_.SetData32(0x10030, 0x6a); + process_memory_.SetData32(0x10034, 0x7a); + process_memory_.SetData32(0x10038, 0x8a); + process_memory_.SetData32(0x1003c, 0x9a); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r4-r11, r14}\n", GetFakeLogPrint()); @@ -550,8 +573,9 @@ TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) { // 10110001 0000iiii: Pop integer registers {r0, r1, r2, r3} data_->push_back(0xb1); data_->push_back(0x01); - process_memory_.SetData(0x10000, 0x45); + process_memory_.SetData32(0x10000, 0x45); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r0}\n", GetFakeLogPrint()); @@ -564,9 +588,10 @@ TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) { ResetLogs(); data_->push_back(0xb1); data_->push_back(0x0a); - process_memory_.SetData(0x10004, 0x23); - process_memory_.SetData(0x10008, 0x24); + process_memory_.SetData32(0x10004, 0x23); + process_memory_.SetData32(0x10008, 0x24); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r1, r3}\n", GetFakeLogPrint()); @@ -580,11 +605,12 @@ TEST_P(ArmExidxDecodeTest, pop_registers_under_mask) { ResetLogs(); data_->push_back(0xb1); data_->push_back(0x0f); - process_memory_.SetData(0x1000c, 0x65); - process_memory_.SetData(0x10010, 0x54); - process_memory_.SetData(0x10014, 0x43); - process_memory_.SetData(0x10018, 0x32); + process_memory_.SetData32(0x1000c, 0x65); + process_memory_.SetData32(0x10010, 0x54); + process_memory_.SetData32(0x10014, 0x43); + process_memory_.SetData32(0x10018, 0x32); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {r0, r1, r2, r3}\n", GetFakeLogPrint()); @@ -603,6 +629,7 @@ TEST_P(ArmExidxDecodeTest, vsp_large_incr) { data_->push_back(0xb2); data_->push_back(0x7f); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 1024\n", GetFakeLogPrint()); @@ -616,6 +643,7 @@ TEST_P(ArmExidxDecodeTest, vsp_large_incr) { data_->push_back(0xff); data_->push_back(0x02); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 2048\n", GetFakeLogPrint()); @@ -630,6 +658,7 @@ TEST_P(ArmExidxDecodeTest, vsp_large_incr) { data_->push_back(0x82); data_->push_back(0x30); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind vsp = vsp + 3147776\n", GetFakeLogPrint()); @@ -644,6 +673,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) { data_->push_back(0xb3); data_->push_back(0x00); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint()); @@ -656,6 +686,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp_fstmfdx) { data_->push_back(0xb3); data_->push_back(0x48); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d4-d12}\n", GetFakeLogPrint()); @@ -669,6 +700,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) { // 10111nnn: Pop VFP double precision registers D[8]-D[8+nnn] by FSTMFDX data_->push_back(0xb8); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint()); @@ -680,6 +712,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) { ResetLogs(); data_->push_back(0xbb); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8-d11}\n", GetFakeLogPrint()); @@ -691,6 +724,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_fstmfdx) { ResetLogs(); data_->push_back(0xbf); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint()); @@ -704,6 +738,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) { // 11000nnn: Intel Wireless MMX pop wR[10]-wR[10+nnn] (nnn != 6, 7) data_->push_back(0xc0); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR10}\n", GetFakeLogPrint()); @@ -715,6 +750,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) { ResetLogs(); data_->push_back(0xc2); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR10-wR12}\n", GetFakeLogPrint()); @@ -726,6 +762,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr10) { ResetLogs(); data_->push_back(0xc5); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR10-wR15}\n", GetFakeLogPrint()); @@ -740,6 +777,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr) { data_->push_back(0xc6); data_->push_back(0x00); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR0}\n", GetFakeLogPrint()); @@ -752,6 +790,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr) { data_->push_back(0xc6); data_->push_back(0x25); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR2-wR7}\n", GetFakeLogPrint()); @@ -764,6 +803,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wr) { data_->push_back(0xc6); data_->push_back(0xff); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wR15-wR30}\n", GetFakeLogPrint()); @@ -778,6 +818,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) { data_->push_back(0xc7); data_->push_back(0x01); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wCGR0}\n", GetFakeLogPrint()); @@ -790,6 +831,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) { data_->push_back(0xc7); data_->push_back(0x0a); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wCGR1, wCGR3}\n", GetFakeLogPrint()); @@ -802,6 +844,7 @@ TEST_P(ArmExidxDecodeTest, pop_mmx_wcgr) { data_->push_back(0xc7); data_->push_back(0x0f); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {wCGR0, wCGR1, wCGR2, wCGR3}\n", GetFakeLogPrint()); @@ -816,6 +859,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) { data_->push_back(0xc8); data_->push_back(0x00); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d16}\n", GetFakeLogPrint()); @@ -828,6 +872,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) { data_->push_back(0xc8); data_->push_back(0x14); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d17-d21}\n", GetFakeLogPrint()); @@ -840,6 +885,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp16_vpush) { data_->push_back(0xc8); data_->push_back(0xff); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d31-d46}\n", GetFakeLogPrint()); @@ -854,6 +900,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) { data_->push_back(0xc9); data_->push_back(0x00); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d0}\n", GetFakeLogPrint()); @@ -866,6 +913,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) { data_->push_back(0xc9); data_->push_back(0x23); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d2-d5}\n", GetFakeLogPrint()); @@ -878,6 +926,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp_vpush) { data_->push_back(0xc9); data_->push_back(0xff); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d15-d30}\n", GetFakeLogPrint()); @@ -891,6 +940,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) { // 11010nnn: Pop VFP double precision registers D[8]-D[8+nnn] by VPUSH data_->push_back(0xd0); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8}\n", GetFakeLogPrint()); @@ -902,6 +952,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) { ResetLogs(); data_->push_back(0xd2); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8-d10}\n", GetFakeLogPrint()); @@ -913,6 +964,7 @@ TEST_P(ArmExidxDecodeTest, pop_vfp8_vpush) { ResetLogs(); data_->push_back(0xd7); ASSERT_TRUE(exidx_->Decode()); + ASSERT_FALSE(exidx_->pc_set()); ASSERT_EQ("", GetFakeLogBuf()); if (log_) { ASSERT_EQ("4 unwind pop {d8-d15}\n", GetFakeLogPrint()); @@ -989,4 +1041,54 @@ TEST_P(ArmExidxDecodeTest, verify_no_truncated) { } } +TEST_P(ArmExidxDecodeTest, eval_multiple_decodes) { + // vsp = vsp + 4 + data_->push_back(0x00); + // vsp = vsp + 8 + data_->push_back(0x02); + // Finish + data_->push_back(0xb0); + + ASSERT_TRUE(exidx_->Eval()); + if (log_) { + ASSERT_EQ("4 unwind vsp = vsp + 4\n" + "4 unwind vsp = vsp + 12\n" + "4 unwind finish\n", GetFakeLogPrint()); + } else { + ASSERT_EQ("", GetFakeLogPrint()); + } + ASSERT_EQ(0x10010U, exidx_->cfa()); + ASSERT_FALSE(exidx_->pc_set()); +} + +TEST_P(ArmExidxDecodeTest, eval_pc_set) { + // vsp = vsp + 4 + data_->push_back(0x00); + // vsp = vsp + 8 + data_->push_back(0x02); + // Pop {r15} + data_->push_back(0x88); + data_->push_back(0x00); + // vsp = vsp + 8 + data_->push_back(0x02); + // Finish + data_->push_back(0xb0); + + process_memory_.SetData32(0x10010, 0x10); + + ASSERT_TRUE(exidx_->Eval()); + if (log_) { + ASSERT_EQ("4 unwind vsp = vsp + 4\n" + "4 unwind vsp = vsp + 12\n" + "4 unwind pop {r15}\n" + "4 unwind vsp = vsp + 12\n" + "4 unwind finish\n", GetFakeLogPrint()); + } else { + ASSERT_EQ("", GetFakeLogPrint()); + } + ASSERT_EQ(0x10020U, exidx_->cfa()); + ASSERT_TRUE(exidx_->pc_set()); + ASSERT_EQ(0x10U, (*exidx_->regs())[15]); +} + INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest, ::testing::Values("logging", "no_logging")); diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp index 021765a29..aed75bf9d 100644 --- a/libunwindstack/tests/ArmExidxExtractTest.cpp +++ b/libunwindstack/tests/ArmExidxExtractTest.cpp @@ -53,16 +53,16 @@ TEST_F(ArmExidxExtractTest, bad_alignment) { } TEST_F(ArmExidxExtractTest, cant_unwind) { - elf_memory_.SetData(0x1000, 0x7fff2340); - elf_memory_.SetData(0x1004, 1); + elf_memory_.SetData32(0x1000, 0x7fff2340); + elf_memory_.SetData32(0x1004, 1); ASSERT_FALSE(exidx_->ExtractEntryData(0x1000)); ASSERT_EQ(ARM_STATUS_NO_UNWIND, exidx_->status()); ASSERT_TRUE(data_->empty()); } TEST_F(ArmExidxExtractTest, compact) { - elf_memory_.SetData(0x4000, 0x7ffa3000); - elf_memory_.SetData(0x4004, 0x80a8b0b0); + elf_memory_.SetData32(0x4000, 0x7ffa3000); + elf_memory_.SetData32(0x4004, 0x80a8b0b0); ASSERT_TRUE(exidx_->ExtractEntryData(0x4000)); ASSERT_EQ(3U, data_->size()); ASSERT_EQ(0xa8, data_->at(0)); @@ -71,8 +71,8 @@ TEST_F(ArmExidxExtractTest, compact) { // Missing finish gets added. elf_memory_.Clear(); - elf_memory_.SetData(0x534, 0x7ffa3000); - elf_memory_.SetData(0x538, 0x80a1a2a3); + elf_memory_.SetData32(0x534, 0x7ffa3000); + elf_memory_.SetData32(0x538, 0x80a1a2a3); ASSERT_TRUE(exidx_->ExtractEntryData(0x534)); ASSERT_EQ(4U, data_->size()); ASSERT_EQ(0xa1, data_->at(0)); @@ -82,29 +82,29 @@ TEST_F(ArmExidxExtractTest, compact) { } TEST_F(ArmExidxExtractTest, compact_non_zero_personality) { - elf_memory_.SetData(0x4000, 0x7ffa3000); + elf_memory_.SetData32(0x4000, 0x7ffa3000); uint32_t compact_value = 0x80a8b0b0; for (size_t i = 1; i < 16; i++) { - elf_memory_.SetData(0x4004, compact_value | (i << 24)); + elf_memory_.SetData32(0x4004, compact_value | (i << 24)); ASSERT_FALSE(exidx_->ExtractEntryData(0x4000)); ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status()); } } TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) { - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x8100f3b0); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x8100f3b0); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(2U, data_->size()); ASSERT_EQ(0xf3, data_->at(0)); ASSERT_EQ(0xb0, data_->at(1)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x8200f3f4); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x8200f3f4); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(3U, data_->size()); ASSERT_EQ(0xf3, data_->at(0)); @@ -112,10 +112,10 @@ TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) { ASSERT_EQ(0xb0, data_->at(2)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x8201f3f4); - elf_memory_.SetData(0x6238, 0x102030b0); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x8201f3f4); + elf_memory_.SetData32(0x6238, 0x102030b0); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(6U, data_->size()); ASSERT_EQ(0xf3, data_->at(0)); @@ -126,12 +126,12 @@ TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) { ASSERT_EQ(0xb0, data_->at(5)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x8103f3f4); - elf_memory_.SetData(0x6238, 0x10203040); - elf_memory_.SetData(0x623c, 0x50607080); - elf_memory_.SetData(0x6240, 0x90a0c0d0); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x8103f3f4); + elf_memory_.SetData32(0x6238, 0x10203040); + elf_memory_.SetData32(0x623c, 0x50607080); + elf_memory_.SetData32(0x6240, 0x90a0c0d0); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(15U, data_->size()); ASSERT_EQ(0xf3, data_->at(0)); @@ -152,33 +152,33 @@ TEST_F(ArmExidxExtractTest, second_read_compact_personality_1_2) { } TEST_F(ArmExidxExtractTest, second_read_compact_personality_illegal) { - elf_memory_.SetData(0x5000, 0x7ffa1e48); - elf_memory_.SetData(0x5004, 0x1230); - elf_memory_.SetData(0x6234, 0x832132b0); + elf_memory_.SetData32(0x5000, 0x7ffa1e48); + elf_memory_.SetData32(0x5004, 0x1230); + elf_memory_.SetData32(0x6234, 0x832132b0); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status()); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x7ffa1e48); - elf_memory_.SetData(0x5004, 0x1230); - elf_memory_.SetData(0x6234, 0x842132b0); + elf_memory_.SetData32(0x5000, 0x7ffa1e48); + elf_memory_.SetData32(0x5004, 0x1230); + elf_memory_.SetData32(0x6234, 0x842132b0); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status()); } TEST_F(ArmExidxExtractTest, second_read_offset_is_negative) { - elf_memory_.SetData(0x5000, 0x7ffa1e48); - elf_memory_.SetData(0x5004, 0x7fffb1e0); - elf_memory_.SetData(0x1e4, 0x842132b0); + elf_memory_.SetData32(0x5000, 0x7ffa1e48); + elf_memory_.SetData32(0x5004, 0x7fffb1e0); + elf_memory_.SetData32(0x1e4, 0x842132b0); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_INVALID_PERSONALITY, exidx_->status()); } TEST_F(ArmExidxExtractTest, second_read_not_compact) { - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x1); - elf_memory_.SetData(0x6238, 0x001122b0); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x1); + elf_memory_.SetData32(0x6238, 0x001122b0); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(3U, data_->size()); ASSERT_EQ(0x11, data_->at(0)); @@ -186,10 +186,10 @@ TEST_F(ArmExidxExtractTest, second_read_not_compact) { ASSERT_EQ(0xb0, data_->at(2)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x2); - elf_memory_.SetData(0x6238, 0x00112233); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x2); + elf_memory_.SetData32(0x6238, 0x00112233); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(4U, data_->size()); ASSERT_EQ(0x11, data_->at(0)); @@ -198,11 +198,11 @@ TEST_F(ArmExidxExtractTest, second_read_not_compact) { ASSERT_EQ(0xb0, data_->at(3)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x3); - elf_memory_.SetData(0x6238, 0x01112233); - elf_memory_.SetData(0x623c, 0x445566b0); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x3); + elf_memory_.SetData32(0x6238, 0x01112233); + elf_memory_.SetData32(0x623c, 0x445566b0); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(7U, data_->size()); ASSERT_EQ(0x11, data_->at(0)); @@ -214,15 +214,15 @@ TEST_F(ArmExidxExtractTest, second_read_not_compact) { ASSERT_EQ(0xb0, data_->at(6)); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x3); - elf_memory_.SetData(0x6238, 0x05112233); - elf_memory_.SetData(0x623c, 0x01020304); - elf_memory_.SetData(0x6240, 0x05060708); - elf_memory_.SetData(0x6244, 0x090a0b0c); - elf_memory_.SetData(0x6248, 0x0d0e0f10); - elf_memory_.SetData(0x624c, 0x11121314); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x3); + elf_memory_.SetData32(0x6238, 0x05112233); + elf_memory_.SetData32(0x623c, 0x01020304); + elf_memory_.SetData32(0x6240, 0x05060708); + elf_memory_.SetData32(0x6244, 0x090a0b0c); + elf_memory_.SetData32(0x6248, 0x0d0e0f10); + elf_memory_.SetData32(0x624c, 0x11121314); ASSERT_TRUE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(24U, data_->size()); ASSERT_EQ(0x11, data_->at(0)); @@ -255,43 +255,43 @@ TEST_F(ArmExidxExtractTest, read_failures) { ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status()); - elf_memory_.SetData(0x5000, 0x100); + elf_memory_.SetData32(0x5000, 0x100); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status()); - elf_memory_.SetData(0x5004, 0x100); + elf_memory_.SetData32(0x5004, 0x100); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status()); - elf_memory_.SetData(0x5104, 0x1); + elf_memory_.SetData32(0x5104, 0x1); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status()); - elf_memory_.SetData(0x5108, 0x01010203); + elf_memory_.SetData32(0x5108, 0x01010203); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_READ_FAILED, exidx_->status()); } TEST_F(ArmExidxExtractTest, malformed) { - elf_memory_.SetData(0x5000, 0x100); - elf_memory_.SetData(0x5004, 0x100); - elf_memory_.SetData(0x5104, 0x1); - elf_memory_.SetData(0x5108, 0x06010203); + elf_memory_.SetData32(0x5000, 0x100); + elf_memory_.SetData32(0x5004, 0x100); + elf_memory_.SetData32(0x5104, 0x1); + elf_memory_.SetData32(0x5108, 0x06010203); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status()); elf_memory_.Clear(); - elf_memory_.SetData(0x5000, 0x100); - elf_memory_.SetData(0x5004, 0x100); - elf_memory_.SetData(0x5104, 0x1); - elf_memory_.SetData(0x5108, 0x81060203); + elf_memory_.SetData32(0x5000, 0x100); + elf_memory_.SetData32(0x5004, 0x100); + elf_memory_.SetData32(0x5104, 0x1); + elf_memory_.SetData32(0x5108, 0x81060203); ASSERT_FALSE(exidx_->ExtractEntryData(0x5000)); ASSERT_EQ(ARM_STATUS_MALFORMED, exidx_->status()); } TEST_F(ArmExidxExtractTest, cant_unwind_log) { - elf_memory_.SetData(0x1000, 0x7fff2340); - elf_memory_.SetData(0x1004, 1); + elf_memory_.SetData32(0x1000, 0x7fff2340); + elf_memory_.SetData32(0x1004, 1); exidx_->set_log(true); exidx_->set_log_indent(0); @@ -305,8 +305,8 @@ TEST_F(ArmExidxExtractTest, cant_unwind_log) { } TEST_F(ArmExidxExtractTest, raw_data_compact) { - elf_memory_.SetData(0x4000, 0x7ffa3000); - elf_memory_.SetData(0x4004, 0x80a8b0b0); + elf_memory_.SetData32(0x4000, 0x7ffa3000); + elf_memory_.SetData32(0x4004, 0x80a8b0b0); exidx_->set_log(true); exidx_->set_log_indent(0); @@ -317,10 +317,10 @@ TEST_F(ArmExidxExtractTest, raw_data_compact) { } TEST_F(ArmExidxExtractTest, raw_data_non_compact) { - elf_memory_.SetData(0x5000, 0x1234); - elf_memory_.SetData(0x5004, 0x00001230); - elf_memory_.SetData(0x6234, 0x2); - elf_memory_.SetData(0x6238, 0x00112233); + elf_memory_.SetData32(0x5000, 0x1234); + elf_memory_.SetData32(0x5004, 0x00001230); + elf_memory_.SetData32(0x6234, 0x2); + elf_memory_.SetData32(0x6238, 0x00112233); exidx_->set_log(true); exidx_->set_log_indent(0); diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp new file mode 100644 index 000000000..67c9a6bfb --- /dev/null +++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#include "ElfInterfaceArm.h" +#include "Machine.h" +#include "Regs.h" + +#include "MemoryFake.h" + +class ElfInterfaceArmTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + process_memory_.Clear(); + } + + MemoryFake memory_; + MemoryFake process_memory_; +}; + +TEST_F(ElfInterfaceArmTest, GetPrel32Addr) { + ElfInterfaceArm interface(&memory_); + memory_.SetData32(0x1000, 0x230000); + + uint32_t value; + ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value)); + ASSERT_EQ(0x231000U, value); + + memory_.SetData32(0x1000, 0x80001000); + ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value)); + ASSERT_EQ(0x2000U, value); + + memory_.SetData32(0x1000, 0x70001000); + ASSERT_TRUE(interface.GetPrel31Addr(0x1000, &value)); + ASSERT_EQ(0xf0002000U, value); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_start_zero) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0); + interface.set_total_entries(10); + + uint64_t entry_offset; + ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset)); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_no_entries) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x100); + interface.set_total_entries(0); + + uint64_t entry_offset; + ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset)); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_no_valid_memory) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x100); + interface.set_total_entries(2); + + uint64_t entry_offset; + ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset)); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_ip_before_first) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(1); + memory_.SetData32(0x1000, 0x6000); + + uint64_t entry_offset; + ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset)); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_single_entry_negative_value) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x8000); + interface.set_total_entries(1); + memory_.SetData32(0x8000, 0x7fffff00); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x7ff0, &entry_offset)); + ASSERT_EQ(0x8000U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_two_entries) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(2); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1008, 0x7000); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x7000, &entry_offset)); + ASSERT_EQ(0x1000U, entry_offset); +} + + +TEST_F(ElfInterfaceArmTest, FindEntry_last_check_single_entry) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(1); + memory_.SetData32(0x1000, 0x6000); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x7000, &entry_offset)); + ASSERT_EQ(0x1000U, entry_offset); + + // To guarantee that we are using the cache on the second run, + // set the memory to a different value. + memory_.SetData32(0x1000, 0x8000); + ASSERT_TRUE(interface.FindEntry(0x7004, &entry_offset)); + ASSERT_EQ(0x1000U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_last_check_multiple_entries) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(2); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1008, 0x8000); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x9008, &entry_offset)); + ASSERT_EQ(0x1008U, entry_offset); + + // To guarantee that we are using the cache on the second run, + // set the memory to a different value. + memory_.SetData32(0x1000, 0x16000); + memory_.SetData32(0x1008, 0x18000); + ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset)); + ASSERT_EQ(0x1008U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_even) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(4); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1008, 0x7000); + memory_.SetData32(0x1010, 0x8000); + memory_.SetData32(0x1018, 0x9000); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset)); + ASSERT_EQ(0x1010U, entry_offset); + + // To guarantee that we are using the cache on the second run, + // set the memory to a different value. + memory_.SetData32(0x1000, 0x16000); + memory_.SetData32(0x1008, 0x17000); + memory_.SetData32(0x1010, 0x18000); + memory_.SetData32(0x1018, 0x19000); + ASSERT_TRUE(interface.FindEntry(0x9100, &entry_offset)); + ASSERT_EQ(0x1010U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_multiple_entries_odd) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(5); + memory_.SetData32(0x1000, 0x5000); + memory_.SetData32(0x1008, 0x6000); + memory_.SetData32(0x1010, 0x7000); + memory_.SetData32(0x1018, 0x8000); + memory_.SetData32(0x1020, 0x9000); + + uint64_t entry_offset; + ASSERT_TRUE(interface.FindEntry(0x8100, &entry_offset)); + ASSERT_EQ(0x1010U, entry_offset); + + // To guarantee that we are using the cache on the second run, + // set the memory to a different value. + memory_.SetData32(0x1000, 0x15000); + memory_.SetData32(0x1008, 0x16000); + memory_.SetData32(0x1010, 0x17000); + memory_.SetData32(0x1018, 0x18000); + memory_.SetData32(0x1020, 0x19000); + ASSERT_TRUE(interface.FindEntry(0x8100, &entry_offset)); + ASSERT_EQ(0x1010U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, iterate) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(5); + memory_.SetData32(0x1000, 0x5000); + memory_.SetData32(0x1008, 0x6000); + memory_.SetData32(0x1010, 0x7000); + memory_.SetData32(0x1018, 0x8000); + memory_.SetData32(0x1020, 0x9000); + + std::vector entries; + for (auto addr : interface) { + entries.push_back(addr); + } + ASSERT_EQ(5U, entries.size()); + ASSERT_EQ(0x6000U, entries[0]); + ASSERT_EQ(0x7008U, entries[1]); + ASSERT_EQ(0x8010U, entries[2]); + ASSERT_EQ(0x9018U, entries[3]); + ASSERT_EQ(0xa020U, entries[4]); + + // Make sure the iterate cached the entries. + memory_.SetData32(0x1000, 0x11000); + memory_.SetData32(0x1008, 0x12000); + memory_.SetData32(0x1010, 0x13000); + memory_.SetData32(0x1018, 0x14000); + memory_.SetData32(0x1020, 0x15000); + + entries.clear(); + for (auto addr : interface) { + entries.push_back(addr); + } + ASSERT_EQ(5U, entries.size()); + ASSERT_EQ(0x6000U, entries[0]); + ASSERT_EQ(0x7008U, entries[1]); + ASSERT_EQ(0x8010U, entries[2]); + ASSERT_EQ(0x9018U, entries[3]); + ASSERT_EQ(0xa020U, entries[4]); +} + +TEST_F(ElfInterfaceArmTest, FindEntry_load_bias) { + ElfInterfaceArm interface(&memory_); + interface.set_start_offset(0x1000); + interface.set_total_entries(2); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1008, 0x8000); + + uint64_t entry_offset; + interface.set_load_bias(0x2000); + ASSERT_FALSE(interface.FindEntry(0x1000, &entry_offset)); + ASSERT_FALSE(interface.FindEntry(0x8000, &entry_offset)); + ASSERT_FALSE(interface.FindEntry(0x8fff, &entry_offset)); + ASSERT_TRUE(interface.FindEntry(0x9000, &entry_offset)); + ASSERT_EQ(0x1000U, entry_offset); + ASSERT_TRUE(interface.FindEntry(0xb007, &entry_offset)); + ASSERT_EQ(0x1000U, entry_offset); + ASSERT_TRUE(interface.FindEntry(0xb008, &entry_offset)); + ASSERT_EQ(0x1008U, entry_offset); +} + +TEST_F(ElfInterfaceArmTest, HandleType_not_arm_exidx) { + ElfInterfaceArm interface(&memory_); + + ASSERT_FALSE(interface.HandleType(0x1000, PT_NULL)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_LOAD)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_DYNAMIC)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_INTERP)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_NOTE)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_SHLIB)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_PHDR)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_TLS)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_LOOS)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_HIOS)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_LOPROC)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_HIPROC)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_EH_FRAME)); + ASSERT_FALSE(interface.HandleType(0x1000, PT_GNU_STACK)); +} + +TEST_F(ElfInterfaceArmTest, HandleType_arm_exidx) { + ElfInterfaceArm interface(&memory_); + + Elf32_Phdr phdr; + interface.set_start_offset(0x1000); + interface.set_total_entries(100); + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0xa00; + + // Verify that if reads fail, we don't set the values but still get true. + ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001)); + ASSERT_EQ(0x1000U, interface.start_offset()); + ASSERT_EQ(100U, interface.total_entries()); + + // Verify that if the second read fails, we still don't set the values. + memory_.SetData32( + 0x1000 + reinterpret_cast(&phdr.p_vaddr) - reinterpret_cast(&phdr), + phdr.p_vaddr); + ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001)); + ASSERT_EQ(0x1000U, interface.start_offset()); + ASSERT_EQ(100U, interface.total_entries()); + + // Everything is correct and present. + memory_.SetData32( + 0x1000 + reinterpret_cast(&phdr.p_memsz) - reinterpret_cast(&phdr), + phdr.p_memsz); + ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001)); + ASSERT_EQ(0x2000U, interface.start_offset()); + ASSERT_EQ(320U, interface.total_entries()); + + // Non-zero load bias. + interface.set_load_bias(0x1000); + ASSERT_TRUE(interface.HandleType(0x1000, 0x70000001)); + ASSERT_EQ(0x1000U, interface.start_offset()); + ASSERT_EQ(320U, interface.total_entries()); +} + +TEST_F(ElfInterfaceArmTest, StepExidx) { + ElfInterfaceArm interface(&memory_); + + // FindEntry fails. + ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr)); + + // ExtractEntry should fail. + interface.set_start_offset(0x1000); + interface.set_total_entries(2); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1008, 0x8000); + + RegsArm regs; + regs[ARM_REG_SP] = 0x1000; + regs[ARM_REG_LR] = 0x20000; + regs.set_sp(regs[ARM_REG_SP]); + regs.set_pc(0x1234); + ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_)); + + // Eval should fail. + memory_.SetData32(0x1004, 0x81000000); + ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_)); + + // Everything should pass. + memory_.SetData32(0x1004, 0x80b0b0b0); + ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_)); + ASSERT_EQ(0x1000U, regs.sp()); + ASSERT_EQ(0x1000U, regs[ARM_REG_SP]); + ASSERT_EQ(0x20000U, regs.pc()); + ASSERT_EQ(0x20000U, regs[ARM_REG_PC]); +} + +TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) { + ElfInterfaceArm interface(&memory_); + + interface.set_start_offset(0x1000); + interface.set_total_entries(2); + memory_.SetData32(0x1000, 0x6000); + memory_.SetData32(0x1004, 0x808800b0); + memory_.SetData32(0x1008, 0x8000); + process_memory_.SetData32(0x10000, 0x10); + + RegsArm regs; + regs[ARM_REG_SP] = 0x10000; + regs[ARM_REG_LR] = 0x20000; + regs.set_sp(regs[ARM_REG_SP]); + regs.set_pc(0x1234); + + // Everything should pass. + ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_)); + ASSERT_EQ(0x10004U, regs.sp()); + ASSERT_EQ(0x10004U, regs[ARM_REG_SP]); + ASSERT_EQ(0x10U, regs.pc()); + ASSERT_EQ(0x10U, regs[ARM_REG_PC]); +} diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp new file mode 100644 index 000000000..c31903dbb --- /dev/null +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#include "ElfInterface.h" +#include "ElfInterfaceArm.h" + +#include "MemoryFake.h" + +#if !defined(PT_ARM_EXIDX) +#define PT_ARM_EXIDX 0x70000001 +#endif + +#if !defined(EM_AARCH64) +#define EM_AARCH64 183 +#endif + +class ElfInterfaceTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + } + + void SetStringMemory(uint64_t offset, const char* string) { + memory_.SetMemory(offset, string, strlen(string) + 1); + } + + template + void SinglePtLoad(); + + template + void MultipleExecutablePtLoads(); + + template + void MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr(); + + template + void NonExecutablePtLoads(); + + template + void ManyPhdrs(); + + template + void Soname(); + + template + void SonameAfterDtNull(); + + template + void SonameSize(); + + MemoryFake memory_; +}; + +template +void ElfInterfaceTest::SinglePtLoad() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + ASSERT_TRUE(elf->Init()); + + const std::unordered_map& pt_loads = elf->pt_loads(); + ASSERT_EQ(1U, pt_loads.size()); + LoadInfo load_data = pt_loads.at(0); + ASSERT_EQ(0U, load_data.offset); + ASSERT_EQ(0x2000U, load_data.table_offset); + ASSERT_EQ(0x10000U, load_data.table_size); +} + +TEST_F(ElfInterfaceTest, elf32_single_pt_load) { + SinglePtLoad(); +} + +TEST_F(ElfInterfaceTest, elf64_single_pt_load) { + SinglePtLoad(); +} + +template +void ElfInterfaceTest::MultipleExecutablePtLoads() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 3; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x1000; + phdr.p_vaddr = 0x2001; + phdr.p_memsz = 0x10001; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1001; + memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x2000; + phdr.p_vaddr = 0x2002; + phdr.p_memsz = 0x10002; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1002; + memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr)); + + ASSERT_TRUE(elf->Init()); + + const std::unordered_map& pt_loads = elf->pt_loads(); + ASSERT_EQ(3U, pt_loads.size()); + + LoadInfo load_data = pt_loads.at(0); + ASSERT_EQ(0U, load_data.offset); + ASSERT_EQ(0x2000U, load_data.table_offset); + ASSERT_EQ(0x10000U, load_data.table_size); + + load_data = pt_loads.at(0x1000); + ASSERT_EQ(0x1000U, load_data.offset); + ASSERT_EQ(0x2001U, load_data.table_offset); + ASSERT_EQ(0x10001U, load_data.table_size); + + load_data = pt_loads.at(0x2000); + ASSERT_EQ(0x2000U, load_data.offset); + ASSERT_EQ(0x2002U, load_data.table_offset); + ASSERT_EQ(0x10002U, load_data.table_size); +} + +TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads) { + MultipleExecutablePtLoads(); +} + +TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads) { + MultipleExecutablePtLoads(); +} + +template +void ElfInterfaceTest::MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 3; + ehdr.e_phentsize = sizeof(Phdr) + 100; + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x1000; + phdr.p_vaddr = 0x2001; + phdr.p_memsz = 0x10001; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1001; + memory_.SetMemory(0x100 + sizeof(phdr) + 100, &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x2000; + phdr.p_vaddr = 0x2002; + phdr.p_memsz = 0x10002; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1002; + memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr)); + + ASSERT_TRUE(elf->Init()); + + const std::unordered_map& pt_loads = elf->pt_loads(); + ASSERT_EQ(3U, pt_loads.size()); + + LoadInfo load_data = pt_loads.at(0); + ASSERT_EQ(0U, load_data.offset); + ASSERT_EQ(0x2000U, load_data.table_offset); + ASSERT_EQ(0x10000U, load_data.table_size); + + load_data = pt_loads.at(0x1000); + ASSERT_EQ(0x1000U, load_data.offset); + ASSERT_EQ(0x2001U, load_data.table_offset); + ASSERT_EQ(0x10001U, load_data.table_size); + + load_data = pt_loads.at(0x2000); + ASSERT_EQ(0x2000U, load_data.offset); + ASSERT_EQ(0x2002U, load_data.table_offset); + ASSERT_EQ(0x10002U, load_data.table_size); +} + +TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads_increments_not_size_of_phdr) { + MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr(); +} + +TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads_increments_not_size_of_phdr) { + MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr(); +} + +template +void ElfInterfaceTest::NonExecutablePtLoads() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 3; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R; + phdr.p_align = 0x1000; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x1000; + phdr.p_vaddr = 0x2001; + phdr.p_memsz = 0x10001; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1001; + memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr)); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0x2000; + phdr.p_vaddr = 0x2002; + phdr.p_memsz = 0x10002; + phdr.p_flags = PF_R; + phdr.p_align = 0x1002; + memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr)); + + ASSERT_TRUE(elf->Init()); + + const std::unordered_map& pt_loads = elf->pt_loads(); + ASSERT_EQ(1U, pt_loads.size()); + + LoadInfo load_data = pt_loads.at(0x1000); + ASSERT_EQ(0x1000U, load_data.offset); + ASSERT_EQ(0x2001U, load_data.table_offset); + ASSERT_EQ(0x10001U, load_data.table_size); +} + +TEST_F(ElfInterfaceTest, elf32_non_executable_pt_loads) { + NonExecutablePtLoads(); +} + +TEST_F(ElfInterfaceTest, elf64_non_executable_pt_loads) { + NonExecutablePtLoads(); +} + +template +void ElfInterfaceTest::ManyPhdrs() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 7; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + uint64_t phdr_offset = 0x100; + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_GNU_EH_FRAME; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_DYNAMIC; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_INTERP; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_NOTE; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_SHLIB; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_GNU_EH_FRAME; + memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr)); + phdr_offset += sizeof(phdr); + + ASSERT_TRUE(elf->Init()); + + const std::unordered_map& pt_loads = elf->pt_loads(); + ASSERT_EQ(1U, pt_loads.size()); + + LoadInfo load_data = pt_loads.at(0); + ASSERT_EQ(0U, load_data.offset); + ASSERT_EQ(0x2000U, load_data.table_offset); + ASSERT_EQ(0x10000U, load_data.table_size); +} + +TEST_F(ElfInterfaceTest, elf32_many_phdrs) { + ElfInterfaceTest::ManyPhdrs(); +} + +TEST_F(ElfInterfaceTest, elf64_many_phdrs) { + ElfInterfaceTest::ManyPhdrs(); +} + +TEST_F(ElfInterfaceTest, elf32_arm) { + ElfInterfaceArm elf_arm(&memory_); + + Elf32_Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Elf32_Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_ARM_EXIDX; + phdr.p_vaddr = 0x2000; + phdr.p_memsz = 16; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + // Add arm exidx entries. + memory_.SetData32(0x2000, 0x1000); + memory_.SetData32(0x2008, 0x1000); + + ASSERT_TRUE(elf_arm.Init()); + + std::vector entries; + for (auto addr : elf_arm) { + entries.push_back(addr); + } + ASSERT_EQ(2U, entries.size()); + ASSERT_EQ(0x3000U, entries[0]); + ASSERT_EQ(0x3008U, entries[1]); + + ASSERT_EQ(0x2000U, elf_arm.start_offset()); + ASSERT_EQ(2U, elf_arm.total_entries()); +} + +template +void ElfInterfaceTest::Soname() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_DYNAMIC; + phdr.p_offset = 0x2000; + phdr.p_memsz = sizeof(Dyn) * 3; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + uint64_t offset = 0x2000; + Dyn dyn; + + dyn.d_tag = DT_STRTAB; + dyn.d_un.d_ptr = 0x10000; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_STRSZ; + dyn.d_un.d_val = 0x1000; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_SONAME; + dyn.d_un.d_val = 0x10; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_NULL; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + + SetStringMemory(0x10010, "fake_soname.so"); + + ASSERT_TRUE(elf->Init()); + std::string name; + ASSERT_TRUE(elf->GetSoname(&name)); + ASSERT_STREQ("fake_soname.so", name.c_str()); +} + +TEST_F(ElfInterfaceTest, elf32_soname) { + Soname(); +} + +TEST_F(ElfInterfaceTest, elf64_soname) { + Soname(); +} + +template +void ElfInterfaceTest::SonameAfterDtNull() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_DYNAMIC; + phdr.p_offset = 0x2000; + phdr.p_memsz = sizeof(Dyn) * 3; + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + Dyn dyn; + uint64_t offset = 0x2000; + + dyn.d_tag = DT_STRTAB; + dyn.d_un.d_ptr = 0x10000; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_STRSZ; + dyn.d_un.d_val = 0x1000; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_NULL; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_SONAME; + dyn.d_un.d_val = 0x10; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + SetStringMemory(0x10010, "fake_soname.so"); + + ASSERT_TRUE(elf->Init()); + std::string name; + ASSERT_FALSE(elf->GetSoname(&name)); +} + +TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) { + SonameAfterDtNull(); +} + +TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) { + SonameAfterDtNull(); +} + +template +void ElfInterfaceTest::SonameSize() { + std::unique_ptr elf(new ElfInterfaceType(&memory_)); + + Ehdr ehdr; + memset(&ehdr, 0, sizeof(ehdr)); + ehdr.e_phoff = 0x100; + ehdr.e_phnum = 1; + ehdr.e_phentsize = sizeof(Phdr); + memory_.SetMemory(0, &ehdr, sizeof(ehdr)); + + Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_DYNAMIC; + phdr.p_offset = 0x2000; + phdr.p_memsz = sizeof(Dyn); + memory_.SetMemory(0x100, &phdr, sizeof(phdr)); + + Dyn dyn; + uint64_t offset = 0x2000; + + dyn.d_tag = DT_STRTAB; + dyn.d_un.d_ptr = 0x10000; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_STRSZ; + dyn.d_un.d_val = 0x10; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_SONAME; + dyn.d_un.d_val = 0x10; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + offset += sizeof(dyn); + + dyn.d_tag = DT_NULL; + memory_.SetMemory(offset, &dyn, sizeof(dyn)); + + SetStringMemory(0x10010, "fake_soname.so"); + + ASSERT_TRUE(elf->Init()); + std::string name; + ASSERT_FALSE(elf->GetSoname(&name)); +} + +TEST_F(ElfInterfaceTest, elf32_soname_size) { + SonameSize(); +} + +TEST_F(ElfInterfaceTest, elf64_soname_size) { + SonameSize(); +} diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp new file mode 100644 index 000000000..25fec8e3a --- /dev/null +++ b/libunwindstack/tests/ElfTest.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "Elf.h" + +#include "MemoryFake.h" + +#if !defined(PT_ARM_EXIDX) +#define PT_ARM_EXIDX 0x70000001 +#endif + +#if !defined(EM_AARCH64) +#define EM_AARCH64 183 +#endif + +class ElfTest : public ::testing::Test { + protected: + void SetUp() override { + memory_ = new MemoryFake; + } + + template + void InitEhdr(Ehdr* ehdr) { + memset(ehdr, 0, sizeof(Ehdr)); + memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; + } + + void InitElf32(uint32_t type) { + Elf32_Ehdr ehdr; + + InitEhdr(&ehdr); + ehdr.e_ident[EI_CLASS] = ELFCLASS32; + + ehdr.e_type = ET_DYN; + ehdr.e_machine = type; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = 0; + ehdr.e_phoff = 0x100; + ehdr.e_shoff = 0; + ehdr.e_flags = 0; + ehdr.e_ehsize = sizeof(ehdr); + ehdr.e_phentsize = sizeof(Elf32_Phdr); + ehdr.e_phnum = 1; + ehdr.e_shentsize = sizeof(Elf32_Shdr); + ehdr.e_shnum = 0; + ehdr.e_shstrndx = 0; + if (type == EM_ARM) { + ehdr.e_flags = 0x5000200; + ehdr.e_phnum = 2; + } + memory_->SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf32_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0; + phdr.p_vaddr = 0; + phdr.p_paddr = 0; + phdr.p_filesz = 0x10000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_->SetMemory(0x100, &phdr, sizeof(phdr)); + + if (type == EM_ARM) { + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_ARM_EXIDX; + phdr.p_offset = 0x30000; + phdr.p_vaddr = 0x30000; + phdr.p_paddr = 0x30000; + phdr.p_filesz = 16; + phdr.p_memsz = 16; + phdr.p_flags = PF_R; + phdr.p_align = 0x4; + memory_->SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr)); + } + } + + void InitElf64(uint32_t type) { + Elf64_Ehdr ehdr; + + InitEhdr(&ehdr); + ehdr.e_ident[EI_CLASS] = ELFCLASS64; + + ehdr.e_type = ET_DYN; + ehdr.e_machine = type; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = 0; + ehdr.e_phoff = 0x100; + ehdr.e_shoff = 0; + ehdr.e_flags = 0x5000200; + ehdr.e_ehsize = sizeof(ehdr); + ehdr.e_phentsize = sizeof(Elf64_Phdr); + ehdr.e_phnum = 1; + ehdr.e_shentsize = sizeof(Elf64_Shdr); + ehdr.e_shnum = 0; + ehdr.e_shstrndx = 0; + memory_->SetMemory(0, &ehdr, sizeof(ehdr)); + + Elf64_Phdr phdr; + memset(&phdr, 0, sizeof(phdr)); + phdr.p_type = PT_LOAD; + phdr.p_offset = 0; + phdr.p_vaddr = 0; + phdr.p_paddr = 0; + phdr.p_filesz = 0x10000; + phdr.p_memsz = 0x10000; + phdr.p_flags = PF_R | PF_X; + phdr.p_align = 0x1000; + memory_->SetMemory(0x100, &phdr, sizeof(phdr)); + } + + MemoryFake* memory_; +}; + +TEST_F(ElfTest, invalid_memory) { + Elf elf(memory_); + + ASSERT_FALSE(elf.Init()); + ASSERT_FALSE(elf.valid()); +} + +TEST_F(ElfTest, elf_invalid) { + Elf elf(memory_); + + InitElf32(EM_386); + + // Corrupt the ELF signature. + memory_->SetData32(0, 0x7f000000); + + ASSERT_FALSE(elf.Init()); + ASSERT_FALSE(elf.valid()); + ASSERT_TRUE(elf.interface() == nullptr); + + std::string name; + ASSERT_FALSE(elf.GetSoname(&name)); + + uint64_t func_offset; + ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset)); + + ASSERT_FALSE(elf.Step(0, nullptr, nullptr)); +} + +TEST_F(ElfTest, elf_arm) { + Elf elf(memory_); + + InitElf32(EM_ARM); + + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_ARM), elf.machine_type()); + ASSERT_EQ(ELFCLASS32, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} + +TEST_F(ElfTest, elf_x86) { + Elf elf(memory_); + + InitElf32(EM_386); + + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_386), elf.machine_type()); + ASSERT_EQ(ELFCLASS32, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} + +TEST_F(ElfTest, elf_arm64) { + Elf elf(memory_); + + InitElf64(EM_AARCH64); + + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_AARCH64), elf.machine_type()); + ASSERT_EQ(ELFCLASS64, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} + +TEST_F(ElfTest, elf_x86_64) { + Elf elf(memory_); + + InitElf64(EM_X86_64); + + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + ASSERT_EQ(static_cast(EM_X86_64), elf.machine_type()); + ASSERT_EQ(ELFCLASS64, elf.class_type()); + ASSERT_TRUE(elf.interface() != nullptr); +} diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp deleted file mode 100644 index 216873f8c..000000000 --- a/libunwindstack/tests/MapsTest.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include - -#include "Maps.h" - -#include "LogFake.h" - -class MapsTest : public ::testing::Test { - protected: - void SetUp() override { - ResetLogs(); - } -}; - -TEST_F(MapsTest, parse_permissions) { - MapsBuffer maps("1000-2000 ---- 00000000 00:00 0\n" - "2000-3000 r--- 00000000 00:00 0\n" - "3000-4000 -w-- 00000000 00:00 0\n" - "4000-5000 --x- 00000000 00:00 0\n" - "5000-6000 rwx- 00000000 00:00 0\n"); - - ASSERT_TRUE(maps.Parse()); - ASSERT_EQ(5U, maps.Total()); - auto it = maps.begin(); - ASSERT_EQ(PROT_NONE, it->flags); - ASSERT_EQ(0x1000U, it->start); - ASSERT_EQ(0x2000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ("", it->name); - ++it; - ASSERT_EQ(PROT_READ, it->flags); - ASSERT_EQ(0x2000U, it->start); - ASSERT_EQ(0x3000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ("", it->name); - ++it; - ASSERT_EQ(PROT_WRITE, it->flags); - ASSERT_EQ(0x3000U, it->start); - ASSERT_EQ(0x4000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ("", it->name); - ++it; - ASSERT_EQ(PROT_EXEC, it->flags); - ASSERT_EQ(0x4000U, it->start); - ASSERT_EQ(0x5000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ("", it->name); - ++it; - ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, it->flags); - ASSERT_EQ(0x5000U, it->start); - ASSERT_EQ(0x6000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ("", it->name); - ++it; - ASSERT_EQ(it, maps.end()); -} - -TEST_F(MapsTest, parse_name) { - MapsBuffer maps("720b29b000-720b29e000 rw-p 00000000 00:00 0\n" - "720b29e000-720b29f000 rw-p 00000000 00:00 0 /system/lib/fake.so\n" - "720b29f000-720b2a0000 rw-p 00000000 00:00 0"); - - ASSERT_TRUE(maps.Parse()); - ASSERT_EQ(3U, maps.Total()); - auto it = maps.begin(); - ASSERT_EQ("", it->name); - ASSERT_EQ(0x720b29b000U, it->start); - ASSERT_EQ(0x720b29e000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags); - ++it; - ASSERT_EQ("/system/lib/fake.so", it->name); - ASSERT_EQ(0x720b29e000U, it->start); - ASSERT_EQ(0x720b29f000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags); - ++it; - ASSERT_EQ("", it->name); - ASSERT_EQ(0x720b29f000U, it->start); - ASSERT_EQ(0x720b2a0000U, it->end); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags); - ++it; - ASSERT_EQ(it, maps.end()); -} - -TEST_F(MapsTest, parse_offset) { - MapsBuffer maps("a000-e000 rw-p 00000000 00:00 0 /system/lib/fake.so\n" - "e000-f000 rw-p 00a12345 00:00 0 /system/lib/fake.so\n"); - - ASSERT_TRUE(maps.Parse()); - ASSERT_EQ(2U, maps.Total()); - auto it = maps.begin(); - ASSERT_EQ(0U, it->offset); - ASSERT_EQ(0xa000U, it->start); - ASSERT_EQ(0xe000U, it->end); - ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags); - ASSERT_EQ("/system/lib/fake.so", it->name); - ++it; - ASSERT_EQ(0xa12345U, it->offset); - ASSERT_EQ(0xe000U, it->start); - ASSERT_EQ(0xf000U, it->end); - ASSERT_EQ(PROT_READ | PROT_WRITE, it->flags); - ASSERT_EQ("/system/lib/fake.so", it->name); - ++it; - ASSERT_EQ(maps.end(), it); -} - -TEST_F(MapsTest, file_smoke) { - TemporaryFile tf; - ASSERT_TRUE(tf.fd != -1); - - ASSERT_TRUE(android::base::WriteStringToFile( - "720b29b000-720b29e000 r-xp a0000000 00:00 0 /fake.so\n" - "720b2b0000-720b2e0000 r-xp b0000000 00:00 0 /fake2.so\n" - "720b2e0000-720b2f0000 r-xp c0000000 00:00 0 /fake3.so\n", - tf.path, 0660, getuid(), getgid())); - - MapsFile maps(tf.path); - - ASSERT_TRUE(maps.Parse()); - ASSERT_EQ(3U, maps.Total()); - auto it = maps.begin(); - ASSERT_EQ(0x720b29b000U, it->start); - ASSERT_EQ(0x720b29e000U, it->end); - ASSERT_EQ(0xa0000000U, it->offset); - ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags); - ASSERT_EQ("/fake.so", it->name); - ++it; - ASSERT_EQ(0x720b2b0000U, it->start); - ASSERT_EQ(0x720b2e0000U, it->end); - ASSERT_EQ(0xb0000000U, it->offset); - ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags); - ASSERT_EQ("/fake2.so", it->name); - ++it; - ASSERT_EQ(0x720b2e0000U, it->start); - ASSERT_EQ(0x720b2f0000U, it->end); - ASSERT_EQ(0xc0000000U, it->offset); - ASSERT_EQ(PROT_READ | PROT_EXEC, it->flags); - ASSERT_EQ("/fake3.so", it->name); - ++it; - ASSERT_EQ(it, maps.end()); -} - -TEST_F(MapsTest, find) { - MapsBuffer maps("1000-2000 r--p 00000010 00:00 0 /system/lib/fake1.so\n" - "3000-4000 -w-p 00000020 00:00 0 /system/lib/fake2.so\n" - "6000-8000 --xp 00000030 00:00 0 /system/lib/fake3.so\n" - "a000-b000 rw-p 00000040 00:00 0 /system/lib/fake4.so\n" - "e000-f000 rwxp 00000050 00:00 0 /system/lib/fake5.so\n"); - ASSERT_TRUE(maps.Parse()); - ASSERT_EQ(5U, maps.Total()); - - ASSERT_TRUE(maps.Find(0x500) == nullptr); - ASSERT_TRUE(maps.Find(0x2000) == nullptr); - ASSERT_TRUE(maps.Find(0x5010) == nullptr); - ASSERT_TRUE(maps.Find(0x9a00) == nullptr); - ASSERT_TRUE(maps.Find(0xf000) == nullptr); - ASSERT_TRUE(maps.Find(0xf010) == nullptr); - - MapInfo* info = maps.Find(0x1000); - ASSERT_TRUE(info != nullptr); - ASSERT_EQ(0x1000U, info->start); - ASSERT_EQ(0x2000U, info->end); - ASSERT_EQ(0x10U, info->offset); - ASSERT_EQ(PROT_READ, info->flags); - ASSERT_EQ("/system/lib/fake1.so", info->name); - - info = maps.Find(0x3020); - ASSERT_TRUE(info != nullptr); - ASSERT_EQ(0x3000U, info->start); - ASSERT_EQ(0x4000U, info->end); - ASSERT_EQ(0x20U, info->offset); - ASSERT_EQ(PROT_WRITE, info->flags); - ASSERT_EQ("/system/lib/fake2.so", info->name); - - info = maps.Find(0x6020); - ASSERT_TRUE(info != nullptr); - ASSERT_EQ(0x6000U, info->start); - ASSERT_EQ(0x8000U, info->end); - ASSERT_EQ(0x30U, info->offset); - ASSERT_EQ(PROT_EXEC, info->flags); - ASSERT_EQ("/system/lib/fake3.so", info->name); - - info = maps.Find(0xafff); - ASSERT_TRUE(info != nullptr); - ASSERT_EQ(0xa000U, info->start); - ASSERT_EQ(0xb000U, info->end); - ASSERT_EQ(0x40U, info->offset); - ASSERT_EQ(PROT_READ | PROT_WRITE, info->flags); - ASSERT_EQ("/system/lib/fake4.so", info->name); - - info = maps.Find(0xe500); - ASSERT_TRUE(info != nullptr); - ASSERT_EQ(0xe000U, info->start); - ASSERT_EQ(0xf000U, info->end); - ASSERT_EQ(0x50U, info->offset); - ASSERT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags); - ASSERT_EQ("/system/lib/fake5.so", info->name); -} diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h index 4f898fae1..e05736bec 100644 --- a/libunwindstack/tests/MemoryFake.h +++ b/libunwindstack/tests/MemoryFake.h @@ -34,7 +34,19 @@ class MemoryFake : public Memory { void SetMemory(uint64_t addr, const void* memory, size_t length); - void SetData(uint64_t addr, uint32_t value) { + void SetData8(uint64_t addr, uint8_t value) { + SetMemory(addr, &value, sizeof(value)); + } + + void SetData16(uint64_t addr, uint16_t value) { + SetMemory(addr, &value, sizeof(value)); + } + + void SetData32(uint64_t addr, uint32_t value) { + SetMemory(addr, &value, sizeof(value)); + } + + void SetData64(uint64_t addr, uint64_t value) { SetMemory(addr, &value, sizeof(value)); } diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp index ebc611867..870ca19d2 100644 --- a/libunwindstack/tests/MemoryFileTest.cpp +++ b/libunwindstack/tests/MemoryFileTest.cpp @@ -20,12 +20,9 @@ #include "Memory.h" -#include "LogFake.h" - class MemoryFileTest : public ::testing::Test { protected: void SetUp() override { - ResetLogs(); tf_ = new TemporaryFile; } @@ -86,6 +83,7 @@ TEST_F(MemoryFileTest, offset_pagesize_aligned) { data += static_cast((i % 10) + '0'); } ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd)); + ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize)); std::vector buffer(11); ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); @@ -106,6 +104,7 @@ TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) { data += static_cast((i % 10) + '0'); } ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd)); + ASSERT_TRUE(memory_.Init(tf_->path, 2 * pagesize + 10)); std::vector buffer(11); ASSERT_TRUE(memory_.Read(0, buffer.data(), 10)); @@ -165,3 +164,111 @@ TEST_F(MemoryFileTest, read_string_error) { // This should fail because there is no terminating \0 ASSERT_FALSE(memory_.ReadString(0, &name)); } + +TEST_F(MemoryFileTest, read_past_file_within_mapping) { + size_t pagesize = getpagesize(); + + ASSERT_TRUE(pagesize > 100); + std::vector buffer(pagesize - 100); + for (size_t i = 0; i < pagesize - 100; i++) { + buffer[i] = static_cast((i % 0x5e) + 0x20); + } + ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size())); + + ASSERT_TRUE(memory_.Init(tf_->path, 0)); + + for (size_t i = 0; i < 100; i++) { + uint8_t value; + ASSERT_FALSE(memory_.Read(buffer.size() + i, &value, 1)) << "Should have failed at value " << i; + } +} + +TEST_F(MemoryFileTest, map_partial_offset_aligned) { + size_t pagesize = getpagesize(); + std::vector buffer(pagesize * 10); + for (size_t i = 0; i < pagesize * 10; i++) { + buffer[i] = i / pagesize + 1; + } + ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size())); + + // Map in only two pages of the data, and after the first page. + ASSERT_TRUE(memory_.Init(tf_->path, pagesize, pagesize * 2)); + + std::vector read_buffer(pagesize * 2); + // Make sure that reading after mapped data is a failure. + ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2)); + for (size_t i = 0; i < pagesize; i++) { + ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; + } + for (size_t i = pagesize; i < pagesize * 2; i++) { + ASSERT_EQ(3, read_buffer[i]) << "Failed at byte " << i; + } +} + +TEST_F(MemoryFileTest, map_partial_offset_unaligned) { + size_t pagesize = getpagesize(); + ASSERT_TRUE(pagesize > 0x100); + std::vector buffer(pagesize * 10); + for (size_t i = 0; i < buffer.size(); i++) { + buffer[i] = i / pagesize + 1; + } + ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size())); + + // Map in only two pages of the data, and after the first page. + ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, pagesize * 2)); + + std::vector read_buffer(pagesize * 2); + // Make sure that reading after mapped data is a failure. + ASSERT_FALSE(memory_.Read(pagesize * 2, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 2)); + for (size_t i = 0; i < pagesize - 0x100; i++) { + ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; + } + for (size_t i = pagesize - 0x100; i < 2 * pagesize - 0x100; i++) { + ASSERT_EQ(3, read_buffer[i]) << "Failed at byte " << i; + } + for (size_t i = 2 * pagesize - 0x100; i < pagesize * 2; i++) { + ASSERT_EQ(4, read_buffer[i]) << "Failed at byte " << i; + } +} + +TEST_F(MemoryFileTest, map_overflow) { + size_t pagesize = getpagesize(); + ASSERT_TRUE(pagesize > 0x100); + std::vector buffer(pagesize * 10); + for (size_t i = 0; i < buffer.size(); i++) { + buffer[i] = i / pagesize + 1; + } + ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size())); + + // Map in only two pages of the data, and after the first page. + ASSERT_TRUE(memory_.Init(tf_->path, pagesize + 0x100, UINT64_MAX)); + + std::vector read_buffer(pagesize * 10); + ASSERT_FALSE(memory_.Read(pagesize * 9 - 0x100 + 1, read_buffer.data(), 1)); + ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize * 9 - 0x100)); +} + +TEST_F(MemoryFileTest, init_reinit) { + size_t pagesize = getpagesize(); + std::vector buffer(pagesize * 2); + for (size_t i = 0; i < buffer.size(); i++) { + buffer[i] = i / pagesize + 1; + } + ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size())); + + ASSERT_TRUE(memory_.Init(tf_->path, 0)); + std::vector read_buffer(buffer.size()); + ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize)); + for (size_t i = 0; i < pagesize; i++) { + ASSERT_EQ(1, read_buffer[i]) << "Failed at byte " << i; + } + + // Now reinit. + ASSERT_TRUE(memory_.Init(tf_->path, pagesize)); + ASSERT_TRUE(memory_.Read(0, read_buffer.data(), pagesize)); + for (size_t i = 0; i < pagesize; i++) { + ASSERT_EQ(2, read_buffer[i]) << "Failed at byte " << i; + } +} diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp index 49ece9dda..0ba5f1c37 100644 --- a/libunwindstack/tests/MemoryLocalTest.cpp +++ b/libunwindstack/tests/MemoryLocalTest.cpp @@ -23,16 +23,7 @@ #include "Memory.h" -#include "LogFake.h" - -class MemoryLocalTest : public ::testing::Test { - protected: - void SetUp() override { - ResetLogs(); - } -}; - -TEST_F(MemoryLocalTest, read) { +TEST(MemoryLocalTest, read) { std::vector src(1024); memset(src.data(), 0x4c, 1024); @@ -56,7 +47,7 @@ TEST_F(MemoryLocalTest, read) { } } -TEST_F(MemoryLocalTest, read_string) { +TEST(MemoryLocalTest, read_string) { std::string name("string_in_memory"); MemoryLocal local; @@ -75,7 +66,7 @@ TEST_F(MemoryLocalTest, read_string) { ASSERT_FALSE(local.ReadString(reinterpret_cast(&name[7]), &dst_name, 9)); } -TEST_F(MemoryLocalTest, read_illegal) { +TEST(MemoryLocalTest, read_illegal) { MemoryLocal local; std::vector dst(100); diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp index fcae3a4c4..d636ec497 100644 --- a/libunwindstack/tests/MemoryRangeTest.cpp +++ b/libunwindstack/tests/MemoryRangeTest.cpp @@ -23,13 +23,11 @@ #include "Memory.h" -#include "LogFake.h" #include "MemoryFake.h" class MemoryRangeTest : public ::testing::Test { protected: void SetUp() override { - ResetLogs(); memory_ = new MemoryFake; } diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp index 49244a50e..7664c3e00 100644 --- a/libunwindstack/tests/MemoryRemoteTest.cpp +++ b/libunwindstack/tests/MemoryRemoteTest.cpp @@ -33,14 +33,8 @@ #include "Memory.h" -#include "LogFake.h" - class MemoryRemoteTest : public ::testing::Test { protected: - void SetUp() override { - ResetLogs(); - } - static uint64_t NanoTime() { struct timespec t = { 0, 0 }; clock_gettime(CLOCK_MONOTONIC, &t); diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp index f9e8b0ed8..0dac2788c 100644 --- a/libunwindstack/tests/RegsTest.cpp +++ b/libunwindstack/tests/RegsTest.cpp @@ -18,50 +18,249 @@ #include +#include "Elf.h" +#include "ElfInterface.h" +#include "MapInfo.h" #include "Regs.h" -class RegsTest : public ::testing::Test {}; +#include "MemoryFake.h" -TEST_F(RegsTest, regs32) { - Regs32 regs32(10, 20, 30); +class ElfFake : public Elf { + public: + ElfFake(Memory* memory) : Elf(memory) { valid_ = true; } + virtual ~ElfFake() = default; - ASSERT_EQ(10U, regs32.pc_reg()); - ASSERT_EQ(20U, regs32.sp_reg()); - ASSERT_EQ(30U, regs32.total_regs()); + void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); } +}; - uint32_t* raw = reinterpret_cast(regs32.raw_data()); - for (size_t i = 0; i < 30; i++) { - raw[i] = 0xf0000000 + i; +class ElfInterfaceFake : public ElfInterface { + public: + ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {} + virtual ~ElfInterfaceFake() = default; + + void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; } + + bool Init() override { return false; } + void InitHeaders() override {} + bool GetSoname(std::string*) override { return false; } + bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; } + bool Step(uint64_t, Regs*, Memory*) override { return false; } +}; + +template +class RegsTestTmpl : public RegsTmpl { + public: + RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp) + : RegsTmpl(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {} + RegsTestTmpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc) + : RegsTmpl(total_regs, regs_sp, return_loc) {} + virtual ~RegsTestTmpl() = default; + + uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; } +}; + +class RegsTest : public ::testing::Test { + protected: + void SetUp() override { + memory_ = new MemoryFake; + elf_.reset(new ElfFake(memory_)); + elf_interface_ = new ElfInterfaceFake(elf_->memory()); + elf_->set_elf_interface(elf_interface_); } - ASSERT_EQ(0xf000000aU, regs32.pc()); - ASSERT_EQ(0xf0000014U, regs32.sp()); + template + void regs_rel_pc(); - ASSERT_EQ(0xf0000001U, regs32[1]); - regs32[1] = 10; - ASSERT_EQ(10U, regs32[1]); + template + void regs_return_address_register(); - ASSERT_EQ(0xf000001dU, regs32[29]); + ElfInterfaceFake* elf_interface_; + MemoryFake* memory_; + std::unique_ptr elf_; +}; + +TEST_F(RegsTest, regs32) { + RegsTestTmpl regs32(50, 10); + ASSERT_EQ(50U, regs32.total_regs()); + ASSERT_EQ(10U, regs32.sp_reg()); + + uint32_t* raw = reinterpret_cast(regs32.RawData()); + for (size_t i = 0; i < 50; i++) { + raw[i] = 0xf0000000 + i; + } + regs32.set_pc(0xf0120340); + regs32.set_sp(0xa0ab0cd0); + + for (size_t i = 0; i < 50; i++) { + ASSERT_EQ(0xf0000000U + i, regs32[i]) << "Failed comparing register " << i; + } + + ASSERT_EQ(0xf0120340U, regs32.pc()); + ASSERT_EQ(0xa0ab0cd0U, regs32.sp()); + + regs32[32] = 10; + ASSERT_EQ(10U, regs32[32]); } TEST_F(RegsTest, regs64) { - Regs64 regs64(10, 20, 30); - - ASSERT_EQ(10U, regs64.pc_reg()); - ASSERT_EQ(20U, regs64.sp_reg()); + RegsTestTmpl regs64(30, 12); ASSERT_EQ(30U, regs64.total_regs()); + ASSERT_EQ(12U, regs64.sp_reg()); - uint64_t* raw = reinterpret_cast(regs64.raw_data()); + uint64_t* raw = reinterpret_cast(regs64.RawData()); for (size_t i = 0; i < 30; i++) { raw[i] = 0xf123456780000000UL + i; } + regs64.set_pc(0xf123456780102030UL); + regs64.set_sp(0xa123456780a0b0c0UL); - ASSERT_EQ(0xf12345678000000aUL, regs64.pc()); - ASSERT_EQ(0xf123456780000014UL, regs64.sp()); + for (size_t i = 0; i < 30; i++) { + ASSERT_EQ(0xf123456780000000U + i, regs64[i]) << "Failed reading register " << i; + } + + ASSERT_EQ(0xf123456780102030UL, regs64.pc()); + ASSERT_EQ(0xa123456780a0b0c0UL, regs64.sp()); - ASSERT_EQ(0xf123456780000008U, regs64[8]); regs64[8] = 10; ASSERT_EQ(10U, regs64[8]); - - ASSERT_EQ(0xf12345678000001dU, regs64[29]); +} + +template +void RegsTest::regs_rel_pc() { + RegsTestTmpl regs(30, 12); + + elf_interface_->set_load_bias(0); + MapInfo map_info{.start = 0x1000, .end = 0x2000}; + regs.set_pc(0x1101); + ASSERT_EQ(0x101U, regs.GetRelPc(elf_.get(), &map_info)); + elf_interface_->set_load_bias(0x3000); + ASSERT_EQ(0x3101U, regs.GetRelPc(elf_.get(), &map_info)); +} + +TEST_F(RegsTest, regs32_rel_pc) { + regs_rel_pc(); +} + +TEST_F(RegsTest, regs64_rel_pc) { + regs_rel_pc(); +} + +template +void RegsTest::regs_return_address_register() { + RegsTestTmpl regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5)); + + regs[5] = 0x12345; + uint64_t value; + ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); + ASSERT_EQ(0x12345U, value); +} + +TEST_F(RegsTest, regs32_return_address_register) { + regs_return_address_register(); +} + +TEST_F(RegsTest, regs64_return_address_register) { + regs_return_address_register(); +} + +TEST_F(RegsTest, regs32_return_address_sp_offset) { + RegsTestTmpl regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2)); + + regs.set_sp(0x2002); + memory_->SetData32(0x2000, 0x12345678); + uint64_t value; + ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); + ASSERT_EQ(0x12345678U, value); +} + +TEST_F(RegsTest, regs64_return_address_sp_offset) { + RegsTestTmpl regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8)); + + regs.set_sp(0x2008); + memory_->SetData64(0x2000, 0x12345678aabbccddULL); + uint64_t value; + ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value)); + ASSERT_EQ(0x12345678aabbccddULL, value); +} + +TEST_F(RegsTest, rel_pc) { + RegsArm64 arm64; + ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get())); + ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x4, elf_.get())); + ASSERT_EQ(0x3U, arm64.GetAdjustedPc(0x3, elf_.get())); + ASSERT_EQ(0x2U, arm64.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(0x1U, arm64.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x0, elf_.get())); + + RegsX86 x86; + ASSERT_EQ(0xffU, x86.GetAdjustedPc(0x100, elf_.get())); + ASSERT_EQ(0x1U, x86.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(0x0U, x86.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0x0U, x86.GetAdjustedPc(0x0, elf_.get())); + + RegsX86_64 x86_64; + ASSERT_EQ(0xffU, x86_64.GetAdjustedPc(0x100, elf_.get())); + ASSERT_EQ(0x1U, x86_64.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x0, elf_.get())); +} + +TEST_F(RegsTest, rel_pc_arm) { + RegsArm arm; + + // Check fence posts. + elf_interface_->set_load_bias(0); + ASSERT_EQ(3U, arm.GetAdjustedPc(0x5, elf_.get())); + ASSERT_EQ(4U, arm.GetAdjustedPc(0x4, elf_.get())); + ASSERT_EQ(3U, arm.GetAdjustedPc(0x3, elf_.get())); + ASSERT_EQ(2U, arm.GetAdjustedPc(0x2, elf_.get())); + ASSERT_EQ(1U, arm.GetAdjustedPc(0x1, elf_.get())); + ASSERT_EQ(0U, arm.GetAdjustedPc(0x0, elf_.get())); + + elf_interface_->set_load_bias(0x100); + ASSERT_EQ(0xffU, arm.GetAdjustedPc(0xff, elf_.get())); + ASSERT_EQ(0x103U, arm.GetAdjustedPc(0x105, elf_.get())); + ASSERT_EQ(0x104U, arm.GetAdjustedPc(0x104, elf_.get())); + ASSERT_EQ(0x103U, arm.GetAdjustedPc(0x103, elf_.get())); + ASSERT_EQ(0x102U, arm.GetAdjustedPc(0x102, elf_.get())); + ASSERT_EQ(0x101U, arm.GetAdjustedPc(0x101, elf_.get())); + ASSERT_EQ(0x100U, arm.GetAdjustedPc(0x100, elf_.get())); + + // Check thumb instructions handling. + elf_interface_->set_load_bias(0); + memory_->SetData32(0x2000, 0); + ASSERT_EQ(0x2003U, arm.GetAdjustedPc(0x2005, elf_.get())); + memory_->SetData32(0x2000, 0xe000f000); + ASSERT_EQ(0x2001U, arm.GetAdjustedPc(0x2005, elf_.get())); + + elf_interface_->set_load_bias(0x400); + memory_->SetData32(0x2100, 0); + ASSERT_EQ(0x2503U, arm.GetAdjustedPc(0x2505, elf_.get())); + memory_->SetData32(0x2100, 0xf111f111); + ASSERT_EQ(0x2501U, arm.GetAdjustedPc(0x2505, elf_.get())); +} + +TEST_F(RegsTest, elf_invalid) { + Elf invalid_elf(new MemoryFake); + RegsArm regs_arm; + RegsArm64 regs_arm64; + RegsX86 regs_x86; + RegsX86_64 regs_x86_64; + MapInfo map_info{.start = 0x1000, .end = 0x2000}; + + regs_arm.set_pc(0x1500); + ASSERT_EQ(0x500U, regs_arm.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, &invalid_elf)); + + regs_arm64.set_pc(0x1600); + ASSERT_EQ(0x600U, regs_arm64.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, &invalid_elf)); + + regs_x86.set_pc(0x1700); + ASSERT_EQ(0x700U, regs_x86.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, &invalid_elf)); + + regs_x86_64.set_pc(0x1800); + ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info)); + ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf)); } diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/unwind_info.cpp new file mode 100644 index 000000000..6f158b0e9 --- /dev/null +++ b/libunwindstack/unwind_info.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ArmExidx.h" +#include "Elf.h" +#include "ElfInterface.h" +#include "ElfInterfaceArm.h" +#include "Log.h" + +void DumpArm(ElfInterfaceArm* interface) { + if (interface == nullptr) { + printf("No ARM Unwind Information.\n\n"); + return; + } + + printf("ARM Unwind Information:\n"); + for (const auto& entry : interface->pt_loads()) { + uint64_t load_bias = entry.second.table_offset; + printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias, + entry.second.table_size + load_bias); + for (auto addr : *interface) { + std::string name; + printf(" PC 0x%" PRIx64, addr + load_bias); + uint64_t func_offset; + if (interface->GetFunctionName(addr + load_bias + 1, &name, &func_offset) && !name.empty()) { + printf(" <%s>", name.c_str()); + } + printf("\n"); + uint64_t entry; + if (!interface->FindEntry(addr + load_bias, &entry)) { + printf(" Cannot find entry for address.\n"); + continue; + } + ArmExidx arm(nullptr, interface->memory(), nullptr); + arm.set_log(true); + arm.set_log_skip_execution(true); + arm.set_log_indent(2); + if (!arm.ExtractEntryData(entry)) { + if (arm.status() != ARM_STATUS_NO_UNWIND) { + printf(" Error trying to extract data.\n"); + } + continue; + } + if (arm.data()->size() > 0) { + if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) { + printf(" Error trying to evaluate dwarf data.\n"); + } + } + } + } + printf("\n"); +} + +int main(int argc, char** argv) { + if (argc != 2) { + printf("Need to pass the name of an elf file to the program.\n"); + return 1; + } + + struct stat st; + if (stat(argv[1], &st) == -1) { + printf("Cannot stat %s: %s\n", argv[1], strerror(errno)); + return 1; + } + if (!S_ISREG(st.st_mode)) { + printf("%s is not a regular file.\n", argv[1]); + return 1; + } + if (S_ISDIR(st.st_mode)) { + printf("%s is a directory.\n", argv[1]); + return 1; + } + + // Send all log messages to stdout. + log_to_stdout(true); + + MemoryFileAtOffset* memory = new MemoryFileAtOffset; + if (!memory->Init(argv[1], 0)) { + // Initializatation failed. + printf("Failed to init\n"); + return 1; + } + + Elf elf(memory); + if (!elf.Init() || !elf.valid()) { + printf("%s is not a valid elf file.\n", argv[1]); + return 1; + } + + ElfInterface* interface = elf.interface(); + if (elf.machine_type() == EM_ARM) { + DumpArm(reinterpret_cast(interface)); + printf("\n"); + } + + return 0; +}