From 9ea5b228f560580f85df895c2f117d7e43340935 Mon Sep 17 00:00:00 2001 From: Ian McKellar Date: Wed, 26 Jul 2023 18:55:57 +0000 Subject: [PATCH] Add support for zstd compressed sections to dump_syms Support for zstd must be enabled by passing --enable-zstd to configure. Change-Id: I57d0196552284de86575d979d673ac20a3fc4d64 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/4722191 Reviewed-by: Joshua Peraza --- Makefile.am | 8 +++- Makefile.in | 4 +- configure | 74 ++++++++++++++++++++++++++++++-- configure.ac | 11 +++++ src/common/linux/dump_symbols.cc | 45 +++++++++++++++++-- src/config.h.in | 3 ++ 6 files changed, 134 insertions(+), 11 deletions(-) diff --git a/Makefile.am b/Makefile.am index 58e41483..06f34ce8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -694,9 +694,11 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \ src/common/linux/safe_readlink.cc \ src/tools/linux/dump_syms/dump_syms.cc src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \ - $(RUSTC_DEMANGLE_CFLAGS) + $(RUSTC_DEMANGLE_CFLAGS) \ + $(ZSTD_CFLAGS) src_tools_linux_dump_syms_dump_syms_LDADD = \ $(RUSTC_DEMANGLE_LIBS) \ + $(ZSTD_CFLAGS) \ -lz src_tools_linux_md2core_minidump_2_core_SOURCES = \ @@ -821,11 +823,13 @@ src_common_dumper_unittest_SOURCES = \ src_common_dumper_unittest_CPPFLAGS = \ $(AM_CPPFLAGS) $(TEST_CFLAGS) \ $(RUSTC_DEMANGLE_CFLAGS) \ - $(PTHREAD_CFLAGS) + $(PTHREAD_CFLAGS) \ + $(ZSTD_CFLAGS) src_common_dumper_unittest_LDADD = \ $(TEST_LIBS) \ $(RUSTC_DEMANGLE_LIBS) \ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ + $(ZSTD_LIBS) \ -lz src_common_mac_macho_reader_unittest_SOURCES = \ diff --git a/Makefile.in b/Makefile.in index 641226a0..0c47bf87 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2695,7 +2695,7 @@ src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \ src_tools_linux_dump_syms_dump_syms_LDADD = \ $(RUSTC_DEMANGLE_LIBS) \ - -lz + -lz -lzstd src_tools_linux_md2core_minidump_2_core_SOURCES = \ src/common/linux/memory_mapped_file.cc \ @@ -2828,7 +2828,7 @@ src_common_dumper_unittest_LDADD = \ $(TEST_LIBS) \ $(RUSTC_DEMANGLE_LIBS) \ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ - -lz + -lz -lzstd src_common_mac_macho_reader_unittest_SOURCES = \ src/common/dwarf_cfi_to_module.cc \ diff --git a/configure b/configure index fb84fb0d..3442e796 100755 --- a/configure +++ b/configure @@ -810,6 +810,7 @@ enable_system_test_libs enable_selftest with_rustc_demangle enable_system_rustc_demangle +enable_zstd with_tests_as_root ' ac_precious_vars='build_alias @@ -1483,6 +1484,7 @@ Optional Features: is no). This assumes that rustc-demangle is installed in your sysroot, and all headers from it are available in your standard include path + --enable-zstd Enable decompression of ELF sections with zstd Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -6594,11 +6596,11 @@ if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5 printf %s "checking for $CXX option to enable C++11 features... " >&6; } -if test ${ac_cv_prog_cxx_11+y} +if test ${ac_cv_prog_cxx_cxx11+y} then : printf %s "(cached) " >&6 else $as_nop - ac_cv_prog_cxx_11=no + ac_cv_prog_cxx_cxx11=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -6640,11 +6642,11 @@ if test x$ac_prog_cxx_stdcxx = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5 printf %s "checking for $CXX option to enable C++98 features... " >&6; } -if test ${ac_cv_prog_cxx_98+y} +if test ${ac_cv_prog_cxx_cxx98+y} then : printf %s "(cached) " >&6 else $as_nop - ac_cv_prog_cxx_98=no + ac_cv_prog_cxx_cxx98=no ac_save_CXX=$CXX cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -10504,6 +10506,70 @@ fi +# Check whether --enable-zstd was given. +if test ${enable_zstd+y} +then : + enableval=$enable_zstd; +else $as_nop + enable_zstd=no +fi + +if test "x${enable_zstd}" != xno; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ZSTD_decompress in -lzstd" >&5 +printf %s "checking for ZSTD_decompress in -lzstd... " >&6; } +if test ${ac_cv_lib_zstd_ZSTD_decompress+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lzstd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char ZSTD_decompress (); +int +main (void) +{ +return ZSTD_decompress (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_zstd_ZSTD_decompress=yes +else $as_nop + ac_cv_lib_zstd_ZSTD_decompress=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_zstd_ZSTD_decompress" >&5 +printf "%s\n" "$ac_cv_lib_zstd_ZSTD_decompress" >&6; } +if test "x$ac_cv_lib_zstd_ZSTD_decompress" = xyes +then : + printf "%s\n" "#define HAVE_LIBZSTD 1" >>confdefs.h + + LIBS="-lzstd $LIBS" + +else $as_nop + as_fn_error $? "zstd library not found." "$LINENO" 5 +fi + + ac_fn_c_check_header_compile "$LINENO" "zstd.h" "ac_cv_header_zstd_h" "$ac_includes_default" +if test "x$ac_cv_header_zstd_h" = xyes +then : + +else $as_nop + as_fn_error $? "zstd header not found." "$LINENO" 5 +fi + +fi + # Check whether --with-tests-as-root was given. if test ${with_tests_as_root+y} diff --git a/configure.ac b/configure.ac index 1d53bc61..bfee372a 100644 --- a/configure.ac +++ b/configure.ac @@ -212,6 +212,17 @@ fi AC_ARG_VAR([RUSTC_DEMANGLE_CFLAGS], [Compiler flags for rustc-demangle]) AC_ARG_VAR([RUSTC_DEMANGLE_LIBS], [Linker flags for rustc-demangle]) +AC_ARG_ENABLE(zstd, + AS_HELP_STRING([--enable-zstd], + [Enable decompression of ELF sections with zstd]),, + [enable_zstd=no]) +if test "x${enable_zstd}" != xno; then + AC_CHECK_LIB(zstd, ZSTD_decompress, [], + [AC_MSG_ERROR([zstd library not found.])]) + AC_CHECK_HEADER(zstd.h, [], + [AC_MSG_ERROR([zstd header not found.])]) +fi + AC_ARG_WITH(tests-as-root, AS_HELP_STRING([--with-tests-as-root], [Run the tests as root. Use this on platforms] diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 86c948d9..b693fc9e 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -51,6 +51,9 @@ #include #include #include +#ifdef HAVE_LIBZSTD +#include +#endif #include #include @@ -108,6 +111,11 @@ using google_breakpad::wasteful_vector; #define EM_AARCH64 183 #endif +// Define ZStd compression if host machine does not include this define. +#ifndef ELFCOMPRESS_ZSTD +#define ELFCOMPRESS_ZSTD 2 +#endif + // // FDWrapper // @@ -305,7 +313,7 @@ uint32_t GetCompressionHeader( return sizeof (*header); } -std::pair UncompressSectionContents( +std::pair UncompressZlibSectionContents( const uint8_t* compressed_buffer, uint64_t compressed_size, uint64_t uncompressed_size) { z_stream stream; memset(&stream, 0, sizeof stream); @@ -334,6 +342,37 @@ std::pair UncompressSectionContents( : std::make_pair(uncompressed_buffer.release(), uncompressed_size); } +#ifdef HAVE_LIBZSTD +std::pair UncompressZstdSectionContents( + const uint8_t* compressed_buffer, uint64_t compressed_size,uint64_t uncompressed_size) { + + google_breakpad::scoped_array uncompressed_buffer(new uint8_t[uncompressed_size]); + size_t out_size = ZSTD_decompress(uncompressed_buffer.get(), uncompressed_size, + compressed_buffer, compressed_size); + if (ZSTD_isError(out_size)) { + return std::make_pair(nullptr, 0); + } + assert(out_size == uncompressed_size); + return std::make_pair(uncompressed_buffer.release(), uncompressed_size); +} +#endif + +std::pair UncompressSectionContents( + uint64_t compression_type, const uint8_t* compressed_buffer, + uint64_t compressed_size, uint64_t uncompressed_size) { + if (compression_type == ELFCOMPRESS_ZLIB) { + return UncompressZlibSectionContents(compressed_buffer, compressed_size, uncompressed_size); + } + +#ifdef HAVE_LIBZSTD + if (compression_type == ELFCOMPRESS_ZSTD) { + return UncompressZstdSectionContents(compressed_buffer, compressed_size, uncompressed_size); + } +#endif + + return std::make_pair(nullptr, 0); +} + void StartProcessSplitDwarf(google_breakpad::CompilationUnit* reader, Module* module, google_breakpad::Endianness endianness, @@ -437,7 +476,7 @@ bool LoadDwarf(const string& dwarf_filename, size -= compression_header_size; std::pair uncompressed = - UncompressSectionContents(contents, size, chdr.ch_size); + UncompressSectionContents(chdr.ch_type, contents, size, chdr.ch_size); if (uncompressed.first != nullptr && uncompressed.second != 0) { file_context.AddManagedSectionToSectionMap(name, uncompressed.first, uncompressed.second); @@ -587,7 +626,7 @@ bool LoadDwarfCFI(const string& dwarf_filename, cfi_size -= compression_header_size; std::pair uncompressed = - UncompressSectionContents(cfi, cfi_size, chdr.ch_size); + UncompressSectionContents(chdr.ch_type, cfi, cfi_size, chdr.ch_size); if (uncompressed.first == nullptr || uncompressed.second == 0) { fprintf(stderr, "%s: decompression failed\n", dwarf_filename.c_str()); diff --git a/src/config.h.in b/src/config.h.in index 8fd7b0aa..9a4eb0de 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -21,6 +21,9 @@ /* Define to 1 if you have the `rustc_demangle' library (-lrustc_demangle). */ #undef HAVE_LIBRUSTC_DEMANGLE +/* Define to 1 if you have the `zstd' library (-lzstd). */ +#undef HAVE_LIBZSTD + /* Define to 1 if you have the `memfd_create' function. */ #undef HAVE_MEMFD_CREATE