diff options
author | Christopher Ferris <cferris@google.com> | 2024-01-10 20:05:49 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2024-01-10 20:05:49 +0000 |
commit | 0a331d84481ddf6934b22a0ca04217ca7c50dde2 (patch) | |
tree | 7d0b293f70a327b91994fc363b6f811082990076 | |
parent | bd6137266f30dba61969a795c241a2a4ec7ab381 (diff) | |
parent | 4ca86d028f6b6c9bca1d4895ec73f5592ab49309 (diff) | |
download | unwinding-0a331d84481ddf6934b22a0ca04217ca7c50dde2.tar.gz |
Add support for compressed section data. am: 7a1247b5db am: 4ca86d028f
Original change: https://android-review.googlesource.com/c/platform/system/unwinding/+/2851606
Change-Id: Ieb4c37b6fb37b3f22bafd4c6322c7c3ee850483e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
28 files changed, 380 insertions, 6 deletions
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 51b6c0b..f0b4f2e 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -127,6 +127,8 @@ cc_defaults { whole_static_libs: [ "librustc_demangle_static", + "libz_static", + "libzstd", ], shared_libs: [ @@ -395,6 +397,7 @@ cc_defaults { "tests/files/boot_arm.oat.gnu_debugdata.xz.one-block", "tests/files/elf32.xz", "tests/files/elf64.xz", + "tests/files/libs/*", "offline_files/common/*", "offline_files/apk_rorx_arm64/*", "offline_files/apk_rorx_unreadable_arm64/*", @@ -437,6 +440,8 @@ cc_defaults { "offline_files/maps_compiled_arm64/28648/*", "offline_files/maps_compiled_arm64/28656_oat_odex_jar/*", "offline_files/maps_compiled_arm64/28667/*", + "offline_files/zlib_compress_arm/*", + "offline_files/zstd_compress_arm/*", ], target: { diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md index ff46829..c042ec6 100644 --- a/libunwindstack/AndroidVersions.md +++ b/libunwindstack/AndroidVersions.md @@ -130,3 +130,7 @@ included in linker arguments when using lld. If the apk file is readable, or dlopen'ing the shared library creates a read-only map of the elf data, and a read-executable map of the code, the offset will be displayed properly without this fix. + +## Android 15 ("V", API level 35) +* Added support for a compressed .debug\_frame, compressed with either zlib or + zstd. diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index 5901f08..8dcee5b 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -21,10 +21,14 @@ #include <string> #include <utility> +#include <zlib.h> +#include <zstd.h> + #include <unwindstack/DwarfError.h> #include <unwindstack/DwarfSection.h> #include <unwindstack/ElfInterface.h> #include <unwindstack/Log.h> +#include <unwindstack/Memory.h> #include <unwindstack/Regs.h> #include "DwarfDebugFrame.h" @@ -90,12 +94,75 @@ std::shared_ptr<Memory> ElfInterface::CreateGnuDebugdataMemory() { return decompressed; } +static bool ZlibDecompress(uint8_t* compressed_data, size_t compressed_size, MemoryBuffer* memory) { + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + if (inflateInit(&stream) != Z_OK) { + return false; + } + stream.next_in = compressed_data; + stream.avail_in = compressed_size; + stream.next_out = memory->Data(); + stream.avail_out = memory->Size(); + int ret = inflate(&stream, Z_FINISH); + if (inflateEnd(&stream) != Z_OK) { + return false; + } + return ret == Z_STREAM_END; +} + +static bool ZstdDecompress(uint8_t* compressed_data, size_t compressed_size, MemoryBuffer* memory) { + size_t decompress_size = + ZSTD_decompress(memory->Data(), memory->Size(), compressed_data, compressed_size); + return memory->Size() == decompress_size; +} + +template <typename ChdrType> +std::shared_ptr<Memory> CreateMemoryFromCompressedSection(SectionInfo& info, + std::shared_ptr<Memory>& elf_memory) { + if (info.size < sizeof(ChdrType)) { + return nullptr; + } + + uint8_t* compressed_data = elf_memory->GetPtr(info.offset); + std::vector<uint8_t> compressed; + if (compressed_data == nullptr || elf_memory->GetPtr(info.offset + info.size - 1) == nullptr) { + compressed.resize(info.size); + if (!elf_memory->ReadFully(info.offset, compressed.data(), info.size)) { + return nullptr; + } + compressed_data = compressed.data(); + } + + ChdrType* chdr = reinterpret_cast<ChdrType*>(compressed_data); + std::shared_ptr<MemoryBuffer> memory(new MemoryBuffer(chdr->ch_size, info.offset)); + + bool ret = false; + if (chdr->ch_type == ELFCOMPRESS_ZLIB) { + ret = ZlibDecompress(&compressed_data[sizeof(ChdrType)], info.size - sizeof(ChdrType), + memory.get()); + } else if (chdr->ch_type == ELFCOMPRESS_ZSTD) { + ret = ZstdDecompress(&compressed_data[sizeof(ChdrType)], info.size - sizeof(ChdrType), + memory.get()); + } + if (!ret) { + return nullptr; + } + // Set the section info to match the uncompressed section data. + info.size = chdr->ch_size; + info.flags &= ~SHF_COMPRESSED; + return memory; +} + template <typename ElfTypes> void ElfInterfaceImpl<ElfTypes>::InitHeaders() { if (eh_frame_hdr_info_.offset != 0) { DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_); eh_frame_.reset(eh_frame_hdr); if (!eh_frame_hdr->EhFrameInit(eh_frame_info_) || !eh_frame_->Init(eh_frame_hdr_info_)) { + eh_frame_hdr_info_ = {}; eh_frame_.reset(nullptr); } } @@ -105,17 +172,17 @@ void ElfInterfaceImpl<ElfTypes>::InitHeaders() { // or using the frame hdr object failed to init. eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_)); if (!eh_frame_->Init(eh_frame_info_)) { + eh_frame_info_ = {}; eh_frame_.reset(nullptr); } } - if (eh_frame_.get() == nullptr) { - eh_frame_hdr_info_ = {}; - eh_frame_info_ = {}; - } - if (debug_frame_info_.offset != 0) { - debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_)); + std::shared_ptr<Memory> debug_memory = memory_; + if (debug_frame_info_.flags & SHF_COMPRESSED) { + debug_memory = CreateMemoryFromCompressedSection<ChdrType>(debug_frame_info_, memory_); + } + debug_frame_.reset(new DwarfDebugFrame<AddressType>(debug_memory)); if (!debug_frame_->Init(debug_frame_info_)) { debug_frame_.reset(nullptr); debug_frame_info_ = {}; diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h index a19b331..851a259 100644 --- a/libunwindstack/MemoryBuffer.h +++ b/libunwindstack/MemoryBuffer.h @@ -36,6 +36,7 @@ class MemoryBuffer : public Memory { uint8_t* GetPtr(size_t offset) override; + uint8_t* Data() { return raw_.data(); } uint64_t Size() { return raw_.size(); } private: diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h index 9b911a0..c40c1fb 100644 --- a/libunwindstack/include/unwindstack/ElfInterface.h +++ b/libunwindstack/include/unwindstack/ElfInterface.h @@ -57,6 +57,7 @@ enum : uint8_t { struct ElfTypes32 { using AddressType = uint32_t; + using Chdr = Elf32_Chdr; using Dyn = Elf32_Dyn; using Ehdr = Elf32_Ehdr; using Nhdr = Elf32_Nhdr; @@ -67,6 +68,7 @@ struct ElfTypes32 { struct ElfTypes64 { using AddressType = uint64_t; + using Chdr = Elf64_Chdr; using Dyn = Elf64_Dyn; using Ehdr = Elf64_Ehdr; using Nhdr = Elf64_Nhdr; @@ -184,6 +186,7 @@ template <typename ElfTypes> class ElfInterfaceImpl : public ElfInterface { public: using AddressType = typename ElfTypes::AddressType; + using ChdrType = typename ElfTypes::Chdr; using DynType = typename ElfTypes::Dyn; using EhdrType = typename ElfTypes::Ehdr; using NhdrType = typename ElfTypes::Nhdr; diff --git a/libunwindstack/offline_files/zlib_compress_arm/crasher.gz b/libunwindstack/offline_files/zlib_compress_arm/crasher.gz Binary files differnew file mode 100644 index 0000000..957cfa4 --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/crasher.gz diff --git a/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz b/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz Binary files differnew file mode 100644 index 0000000..eb236bb --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz diff --git a/libunwindstack/offline_files/zlib_compress_arm/linker.gz b/libunwindstack/offline_files/zlib_compress_arm/linker.gz Binary files differnew file mode 100644 index 0000000..e4e1fcf --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/linker.gz diff --git a/libunwindstack/offline_files/zlib_compress_arm/maps.txt b/libunwindstack/offline_files/zlib_compress_arm/maps.txt new file mode 100644 index 0000000..bca38d2 --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/maps.txt @@ -0,0 +1,6 @@ +2d6f000-2d72000 r--p 0 00:00 0 crasher +2d72000-2d76000 r-xp 2000 00:00 0 crasher +ecf86000-ecfb0000 r--p 0 00:00 0 libc.so +ecfb0000-ed018000 r-xp 29000 00:00 0 libc.so +ed81b000-ed83d000 r--p 0 00:00 0 linker +ed83d000-ed8fa000 r-xp 21000 00:00 0 linker diff --git a/libunwindstack/offline_files/zlib_compress_arm/output.txt b/libunwindstack/offline_files/zlib_compress_arm/output.txt new file mode 100644 index 0000000..38eaa7c --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/output.txt @@ -0,0 +1,8 @@ + #00 pc 000c1324 linker (__dl_syscall+28) + #01 pc 000361f5 linker (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1048) + #02 pc 000c6c40 linker (__dl___restore_rt) + #03 pc 0003ceae libc.so (abort+134) + #04 pc 00003f4f crasher (maybe_abort+42) + #05 pc 00004337 crasher (do_action+622) + #06 pc 00005191 crasher (main+48) + #07 pc 0003591f libc.so (__libc_init+58) diff --git a/libunwindstack/offline_files/zlib_compress_arm/regs.txt b/libunwindstack/offline_files/zlib_compress_arm/regs.txt new file mode 100644 index 0000000..4d01bfd --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/regs.txt @@ -0,0 +1,16 @@ +r0: ed812b54 +r1: 0 +r2: de6 +r3: 0 +r4: 0 +r5: 0 +r6: ed812b50 +r7: f0 +r8: ed812c90 +r9: 1 +r10: ed90b62c +r11: ffffffff +ip: ed812af0 +sp: ed812ae0 +lr: ed8511f9 +pc: ed8dc324 diff --git a/libunwindstack/offline_files/zlib_compress_arm/stack0.data b/libunwindstack/offline_files/zlib_compress_arm/stack0.data Binary files differnew file mode 100644 index 0000000..78a6271 --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/stack0.data diff --git a/libunwindstack/offline_files/zlib_compress_arm/stack1.data b/libunwindstack/offline_files/zlib_compress_arm/stack1.data Binary files differnew file mode 100644 index 0000000..3ca1296 --- /dev/null +++ b/libunwindstack/offline_files/zlib_compress_arm/stack1.data diff --git a/libunwindstack/offline_files/zstd_compress_arm/crasher.gz b/libunwindstack/offline_files/zstd_compress_arm/crasher.gz Binary files differnew file mode 100644 index 0000000..b1cdb9b --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/crasher.gz diff --git a/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz b/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz Binary files differnew file mode 100644 index 0000000..8b13fed --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz diff --git a/libunwindstack/offline_files/zstd_compress_arm/linker.gz b/libunwindstack/offline_files/zstd_compress_arm/linker.gz Binary files differnew file mode 100644 index 0000000..a48b6f4 --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/linker.gz diff --git a/libunwindstack/offline_files/zstd_compress_arm/maps.txt b/libunwindstack/offline_files/zstd_compress_arm/maps.txt new file mode 100644 index 0000000..d58c6e8 --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/maps.txt @@ -0,0 +1,6 @@ +bafc000-baff000 r--p 0 00:00 0 crasher +baff000-bb03000 r-xp 2000 00:00 0 crasher +f6e82000-f6eac000 r--p 0 00:00 0 libc.so +f6eac000-f6f14000 r-xp 29000 00:00 0 libc.so +f77a7000-f77c9000 r--p 0 00:00 0 linker +f77c9000-f7886000 r-xp 21000 00:00 0 linker diff --git a/libunwindstack/offline_files/zstd_compress_arm/output.txt b/libunwindstack/offline_files/zstd_compress_arm/output.txt new file mode 100644 index 0000000..efccfc2 --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/output.txt @@ -0,0 +1,8 @@ + #00 pc 000c1324 linker (__dl_syscall+28) + #01 pc 000361e3 linker (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1030) + #02 pc 000c6c40 linker (__dl___restore_rt) + #03 pc 0003ceae libc.so (abort+134) + #04 pc 00003f4f crasher (maybe_abort+42) + #05 pc 00004337 crasher (do_action+622) + #06 pc 00005191 crasher (main+48) + #07 pc 0003591f libc.so (__libc_init+58) diff --git a/libunwindstack/offline_files/zstd_compress_arm/regs.txt b/libunwindstack/offline_files/zstd_compress_arm/regs.txt new file mode 100644 index 0000000..b981884 --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/regs.txt @@ -0,0 +1,16 @@ +r0: f779eb54 +r1: 0 +r2: ffffffff +r3: 0 +r4: 0 +r5: 0 +r6: f779eb50 +r7: f0 +r8: f779ec90 +r9: 1 +r10: f789762c +r11: ffffffff +ip: f779eaf0 +sp: f779eae0 +lr: f77dd1e7 +pc: f7868324 diff --git a/libunwindstack/offline_files/zstd_compress_arm/stack0.data b/libunwindstack/offline_files/zstd_compress_arm/stack0.data Binary files differnew file mode 100644 index 0000000..6057030 --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/stack0.data diff --git a/libunwindstack/offline_files/zstd_compress_arm/stack1.data b/libunwindstack/offline_files/zstd_compress_arm/stack1.data Binary files differnew file mode 100644 index 0000000..07b96ad --- /dev/null +++ b/libunwindstack/offline_files/zstd_compress_arm/stack1.data diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h index 6286f08..72c487d 100644 --- a/libunwindstack/tests/ElfFake.h +++ b/libunwindstack/tests/ElfFake.h @@ -126,6 +126,7 @@ class ElfInterface32Fake : public ElfInterface32 { virtual ~ElfInterface32Fake() = default; void FakeSetEhFrameInfo(const SectionInfo& info) { eh_frame_info_ = info; } + void FakeSetEhFrameHdrInfo(const SectionInfo& info) { eh_frame_hdr_info_ = info; } void FakeSetDebugFrameInfo(const SectionInfo& info) { debug_frame_info_ = info; } }; @@ -135,6 +136,7 @@ class ElfInterface64Fake : public ElfInterface64 { virtual ~ElfInterface64Fake() = default; void FakeSetEhFrameInfo(const SectionInfo& info) { eh_frame_info_ = info; } + void FakeSetEhFrameHdrInfo(const SectionInfo& info) { eh_frame_hdr_info_ = info; } void FakeSetDebugFrameInfo(const SectionInfo& info) { debug_frame_info_ = info; } }; diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index 68a2e8d..27b9f81 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -15,17 +15,24 @@ */ #include <elf.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> #include <memory> +#include <vector> +#include <android-base/unique_fd.h> #include <gtest/gtest.h> #include <unwindstack/ElfInterface.h> #include "DwarfEncoding.h" #include "ElfInterfaceArm.h" +#include "MemoryRange.h" #include "ElfFake.h" +#include "ElfTestUtils.h" #include "utils/MemoryFake.h" #if !defined(PT_ARM_EXIDX) @@ -1977,4 +1984,157 @@ TEST_F(ElfInterfaceTest, huge_gnu_debugdata_size) { ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr); } +TEST_F(ElfInterfaceTest, compressed_eh_frames) { + SectionInfo eh_hdr_info = {.offset = 0x1000}; + uint8_t data[5] = {/*version*/ 1, /*ptr_encoding DW_EH_PE_omit*/ 0xff, + /*fde_count_encoding DW_EH_PE_udata1*/ 0xd, + /*table_encoding DW_EH_PE_absptr*/ 0, /*fde_count*/ 1}; + fake_memory_->SetMemory(0x1000, data, sizeof(data)); + SectionInfo eh_info = {.offset = 0x2000}; + + // Verify that the eh_frame and eh_frame_hdr are created properly. + ElfInterface32Fake interface(memory_); + eh_hdr_info.flags = 0; + interface.FakeSetEhFrameHdrInfo(eh_hdr_info); + eh_info.flags = 0; + interface.FakeSetEhFrameInfo(eh_info); + interface.InitHeaders(); + EXPECT_NE(0U, interface.eh_frame_hdr_info().offset); + EXPECT_NE(0U, interface.eh_frame_info().offset); + EXPECT_TRUE(interface.eh_frame() != nullptr); + + // Init setting SHF_COMPRESSED for both sections, both should fail to init. + ElfInterface32Fake interface_both(memory_); + eh_hdr_info.flags = 0x800; + interface_both.FakeSetEhFrameHdrInfo(eh_hdr_info); + eh_info.flags = 0x800; + interface_both.FakeSetEhFrameInfo(eh_info); + interface_both.InitHeaders(); + EXPECT_EQ(0U, interface_both.eh_frame_hdr_info().offset); + EXPECT_EQ(0U, interface_both.eh_frame_info().offset); + EXPECT_TRUE(interface_both.eh_frame() == nullptr); + + // Init setting SHF_COMPRESSED for only the eh_frame_hdr, eh_frame should init. + ElfInterface32Fake interface_hdr(memory_); + eh_hdr_info.flags = 0x800; + interface_hdr.FakeSetEhFrameHdrInfo(eh_hdr_info); + eh_info.flags = 0; + interface_hdr.FakeSetEhFrameInfo(eh_info); + interface_hdr.InitHeaders(); + EXPECT_EQ(0U, interface_hdr.eh_frame_hdr_info().offset); + EXPECT_NE(0U, interface_hdr.eh_frame_info().offset); + EXPECT_TRUE(interface_hdr.eh_frame() != nullptr); + + // Init setting SHF_COMPRESSED for only the eh_frame, both should fail to init. + ElfInterface32Fake interface_eh(memory_); + eh_hdr_info.flags = 0; + interface_eh.FakeSetEhFrameHdrInfo(eh_hdr_info); + eh_info.flags = 0x800; + interface_eh.FakeSetEhFrameInfo(eh_info); + interface_eh.InitHeaders(); + EXPECT_EQ(0U, interface_eh.eh_frame_hdr_info().offset); + EXPECT_EQ(0U, interface_eh.eh_frame_info().offset); + EXPECT_TRUE(interface_eh.eh_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, compressed_debug_frame_fde_verify) { + std::string lib_dir = TestGetFileDirectory() + "libs/"; + auto elf_memory = Memory::CreateFileMemory(lib_dir + "libc.so", 0); + Elf elf(elf_memory); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + auto section = elf.interface()->debug_frame(); + ASSERT_TRUE(section != nullptr); + + elf_memory = Memory::CreateFileMemory(lib_dir + "libc_zlib.so", 0); + Elf elf_zlib(elf_memory); + ASSERT_TRUE(elf_zlib.Init()); + ASSERT_TRUE(elf_zlib.valid()); + auto section_zlib = elf_zlib.interface()->debug_frame(); + ASSERT_TRUE(section_zlib != nullptr); + + elf_memory = Memory::CreateFileMemory(lib_dir + "libc_zstd.so", 0); + Elf elf_zstd(elf_memory); + ASSERT_TRUE(elf_zstd.Init()); + ASSERT_TRUE(elf_zstd.valid()); + auto section_zstd = elf_zstd.interface()->debug_frame(); + ASSERT_TRUE(section_zstd != nullptr); + + auto iter = section->begin(); + auto iter_zlib = section_zlib->begin(); + auto iter_zstd = section_zstd->begin(); + + // Check that all of the fdes are in the same order, and contain the same data. + size_t total_fdes = 0; + while (iter != section->end() && iter_zlib != section_zlib->end() && + iter_zstd != section_zstd->end()) { + ASSERT_TRUE(iter != section->end()); + ASSERT_TRUE(iter_zlib != section_zlib->end()); + ASSERT_TRUE(iter_zstd != section_zstd->end()); + auto fde = *iter; + auto fde_zlib = *iter_zlib; + auto fde_zstd = *iter_zstd; + EXPECT_EQ(fde->cie_offset, fde_zlib->cie_offset); + EXPECT_EQ(fde->cie_offset, fde_zstd->cie_offset); + EXPECT_EQ(fde->cfa_instructions_offset, fde_zlib->cfa_instructions_offset); + EXPECT_EQ(fde->cfa_instructions_offset, fde_zstd->cfa_instructions_offset); + EXPECT_EQ(fde->cfa_instructions_end, fde_zlib->cfa_instructions_end); + EXPECT_EQ(fde->cfa_instructions_end, fde_zstd->cfa_instructions_end); + EXPECT_EQ(fde->pc_start, fde_zlib->pc_start); + EXPECT_EQ(fde->pc_start, fde_zstd->pc_start); + EXPECT_EQ(fde->pc_end, fde_zlib->pc_end); + EXPECT_EQ(fde->pc_end, fde_zstd->pc_end); + EXPECT_EQ(fde->lsda_address, fde_zlib->lsda_address); + EXPECT_EQ(fde->lsda_address, fde_zstd->lsda_address); + ++iter; + ++iter_zlib; + ++iter_zstd; + ++total_fdes; + } + EXPECT_EQ(2320U, total_fdes); +} + +TEST_F(ElfInterfaceTest, compressed_debug_frame_from_memory) { + std::string lib_dir = TestGetFileDirectory() + "libs/"; + android::base::unique_fd fd( + TEMP_FAILURE_RETRY(open((lib_dir + "libc_zstd.so").c_str(), O_RDONLY | O_CLOEXEC))); + ASSERT_NE(-1, fd); + struct stat buf; + ASSERT_NE(-1, fstat(fd, &buf)); + void* map_addr = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + ASSERT_NE(MAP_FAILED, map_addr); + auto process_memory = Memory::CreateProcessMemory(getpid()); + std::shared_ptr<Memory> elf_memory( + new MemoryRange(process_memory, reinterpret_cast<uint64_t>(map_addr), buf.st_size, 0)); + + Elf elf(elf_memory); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + auto section = elf.interface()->debug_frame(); + ASSERT_TRUE(section != nullptr); + + // Don't check all of the fdes, just verify the first one. + std::vector<const DwarfFde*> fdes; + section->GetFdes(&fdes); + EXPECT_EQ(0x9309cU, fdes[0]->cie_offset); + EXPECT_EQ(0x930c0U, fdes[0]->cfa_instructions_offset); + EXPECT_EQ(0x930c0U, fdes[0]->cfa_instructions_end); + EXPECT_EQ(0U, fdes[0]->pc_start); + EXPECT_EQ(2U, fdes[0]->pc_end); + EXPECT_EQ(0U, fdes[0]->lsda_address); + EXPECT_EQ(2320U, fdes.size()); + + munmap(map_addr, buf.st_size); +} + +TEST_F(ElfInterfaceTest, bad_compressed_debug_frame) { + std::string lib_dir = TestGetFileDirectory() + "libs/"; + auto elf_memory = Memory::CreateFileMemory(TestGetFileDirectory() + "libs/elf_bad_compress", 0); + Elf elf(elf_memory); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.valid()); + // This elf file has a compressed debug frame, but it's bad compress data. + ASSERT_TRUE(elf.interface()->debug_frame() == nullptr); +} + } // namespace unwindstack diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp index b78507d..156fac6 100644 --- a/libunwindstack/tests/UnwindOfflineTest.cpp +++ b/libunwindstack/tests/UnwindOfflineTest.cpp @@ -1689,5 +1689,77 @@ TEST_F(UnwindOfflineTest, apk_soname_at_end_arm64) { EXPECT_EQ(0x7ff9ec4c10ULL, unwinder.frames()[2].sp); } +// Unwind through libraries with debug_frames compressed with zstd. +TEST_F(UnwindOfflineTest, zstd_compress_arm) { + std::string error_msg; + if (!offline_utils_.Init({.offline_files_dir = "zstd_compress_arm/", .arch = ARCH_ARM}, + &error_msg)) + FAIL() << error_msg; + + Regs* regs = offline_utils_.GetRegs(); + std::unique_ptr<Regs> regs_copy(regs->Clone()); + Unwinder unwinder(128, offline_utils_.GetMaps(), regs, offline_utils_.GetProcessMemory()); + unwinder.Unwind(); + + size_t expected_num_frames; + if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg; + std::string expected_frame_info; + if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg; + + std::string frame_info(DumpFrames(unwinder)); + ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ(expected_frame_info, frame_info); + EXPECT_EQ(0xf7868324ULL, unwinder.frames()[0].pc); + EXPECT_EQ(0xf779eae0ULL, unwinder.frames()[0].sp); + EXPECT_EQ(0xf77dd1e3ULL, unwinder.frames()[1].pc); + EXPECT_EQ(0xf779eaf0ULL, unwinder.frames()[1].sp); + EXPECT_EQ(0xf786dc40ULL, unwinder.frames()[2].pc); + EXPECT_EQ(0xf779ec90ULL, unwinder.frames()[2].sp); + EXPECT_EQ(0xf6ebeeaeULL, unwinder.frames()[3].pc); + EXPECT_EQ(0xff7fa398ULL, unwinder.frames()[3].sp); + EXPECT_EQ(0xbafff4fULL, unwinder.frames()[4].pc); + EXPECT_EQ(0xff7fa468ULL, unwinder.frames()[4].sp); + EXPECT_EQ(0xbb00337ULL, unwinder.frames()[5].pc); + EXPECT_EQ(0xff7fa478ULL, unwinder.frames()[5].sp); + EXPECT_EQ(0xbb01191ULL, unwinder.frames()[6].pc); + EXPECT_EQ(0xff7fa568ULL, unwinder.frames()[6].sp); +} + +// Unwind through libraries with debug_frames compressed with zlib. +TEST_F(UnwindOfflineTest, zlib_compress_arm) { + std::string error_msg; + if (!offline_utils_.Init({.offline_files_dir = "zlib_compress_arm/", .arch = ARCH_ARM}, + &error_msg)) + FAIL() << error_msg; + + Regs* regs = offline_utils_.GetRegs(); + std::unique_ptr<Regs> regs_copy(regs->Clone()); + Unwinder unwinder(128, offline_utils_.GetMaps(), regs, offline_utils_.GetProcessMemory()); + unwinder.Unwind(); + + size_t expected_num_frames; + if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg; + std::string expected_frame_info; + if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg; + + std::string frame_info(DumpFrames(unwinder)); + ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info; + EXPECT_EQ(expected_frame_info, frame_info); + EXPECT_EQ(0xed8dc324ULL, unwinder.frames()[0].pc); + EXPECT_EQ(0xed812ae0ULL, unwinder.frames()[0].sp); + EXPECT_EQ(0xed8511f5ULL, unwinder.frames()[1].pc); + EXPECT_EQ(0xed812af0ULL, unwinder.frames()[1].sp); + EXPECT_EQ(0xed8e1c40ULL, unwinder.frames()[2].pc); + EXPECT_EQ(0xed812c90ULL, unwinder.frames()[2].sp); + EXPECT_EQ(0xecfc2eaeULL, unwinder.frames()[3].pc); + EXPECT_EQ(0xff96c328ULL, unwinder.frames()[3].sp); + EXPECT_EQ(0x2d72f4fULL, unwinder.frames()[4].pc); + EXPECT_EQ(0xff96c3f8ULL, unwinder.frames()[4].sp); + EXPECT_EQ(0x2d73337ULL, unwinder.frames()[5].pc); + EXPECT_EQ(0xff96c408ULL, unwinder.frames()[5].sp); + EXPECT_EQ(0x2d74191ULL, unwinder.frames()[6].pc); + EXPECT_EQ(0xff96c4f8ULL, unwinder.frames()[6].sp); +} + } // namespace } // namespace unwindstack diff --git a/libunwindstack/tests/files/libs/elf_bad_compress b/libunwindstack/tests/files/libs/elf_bad_compress Binary files differnew file mode 100755 index 0000000..8a3e16d --- /dev/null +++ b/libunwindstack/tests/files/libs/elf_bad_compress diff --git a/libunwindstack/tests/files/libs/libc.so b/libunwindstack/tests/files/libs/libc.so Binary files differnew file mode 100644 index 0000000..46d4661 --- /dev/null +++ b/libunwindstack/tests/files/libs/libc.so diff --git a/libunwindstack/tests/files/libs/libc_zlib.so b/libunwindstack/tests/files/libs/libc_zlib.so Binary files differnew file mode 100644 index 0000000..851f6ec --- /dev/null +++ b/libunwindstack/tests/files/libs/libc_zlib.so diff --git a/libunwindstack/tests/files/libs/libc_zstd.so b/libunwindstack/tests/files/libs/libc_zstd.so Binary files differnew file mode 100644 index 0000000..da912d7 --- /dev/null +++ b/libunwindstack/tests/files/libs/libc_zstd.so |