summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2024-01-10 20:05:49 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-01-10 20:05:49 +0000
commit0a331d84481ddf6934b22a0ca04217ca7c50dde2 (patch)
tree7d0b293f70a327b91994fc363b6f811082990076
parentbd6137266f30dba61969a795c241a2a4ec7ab381 (diff)
parent4ca86d028f6b6c9bca1d4895ec73f5592ab49309 (diff)
downloadunwinding-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>
-rw-r--r--libunwindstack/Android.bp5
-rw-r--r--libunwindstack/AndroidVersions.md4
-rw-r--r--libunwindstack/ElfInterface.cpp79
-rw-r--r--libunwindstack/MemoryBuffer.h1
-rw-r--r--libunwindstack/include/unwindstack/ElfInterface.h3
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/crasher.gzbin0 -> 12793 bytes
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/libc.so.gzbin0 -> 436924 bytes
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/linker.gzbin0 -> 727770 bytes
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/maps.txt6
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/output.txt8
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/regs.txt16
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/stack0.databin0 -> 1320 bytes
-rw-r--r--libunwindstack/offline_files/zlib_compress_arm/stack1.databin0 -> 7392 bytes
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/crasher.gzbin0 -> 12793 bytes
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/libc.so.gzbin0 -> 436532 bytes
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/linker.gzbin0 -> 722464 bytes
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/maps.txt6
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/output.txt8
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/regs.txt16
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/stack0.databin0 -> 1320 bytes
-rw-r--r--libunwindstack/offline_files/zstd_compress_arm/stack1.databin0 -> 7280 bytes
-rw-r--r--libunwindstack/tests/ElfFake.h2
-rw-r--r--libunwindstack/tests/ElfInterfaceTest.cpp160
-rw-r--r--libunwindstack/tests/UnwindOfflineTest.cpp72
-rwxr-xr-xlibunwindstack/tests/files/libs/elf_bad_compressbin0 -> 4309 bytes
-rw-r--r--libunwindstack/tests/files/libs/libc.sobin0 -> 895092 bytes
-rw-r--r--libunwindstack/tests/files/libs/libc_zlib.sobin0 -> 843756 bytes
-rw-r--r--libunwindstack/tests/files/libs/libc_zstd.sobin0 -> 843228 bytes
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
new file mode 100644
index 0000000..957cfa4
--- /dev/null
+++ b/libunwindstack/offline_files/zlib_compress_arm/crasher.gz
Binary files differ
diff --git a/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz b/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz
new file mode 100644
index 0000000..eb236bb
--- /dev/null
+++ b/libunwindstack/offline_files/zlib_compress_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/zlib_compress_arm/linker.gz b/libunwindstack/offline_files/zlib_compress_arm/linker.gz
new file mode 100644
index 0000000..e4e1fcf
--- /dev/null
+++ b/libunwindstack/offline_files/zlib_compress_arm/linker.gz
Binary files differ
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
new file mode 100644
index 0000000..78a6271
--- /dev/null
+++ b/libunwindstack/offline_files/zlib_compress_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/offline_files/zlib_compress_arm/stack1.data b/libunwindstack/offline_files/zlib_compress_arm/stack1.data
new file mode 100644
index 0000000..3ca1296
--- /dev/null
+++ b/libunwindstack/offline_files/zlib_compress_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/zstd_compress_arm/crasher.gz b/libunwindstack/offline_files/zstd_compress_arm/crasher.gz
new file mode 100644
index 0000000..b1cdb9b
--- /dev/null
+++ b/libunwindstack/offline_files/zstd_compress_arm/crasher.gz
Binary files differ
diff --git a/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz b/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz
new file mode 100644
index 0000000..8b13fed
--- /dev/null
+++ b/libunwindstack/offline_files/zstd_compress_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/zstd_compress_arm/linker.gz b/libunwindstack/offline_files/zstd_compress_arm/linker.gz
new file mode 100644
index 0000000..a48b6f4
--- /dev/null
+++ b/libunwindstack/offline_files/zstd_compress_arm/linker.gz
Binary files differ
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
new file mode 100644
index 0000000..6057030
--- /dev/null
+++ b/libunwindstack/offline_files/zstd_compress_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/offline_files/zstd_compress_arm/stack1.data b/libunwindstack/offline_files/zstd_compress_arm/stack1.data
new file mode 100644
index 0000000..07b96ad
--- /dev/null
+++ b/libunwindstack/offline_files/zstd_compress_arm/stack1.data
Binary files differ
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
new file mode 100755
index 0000000..8a3e16d
--- /dev/null
+++ b/libunwindstack/tests/files/libs/elf_bad_compress
Binary files differ
diff --git a/libunwindstack/tests/files/libs/libc.so b/libunwindstack/tests/files/libs/libc.so
new file mode 100644
index 0000000..46d4661
--- /dev/null
+++ b/libunwindstack/tests/files/libs/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/libs/libc_zlib.so b/libunwindstack/tests/files/libs/libc_zlib.so
new file mode 100644
index 0000000..851f6ec
--- /dev/null
+++ b/libunwindstack/tests/files/libs/libc_zlib.so
Binary files differ
diff --git a/libunwindstack/tests/files/libs/libc_zstd.so b/libunwindstack/tests/files/libs/libc_zstd.so
new file mode 100644
index 0000000..da912d7
--- /dev/null
+++ b/libunwindstack/tests/files/libs/libc_zstd.so
Binary files differ