From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp-out1.suse.de (smtp-out1.suse.de [IPv6:2001:67c:2178:6::1c]) by sourceware.org (Postfix) with ESMTPS id D259B391BDDA for ; Fri, 9 Dec 2022 10:17:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D259B391BDDA Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=suse.cz Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=suse.cz Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 00604337DE; Fri, 9 Dec 2022 10:17:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_rsa; t=1670581038; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pd8k3yvZ/wZ3ayKUOUML9FYa4lPZECrqA//qGTEc0+E=; b=22r+r81bkrYm0jLCegbZ8/3AQSUymgPQ4SBqRz1BDACndnkeHyTMOcrAzCd74wtBNa2Hrv /xhxSQmo1yONbV20OuopMkkxQEqmMezUulj9YSDlhEIWBBc/DXHX0fHQL95n5+XPrNHXRy tDUz48Xcw3s1Td2lOkwC2GfdMxb9TQ8= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.cz; s=susede2_ed25519; t=1670581038; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pd8k3yvZ/wZ3ayKUOUML9FYa4lPZECrqA//qGTEc0+E=; b=rr2wKu3y8qqmwPWzeaB9t6DTOoMFFXRFo4GO/ZLe99qL+PoR3DHTkahN7zMhmjDpbXzGeI Xyi3XHHP1YEr/oCA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id C481A138E0; Fri, 9 Dec 2022 10:17:17 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id moLFLS0Lk2NNAwAAMHmgww (envelope-from ); Fri, 09 Dec 2022 10:17:17 +0000 Message-ID: <84335a5e-683a-2f55-88ba-3c3708faffac@suse.cz> Date: Fri, 9 Dec 2022 11:17:17 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.5.1 Subject: Re: [PATCHv2] support ZSTD compression algorithm Content-Language: en-US From: =?UTF-8?Q?Martin_Li=c5=a1ka?= To: elfutils-devel@sourceware.org Cc: Mark Wielaard References: <24d7165b-b8ac-ea5d-a046-aec2203696a9@suse.cz> In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00,BODY_8BITS,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,NICE_REPLY_A,SPF_HELO_NONE,SPF_SOFTFAIL,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: PING^1 On 11/29/22 13:05, Martin Liška wrote: > Hi. > > There's second version of the patch that fully support both compression and decompression. > > Changes from the v1: > - compression support added > - zstd detection is fixed > - new tests are added > - builds fine w/ and w/o the ZSTD library > > What's currently missing and where I need a help: > > 1) When I build ./configure --without-zstd, I don't have > a reasonable error message (something similar to binutils's readelf: > readelf: Warning: section '.debug_str' has unsupported compress type: 2) > even though, __libelf_decompress returns NULL and __libelf_seterrno). > One can see a garbage in the console. > > How to handle that properly? > > 2) How should I run my newly added tests conditionally if zstd > configuration support is enabled? > > Cheers, > Martin > > --- >  configure.ac               |   8 +- >  libelf/Makefile.am         |   2 +- >  libelf/elf_compress.c      | 294 ++++++++++++++++++++++++++++++------- >  libelf/elf_compress_gnu.c  |   5 +- >  libelf/libelfP.h           |   4 +- >  src/elfcompress.c          | 144 ++++++++++-------- >  src/readelf.c              |  18 ++- >  tests/run-compress-test.sh |  24 +++ >  8 files changed, 373 insertions(+), 126 deletions(-) > > diff --git a/configure.ac b/configure.ac > index 59be27ac..07cfa54b 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -410,6 +410,11 @@ dnl Test for bzlib and xz/lzma/zstd, gives BZLIB/LZMALIB/ZSTD .am >  dnl conditional and config.h USE_BZLIB/USE_LZMALIB/USE_ZSTD #define. >  save_LIBS="$LIBS" >  LIBS= > +eu_ZIPLIB(zstd,ZSTD,zstd,ZSTD_decompress,[ZSTD (zst)]) > +AS_IF([test "x$with_zstd" = xyes], [LIBZSTD="libzstd"], [LIBLZSTD=""]) > +AC_SUBST([LIBZSTD]) > +zstd_LIBS="$LIBS" > +AC_SUBST([zstd_LIBS]) >  eu_ZIPLIB(bzlib,BZLIB,bz2,BZ2_bzdopen,bzip2) >  # We need this since bzip2 doesn't have a pkgconfig file. >  BZ2_LIB="$LIBS" > @@ -417,9 +422,6 @@ AC_SUBST([BZ2_LIB]) >  eu_ZIPLIB(lzma,LZMA,lzma,lzma_auto_decoder,[LZMA (xz)]) >  AS_IF([test "x$with_lzma" = xyes], [LIBLZMA="liblzma"], [LIBLZMA=""]) >  AC_SUBST([LIBLZMA]) > -eu_ZIPLIB(zstd,ZSTD,zstd,ZSTD_decompress,[ZSTD (zst)]) > -AS_IF([test "x$with_zstd" = xyes], [LIBZSTD="libzstd"], [LIBLZSTD=""]) > -AC_SUBST([LIBZSTD]) >  zip_LIBS="$LIBS" >  LIBS="$save_LIBS" >  AC_SUBST([zip_LIBS]) > diff --git a/libelf/Makefile.am b/libelf/Makefile.am > index 560ed45f..24c25cf8 100644 > --- a/libelf/Makefile.am > +++ b/libelf/Makefile.am > @@ -106,7 +106,7 @@ libelf_pic_a_SOURCES = >  am_libelf_pic_a_OBJECTS = $(libelf_a_SOURCES:.c=.os) >   >  libelf_so_DEPS = ../lib/libeu.a > -libelf_so_LDLIBS = $(libelf_so_DEPS) -lz > +libelf_so_LDLIBS = $(libelf_so_DEPS) -lz $(zstd_LIBS) >  if USE_LOCKS >  libelf_so_LDLIBS += -lpthread >  endif > diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c > index d7f53af2..7a6e37a4 100644 > --- a/libelf/elf_compress.c > +++ b/libelf/elf_compress.c > @@ -39,6 +39,10 @@ >  #include >  #include >   > +#ifdef USE_ZSTD > +#include > +#endif > + >  /* Cleanup and return result.  Don't leak memory.  */ >  static void * >  do_deflate_cleanup (void *result, z_stream *z, void *out_buf, > @@ -54,53 +58,14 @@ do_deflate_cleanup (void *result, z_stream *z, void *out_buf, >  #define deflate_cleanup(result, cdata) \ >      do_deflate_cleanup(result, &z, out_buf, cdata) >   > -/* Given a section, uses the (in-memory) Elf_Data to extract the > -   original data size (including the given header size) and data > -   alignment.  Returns a buffer that has at least hsize bytes (for the > -   caller to fill in with a header) plus zlib compressed date.  Also > -   returns the new buffer size in new_size (hsize + compressed data > -   size).  Returns (void *) -1 when FORCE is false and the compressed > -   data would be bigger than the original data.  */ >  void * >  internal_function > -__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, > -           size_t *orig_size, size_t *orig_addralign, > -           size_t *new_size, bool force) > +__libelf_compress_zlib (Elf_Scn *scn, size_t hsize, int ei_data, > +            size_t *orig_size, size_t *orig_addralign, > +            size_t *new_size, bool force, > +            Elf_Data *data, Elf_Data *next_data, > +            void *out_buf, size_t out_size, size_t block) >  { > -  /* The compressed data is the on-disk data.  We simplify the > -     implementation a bit by asking for the (converted) in-memory > -     data (which might be all there is if the user created it with > -     elf_newdata) and then convert back to raw if needed before > -     compressing.  Should be made a bit more clever to directly > -     use raw if that is directly available.  */ > -  Elf_Data *data = elf_getdata (scn, NULL); > -  if (data == NULL) > -    return NULL; > - > -  /* When not forced and we immediately know we would use more data by > -     compressing, because of the header plus zlib overhead (five bytes > -     per 16 KB block, plus a one-time overhead of six bytes for the > -     entire stream), don't do anything.  */ > -  Elf_Data *next_data = elf_getdata (scn, data); > -  if (next_data == NULL && !force > -      && data->d_size <= hsize + 5 + 6) > -    return (void *) -1; > - > -  *orig_addralign = data->d_align; > -  *orig_size = data->d_size; > - > -  /* Guess an output block size. 1/8th of the original Elf_Data plus > -     hsize.  Make the first chunk twice that size (25%), then increase > -     by a block (12.5%) when necessary.  */ > -  size_t block = (data->d_size / 8) + hsize; > -  size_t out_size = 2 * block; > -  void *out_buf = malloc (out_size); > -  if (out_buf == NULL) > -    { > -      __libelf_seterrno (ELF_E_NOMEM); > -      return NULL; > -    } > - >    /* Caller gets to fill in the header at the start.  Just skip it here.  */ >    size_t used = hsize; >   > @@ -205,9 +170,186 @@ __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, >    return out_buf; >  } >   > +#ifdef USE_ZSTD > +/* Cleanup and return result.  Don't leak memory.  */ > +static void * > +do_zstd_cleanup (void *result, ZSTD_CCtx * const cctx, void *out_buf, > +         Elf_Data *cdatap) > +{ > +  ZSTD_freeCCtx (cctx); > +  free (out_buf); > +  if (cdatap != NULL) > +    free (cdatap->d_buf); > +  return result; > +} > + > +#define zstd_cleanup(result, cdata) \ > +    do_zstd_cleanup(result, cctx, out_buf, cdata) > + >  void * >  internal_function > -__libelf_decompress (void *buf_in, size_t size_in, size_t size_out) > +__libelf_compress_zstd (Elf_Scn *scn, size_t hsize, int ei_data, > +            size_t *orig_size, size_t *orig_addralign, > +            size_t *new_size, bool force, > +            Elf_Data *data, Elf_Data *next_data, > +            void *out_buf, size_t out_size, size_t block) > +{ > +  /* Caller gets to fill in the header at the start.  Just skip it here.  */ > +  size_t used = hsize; > + > +  ZSTD_CCtx* const cctx = ZSTD_createCCtx(); > +  Elf_Data cdata; > +  cdata.d_buf = NULL; > + > +  /* Loop over data buffers.  */ > +  ZSTD_EndDirective mode = ZSTD_e_continue; > + > +  do > +    { > +      /* Convert to raw if different endianness.  */ > +      cdata = *data; > +      bool convert = ei_data != MY_ELFDATA && data->d_size > 0; > +      if (convert) > +    { > +      /* Don't do this conversion in place, we might want to keep > +         the original data around, caller decides.  */ > +      cdata.d_buf = malloc (data->d_size); > +      if (cdata.d_buf == NULL) > +        { > +          __libelf_seterrno (ELF_E_NOMEM); > +          return zstd_cleanup (NULL, NULL); > +        } > +      if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL) > +        return zstd_cleanup (NULL, &cdata); > +    } > + > +      ZSTD_inBuffer ib = { cdata.d_buf, cdata.d_size, 0 }; > + > +      /* Get next buffer to see if this is the last one.  */ > +      data = next_data; > +      if (data != NULL) > +    { > +      *orig_addralign = MAX (*orig_addralign, data->d_align); > +      *orig_size += data->d_size; > +      next_data = elf_getdata (scn, data); > +    } > +      else > +    mode = ZSTD_e_end; > + > +      /* Flush one data buffer.  */ > +      for (;;) > +    { > +      ZSTD_outBuffer ob = { out_buf + used, out_size - used, 0 }; > +      size_t ret = ZSTD_compressStream2 (cctx, &ob, &ib, mode); > +      if (ZSTD_isError (ret)) > +        { > +          __libelf_seterrno (ELF_E_COMPRESS_ERROR); > +          return zstd_cleanup (NULL, convert ? &cdata : NULL); > +        } > +      used += ob.pos; > + > +      /* Bail out if we are sure the user doesn't want the > +         compression forced and we are using more compressed data > +         than original data.  */ > +      if (!force && mode == ZSTD_e_end && used >= *orig_size) > +        return zstd_cleanup ((void *) -1, convert ? &cdata : NULL); > + > +      if (ret > 0) > +        { > +          void *bigger = realloc (out_buf, out_size + block); > +          if (bigger == NULL) > +        { > +          __libelf_seterrno (ELF_E_NOMEM); > +          return zstd_cleanup (NULL, convert ? &cdata : NULL); > +        } > +          out_buf = bigger; > +          out_size += block; > +        } > +      else > +        break; > +    } > + > +      if (convert) > +    { > +      free (cdata.d_buf); > +      cdata.d_buf = NULL; > +    } > +    } > +  while (mode != ZSTD_e_end); /* More data blocks.  */ > + > +  ZSTD_freeCCtx (cctx); > +  *new_size = used; > +  return out_buf; > +} > +#endif > + > +/* Given a section, uses the (in-memory) Elf_Data to extract the > +   original data size (including the given header size) and data > +   alignment.  Returns a buffer that has at least hsize bytes (for the > +   caller to fill in with a header) plus zlib compressed date.  Also > +   returns the new buffer size in new_size (hsize + compressed data > +   size).  Returns (void *) -1 when FORCE is false and the compressed > +   data would be bigger than the original data.  */ > +void * > +internal_function > +__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, > +           size_t *orig_size, size_t *orig_addralign, > +           size_t *new_size, bool force, bool use_zstd) > +{ > +  /* The compressed data is the on-disk data.  We simplify the > +     implementation a bit by asking for the (converted) in-memory > +     data (which might be all there is if the user created it with > +     elf_newdata) and then convert back to raw if needed before > +     compressing.  Should be made a bit more clever to directly > +     use raw if that is directly available.  */ > +  Elf_Data *data = elf_getdata (scn, NULL); > +  if (data == NULL) > +    return NULL; > + > +  /* When not forced and we immediately know we would use more data by > +     compressing, because of the header plus zlib overhead (five bytes > +     per 16 KB block, plus a one-time overhead of six bytes for the > +     entire stream), don't do anything. > +     Size estimation for ZSTD compression would be similar.  */ > +  Elf_Data *next_data = elf_getdata (scn, data); > +  if (next_data == NULL && !force > +      && data->d_size <= hsize + 5 + 6) > +    return (void *) -1; > + > +  *orig_addralign = data->d_align; > +  *orig_size = data->d_size; > + > +  /* Guess an output block size. 1/8th of the original Elf_Data plus > +     hsize.  Make the first chunk twice that size (25%), then increase > +     by a block (12.5%) when necessary.  */ > +  size_t block = (data->d_size / 8) + hsize; > +  size_t out_size = 2 * block; > +  void *out_buf = malloc (out_size); > +  if (out_buf == NULL) > +    { > +      __libelf_seterrno (ELF_E_NOMEM); > +      return NULL; > +    } > + > +  if (use_zstd) > +#ifdef USE_ZSTD > +    return __libelf_compress_zstd (scn, hsize, ei_data, orig_size, > +                   orig_addralign, new_size, force, > +                   data, next_data, out_buf, out_size, > +                   block); > +#else > +    return NULL; > +#endif > +  else > +    return __libelf_compress_zlib (scn, hsize, ei_data, orig_size, > +                   orig_addralign, new_size, force, > +                   data, next_data, out_buf, out_size, > +                   block); > +} > + > +void * > +internal_function > +__libelf_decompress_zlib (void *buf_in, size_t size_in, size_t size_out) >  { >    /* Catch highly unlikely compression ratios so we don't allocate >       some giant amount of memory for nothing. The max compression > @@ -218,7 +360,7 @@ __libelf_decompress (void *buf_in, size_t size_in, size_t size_out) >        return NULL; >      } >   > -  /* Malloc might return NULL when requestion zero size.  This is highly > +  /* Malloc might return NULL when requesting zero size.  This is highly >       unlikely, it would only happen when the compression was forced. >       But we do need a non-NULL buffer to return and set as result. >       Just make sure to always allocate at least 1 byte.  */ > @@ -260,6 +402,51 @@ __libelf_decompress (void *buf_in, size_t size_in, size_t size_out) >    return buf_out; >  } >   > +#ifdef USE_ZSTD > +void * > +internal_function > +__libelf_decompress_zstd (void *buf_in, size_t size_in, size_t size_out) > +{ > +  /* Malloc might return NULL when requesting zero size.  This is highly > +     unlikely, it would only happen when the compression was forced. > +     But we do need a non-NULL buffer to return and set as result. > +     Just make sure to always allocate at least 1 byte.  */ > +  void *buf_out = malloc (size_out ?: 1); > +  if (unlikely (buf_out == NULL)) > +    { > +      __libelf_seterrno (ELF_E_NOMEM); > +      return NULL; > +    } > + > +  size_t ret = ZSTD_decompress (buf_out, size_out, buf_in, size_in); > +  if (ZSTD_isError (ret)) > +    { > +      free (buf_out); > +      __libelf_seterrno (ELF_E_DECOMPRESS_ERROR); > +      return NULL; > +    } > +  else > +    return buf_out; > +} > +#endif > + > +void * > +internal_function > +__libelf_decompress (int chtype, void *buf_in, size_t size_in, size_t size_out) > +{ > +  if (chtype == ELFCOMPRESS_ZLIB) > +    return __libelf_decompress_zlib (buf_in, size_in, size_out); > +  else > +    { > +#ifdef USE_ZSTD > +    return __libelf_decompress_zstd (buf_in, size_in, size_out); > +#else > +    __libelf_seterrno (ELF_E_DECOMPRESS_ERROR); > +    return NULL; > +#endif > +    } > +} > + >  void * >  internal_function >  __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) > @@ -268,7 +455,7 @@ __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) >    if (gelf_getchdr (scn, &chdr) == NULL) >      return NULL; >   > -  if (chdr.ch_type != ELFCOMPRESS_ZLIB) > +  if (chdr.ch_type != ELFCOMPRESS_ZLIB && chdr.ch_type != ELFCOMPRESS_ZSTD) >      { >        __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); >        return NULL; > @@ -295,7 +482,9 @@ __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) >            ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); >    size_t size_in = data->d_size - hsize; >    void *buf_in = data->d_buf + hsize; > -  void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size); > +  void *buf_out > +    = __libelf_decompress (chdr.ch_type, buf_in, size_in, chdr.ch_size); > + >    *size_out = chdr.ch_size; >    *addralign = chdr.ch_addralign; >    return buf_out; > @@ -394,7 +583,7 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) >      } >   >    int compressed = (sh_flags & SHF_COMPRESSED); > -  if (type == ELFCOMPRESS_ZLIB) > +  if (type == ELFCOMPRESS_ZLIB || type == ELFCOMPRESS_ZSTD) >      { >        /* Compress/Deflate.  */ >        if (compressed == 1) > @@ -408,7 +597,8 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) >        size_t orig_size, orig_addralign, new_size; >        void *out_buf = __libelf_compress (scn, hsize, elfdata, >                       &orig_size, &orig_addralign, > -                     &new_size, force); > +                     &new_size, force, > +                     type == ELFCOMPRESS_ZSTD); >   >        /* Compression would make section larger, don't change anything.  */ >        if (out_buf == (void *) -1) > @@ -422,7 +612,7 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) >        if (elfclass == ELFCLASS32) >      { >        Elf32_Chdr chdr; > -      chdr.ch_type = ELFCOMPRESS_ZLIB; > +      chdr.ch_type = type; >        chdr.ch_size = orig_size; >        chdr.ch_addralign = orig_addralign; >        if (elfdata != MY_ELFDATA) > @@ -436,7 +626,7 @@ elf_compress (Elf_Scn *scn, int type, unsigned int flags) >        else >      { >        Elf64_Chdr chdr; > -      chdr.ch_type = ELFCOMPRESS_ZLIB; > +      chdr.ch_type = type; >        chdr.ch_reserved = 0; >        chdr.ch_size = orig_size; >        chdr.ch_addralign = sh_addralign; > diff --git a/libelf/elf_compress_gnu.c b/libelf/elf_compress_gnu.c > index 3d2977e7..8e20b30e 100644 > --- a/libelf/elf_compress_gnu.c > +++ b/libelf/elf_compress_gnu.c > @@ -103,7 +103,8 @@ elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags) >        size_t orig_size, new_size, orig_addralign; >        void *out_buf = __libelf_compress (scn, hsize, elfdata, >                       &orig_size, &orig_addralign, > -                     &new_size, force); > +                     &new_size, force, > +                     /* use_zstd */ false); >   >        /* Compression would make section larger, don't change anything.  */ >        if (out_buf == (void *) -1) > @@ -178,7 +179,7 @@ elf_compress_gnu (Elf_Scn *scn, int inflate, unsigned int flags) >        size_t size = gsize; >        size_t size_in = data->d_size - hsize; >        void *buf_in = data->d_buf + hsize; > -      void *buf_out = __libelf_decompress (buf_in, size_in, size); > +      void *buf_out = __libelf_decompress (ELFCOMPRESS_ZLIB, buf_in, size_in, size); >        if (buf_out == NULL) >      return -1; >   > diff --git a/libelf/libelfP.h b/libelf/libelfP.h > index d88a613c..6624f38a 100644 > --- a/libelf/libelfP.h > +++ b/libelf/libelfP.h > @@ -574,10 +574,10 @@ extern uint32_t __libelf_crc32 (uint32_t crc, unsigned char *buf, size_t len) >   >  extern void * __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data, >                   size_t *orig_size, size_t *orig_addralign, > -                 size_t *size, bool force) > +                 size_t *size, bool force, bool use_zstd) >       internal_function; >   > -extern void * __libelf_decompress (void *buf_in, size_t size_in, > +extern void * __libelf_decompress (int chtype, void *buf_in, size_t size_in, >                     size_t size_out) internal_function; >  extern void * __libelf_decompress_elf (Elf_Scn *scn, >                         size_t *size_out, size_t *addralign) > diff --git a/src/elfcompress.c b/src/elfcompress.c > index eff765e8..b0f1677c 100644 > --- a/src/elfcompress.c > +++ b/src/elfcompress.c > @@ -55,9 +55,10 @@ enum ch_type >    UNSET = -1, >    NONE, >    ZLIB, > +  ZSTD, >   >    /* Maximal supported ch_type.  */ > -  MAXIMAL_CH_TYPE = ZLIB, > +  MAXIMAL_CH_TYPE = ZSTD, >   >    ZLIB_GNU = 1 << 16 >  }; > @@ -139,6 +140,12 @@ parse_opt (int key, char *arg __attribute__ ((unused)), >      type = ZLIB; >        else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0) >      type = ZLIB_GNU; > +      else if (strcmp ("zstd", arg) == 0) > +#ifdef USE_ZSTD > +    type = ZSTD; > +#else > +    argp_error (state, N_("ZSTD support is not enabled")); > +#endif >        else >      argp_error (state, N_("unknown compression type '%s'"), arg); >        break; > @@ -281,6 +288,44 @@ get_sections (unsigned int *sections, size_t shnum) >    return s; >  } >   > +/* Return compression type of a given section SHDR.  */ > + > +static enum ch_type > +get_section_chtype (Elf_Scn *scn, GElf_Shdr *shdr, const char *sname, > +            size_t ndx) > +{ > +  enum ch_type chtype = UNSET; > +  if ((shdr->sh_flags & SHF_COMPRESSED) != 0) > +    { > +      GElf_Chdr chdr; > +      if (gelf_getchdr (scn, &chdr) != NULL) > +    { > +      chtype = (enum ch_type)chdr.ch_type; > +      if (chtype == NONE) > +        { > +          error (0, 0, "Compression type for section %zd" > +             " can't be zero ", ndx); > +          chtype = UNSET; > +        } > +      else if (chtype > MAXIMAL_CH_TYPE) > +        { > +          error (0, 0, "Compression type (%d) for section %zd" > +             " is unsupported ", chtype, ndx); > +          chtype = UNSET; > +        } > +    } > +      else > +    error (0, 0, "Couldn't get chdr for section %zd", ndx); > +    } > +  /* Set ZLIB_GNU compression manually for .zdebug* sections.  */ > +  else if (startswith (sname, ".zdebug")) > +    chtype = ZLIB_GNU; > +  else > +    chtype = NONE; > + > +  return chtype; > +} > + >  static int >  process_file (const char *fname) >  { > @@ -461,26 +506,29 @@ process_file (const char *fname) >   >        if (section_name_matches (sname)) >      { > -      if (!force && type == NONE > -          && (shdr->sh_flags & SHF_COMPRESSED) == 0 > -          && !startswith (sname, ".zdebug")) > -        { > -          if (verbose > 0) > -        printf ("[%zd] %s already decompressed\n", ndx, sname); > -        } > -      else if (!force && type == ZLIB > -           && (shdr->sh_flags & SHF_COMPRESSED) != 0) > -        { > -          if (verbose > 0) > -        printf ("[%zd] %s already compressed\n", ndx, sname); > -        } > -      else if (!force && type == ZLIB_GNU > -           && startswith (sname, ".zdebug")) > +      enum ch_type schtype = get_section_chtype (scn, shdr, sname, ndx); > +      if (!force && verbose > 0) >          { > -          if (verbose > 0) > -        printf ("[%zd] %s already GNU compressed\n", ndx, sname); > +          /* The current compression matches the final one.  */ > +          if (type == schtype) > +        switch (type) > +          { > +          case NONE: > +            printf ("[%zd] %s already decompressed\n", ndx, sname); > +            break; > +          case ZLIB: > +          case ZSTD: > +            printf ("[%zd] %s already compressed\n", ndx, sname); > +            break; > +          case ZLIB_GNU: > +            printf ("[%zd] %s already GNU compressed\n", ndx, sname); > +            break; > +          default: > +            abort (); > +          } >          } > -      else if (shdr->sh_type != SHT_NOBITS > + > +      if (shdr->sh_type != SHT_NOBITS >            && (shdr->sh_flags & SHF_ALLOC) == 0) >          { >            set_section (sections, ndx); > @@ -692,37 +740,12 @@ process_file (const char *fname) >           (de)compressed, invalidating the string pointers.  */ >        sname = xstrdup (sname); >   > + >        /* Detect source compression that is how is the section compressed >           now.  */ > -      GElf_Chdr chdr; > -      enum ch_type schtype = NONE; > -      if ((shdr->sh_flags & SHF_COMPRESSED) != 0) > -        { > -          if (gelf_getchdr (scn, &chdr) != NULL) > -        { > -          schtype = (enum ch_type)chdr.ch_type; > -          if (schtype == NONE) > -            { > -              error (0, 0, "Compression type for section %zd" > -                 " can't be zero ", ndx); > -              goto cleanup; > -            } > -          else if (schtype > MAXIMAL_CH_TYPE) > -            { > -              error (0, 0, "Compression type (%d) for section %zd" > -                 " is unsupported ", schtype, ndx); > -              goto cleanup; > -            } > -        } > -          else > -        { > -          error (0, 0, "Couldn't get chdr for section %zd", ndx); > -          goto cleanup; > -        } > -        } > -      /* Set ZLIB compression manually for .zdebug* sections.  */ > -      else if (startswith (sname, ".zdebug")) > -        schtype = ZLIB_GNU; > +      enum ch_type schtype = get_section_chtype (scn, shdr, sname, ndx); > +      if (schtype == UNSET) > +        goto cleanup; >   >        /* We might want to decompress (and rename), but not >           compress during this pass since we might need the section > @@ -754,7 +777,7 @@ process_file (const char *fname) >          case ZLIB_GNU: >            if (startswith (sname, ".debug")) >          { > -          if (schtype == ZLIB) > +          if (schtype == ZLIB || schtype == ZSTD) >              { >                /* First decompress to recompress GNU style. >               Don't report even when verbose.  */ > @@ -818,19 +841,22 @@ process_file (const char *fname) >            break; >   >          case ZLIB: > -          if ((shdr->sh_flags & SHF_COMPRESSED) == 0) > +        case ZSTD: > +          if (schtype != type) >          { > -          if (schtype == ZLIB_GNU) > +          if (schtype != NONE) >              { > -              /* First decompress to recompress zlib style. > -             Don't report even when verbose.  */ > +              /* Decompress first.  */ >                if (compress_section (scn, size, sname, NULL, ndx, >                          schtype, NONE, false) < 0) >              goto cleanup; >   > -              snamebuf[0] = '.'; > -              strcpy (&snamebuf[1], &sname[2]); > -              newname = snamebuf; > +              if (schtype == ZLIB_GNU) > +            { > +              snamebuf[0] = '.'; > +              strcpy (&snamebuf[1], &sname[2]); > +              newname = snamebuf; > +            } >              } >   >            if (skip_compress_section) > @@ -838,7 +864,7 @@ process_file (const char *fname) >                if (ndx == shdrstrndx) >              { >                shstrtab_size = size; > -              shstrtab_compressed = ZLIB; > +              shstrtab_compressed = type; >                if (shstrtab_name != NULL >                    || shstrtab_newname != NULL) >                  { > @@ -855,7 +881,7 @@ process_file (const char *fname) >                else >              { >                symtab_size = size; > -              symtab_compressed = ZLIB; > +              symtab_compressed = type; >                symtab_name = xstrdup (sname); >                symtab_newname = (newname == NULL >                          ? NULL : xstrdup (newname)); > @@ -1378,7 +1404,7 @@ main (int argc, char **argv) >      N_("Place (de)compressed output into FILE"), >      0 }, >        { "type", 't', "TYPE", 0, > -    N_("What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias) or 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias)"), > +    N_("What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias), 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias) or 'zstd'"), >      0 }, >        { "name", 'n', "SECTION", 0, >      N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"), > diff --git a/src/readelf.c b/src/readelf.c > index cc3e0229..451f8400 100644 > --- a/src/readelf.c > +++ b/src/readelf.c > @@ -1238,13 +1238,17 @@ get_visibility_type (int value) >  static const char * >  elf_ch_type_name (unsigned int code) >  { > -  if (code == 0) > -    return "NONE"; > - > -  if (code == ELFCOMPRESS_ZLIB) > -    return "ZLIB"; > - > -  return "UNKNOWN"; > +  switch (code) > +    { > +    case 0: > +      return "NONE"; > +    case ELFCOMPRESS_ZLIB: > +      return "ZLIB"; > +    case ELFCOMPRESS_ZSTD: > +      return "ZSTD"; > +    default: > +      return "UNKNOWN"; > +    } >  } >   >  /* Print the section headers.  */ > diff --git a/tests/run-compress-test.sh b/tests/run-compress-test.sh > index a6a298f5..3f9c990e 100755 > --- a/tests/run-compress-test.sh > +++ b/tests/run-compress-test.sh > @@ -61,6 +61,30 @@ testrun_elfcompress_file() >      echo "uncompress $elfcompressedfile -> $elfuncompressedfile" >      testrun ${abs_top_builddir}/src/elfcompress -v -t none -o ${elfuncompressedfile} ${elfcompressedfile} >      testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${elfuncompressedfile} > + > +    outputfile="${infile}.gabi.zstd" > +    tempfiles "$outputfile" > +    echo "zstd compress $elfcompressedfile -> $outputfile" > +    testrun ${abs_top_builddir}/src/elfcompress -v -t zstd -o ${outputfile} ${elfcompressedfile} > +    testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${outputfile} > +    echo "checking compressed section header" $outputfile > +    testrun ${abs_top_builddir}/src/readelf -Sz ${outputfile} | grep "ELF ZSTD" >/dev/null > + > +    zstdfile="${infile}.zstd" > +    tempfiles "$zstdfile" > +    echo "zstd compress $uncompressedfile -> $zstdfile" > +    testrun ${abs_top_builddir}/src/elfcompress -v -t zstd -o ${zstdfile} ${elfuncompressedfile} > +    testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${zstdfile} > +    echo "checking compressed section header" $zstdfile > +    testrun ${abs_top_builddir}/src/readelf -Sz ${zstdfile} | grep "ELF ZSTD" >/dev/null > + > +    zstdgnufile="${infile}.zstd.gnu" > +    tempfiles "$zstdgnufile" > +    echo "zstd re-compress to GNU ZLIB $zstdfile -> $zstdgnufile" > +    testrun ${abs_top_builddir}/src/elfcompress -v -t zlib-gnu -o ${zstdgnufile} ${zstdfile} > +    testrun ${abs_top_builddir}/src/elfcmp ${uncompressedfile} ${zstdgnufile} > +    echo "checking .zdebug section name" $zstdgnufile > +    testrun ${abs_top_builddir}/src/readelf -S ${zstdgnufile} | grep ".zdebug" >/dev/null >  } >   >  testrun_elfcompress()