From 72a6fa693b1d5d0fa2a83264776d73eeeae6478d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Tue, 21 Mar 2017 12:41:17 -0700 Subject: [PATCH] Add DwarfMemory class. Bug: 23762183 Test: Passes new unit tests. Change-Id: I80293af0b5a8a042195c37a06d64082d2f7c781a --- libunwindstack/Android.bp | 2 + libunwindstack/DwarfEncoding.h | 47 +++ libunwindstack/DwarfMemory.cpp | 248 ++++++++++++ libunwindstack/DwarfMemory.h | 72 ++++ libunwindstack/tests/DwarfMemoryTest.cpp | 472 +++++++++++++++++++++++ 5 files changed, 841 insertions(+) create mode 100644 libunwindstack/DwarfEncoding.h create mode 100644 libunwindstack/DwarfMemory.cpp create mode 100644 libunwindstack/DwarfMemory.h create mode 100644 libunwindstack/tests/DwarfMemoryTest.cpp diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index ece623b07..dabeac1b9 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -47,6 +47,7 @@ cc_defaults { srcs: [ "ArmExidx.cpp", + "DwarfMemory.cpp", "Elf.cpp", "ElfInterface.cpp", "ElfInterfaceArm.cpp", @@ -87,6 +88,7 @@ cc_defaults { srcs: [ "tests/ArmExidxDecodeTest.cpp", "tests/ArmExidxExtractTest.cpp", + "tests/DwarfMemoryTest.cpp", "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h new file mode 100644 index 000000000..0ff3b8c2c --- /dev/null +++ b/libunwindstack/DwarfEncoding.h @@ -0,0 +1,47 @@ +/* + * 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_DWARF_ENCODING_H +#define _LIBUNWINDSTACK_DWARF_ENCODING_H + +#include + +enum DwarfEncoding : uint8_t { + DW_EH_PE_omit = 0xff, + + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0a, + DW_EH_PE_sdata4 = 0x0b, + DW_EH_PE_sdata8 = 0x0c, + + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + // The following are special values used to encode CFA and OP operands. + DW_EH_PE_udata1 = 0x0d, + DW_EH_PE_sdata1 = 0x0e, + DW_EH_PE_block = 0x0f, +}; + +#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp new file mode 100644 index 000000000..11806eace --- /dev/null +++ b/libunwindstack/DwarfMemory.cpp @@ -0,0 +1,248 @@ +/* + * 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 "DwarfEncoding.h" +#include "DwarfMemory.h" +#include "Memory.h" + +bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) { + if (!memory_->Read(cur_offset_, dst, num_bytes)) { + return false; + } + cur_offset_ += num_bytes; + return true; +} + +template +bool DwarfMemory::ReadSigned(uint64_t* value) { + SignedType signed_value; + if (!ReadBytes(&signed_value, sizeof(SignedType))) { + return false; + } + *value = static_cast(signed_value); + return true; +} + +bool DwarfMemory::ReadULEB128(uint64_t* value) { + uint64_t cur_value = 0; + uint64_t shift = 0; + uint8_t byte; + do { + if (!ReadBytes(&byte, 1)) { + return false; + } + cur_value += static_cast(byte & 0x7f) << shift; + shift += 7; + } while (byte & 0x80); + *value = cur_value; + return true; +} + +bool DwarfMemory::ReadSLEB128(int64_t* value) { + uint64_t cur_value = 0; + uint64_t shift = 0; + uint8_t byte; + do { + if (!ReadBytes(&byte, 1)) { + return false; + } + cur_value += static_cast(byte & 0x7f) << shift; + shift += 7; + } while (byte & 0x80); + if (byte & 0x40) { + // Negative value, need to sign extend. + cur_value |= static_cast(-1) << shift; + } + *value = static_cast(cur_value); + return true; +} + +template +size_t DwarfMemory::GetEncodedSize(uint8_t encoding) { + switch (encoding & 0x0f) { + case DW_EH_PE_absptr: + return sizeof(AddressType); + case DW_EH_PE_udata1: + case DW_EH_PE_sdata1: + return 1; + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + return 2; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + return 4; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + return 8; + case DW_EH_PE_uleb128: + case DW_EH_PE_sleb128: + default: + return 0; + } +} + +bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) { + assert((encoding & 0x0f) == 0); + assert(encoding != DW_EH_PE_aligned); + + // Handle the encoding. + switch (encoding) { + case DW_EH_PE_absptr: + // Nothing to do. + break; + case DW_EH_PE_pcrel: + if (pc_offset_ == static_cast(-1)) { + // Unsupported encoding. + return false; + } + *value += pc_offset_; + break; + case DW_EH_PE_textrel: + if (text_offset_ == static_cast(-1)) { + // Unsupported encoding. + return false; + } + *value += text_offset_; + break; + case DW_EH_PE_datarel: + if (data_offset_ == static_cast(-1)) { + // Unsupported encoding. + return false; + } + *value += data_offset_; + break; + case DW_EH_PE_funcrel: + if (func_offset_ == static_cast(-1)) { + // Unsupported encoding. + return false; + } + *value += func_offset_; + break; + default: + return false; + } + + return true; +} + +template +bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) { + if (encoding == DW_EH_PE_omit) { + *value = 0; + return true; + } else if (encoding == DW_EH_PE_aligned) { + if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) { + return false; + } + cur_offset_ &= -sizeof(AddressType); + + if (sizeof(AddressType) != sizeof(uint64_t)) { + *value = 0; + } + return ReadBytes(value, sizeof(AddressType)); + } + + // Get the data. + switch (encoding & 0x0f) { + case DW_EH_PE_absptr: + if (sizeof(AddressType) != sizeof(uint64_t)) { + *value = 0; + } + if (!ReadBytes(value, sizeof(AddressType))) { + return false; + } + break; + case DW_EH_PE_uleb128: + if (!ReadULEB128(value)) { + return false; + } + break; + case DW_EH_PE_sleb128: + int64_t signed_value; + if (!ReadSLEB128(&signed_value)) { + return false; + } + *value = static_cast(signed_value); + break; + case DW_EH_PE_udata1: { + uint8_t value8; + if (!ReadBytes(&value8, 1)) { + return false; + } + *value = value8; + } break; + case DW_EH_PE_sdata1: + if (!ReadSigned(value)) { + return false; + } + break; + case DW_EH_PE_udata2: { + uint16_t value16; + if (!ReadBytes(&value16, 2)) { + return false; + } + *value = value16; + } break; + case DW_EH_PE_sdata2: + if (!ReadSigned(value)) { + return false; + } + break; + case DW_EH_PE_udata4: { + uint32_t value32; + if (!ReadBytes(&value32, 4)) { + return false; + } + *value = value32; + } break; + case DW_EH_PE_sdata4: + if (!ReadSigned(value)) { + return false; + } + break; + case DW_EH_PE_udata8: + if (!ReadBytes(value, sizeof(uint64_t))) { + return false; + } + break; + case DW_EH_PE_sdata8: + if (!ReadSigned(value)) { + return false; + } + break; + default: + return false; + } + + return AdjustEncodedValue(encoding & 0xf0, value); +} + +// Instantiate all of the needed template functions. +template bool DwarfMemory::ReadSigned(uint64_t*); +template bool DwarfMemory::ReadSigned(uint64_t*); +template bool DwarfMemory::ReadSigned(uint64_t*); +template bool DwarfMemory::ReadSigned(uint64_t*); + +template size_t DwarfMemory::GetEncodedSize(uint8_t); +template size_t DwarfMemory::GetEncodedSize(uint8_t); + +template bool DwarfMemory::ReadEncodedValue(uint8_t, uint64_t*); +template bool DwarfMemory::ReadEncodedValue(uint8_t, uint64_t*); diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h new file mode 100644 index 000000000..a304dd932 --- /dev/null +++ b/libunwindstack/DwarfMemory.h @@ -0,0 +1,72 @@ +/* + * 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_DWARF_MEMORY_H +#define _LIBUNWINDSTACK_DWARF_MEMORY_H + +#include + +// Forward declarations. +class Memory; + +class DwarfMemory { + public: + DwarfMemory(Memory* memory) : memory_(memory) {} + virtual ~DwarfMemory() = default; + + bool ReadBytes(void* dst, size_t num_bytes); + + template + bool ReadSigned(uint64_t* value); + + bool ReadULEB128(uint64_t* value); + + bool ReadSLEB128(int64_t* value); + + template + size_t GetEncodedSize(uint8_t encoding); + + bool AdjustEncodedValue(uint8_t encoding, uint64_t* value); + + template + bool ReadEncodedValue(uint8_t encoding, uint64_t* value); + + uint64_t cur_offset() { return cur_offset_; } + void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; } + + void set_pc_offset(uint64_t offset) { pc_offset_ = offset; } + void clear_pc_offset() { pc_offset_ = static_cast(-1); } + + void set_data_offset(uint64_t offset) { data_offset_ = offset; } + void clear_data_offset() { data_offset_ = static_cast(-1); } + + void set_func_offset(uint64_t offset) { func_offset_ = offset; } + void clear_func_offset() { func_offset_ = static_cast(-1); } + + void set_text_offset(uint64_t offset) { text_offset_ = offset; } + void clear_text_offset() { text_offset_ = static_cast(-1); } + + private: + Memory* memory_; + uint64_t cur_offset_ = 0; + + uint64_t pc_offset_ = static_cast(-1); + uint64_t data_offset_ = static_cast(-1); + uint64_t func_offset_ = static_cast(-1); + uint64_t text_offset_ = static_cast(-1); +}; + +#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp new file mode 100644 index 000000000..4877f36a7 --- /dev/null +++ b/libunwindstack/tests/DwarfMemoryTest.cpp @@ -0,0 +1,472 @@ +/* + * 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 "DwarfMemory.h" + +#include "MemoryFake.h" + +class DwarfMemoryTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + dwarf_mem_.reset(new DwarfMemory(&memory_)); + } + + template + void GetEncodedSizeTest(uint8_t value, size_t expected); + template + void ReadEncodedValue_omit(); + template + void ReadEncodedValue_leb128(); + template + void ReadEncodedValue_data1(); + template + void ReadEncodedValue_data2(); + template + void ReadEncodedValue_data4(); + template + void ReadEncodedValue_data8(); + template + void ReadEncodedValue_non_zero_adjust(); + template + void ReadEncodedValue_overflow(); + + MemoryFake memory_; + std::unique_ptr dwarf_mem_; +}; + +TEST_F(DwarfMemoryTest, ReadBytes) { + memory_.SetMemory(0, std::vector{0x10, 0x18, 0xff, 0xfe}); + + uint8_t byte; + ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1)); + ASSERT_EQ(0x10U, byte); + ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1)); + ASSERT_EQ(0x18U, byte); + ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1)); + ASSERT_EQ(0xffU, byte); + ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1)); + ASSERT_EQ(0xfeU, byte); + ASSERT_EQ(4U, dwarf_mem_->cur_offset()); + + dwarf_mem_->set_cur_offset(2); + ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1)); + ASSERT_EQ(0xffU, byte); + ASSERT_EQ(3U, dwarf_mem_->cur_offset()); +} + +TEST_F(DwarfMemoryTest, ReadSigned_check) { + uint64_t value; + + // Signed 8 byte reads. + memory_.SetData8(0, static_cast(-10)); + memory_.SetData8(1, 200); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(-10), static_cast(value)); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(200), static_cast(value)); + + // Signed 16 byte reads. + memory_.SetData16(0x10, static_cast(-1000)); + memory_.SetData16(0x12, 50100); + dwarf_mem_->set_cur_offset(0x10); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(-1000), static_cast(value)); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(50100), static_cast(value)); + + // Signed 32 byte reads. + memory_.SetData32(0x100, static_cast(-1000000000)); + memory_.SetData32(0x104, 3000000000); + dwarf_mem_->set_cur_offset(0x100); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(-1000000000), static_cast(value)); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(3000000000), static_cast(value)); + + // Signed 64 byte reads. + memory_.SetData64(0x200, static_cast(-2000000000000LL)); + memory_.SetData64(0x208, 5000000000000LL); + dwarf_mem_->set_cur_offset(0x200); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(-2000000000000), static_cast(value)); + ASSERT_TRUE(dwarf_mem_->ReadSigned(&value)); + ASSERT_EQ(static_cast(5000000000000), static_cast(value)); +} + +TEST_F(DwarfMemoryTest, ReadULEB128) { + memory_.SetMemory(0, std::vector{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f}); + + uint64_t value; + ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value)); + ASSERT_EQ(1U, dwarf_mem_->cur_offset()); + ASSERT_EQ(1U, value); + + ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value)); + ASSERT_EQ(3U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x1200U, value); + + ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value)); + ASSERT_EQ(7U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0xfffe1ffU, value); +} + +TEST_F(DwarfMemoryTest, ReadSLEB128) { + memory_.SetMemory(0, std::vector{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f, + 0x2f, 0xbf, 0xc3, 0xf7, 0x5f}); + + int64_t value; + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(1U, dwarf_mem_->cur_offset()); + ASSERT_EQ(6U, value); + + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(2U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0xffffffffffffffc0ULL, static_cast(value)); + + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(4U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x1a02U, value); + + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(6U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0xfffffffffffff209ULL, static_cast(value)); + + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(10U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x5e3e1f9U, value); + + ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value)); + ASSERT_EQ(14U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast(value)); +} + +template +void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) { + for (size_t i = 0; i < 16; i++) { + uint8_t encoding = (i << 4) | value; + ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize(encoding)) + << "encoding 0x" << std::hex << static_cast(encoding) << " test value 0x" + << static_cast(value); + } +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) { + GetEncodedSizeTest(0, sizeof(uint32_t)); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) { + GetEncodedSizeTest(0, sizeof(uint64_t)); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_data1) { + // udata1 + GetEncodedSizeTest(0x0d, 1); + GetEncodedSizeTest(0x0d, 1); + + // sdata1 + GetEncodedSizeTest(0x0e, 1); + GetEncodedSizeTest(0x0e, 1); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_data2) { + // udata2 + GetEncodedSizeTest(0x02, 2); + GetEncodedSizeTest(0x02, 2); + + // sdata2 + GetEncodedSizeTest(0x0a, 2); + GetEncodedSizeTest(0x0a, 2); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_data4) { + // udata4 + GetEncodedSizeTest(0x03, 4); + GetEncodedSizeTest(0x03, 4); + + // sdata4 + GetEncodedSizeTest(0x0b, 4); + GetEncodedSizeTest(0x0b, 4); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_data8) { + // udata8 + GetEncodedSizeTest(0x04, 8); + GetEncodedSizeTest(0x04, 8); + + // sdata8 + GetEncodedSizeTest(0x0c, 8); + GetEncodedSizeTest(0x0c, 8); +} + +TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) { + GetEncodedSizeTest(0x01, 0); + GetEncodedSizeTest(0x01, 0); + + GetEncodedSizeTest(0x09, 0); + GetEncodedSizeTest(0x09, 0); + + GetEncodedSizeTest(0x0f, 0); + GetEncodedSizeTest(0x0f, 0); +} + +template +void DwarfMemoryTest::ReadEncodedValue_omit() { + uint64_t value = 123; + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0xff, &value)); + ASSERT_EQ(0U, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) { + uint64_t value = 100; + ASSERT_FALSE(dwarf_mem_->ReadEncodedValue(0x00, &value)); + + memory_.SetData32(0, 0x12345678); + + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x00, &value)); + ASSERT_EQ(4U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x12345678U, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) { + uint64_t value = 100; + ASSERT_FALSE(dwarf_mem_->ReadEncodedValue(0x00, &value)); + + memory_.SetData64(0, 0x12345678f1f2f3f4ULL); + + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x00, &value)); + ASSERT_EQ(8U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x12345678f1f2f3f4ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) { + uint64_t value = 100; + dwarf_mem_->set_cur_offset(1); + ASSERT_FALSE(dwarf_mem_->ReadEncodedValue(0x50, &value)); + + memory_.SetData32(4, 0x12345678); + + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x50, &value)); + ASSERT_EQ(8U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x12345678U, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) { + uint64_t value = 100; + dwarf_mem_->set_cur_offset(1); + ASSERT_FALSE(dwarf_mem_->ReadEncodedValue(0x50, &value)); + + memory_.SetData64(8, 0x12345678f1f2f3f4ULL); + + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x50, &value)); + ASSERT_EQ(16U, dwarf_mem_->cur_offset()); + ASSERT_EQ(0x12345678f1f2f3f4ULL, value); +} + +template +void DwarfMemoryTest::ReadEncodedValue_leb128() { + memory_.SetMemory(0, std::vector{0x80, 0x42}); + + uint64_t value = 100; + // uleb128 + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x01, &value)); + ASSERT_EQ(0x2100U, value); + + dwarf_mem_->set_cur_offset(0); + // sleb128 + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x09, &value)); + ASSERT_EQ(0xffffffffffffe100ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128(); } + +template +void DwarfMemoryTest::ReadEncodedValue_data1() { + memory_.SetData8(0, 0xe0); + + uint64_t value = 0; + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x0d, &value)); + ASSERT_EQ(0xe0U, value); + + dwarf_mem_->set_cur_offset(0); + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x0e, &value)); + ASSERT_EQ(0xffffffffffffffe0ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1(); } + +template +void DwarfMemoryTest::ReadEncodedValue_data2() { + memory_.SetData16(0, 0xe000); + + uint64_t value = 0; + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x02, &value)); + ASSERT_EQ(0xe000U, value); + + dwarf_mem_->set_cur_offset(0); + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x0a, &value)); + ASSERT_EQ(0xffffffffffffe000ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2(); } + +template +void DwarfMemoryTest::ReadEncodedValue_data4() { + memory_.SetData32(0, 0xe0000000); + + uint64_t value = 0; + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x03, &value)); + ASSERT_EQ(0xe0000000U, value); + + dwarf_mem_->set_cur_offset(0); + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x0b, &value)); + ASSERT_EQ(0xffffffffe0000000ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4(); } + +template +void DwarfMemoryTest::ReadEncodedValue_data8() { + memory_.SetData64(0, 0xe000000000000000ULL); + + uint64_t value = 0; + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x04, &value)); + ASSERT_EQ(0xe000000000000000ULL, value); + + dwarf_mem_->set_cur_offset(0); + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x0c, &value)); + ASSERT_EQ(0xe000000000000000ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8(); } + +TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8(); } + +template +void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() { + memory_.SetData64(0, 0xe000000000000000ULL); + + uint64_t value = 0; + dwarf_mem_->set_pc_offset(0x2000); + ASSERT_TRUE(dwarf_mem_->ReadEncodedValue(0x14, &value)); + ASSERT_EQ(0xe000000000002000ULL, value); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) { + ReadEncodedValue_non_zero_adjust(); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) { + ReadEncodedValue_non_zero_adjust(); +} + +template +void DwarfMemoryTest::ReadEncodedValue_overflow() { + memory_.SetData64(0, 0); + + uint64_t value = 0; + dwarf_mem_->set_cur_offset(UINT64_MAX); + ASSERT_FALSE(dwarf_mem_->ReadEncodedValue(0x50, &value)); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) { + ReadEncodedValue_overflow(); +} + +TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) { + ReadEncodedValue_overflow(); +} + +TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) { + uint64_t value = 0x1234; + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value)); + ASSERT_EQ(0x1234U, value); +} + +TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) { + uint64_t value = 0x1234; + ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value)); + + dwarf_mem_->set_pc_offset(0x2000); + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value)); + ASSERT_EQ(0x3234U, value); + + dwarf_mem_->set_pc_offset(static_cast(-4)); + value = 0x1234; + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value)); + ASSERT_EQ(0x1230U, value); +} + +TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) { + uint64_t value = 0x8234; + ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value)); + + dwarf_mem_->set_text_offset(0x1000); + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value)); + ASSERT_EQ(0x9234U, value); + + dwarf_mem_->set_text_offset(static_cast(-16)); + value = 0x8234; + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value)); + ASSERT_EQ(0x8224U, value); +} + +TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) { + uint64_t value = 0xb234; + ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value)); + + dwarf_mem_->set_data_offset(0x1200); + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value)); + ASSERT_EQ(0xc434U, value); + + dwarf_mem_->set_data_offset(static_cast(-256)); + value = 0xb234; + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value)); + ASSERT_EQ(0xb134U, value); +} + +TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) { + uint64_t value = 0x15234; + ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value)); + + dwarf_mem_->set_func_offset(0x60000); + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value)); + ASSERT_EQ(0x75234U, value); + + dwarf_mem_->set_func_offset(static_cast(-4096)); + value = 0x15234; + ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value)); + ASSERT_EQ(0x14234U, value); +}