LCOV - code coverage report
Current view: top level - src - elfcompress.c (source / functions) Hit Total Coverage
Test: elfutils-0.187 Lines: 415 622 66.7 %
Date: 2022-04-26 02:07:47 Functions: 8 8 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 272 431 63.1 %

           Branch data     Line data    Source code
       1                 :            : /* Compress or decompress an ELF file.
       2                 :            :    Copyright (C) 2015, 2016, 2018 Red Hat, Inc.
       3                 :            :    This file is part of elfutils.
       4                 :            : 
       5                 :            :    This file is free software; you can redistribute it and/or modify
       6                 :            :    it under the terms of the GNU General Public License as published by
       7                 :            :    the Free Software Foundation; either version 3 of the License, or
       8                 :            :    (at your option) any later version.
       9                 :            : 
      10                 :            :    elfutils is distributed in the hope that it will be useful, but
      11                 :            :    WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :            :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13                 :            :    GNU General Public License for more details.
      14                 :            : 
      15                 :            :    You should have received a copy of the GNU General Public License
      16                 :            :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      17                 :            : 
      18                 :            : #include <config.h>
      19                 :            : #include <assert.h>
      20                 :            : #include <argp.h>
      21                 :            : #include <stdbool.h>
      22                 :            : #include <stdlib.h>
      23                 :            : #include <inttypes.h>
      24                 :            : #include <stdio.h>
      25                 :            : #include <string.h>
      26                 :            : #include <locale.h>
      27                 :            : #include <fcntl.h>
      28                 :            : #include <fnmatch.h>
      29                 :            : #include <sys/types.h>
      30                 :            : #include <sys/stat.h>
      31                 :            : #include <unistd.h>
      32                 :            : #include ELFUTILS_HEADER(elf)
      33                 :            : #include ELFUTILS_HEADER(ebl)
      34                 :            : #include ELFUTILS_HEADER(dwelf)
      35                 :            : #include <gelf.h>
      36                 :            : #include "system.h"
      37                 :            : #include "libeu.h"
      38                 :            : #include "printversion.h"
      39                 :            : 
      40                 :            : /* Name and version of program.  */
      41                 :            : ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
      42                 :            : 
      43                 :            : /* Bug report address.  */
      44                 :            : ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
      45                 :            : 
      46                 :            : static int verbose = 0; /* < 0, no warnings, > 0 extra verbosity.  */
      47                 :            : static bool force = false;
      48                 :            : static bool permissive = false;
      49                 :            : static const char *foutput = NULL;
      50                 :            : 
      51                 :            : #define T_UNSET 0
      52                 :            : #define T_DECOMPRESS 1    /* none */
      53                 :            : #define T_COMPRESS_ZLIB 2 /* zlib */
      54                 :            : #define T_COMPRESS_GNU  3 /* zlib-gnu */
      55                 :            : #define WORD_BITS (8U * sizeof (unsigned int))
      56                 :            : 
      57                 :            : static int type = T_UNSET;
      58                 :            : 
      59                 :            : struct section_pattern
      60                 :            : {
      61                 :            :   char *pattern;
      62                 :            :   struct section_pattern *next;
      63                 :            : };
      64                 :            : 
      65                 :            : static struct section_pattern *patterns = NULL;
      66                 :            : 
      67                 :            : static void
      68                 :        142 : add_pattern (const char *pattern)
      69                 :            : {
      70                 :        142 :   struct section_pattern *p = xmalloc (sizeof *p);
      71                 :        142 :   p->pattern = xstrdup (pattern);
      72                 :        142 :   p->next = patterns;
      73                 :        142 :   patterns = p;
      74                 :        142 : }
      75                 :            : 
      76                 :            : static void
      77                 :        142 : free_patterns (void)
      78                 :            : {
      79                 :        142 :   struct section_pattern *pattern = patterns;
      80         [ +  + ]:        284 :   while (pattern != NULL)
      81                 :            :     {
      82                 :        142 :       struct section_pattern *p = pattern;
      83                 :        142 :       pattern = p->next;
      84                 :        142 :       free (p->pattern);
      85                 :        142 :       free (p);
      86                 :            :     }
      87                 :        142 : }
      88                 :            : 
      89                 :            : static error_t
      90                 :       1134 : parse_opt (int key, char *arg __attribute__ ((unused)),
      91                 :            :            struct argp_state *state __attribute__ ((unused)))
      92                 :            : {
      93   [ +  +  +  -  :       1134 :   switch (key)
          +  +  +  +  -  
                   +  + ]
      94                 :            :     {
      95                 :        120 :     case 'v':
      96                 :        120 :       verbose++;
      97                 :        120 :       break;
      98                 :            : 
      99                 :         18 :     case 'q':
     100                 :         18 :       verbose--;
     101                 :         18 :       break;
     102                 :            : 
     103                 :         21 :     case 'f':
     104                 :         21 :       force = true;
     105                 :         21 :       break;
     106                 :            : 
     107                 :          0 :     case 'p':
     108                 :          0 :       permissive = true;
     109                 :          0 :       break;
     110                 :            : 
     111                 :          9 :     case 'n':
     112                 :          9 :       add_pattern (arg);
     113                 :          9 :       break;
     114                 :            : 
     115                 :        132 :     case 'o':
     116         [ -  + ]:        132 :       if (foutput != NULL)
     117                 :          0 :         argp_error (state, N_("-o option specified twice"));
     118                 :            :       else
     119                 :        132 :         foutput = arg;
     120                 :            :       break;
     121                 :            : 
     122                 :        124 :     case 't':
     123         [ -  + ]:        124 :       if (type != T_UNSET)
     124                 :          0 :         argp_error (state, N_("-t option specified twice"));
     125                 :            : 
     126         [ +  + ]:        124 :       if (strcmp ("none", arg) == 0)
     127                 :         73 :         type = T_DECOMPRESS;
     128   [ +  +  -  + ]:         51 :       else if (strcmp ("zlib", arg) == 0 || strcmp ("zlib-gabi", arg) == 0)
     129                 :         25 :         type = T_COMPRESS_ZLIB;
     130   [ +  -  +  - ]:         26 :       else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0)
     131                 :         26 :         type = T_COMPRESS_GNU;
     132                 :            :       else
     133                 :          0 :         argp_error (state, N_("unknown compression type '%s'"), arg);
     134                 :            :       break;
     135                 :            : 
     136                 :        142 :     case ARGP_KEY_SUCCESS:
     137         [ +  + ]:        142 :       if (type == T_UNSET)
     138                 :         18 :         type = T_COMPRESS_ZLIB;
     139         [ +  + ]:        142 :       if (patterns == NULL)
     140                 :        133 :         add_pattern (".?(z)debug*");
     141                 :            :       break;
     142                 :            : 
     143                 :          0 :     case ARGP_KEY_NO_ARGS:
     144                 :            :       /* We need at least one input file.  */
     145                 :          0 :       argp_error (state, N_("No input file given"));
     146                 :          0 :       break;
     147                 :            : 
     148                 :        142 :     case ARGP_KEY_ARGS:
     149   [ +  +  -  + ]:        142 :       if (foutput != NULL && state->argc - state->next > 1)
     150                 :          0 :         argp_error (state,
     151                 :            :                     N_("Only one input file allowed together with '-o'"));
     152                 :            :       /* We only use this for checking the number of arguments, we don't
     153                 :            :          actually want to consume them.  */
     154                 :            :       FALLTHROUGH;
     155                 :            :     default:
     156                 :            :       return ARGP_ERR_UNKNOWN;
     157                 :            :     }
     158                 :            :   return 0;
     159                 :            : }
     160                 :            : 
     161                 :            : static bool
     162                 :       3301 : section_name_matches (const char *name)
     163                 :            : {
     164                 :       3301 :   struct section_pattern *pattern = patterns;
     165         [ +  + ]:       5780 :   while (pattern != NULL)
     166                 :            :     {
     167         [ +  + ]:       3301 :       if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0)
     168                 :            :         return true;
     169                 :       2479 :       pattern = pattern->next;
     170                 :            :     }
     171                 :            :   return false;
     172                 :            : }
     173                 :            : 
     174                 :            : static int
     175                 :        158 : setshdrstrndx (Elf *elf, GElf_Ehdr *ehdr, size_t ndx)
     176                 :            : {
     177         [ +  - ]:        158 :   if (ndx < SHN_LORESERVE)
     178                 :        158 :     ehdr->e_shstrndx = ndx;
     179                 :            :   else
     180                 :            :     {
     181                 :          0 :       ehdr->e_shstrndx = SHN_XINDEX;
     182                 :          0 :       Elf_Scn *zscn = elf_getscn (elf, 0);
     183                 :          0 :       GElf_Shdr zshdr_mem;
     184                 :          0 :       GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
     185         [ #  # ]:          0 :       if (zshdr == NULL)
     186                 :          0 :         return -1;
     187                 :          0 :       zshdr->sh_link = ndx;
     188         [ #  # ]:          0 :       if (gelf_update_shdr (zscn, zshdr) == 0)
     189                 :            :         return -1;
     190                 :            :     }
     191                 :            : 
     192         [ -  + ]:        158 :   if (gelf_update_ehdr (elf, ehdr) == 0)
     193                 :          0 :     return -1;
     194                 :            : 
     195                 :            :   return 0;
     196                 :            : }
     197                 :            : 
     198                 :            : static int
     199                 :        695 : compress_section (Elf_Scn *scn, size_t orig_size, const char *name,
     200                 :            :                   const char *newname, size_t ndx,
     201                 :            :                   bool gnu, bool compress, bool report_verbose)
     202                 :            : {
     203                 :        695 :   int res;
     204   [ +  +  +  + ]:        695 :   unsigned int flags = compress && force ? ELF_CHF_FORCE : 0;
     205         [ +  + ]:        695 :   if (gnu)
     206                 :        359 :     res = elf_compress_gnu (scn, compress ? 1 : 0, flags);
     207                 :            :   else
     208                 :        336 :     res = elf_compress (scn, compress ? ELFCOMPRESS_ZLIB : 0, flags);
     209                 :            : 
     210         [ -  + ]:        695 :   if (res < 0)
     211                 :          0 :     error (0, 0, "Couldn't decompress section [%zd] %s: %s",
     212                 :            :            ndx, name, elf_errmsg (-1));
     213                 :            :   else
     214                 :            :     {
     215         [ +  + ]:        695 :       if (compress && res == 0)
     216                 :            :         {
     217         [ +  - ]:         48 :           if (verbose >= 0)
     218                 :         48 :             printf ("[%zd] %s NOT compressed, wouldn't be smaller\n",
     219                 :            :                     ndx, name);
     220                 :            :         }
     221                 :            : 
     222         [ +  + ]:        695 :       if (report_verbose && res > 0)
     223                 :            :         {
     224         [ +  + ]:        440 :           printf ("[%zd] %s %s", ndx, name,
     225                 :            :                   compress ? "compressed" : "decompressed");
     226         [ +  + ]:        440 :           if (newname != NULL)
     227                 :        220 :             printf (" -> %s", newname);
     228                 :            : 
     229                 :            :           /* Reload shdr, it has changed.  */
     230                 :        440 :           GElf_Shdr shdr_mem;
     231                 :        440 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     232         [ -  + ]:        440 :           if (shdr == NULL)
     233                 :            :             {
     234                 :          0 :               error (0, 0, "Couldn't get shdr for section [%zd]", ndx);
     235                 :          0 :               return -1;
     236                 :            :             }
     237                 :        440 :           float new = shdr->sh_size;
     238         [ +  - ]:        440 :           float orig = orig_size ?: 1;
     239                 :        440 :           printf (" (%zu => %" PRIu64 " %.2f%%)\n",
     240                 :        440 :                   orig_size, shdr->sh_size, (new / orig) * 100);
     241                 :            :         }
     242                 :            :     }
     243                 :            : 
     244                 :            :   return res;
     245                 :            : }
     246                 :            : 
     247                 :            : static void
     248                 :        693 : set_section (unsigned int *sections, size_t ndx)
     249                 :            : {
     250                 :        693 :   sections[ndx / WORD_BITS] |= (1U << (ndx % WORD_BITS));
     251                 :            : }
     252                 :            : 
     253                 :            : static bool
     254                 :       3301 : get_section (unsigned int *sections, size_t ndx)
     255                 :            : {
     256                 :       3301 :   return (sections[ndx / WORD_BITS] & (1U << (ndx % WORD_BITS))) != 0;
     257                 :            : }
     258                 :            : 
     259                 :            : /* How many sections are we going to change?  */
     260                 :            : static size_t
     261                 :         26 : get_sections (unsigned int *sections, size_t shnum)
     262                 :            : {
     263                 :         26 :   size_t s = 0;
     264         [ +  + ]:         69 :   for (size_t i = 0; i < shnum / WORD_BITS + 1; i++)
     265                 :         43 :     s += __builtin_popcount (sections[i]);
     266                 :         26 :   return s;
     267                 :            : }
     268                 :            : 
     269                 :            : static int
     270                 :        158 : process_file (const char *fname)
     271                 :            : {
     272         [ +  + ]:        158 :   if (verbose > 0)
     273                 :        120 :     printf ("processing: %s\n", fname);
     274                 :            : 
     275                 :            :   /* The input ELF.  */
     276                 :        158 :   int fd = -1;
     277                 :        158 :   Elf *elf = NULL;
     278                 :            : 
     279                 :            :   /* The output ELF.  */
     280                 :        158 :   char *fnew = NULL;
     281                 :        158 :   int fdnew = -1;
     282                 :        158 :   Elf *elfnew = NULL;
     283                 :            : 
     284                 :            :   /* Buffer for (one) new section name if necessary.  */
     285                 :        158 :   char *snamebuf = NULL;
     286                 :            : 
     287                 :            :   /* String table (and symbol table), if section names need adjusting.  */
     288                 :        158 :   Dwelf_Strtab *names = NULL;
     289                 :        158 :   Dwelf_Strent **scnstrents = NULL;
     290                 :        158 :   Dwelf_Strent **symstrents = NULL;
     291                 :        158 :   char **scnnames = NULL;
     292                 :            : 
     293                 :            :   /* Section data from names.  */
     294                 :        158 :   void *namesbuf = NULL;
     295                 :            : 
     296                 :            :   /* Which sections match and need to be (un)compressed.  */
     297                 :        158 :   unsigned int *sections = NULL;
     298                 :            : 
     299                 :            :   /* How many sections are we talking about?  */
     300                 :        158 :   size_t shnum = 0;
     301                 :        158 :   int res = 1;
     302                 :            : 
     303                 :        158 :   fd = open (fname, O_RDONLY);
     304         [ -  + ]:        158 :   if (fd < 0)
     305                 :            :     {
     306                 :          0 :       error (0, errno, "Couldn't open %s\n", fname);
     307                 :          0 :       goto cleanup;
     308                 :            :     }
     309                 :            : 
     310                 :        158 :   elf = elf_begin (fd, ELF_C_READ, NULL);
     311         [ -  + ]:        158 :   if (elf == NULL)
     312                 :            :     {
     313                 :          0 :       error (0, 0, "Couldn't open ELF file %s for reading: %s",
     314                 :            :              fname, elf_errmsg (-1));
     315                 :          0 :       goto cleanup;
     316                 :            :     }
     317                 :            : 
     318                 :            :   /* We don't handle ar files (or anything else), we probably should.  */
     319                 :        158 :   Elf_Kind kind = elf_kind (elf);
     320         [ -  + ]:        158 :   if (kind != ELF_K_ELF)
     321                 :            :     {
     322         [ #  # ]:          0 :       if (kind == ELF_K_AR)
     323                 :          0 :         error (0, 0, "Cannot handle ar files: %s", fname);
     324                 :            :       else
     325                 :          0 :         error (0, 0, "Unknown file type: %s", fname);
     326                 :          0 :       goto cleanup;
     327                 :            :     }
     328                 :            : 
     329                 :        158 :   struct stat st;
     330         [ -  + ]:        158 :   if (fstat (fd, &st) != 0)
     331                 :            :     {
     332                 :          0 :       error (0, errno, "Couldn't fstat %s", fname);
     333                 :          0 :       goto cleanup;
     334                 :            :     }
     335                 :            : 
     336                 :        158 :   GElf_Ehdr ehdr;
     337         [ -  + ]:        158 :   if (gelf_getehdr (elf, &ehdr) == NULL)
     338                 :            :     {
     339                 :          0 :       error (0, 0, "Couldn't get ehdr for %s: %s", fname, elf_errmsg (-1));
     340                 :          0 :       goto cleanup;
     341                 :            :     }
     342                 :            : 
     343                 :            :   /* Get the section header string table.  */
     344                 :        158 :   size_t shdrstrndx;
     345         [ -  + ]:        158 :   if (elf_getshdrstrndx (elf, &shdrstrndx) != 0)
     346                 :            :     {
     347                 :          0 :       error (0, 0, "Couldn't get section header string table index in %s: %s",
     348                 :            :              fname, elf_errmsg (-1));
     349                 :          0 :       goto cleanup;
     350                 :            :     }
     351                 :            : 
     352                 :            :   /* How many sections are we talking about?  */
     353         [ -  + ]:        158 :   if (elf_getshdrnum (elf, &shnum) != 0)
     354                 :            :     {
     355                 :          0 :       error (0, 0, "Couldn't get number of sections in %s: %s",
     356                 :            :              fname, elf_errmsg (1));
     357                 :          0 :       goto cleanup;
     358                 :            :     }
     359                 :            : 
     360         [ -  + ]:        158 :   if (shnum == 0)
     361                 :            :     {
     362                 :          0 :       error (0, 0, "ELF file %s has no sections", fname);
     363                 :          0 :       goto cleanup;
     364                 :            :     }
     365                 :            : 
     366                 :        158 :   sections = xcalloc (shnum / 8 + 1, sizeof (unsigned int));
     367                 :            : 
     368                 :        158 :   size_t phnum;
     369         [ -  + ]:        158 :   if (elf_getphdrnum (elf, &phnum) != 0)
     370                 :            :     {
     371                 :          0 :       error (0, 0, "Couldn't get phdrnum: %s", elf_errmsg (-1));
     372                 :          0 :       goto cleanup;
     373                 :            :     }
     374                 :            : 
     375                 :            :   /* Whether we need to adjust any section names (going to/from GNU
     376                 :            :      naming).  If so we'll need to build a new section header string
     377                 :            :      table.  */
     378                 :        158 :   bool adjust_names = false;
     379                 :            : 
     380                 :            :   /* If there are phdrs we want to maintain the layout of the
     381                 :            :      allocated sections in the file.  */
     382                 :        158 :   bool layout = phnum != 0;
     383                 :            : 
     384                 :            :   /* While going through all sections keep track of last section data
     385                 :            :      offset if needed to keep the layout.  We are responsible for
     386                 :            :      adding the section offsets and headers (e_shoff) in that case
     387                 :            :      (which we will place after the last section).  */
     388                 :        158 :   GElf_Off last_offset = 0;
     389         [ +  + ]:        158 :   if (layout)
     390                 :        141 :     last_offset = (ehdr.e_phoff
     391                 :        141 :                    + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT));
     392                 :            : 
     393                 :            :   /* Which section, if any, is a symbol table that shares a string
     394                 :            :      table with the section header string table?  */
     395                 :            :   size_t symtabndx = 0;
     396                 :            : 
     397                 :            :   /* We do three passes over all sections.
     398                 :            : 
     399                 :            :      First an inspection pass over the old Elf to see which section
     400                 :            :      data needs to be copied and/or transformed, which sections need a
     401                 :            :      names change and whether there is a symbol table that might need
     402                 :            :      to be adjusted be if the section header name table is changed.
     403                 :            : 
     404                 :            :      If nothing needs changing, and the input and output file are the
     405                 :            :      same, we are done.
     406                 :            : 
     407                 :            :      Second a collection pass that creates the Elf sections and copies
     408                 :            :      the data.  This pass will compress/decompress section data when
     409                 :            :      needed.  And it will collect all data needed if we'll need to
     410                 :            :      construct a new string table. Afterwards the new string table is
     411                 :            :      constructed.
     412                 :            : 
     413                 :            :      Third a fixup/adjustment pass over the new Elf that will adjust
     414                 :            :      any section references (names) and adjust the layout based on the
     415                 :            :      new sizes of the sections if necessary.  This pass is optional if
     416                 :            :      we aren't responsible for the layout and the section header
     417                 :            :      string table hasn't been changed.  */
     418                 :            : 
     419                 :            :   /* Inspection pass.  */
     420                 :            :   size_t maxnamelen = 0;
     421                 :            :   Elf_Scn *scn = NULL;
     422         [ +  + ]:       3459 :   while ((scn = elf_nextscn (elf, scn)) != NULL)
     423                 :            :     {
     424                 :       3301 :       size_t ndx = elf_ndxscn (scn);
     425         [ -  + ]:       3301 :       if (ndx > shnum)
     426                 :            :         {
     427                 :          0 :           error (0, 0, "Unexpected section number %zd, expected only %zd",
     428                 :            :                  ndx, shnum);
     429                 :          0 :           goto cleanup;
     430                 :            :         }
     431                 :            : 
     432                 :       3301 :       GElf_Shdr shdr_mem;
     433                 :       3301 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     434         [ -  + ]:       3301 :       if (shdr == NULL)
     435                 :            :         {
     436                 :          0 :           error (0, 0, "Couldn't get shdr for section %zd", ndx);
     437                 :          0 :           goto cleanup;
     438                 :            :         }
     439                 :            : 
     440                 :       3301 :       const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
     441         [ -  + ]:       3301 :       if (sname == NULL)
     442                 :            :         {
     443                 :          0 :           error (0, 0, "Couldn't get name for section %zd", ndx);
     444                 :          0 :           goto cleanup;
     445                 :            :         }
     446                 :            : 
     447         [ +  + ]:       3301 :       if (section_name_matches (sname))
     448                 :            :         {
     449   [ +  +  +  + ]:        822 :           if (!force && type == T_DECOMPRESS
     450         [ +  + ]:        373 :               && (shdr->sh_flags & SHF_COMPRESSED) == 0
     451         [ +  + ]:        251 :               && !startswith (sname, ".zdebug"))
     452                 :            :             {
     453         [ +  + ]:        129 :               if (verbose > 0)
     454                 :        122 :                 printf ("[%zd] %s already decompressed\n", ndx, sname);
     455                 :            :             }
     456   [ +  +  +  + ]:        693 :           else if (!force && type == T_COMPRESS_ZLIB
     457         [ -  + ]:        122 :                    && (shdr->sh_flags & SHF_COMPRESSED) != 0)
     458                 :            :             {
     459         [ #  # ]:          0 :               if (verbose > 0)
     460                 :          0 :                 printf ("[%zd] %s already compressed\n", ndx, sname);
     461                 :            :             }
     462   [ +  +  +  + ]:        693 :           else if (!force && type == T_COMPRESS_GNU
     463         [ -  + ]:        122 :                    && startswith (sname, ".zdebug"))
     464                 :            :             {
     465         [ #  # ]:          0 :               if (verbose > 0)
     466                 :          0 :                 printf ("[%zd] %s already GNU compressed\n", ndx, sname);
     467                 :            :             }
     468         [ +  - ]:        693 :           else if (shdr->sh_type != SHT_NOBITS
     469         [ +  - ]:        693 :               && (shdr->sh_flags & SHF_ALLOC) == 0)
     470                 :            :             {
     471                 :        693 :               set_section (sections, ndx);
     472                 :            :               /* Check if we might want to change this section name.  */
     473         [ +  + ]:        693 :               if (! adjust_names
     474         [ +  + ]:        408 :                   && ((type != T_COMPRESS_GNU
     475         [ +  + ]:        366 :                        && startswith (sname, ".zdebug"))
     476         [ +  + ]:        376 :                       || (type == T_COMPRESS_GNU
     477         [ -  + ]:         42 :                           && startswith (sname, ".debug"))))
     478                 :            :                 adjust_names = true;
     479                 :            : 
     480                 :            :               /* We need a buffer this large if we change the names.  */
     481         [ +  + ]:        619 :               if (adjust_names)
     482                 :            :                 {
     483                 :        359 :                   size_t slen = strlen (sname);
     484                 :        359 :                   if (slen > maxnamelen)
     485                 :            :                     maxnamelen = slen;
     486                 :            :                 }
     487                 :            :             }
     488                 :            :           else
     489         [ #  # ]:          0 :             if (verbose >= 0)
     490         [ #  # ]:          0 :               printf ("[%zd] %s ignoring %s section\n", ndx, sname,
     491                 :            :                       (shdr->sh_type == SHT_NOBITS ? "no bits" : "allocated"));
     492                 :            :         }
     493                 :            : 
     494         [ +  + ]:       3301 :       if (shdr->sh_type == SHT_SYMTAB)
     495                 :            :         {
     496                 :            :           /* Check if we might have to adjust the symbol name indexes.  */
     497         [ +  + ]:        158 :           if (shdr->sh_link == shdrstrndx)
     498                 :            :             {
     499         [ -  + ]:         60 :               if (symtabndx != 0)
     500                 :            :                 {
     501                 :          0 :                   error (0, 0,
     502                 :            :                          "Multiple symbol tables (%zd, %zd) using the same string table unsupported", symtabndx, ndx);
     503                 :          0 :                   goto cleanup;
     504                 :            :                 }
     505                 :            :               symtabndx = ndx;
     506                 :            :             }
     507                 :            :         }
     508                 :            : 
     509                 :            :       /* Keep track of last allocated data offset.  */
     510         [ +  + ]:       3301 :       if (layout)
     511         [ +  + ]:       2786 :         if ((shdr->sh_flags & SHF_ALLOC) != 0)
     512                 :            :           {
     513                 :       3148 :             GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS
     514         [ +  + ]:       1574 :                                               ? shdr->sh_size : 0);
     515                 :       1574 :             if (last_offset < off)
     516                 :            :               last_offset = off;
     517                 :            :           }
     518                 :            :     }
     519                 :            : 
     520   [ +  +  -  + ]:        184 :   if (foutput == NULL && get_sections (sections, shnum) == 0)
     521                 :            :     {
     522         [ #  # ]:          0 :       if (verbose > 0)
     523                 :          0 :         printf ("Nothing to do.\n");
     524                 :          0 :       res = 0;
     525                 :          0 :       goto cleanup;
     526                 :            :     }
     527                 :            : 
     528         [ +  + ]:        158 :   if (adjust_names)
     529                 :            :     {
     530                 :         74 :       names = dwelf_strtab_init (true);
     531         [ -  + ]:         74 :       if (names == NULL)
     532                 :            :         {
     533                 :          0 :           error (0, 0, "Not enough memory for new strtab");
     534                 :          0 :           goto cleanup;
     535                 :            :         }
     536                 :         74 :       scnstrents = xmalloc (shnum
     537                 :            :                             * sizeof (Dwelf_Strent *));
     538                 :         74 :       scnnames = xcalloc (shnum, sizeof (char *));
     539                 :            :     }
     540                 :            : 
     541                 :            :   /* Create a new (temporary) ELF file for the result.  */
     542         [ +  + ]:        158 :   if (foutput == NULL)
     543                 :            :     {
     544                 :         26 :       size_t fname_len = strlen (fname);
     545                 :         26 :       fnew = xmalloc (fname_len + sizeof (".XXXXXX"));
     546                 :         26 :       strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
     547                 :         26 :       fdnew = mkstemp (fnew);
     548                 :            :     }
     549                 :            :   else
     550                 :            :     {
     551                 :        132 :       fnew = xstrdup (foutput);
     552                 :        132 :       fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS);
     553                 :            :     }
     554                 :            : 
     555         [ -  + ]:        158 :   if (fdnew < 0)
     556                 :            :     {
     557                 :          0 :       error (0, errno, "Couldn't create output file %s", fnew);
     558                 :            :       /* Since we didn't create it we don't want to try to unlink it.  */
     559                 :          0 :       free (fnew);
     560                 :          0 :       fnew = NULL;
     561                 :          0 :       goto cleanup;
     562                 :            :     }
     563                 :            : 
     564                 :        158 :   elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
     565         [ -  + ]:        158 :   if (elfnew == NULL)
     566                 :            :     {
     567                 :          0 :       error (0, 0, "Couldn't open new ELF %s for writing: %s",
     568                 :            :              fnew, elf_errmsg (-1));
     569                 :          0 :       goto cleanup;
     570                 :            :     }
     571                 :            : 
     572                 :            :   /* Create the new ELF header and copy over all the data.  */
     573         [ -  + ]:        158 :   if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0)
     574                 :            :     {
     575                 :          0 :       error (0, 0, "Couldn't create new ehdr: %s", elf_errmsg (-1));
     576                 :          0 :       goto cleanup;
     577                 :            :     }
     578                 :            : 
     579                 :        158 :   GElf_Ehdr newehdr;
     580         [ -  + ]:        158 :   if (gelf_getehdr (elfnew, &newehdr) == NULL)
     581                 :            :     {
     582                 :          0 :       error (0, 0, "Couldn't get new ehdr: %s", elf_errmsg (-1));
     583                 :          0 :       goto cleanup;
     584                 :            :     }
     585                 :            : 
     586                 :        158 :   newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA];
     587                 :        158 :   newehdr.e_type = ehdr.e_type;
     588                 :        158 :   newehdr.e_machine = ehdr.e_machine;
     589                 :        158 :   newehdr.e_version = ehdr.e_version;
     590                 :        158 :   newehdr.e_entry = ehdr.e_entry;
     591                 :        158 :   newehdr.e_flags = ehdr.e_flags;
     592                 :            : 
     593         [ -  + ]:        158 :   if (gelf_update_ehdr (elfnew, &newehdr) == 0)
     594                 :            :     {
     595                 :          0 :       error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
     596                 :          0 :       goto cleanup;
     597                 :            :     }
     598                 :            : 
     599                 :            :   /* Copy over the phdrs as is.  */
     600         [ +  + ]:        158 :   if (phnum != 0)
     601                 :            :     {
     602         [ -  + ]:        141 :       if (gelf_newphdr (elfnew, phnum) == 0)
     603                 :            :         {
     604                 :          0 :           error (0, 0, "Couldn't create phdrs: %s", elf_errmsg (-1));
     605                 :          0 :           goto cleanup;
     606                 :            :         }
     607                 :            : 
     608         [ +  + ]:        690 :       for (size_t cnt = 0; cnt < phnum; ++cnt)
     609                 :            :         {
     610                 :        549 :           GElf_Phdr phdr_mem;
     611                 :        549 :           GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
     612         [ -  + ]:        549 :           if (phdr == NULL)
     613                 :            :             {
     614                 :          0 :               error (0, 0, "Couldn't get phdr %zd: %s", cnt, elf_errmsg (-1));
     615                 :          0 :               goto cleanup;
     616                 :            :             }
     617         [ -  + ]:        549 :           if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
     618                 :            :             {
     619                 :          0 :               error (0, 0, "Couldn't create phdr %zd: %s", cnt,
     620                 :            :                      elf_errmsg (-1));
     621                 :          0 :               goto cleanup;
     622                 :            :             }
     623                 :            :         }
     624                 :            :     }
     625                 :            : 
     626                 :            :   /* Possibly add a 'z' and zero terminator.  */
     627         [ +  + ]:        158 :   if (maxnamelen > 0)
     628                 :         74 :     snamebuf = xmalloc (maxnamelen + 2);
     629                 :            : 
     630                 :            :   /* We might want to read/adjust the section header strings and
     631                 :            :      symbol tables.  If so, and those sections are to be compressed
     632                 :            :      then we will have to decompress it during the collection pass and
     633                 :            :      compress it again in the fixup pass.  Don't compress unnecessary
     634                 :            :      and keep track of whether or not to compress them (later in the
     635                 :            :      fixup pass).  Also record the original size, so we can report the
     636                 :            :      difference later when we do compress.  */
     637                 :        158 :   int shstrtab_compressed = T_UNSET;
     638                 :        158 :   size_t shstrtab_size = 0;
     639                 :        158 :   char *shstrtab_name = NULL;
     640                 :        158 :   char *shstrtab_newname = NULL;
     641                 :        158 :   int symtab_compressed = T_UNSET;
     642                 :        158 :   size_t symtab_size = 0;
     643                 :        158 :   char *symtab_name = NULL;
     644                 :        158 :   char *symtab_newname = NULL;
     645                 :            : 
     646                 :            :   /* Collection pass.  Copy over the sections, (de)compresses matching
     647                 :            :      sections, collect names of sections and symbol table if
     648                 :            :      necessary.  */
     649                 :        158 :   scn = NULL;
     650         [ +  + ]:       3459 :   while ((scn = elf_nextscn (elf, scn)) != NULL)
     651                 :            :     {
     652                 :       3301 :       size_t ndx = elf_ndxscn (scn);
     653         [ -  + ]:       3301 :       assert (ndx < shnum);
     654                 :            : 
     655                 :            :       /* (de)compress if section matched.  */
     656                 :       3301 :       char *sname = NULL;
     657                 :       3301 :       char *newname = NULL;
     658         [ +  + ]:       3301 :       if (get_section (sections, ndx))
     659                 :            :         {
     660                 :        693 :           GElf_Shdr shdr_mem;
     661                 :        693 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     662         [ -  + ]:        693 :           if (shdr == NULL)
     663                 :            :             {
     664                 :          0 :               error (0, 0, "Couldn't get shdr for section %zd", ndx);
     665                 :          0 :               goto cleanup;
     666                 :            :             }
     667                 :            : 
     668                 :        693 :           uint64_t size = shdr->sh_size;
     669                 :        693 :           sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
     670         [ -  + ]:        693 :           if (sname == NULL)
     671                 :            :             {
     672                 :          0 :               error (0, 0, "Couldn't get name for section %zd", ndx);
     673                 :          0 :               goto cleanup;
     674                 :            :             }
     675                 :            : 
     676                 :            :           /* strdup sname, the shdrstrndx section itself might be
     677                 :            :              (de)compressed, invalidating the string pointers.  */
     678                 :        693 :           sname = xstrdup (sname);
     679                 :            : 
     680                 :            :           /* We might want to decompress (and rename), but not
     681                 :            :              compress during this pass since we might need the section
     682                 :            :              data in later passes.  Skip those sections for now and
     683                 :            :              compress them in the fixup pass.  */
     684                 :       1386 :           bool skip_compress_section = (adjust_names
     685   [ +  +  +  - ]:        693 :                                         && (ndx == shdrstrndx
     686         [ +  - ]:        359 :                                             || ndx == symtabndx));
     687                 :            : 
     688   [ +  +  +  - ]:        693 :           switch (type)
     689                 :            :             {
     690                 :        244 :             case T_DECOMPRESS:
     691         [ +  + ]:        244 :               if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
     692                 :            :                 {
     693         [ -  + ]:        122 :                   if (compress_section (scn, size, sname, NULL, ndx,
     694                 :            :                                         false, false, verbose > 0) < 0)
     695                 :          0 :                     goto cleanup;
     696                 :            :                 }
     697         [ +  - ]:        122 :               else if (startswith (sname, ".zdebug"))
     698                 :            :                 {
     699                 :        122 :                   snamebuf[0] = '.';
     700                 :        122 :                   strcpy (&snamebuf[1], &sname[2]);
     701                 :        122 :                   newname = snamebuf;
     702         [ -  + ]:        122 :                   if (compress_section (scn, size, sname, newname, ndx,
     703                 :            :                                         true, false, verbose > 0) < 0)
     704                 :          0 :                     goto cleanup;
     705                 :            :                 }
     706         [ #  # ]:          0 :               else if (verbose > 0)
     707                 :          0 :                 printf ("[%zd] %s already decompressed\n", ndx, sname);
     708                 :            :               break;
     709                 :            : 
     710                 :        237 :             case T_COMPRESS_GNU:
     711         [ +  - ]:        237 :               if (startswith (sname, ".debug"))
     712                 :            :                 {
     713         [ +  + ]:        237 :                   if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
     714                 :            :                     {
     715                 :            :                       /* First decompress to recompress GNU style.
     716                 :            :                          Don't report even when verbose.  */
     717         [ -  + ]:          2 :                       if (compress_section (scn, size, sname, NULL, ndx,
     718                 :            :                                             false, false, false) < 0)
     719                 :          0 :                         goto cleanup;
     720                 :            :                     }
     721                 :            : 
     722                 :        237 :                   snamebuf[0] = '.';
     723                 :        237 :                   snamebuf[1] = 'z';
     724         [ -  + ]:        237 :                   strcpy (&snamebuf[2], &sname[1]);
     725                 :        237 :                   newname = snamebuf;
     726                 :            : 
     727         [ -  + ]:        237 :                   if (skip_compress_section)
     728                 :            :                     {
     729         [ #  # ]:          0 :                       if (ndx == shdrstrndx)
     730                 :            :                         {
     731                 :          0 :                           shstrtab_size = size;
     732                 :          0 :                           shstrtab_compressed = T_COMPRESS_GNU;
     733                 :          0 :                           shstrtab_name = xstrdup (sname);
     734                 :          0 :                           shstrtab_newname = xstrdup (newname);
     735                 :            :                         }
     736                 :            :                       else
     737                 :            :                         {
     738                 :          0 :                           symtab_size = size;
     739                 :          0 :                           symtab_compressed = T_COMPRESS_GNU;
     740                 :          0 :                           symtab_name = xstrdup (sname);
     741                 :          0 :                           symtab_newname = xstrdup (newname);
     742                 :            :                         }
     743                 :            :                     }
     744                 :            :                   else
     745                 :            :                     {
     746                 :        237 :                       int result = compress_section (scn, size, sname, newname,
     747                 :            :                                                      ndx, true, true,
     748                 :            :                                                      verbose > 0);
     749         [ -  + ]:        237 :                       if (result < 0)
     750                 :          0 :                         goto cleanup;
     751                 :            : 
     752         [ +  + ]:        237 :                       if (result == 0)
     753                 :         24 :                         newname = NULL;
     754                 :            :                     }
     755                 :            :                 }
     756         [ #  # ]:          0 :               else if (verbose >= 0)
     757                 :            :                 {
     758         [ #  # ]:          0 :                   if (startswith (sname, ".zdebug"))
     759                 :          0 :                     printf ("[%zd] %s unchanged, already GNU compressed",
     760                 :            :                             ndx, sname);
     761                 :            :                   else
     762                 :          0 :                     printf ("[%zd] %s cannot GNU compress section not starting with .debug\n",
     763                 :            :                             ndx, sname);
     764                 :            :                 }
     765                 :            :               break;
     766                 :            : 
     767                 :        212 :             case T_COMPRESS_ZLIB:
     768         [ +  - ]:        212 :               if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
     769                 :            :                 {
     770         [ -  + ]:        212 :                   if (startswith (sname, ".zdebug"))
     771                 :            :                     {
     772                 :            :                       /* First decompress to recompress zlib style.
     773                 :            :                          Don't report even when verbose.  */
     774         [ #  # ]:          0 :                       if (compress_section (scn, size, sname, NULL, ndx,
     775                 :            :                                             true, false, false) < 0)
     776                 :          0 :                         goto cleanup;
     777                 :            : 
     778                 :          0 :                       snamebuf[0] = '.';
     779                 :          0 :                       strcpy (&snamebuf[1], &sname[2]);
     780                 :          0 :                       newname = snamebuf;
     781                 :            :                     }
     782                 :            : 
     783         [ -  + ]:        212 :                   if (skip_compress_section)
     784                 :            :                     {
     785         [ #  # ]:          0 :                       if (ndx == shdrstrndx)
     786                 :            :                         {
     787                 :          0 :                           shstrtab_size = size;
     788                 :          0 :                           shstrtab_compressed = T_COMPRESS_ZLIB;
     789                 :          0 :                           shstrtab_name = xstrdup (sname);
     790                 :          0 :                           shstrtab_newname = (newname == NULL
     791         [ #  # ]:          0 :                                               ? NULL : xstrdup (newname));
     792                 :            :                         }
     793                 :            :                       else
     794                 :            :                         {
     795                 :          0 :                           symtab_size = size;
     796                 :          0 :                           symtab_compressed = T_COMPRESS_ZLIB;
     797                 :          0 :                           symtab_name = xstrdup (sname);
     798                 :          0 :                           symtab_newname = (newname == NULL
     799         [ #  # ]:          0 :                                             ? NULL : xstrdup (newname));
     800                 :            :                         }
     801                 :            :                     }
     802         [ -  + ]:        212 :                   else if (compress_section (scn, size, sname, newname, ndx,
     803                 :            :                                              false, true, verbose > 0) < 0)
     804                 :          0 :                     goto cleanup;
     805                 :            :                 }
     806         [ #  # ]:          0 :               else if (verbose > 0)
     807                 :          0 :                 printf ("[%zd] %s already compressed\n", ndx, sname);
     808                 :            :               break;
     809                 :            :             }
     810                 :            : 
     811                 :        693 :           free (sname);
     812                 :            :         }
     813                 :            : 
     814                 :       3301 :       Elf_Scn *newscn = elf_newscn (elfnew);
     815         [ -  + ]:       3301 :       if (newscn == NULL)
     816                 :            :         {
     817                 :          0 :           error (0, 0, "Couldn't create new section %zd", ndx);
     818                 :          0 :           goto cleanup;
     819                 :            :         }
     820                 :            : 
     821                 :       3301 :       GElf_Shdr shdr_mem;
     822                 :       3301 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     823         [ -  + ]:       3301 :       if (shdr == NULL)
     824                 :            :         {
     825                 :          0 :           error (0, 0, "Couldn't get shdr for section %zd", ndx);
     826                 :          0 :           goto cleanup;
     827                 :            :         }
     828                 :            : 
     829         [ -  + ]:       3301 :       if (gelf_update_shdr (newscn, shdr) == 0)
     830                 :            :         {
     831                 :          0 :           error (0, 0, "Couldn't update section header %zd", ndx);
     832                 :          0 :           goto cleanup;
     833                 :            :         }
     834                 :            : 
     835                 :            :       /* Except for the section header string table all data can be
     836                 :            :          copied as is.  The section header string table will be
     837                 :            :          created later and the symbol table might be fixed up if
     838                 :            :          necessary.  */
     839   [ +  +  +  + ]:       3301 :       if (! adjust_names || ndx != shdrstrndx)
     840                 :            :         {
     841                 :       3227 :           Elf_Data *data = elf_getdata (scn, NULL);
     842         [ -  + ]:       3227 :           if (data == NULL)
     843                 :            :             {
     844                 :          0 :               error (0, 0, "Couldn't get data from section %zd", ndx);
     845                 :          0 :               goto cleanup;
     846                 :            :             }
     847                 :            : 
     848                 :       3227 :           Elf_Data *newdata = elf_newdata (newscn);
     849         [ -  + ]:       3227 :           if (newdata == NULL)
     850                 :            :             {
     851                 :          0 :               error (0, 0, "Couldn't create new data for section %zd", ndx);
     852                 :          0 :               goto cleanup;
     853                 :            :             }
     854                 :            : 
     855                 :       3227 :           *newdata = *data;
     856                 :            :         }
     857                 :            : 
     858                 :            :       /* Keep track of the (new) section names.  */
     859         [ +  + ]:       3301 :       if (adjust_names)
     860                 :            :         {
     861                 :       1382 :           char *name;
     862         [ +  + ]:       1382 :           if (newname != NULL)
     863                 :            :             name = newname;
     864                 :            :           else
     865                 :            :             {
     866                 :       1047 :               name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
     867         [ -  + ]:       1047 :               if (name == NULL)
     868                 :            :                 {
     869                 :          0 :                   error (0, 0, "Couldn't get name for section [%zd]", ndx);
     870                 :          0 :                   goto cleanup;
     871                 :            :                 }
     872                 :            :             }
     873                 :            : 
     874                 :            :           /* We need to keep a copy of the name till the strtab is done.  */
     875                 :       1382 :           name = scnnames[ndx] = xstrdup (name);
     876         [ -  + ]:       1382 :           if ((scnstrents[ndx] = dwelf_strtab_add (names, name)) == NULL)
     877                 :            :             {
     878                 :          0 :               error (0, 0, "No memory to add section name string table");
     879                 :          0 :               goto cleanup;
     880                 :            :             }
     881                 :            : 
     882                 :            :           /* If the symtab shares strings then add those too.  */
     883         [ +  + ]:       1382 :           if (ndx == symtabndx)
     884                 :            :             {
     885                 :            :               /* If the section is (still) compressed we'll need to
     886                 :            :                  uncompress it first to adjust the data, then
     887                 :            :                  recompress it in the fixup pass.  */
     888         [ +  - ]:         28 :               if (symtab_compressed == T_UNSET)
     889                 :            :                 {
     890                 :         28 :                   size_t size = shdr->sh_size;
     891         [ -  + ]:         28 :                   if ((shdr->sh_flags == SHF_COMPRESSED) != 0)
     892                 :            :                     {
     893                 :            :                       /* Don't report the (internal) uncompression.  */
     894         [ #  # ]:          0 :                       if (compress_section (newscn, size, sname, NULL, ndx,
     895                 :            :                                             false, false, false) < 0)
     896                 :          0 :                         goto cleanup;
     897                 :            : 
     898                 :            :                       symtab_size = size;
     899                 :            :                       symtab_compressed = T_COMPRESS_ZLIB;
     900                 :            :                     }
     901         [ -  + ]:         28 :                   else if (startswith (name, ".zdebug"))
     902                 :            :                     {
     903                 :            :                       /* Don't report the (internal) uncompression.  */
     904         [ #  # ]:          0 :                       if (compress_section (newscn, size, sname, NULL, ndx,
     905                 :            :                                             true, false, false) < 0)
     906                 :          0 :                         goto cleanup;
     907                 :            : 
     908                 :            :                       symtab_size = size;
     909                 :            :                       symtab_compressed = T_COMPRESS_GNU;
     910                 :            :                     }
     911                 :            :                 }
     912                 :            : 
     913                 :         28 :               Elf_Data *symd = elf_getdata (newscn, NULL);
     914         [ -  + ]:         28 :               if (symd == NULL)
     915                 :            :                 {
     916                 :          0 :                   error (0, 0, "Couldn't get symtab data for section [%zd] %s",
     917                 :            :                          ndx, name);
     918                 :          0 :                   goto cleanup;
     919                 :            :                 }
     920                 :         28 :               size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
     921                 :         28 :               size_t syms = symd->d_size / elsize;
     922                 :         28 :               symstrents = xmalloc (syms * sizeof (Dwelf_Strent *));
     923         [ +  + ]:       1495 :               for (size_t i = 0; i < syms; i++)
     924                 :            :                 {
     925                 :       1467 :                   GElf_Sym sym_mem;
     926                 :       1467 :                   GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
     927         [ -  + ]:       1467 :                   if (sym == NULL)
     928                 :            :                     {
     929                 :          0 :                       error (0, 0, "Couldn't get symbol %zd", i);
     930                 :          0 :                       goto cleanup;
     931                 :            :                     }
     932         [ +  + ]:       1467 :                   if (sym->st_name != 0)
     933                 :            :                     {
     934                 :            :                       /* Note we take the name from the original ELF,
     935                 :            :                          since the new one will not have setup the
     936                 :            :                          strtab yet.  */
     937                 :       1053 :                       const char *symname = elf_strptr (elf, shdrstrndx,
     938                 :            :                                                         sym->st_name);
     939         [ -  + ]:       1053 :                       if (symname == NULL)
     940                 :            :                         {
     941                 :          0 :                           error (0, 0, "Couldn't get symbol %zd name", i);
     942                 :          0 :                           goto cleanup;
     943                 :            :                         }
     944                 :       1053 :                       symstrents[i] = dwelf_strtab_add (names, symname);
     945         [ -  + ]:       1053 :                       if (symstrents[i] == NULL)
     946                 :            :                         {
     947                 :          0 :                           error (0, 0, "No memory to add to symbol name");
     948                 :          0 :                           goto cleanup;
     949                 :            :                         }
     950                 :            :                     }
     951                 :            :                 }
     952                 :            :             }
     953                 :            :         }
     954                 :            :     }
     955                 :            : 
     956         [ +  + ]:        158 :   if (adjust_names)
     957                 :            :     {
     958                 :            :       /* We got all needed strings, put the new data in the shstrtab.  */
     959         [ +  + ]:         74 :       if (verbose > 0)
     960                 :         56 :         printf ("[%zd] Updating section string table\n", shdrstrndx);
     961                 :            : 
     962                 :         74 :       scn = elf_getscn (elfnew, shdrstrndx);
     963         [ -  + ]:         74 :       if (scn == NULL)
     964                 :            :         {
     965                 :          0 :           error (0, 0, "Couldn't get new section header string table [%zd]",
     966                 :            :                  shdrstrndx);
     967                 :          0 :           goto cleanup;
     968                 :            :         }
     969                 :            : 
     970                 :         74 :       Elf_Data *data = elf_newdata (scn);
     971         [ -  + ]:         74 :       if (data == NULL)
     972                 :            :         {
     973                 :          0 :           error (0, 0, "Couldn't create new section header string table data");
     974                 :          0 :           goto cleanup;
     975                 :            :         }
     976         [ -  + ]:         74 :       if (dwelf_strtab_finalize (names, data) == NULL)
     977                 :            :         {
     978                 :          0 :           error (0, 0, "Not enough memory to create string table");
     979                 :          0 :           goto cleanup;
     980                 :            :         }
     981                 :         74 :       namesbuf = data->d_buf;
     982                 :            : 
     983                 :         74 :       GElf_Shdr shdr_mem;
     984                 :         74 :       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
     985         [ -  + ]:         74 :       if (shdr == NULL)
     986                 :            :         {
     987                 :          0 :           error (0, 0, "Couldn't get shdr for new section strings %zd",
     988                 :            :                  shdrstrndx);
     989                 :          0 :           goto cleanup;
     990                 :            :         }
     991                 :            : 
     992                 :            :       /* Note that we also might have to compress and possibly set
     993                 :            :          sh_off below */
     994                 :         74 :       shdr->sh_name = dwelf_strent_off (scnstrents[shdrstrndx]);
     995                 :         74 :       shdr->sh_type = SHT_STRTAB;
     996                 :         74 :       shdr->sh_flags = 0;
     997                 :         74 :       shdr->sh_addr = 0;
     998                 :         74 :       shdr->sh_offset = 0;
     999                 :         74 :       shdr->sh_size = data->d_size;
    1000                 :         74 :       shdr->sh_link = SHN_UNDEF;
    1001                 :         74 :       shdr->sh_info = SHN_UNDEF;
    1002                 :         74 :       shdr->sh_addralign = 1;
    1003                 :         74 :       shdr->sh_entsize = 0;
    1004                 :            : 
    1005         [ -  + ]:         74 :       if (gelf_update_shdr (scn, shdr) == 0)
    1006                 :            :         {
    1007                 :          0 :           error (0, 0, "Couldn't update new section strings [%zd]",
    1008                 :            :                  shdrstrndx);
    1009                 :          0 :           goto cleanup;
    1010                 :            :         }
    1011                 :            : 
    1012                 :            :       /* We might have to compress the data if the user asked us to,
    1013                 :            :          or if the section was already compressed (and the user didn't
    1014                 :            :          ask for decompression).  Note somewhat identical code for
    1015                 :            :          symtab below.  */
    1016         [ +  - ]:         74 :       if (shstrtab_compressed == T_UNSET)
    1017                 :            :         {
    1018                 :            :           /* The user didn't ask for compression, but maybe it was
    1019                 :            :              compressed in the original ELF file.  */
    1020                 :         74 :           Elf_Scn *oldscn = elf_getscn (elf, shdrstrndx);
    1021         [ -  + ]:         74 :           if (oldscn == NULL)
    1022                 :            :             {
    1023                 :          0 :               error (0, 0, "Couldn't get section header string table [%zd]",
    1024                 :            :                      shdrstrndx);
    1025                 :          0 :               goto cleanup;
    1026                 :            :             }
    1027                 :            : 
    1028                 :         74 :           shdr = gelf_getshdr (oldscn, &shdr_mem);
    1029         [ -  + ]:         74 :           if (shdr == NULL)
    1030                 :            :             {
    1031                 :          0 :               error (0, 0, "Couldn't get shdr for old section strings [%zd]",
    1032                 :            :                      shdrstrndx);
    1033                 :          0 :               goto cleanup;
    1034                 :            :             }
    1035                 :            : 
    1036                 :         74 :           shstrtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
    1037         [ -  + ]:         74 :           if (shstrtab_name == NULL)
    1038                 :            :             {
    1039                 :          0 :               error (0, 0, "Couldn't get name for old section strings [%zd]",
    1040                 :            :                      shdrstrndx);
    1041                 :          0 :               goto cleanup;
    1042                 :            :             }
    1043                 :            : 
    1044                 :         74 :           shstrtab_size = shdr->sh_size;
    1045         [ +  - ]:         74 :           if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
    1046                 :            :             shstrtab_compressed = T_COMPRESS_ZLIB;
    1047         [ -  + ]:         74 :           else if (startswith (shstrtab_name, ".zdebug"))
    1048                 :          0 :             shstrtab_compressed = T_COMPRESS_GNU;
    1049                 :            :         }
    1050                 :            : 
    1051                 :            :       /* Should we (re)compress?  */
    1052         [ -  + ]:         74 :       if (shstrtab_compressed != T_UNSET)
    1053                 :            :         {
    1054         [ #  # ]:          0 :           if (compress_section (scn, shstrtab_size, shstrtab_name,
    1055                 :            :                                 shstrtab_newname, shdrstrndx,
    1056                 :            :                                 shstrtab_compressed == T_COMPRESS_GNU,
    1057                 :            :                                 true, verbose > 0) < 0)
    1058                 :          0 :             goto cleanup;
    1059                 :            :         }
    1060                 :            :     }
    1061                 :            : 
    1062                 :            :   /* Make sure to re-get the new ehdr.  Adding phdrs and shdrs will
    1063                 :            :      have changed it.  */
    1064         [ -  + ]:        158 :   if (gelf_getehdr (elfnew, &newehdr) == NULL)
    1065                 :            :     {
    1066                 :          0 :       error (0, 0, "Couldn't re-get new ehdr: %s", elf_errmsg (-1));
    1067                 :          0 :       goto cleanup;
    1068                 :            :     }
    1069                 :            : 
    1070                 :            :   /* Set this after the sections have been created, otherwise section
    1071                 :            :      zero might not exist yet.  */
    1072         [ -  + ]:        158 :   if (setshdrstrndx (elfnew, &newehdr, shdrstrndx) != 0)
    1073                 :            :     {
    1074                 :          0 :       error (0, 0, "Couldn't set new shdrstrndx: %s", elf_errmsg (-1));
    1075                 :          0 :       goto cleanup;
    1076                 :            :     }
    1077                 :            : 
    1078                 :            :   /* Fixup pass.  Adjust string table references, symbol table and
    1079                 :            :      layout if necessary.  */
    1080         [ +  + ]:        158 :   if (layout || adjust_names)
    1081                 :            :     {
    1082                 :            :       scn = NULL;
    1083         [ +  + ]:       3207 :       while ((scn = elf_nextscn (elfnew, scn)) != NULL)
    1084                 :            :         {
    1085                 :       3055 :           size_t ndx = elf_ndxscn (scn);
    1086                 :            : 
    1087                 :       3055 :           GElf_Shdr shdr_mem;
    1088                 :       3055 :           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    1089         [ -  + ]:       3055 :           if (shdr == NULL)
    1090                 :            :             {
    1091                 :          0 :               error (0, 0, "Couldn't get shdr for section %zd", ndx);
    1092                 :          0 :               goto cleanup;
    1093                 :            :             }
    1094                 :            : 
    1095                 :            :           /* Keep the offset of allocated sections so they are at the
    1096                 :            :              same place in the file. Add (possibly changed)
    1097                 :            :              unallocated ones after the allocated ones.  */
    1098         [ +  + ]:       3055 :           if ((shdr->sh_flags & SHF_ALLOC) == 0)
    1099                 :            :             {
    1100                 :            :               /* Zero means one.  No alignment constraints.  */
    1101                 :       1403 :               size_t addralign = shdr->sh_addralign ?: 1;
    1102                 :       1403 :               last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
    1103                 :       1403 :               shdr->sh_offset = last_offset;
    1104         [ +  + ]:       1403 :               if (shdr->sh_type != SHT_NOBITS)
    1105                 :       1396 :                 last_offset += shdr->sh_size;
    1106                 :            :             }
    1107                 :            : 
    1108         [ +  + ]:       3055 :           if (adjust_names)
    1109                 :       1382 :             shdr->sh_name = dwelf_strent_off (scnstrents[ndx]);
    1110                 :            : 
    1111         [ -  + ]:       3055 :           if (gelf_update_shdr (scn, shdr) == 0)
    1112                 :            :             {
    1113                 :          0 :               error (0, 0, "Couldn't update section header %zd", ndx);
    1114                 :          0 :               goto cleanup;
    1115                 :            :             }
    1116                 :            : 
    1117         [ +  + ]:       3055 :           if (adjust_names && ndx == symtabndx)
    1118                 :            :             {
    1119         [ +  - ]:         28 :               if (verbose > 0)
    1120                 :         28 :                 printf ("[%zd] Updating symbol table\n", symtabndx);
    1121                 :            : 
    1122                 :         28 :               Elf_Data *symd = elf_getdata (scn, NULL);
    1123         [ -  + ]:         28 :               if (symd == NULL)
    1124                 :            :                 {
    1125                 :          0 :                   error (0, 0, "Couldn't get new symtab data section [%zd]",
    1126                 :            :                          ndx);
    1127                 :          0 :                   goto cleanup;
    1128                 :            :                 }
    1129                 :         28 :               size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
    1130                 :         28 :               size_t syms = symd->d_size / elsize;
    1131         [ +  + ]:       1495 :               for (size_t i = 0; i < syms; i++)
    1132                 :            :                 {
    1133                 :       1467 :                   GElf_Sym sym_mem;
    1134                 :       1467 :                   GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
    1135         [ -  + ]:       1467 :                   if (sym == NULL)
    1136                 :            :                     {
    1137                 :          0 :                       error (0, 0, "2 Couldn't get symbol %zd", i);
    1138                 :          0 :                       goto cleanup;
    1139                 :            :                     }
    1140                 :            : 
    1141         [ +  + ]:       1467 :                   if (sym->st_name != 0)
    1142                 :            :                     {
    1143                 :       1053 :                       sym->st_name = dwelf_strent_off (symstrents[i]);
    1144                 :            : 
    1145         [ -  + ]:       1053 :                       if (gelf_update_sym (symd, i, sym) == 0)
    1146                 :            :                         {
    1147                 :          0 :                           error (0, 0, "Couldn't update symbol %zd", i);
    1148                 :          0 :                           goto cleanup;
    1149                 :            :                         }
    1150                 :            :                     }
    1151                 :            :                 }
    1152                 :            : 
    1153                 :            :               /* We might have to compress the data if the user asked
    1154                 :            :                  us to, or if the section was already compressed (and
    1155                 :            :                  the user didn't ask for decompression).  Note
    1156                 :            :                  somewhat identical code for shstrtab above.  */
    1157         [ +  - ]:         28 :               if (symtab_compressed == T_UNSET)
    1158                 :            :                 {
    1159                 :            :                   /* The user didn't ask for compression, but maybe it was
    1160                 :            :                      compressed in the original ELF file.  */
    1161                 :         28 :                   Elf_Scn *oldscn = elf_getscn (elf, symtabndx);
    1162         [ -  + ]:         28 :                   if (oldscn == NULL)
    1163                 :            :                     {
    1164                 :          0 :                       error (0, 0, "Couldn't get symbol table [%zd]",
    1165                 :            :                              symtabndx);
    1166                 :          0 :                       goto cleanup;
    1167                 :            :                     }
    1168                 :            : 
    1169                 :         28 :                   shdr = gelf_getshdr (oldscn, &shdr_mem);
    1170         [ -  + ]:         28 :                   if (shdr == NULL)
    1171                 :            :                     {
    1172                 :          0 :                       error (0, 0, "Couldn't get old symbol table shdr [%zd]",
    1173                 :            :                              symtabndx);
    1174                 :          0 :                       goto cleanup;
    1175                 :            :                     }
    1176                 :            : 
    1177                 :         28 :                   symtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
    1178         [ -  + ]:         28 :                   if (symtab_name == NULL)
    1179                 :            :                     {
    1180                 :          0 :                       error (0, 0, "Couldn't get old symbol table name [%zd]",
    1181                 :            :                              symtabndx);
    1182                 :          0 :                       goto cleanup;
    1183                 :            :                     }
    1184                 :            : 
    1185                 :         28 :                   symtab_size = shdr->sh_size;
    1186         [ +  - ]:         28 :                   if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
    1187                 :            :                     symtab_compressed = T_COMPRESS_ZLIB;
    1188         [ -  + ]:         28 :                   else if (startswith (symtab_name, ".zdebug"))
    1189                 :          0 :                     symtab_compressed = T_COMPRESS_GNU;
    1190                 :            :                 }
    1191                 :            : 
    1192                 :            :               /* Should we (re)compress?  */
    1193         [ -  + ]:         28 :               if (symtab_compressed != T_UNSET)
    1194                 :            :                 {
    1195         [ #  # ]:          0 :                   if (compress_section (scn, symtab_size, symtab_name,
    1196                 :            :                                         symtab_newname, symtabndx,
    1197                 :            :                                         symtab_compressed == T_COMPRESS_GNU,
    1198                 :            :                                         true, verbose > 0) < 0)
    1199                 :          0 :                     goto cleanup;
    1200                 :            :                 }
    1201                 :            :             }
    1202                 :            :         }
    1203                 :            :     }
    1204                 :            : 
    1205                 :            :   /* If we have phdrs we want elf_update to layout the SHF_ALLOC
    1206                 :            :      sections precisely as in the original file.  In that case we are
    1207                 :            :      also responsible for setting phoff and shoff */
    1208         [ +  + ]:        158 :   if (layout)
    1209                 :            :     {
    1210         [ -  + ]:        141 :       if (gelf_getehdr (elfnew, &newehdr) == NULL)
    1211                 :            :         {
    1212                 :          0 :           error (0, 0, "Couldn't get ehdr: %s", elf_errmsg (-1));
    1213                 :          0 :           goto cleanup;
    1214                 :            :         }
    1215                 :            : 
    1216                 :            :       /* Position the shdrs after the last (unallocated) section.  */
    1217                 :        141 :       const size_t offsize = gelf_fsize (elfnew, ELF_T_OFF, 1, EV_CURRENT);
    1218                 :        141 :       newehdr.e_shoff = ((last_offset + offsize - 1)
    1219                 :        141 :                          & ~((GElf_Off) (offsize - 1)));
    1220                 :            : 
    1221                 :            :       /* The phdrs go in the same place as in the original file.
    1222                 :            :          Normally right after the ELF header.  */
    1223                 :        141 :       newehdr.e_phoff = ehdr.e_phoff;
    1224                 :            : 
    1225         [ -  + ]:        141 :       if (gelf_update_ehdr (elfnew, &newehdr) == 0)
    1226                 :            :         {
    1227                 :          0 :           error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
    1228                 :          0 :           goto cleanup;
    1229                 :            :         }
    1230                 :            :     }
    1231                 :            : 
    1232         [ +  + ]:        158 :   elf_flagelf (elfnew, ELF_C_SET, ((layout ? ELF_F_LAYOUT : 0)
    1233         [ +  - ]:        316 :                                    | (permissive ? ELF_F_PERMISSIVE : 0)));
    1234                 :            : 
    1235         [ -  + ]:        158 :   if (elf_update (elfnew, ELF_C_WRITE) < 0)
    1236                 :            :     {
    1237                 :          0 :       error (0, 0, "Couldn't write %s: %s", fnew, elf_errmsg (-1));
    1238                 :          0 :       goto cleanup;
    1239                 :            :     }
    1240                 :            : 
    1241                 :        158 :   elf_end (elfnew);
    1242                 :        158 :   elfnew = NULL;
    1243                 :            : 
    1244                 :            :   /* Try to match mode and owner.group of the original file.
    1245                 :            :      Note to set suid bits we have to make sure the owner is setup
    1246                 :            :      correctly first. Otherwise fchmod will drop them silently
    1247                 :            :      or fchown may clear them.  */
    1248         [ -  + ]:        158 :   if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
    1249         [ #  # ]:          0 :     if (verbose >= 0)
    1250                 :          0 :       error (0, errno, "Couldn't fchown %s", fnew);
    1251         [ -  + ]:        158 :   if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
    1252         [ #  # ]:          0 :     if (verbose >= 0)
    1253                 :          0 :       error (0, errno, "Couldn't fchmod %s", fnew);
    1254                 :            : 
    1255                 :            :   /* Finally replace the old file with the new file.  */
    1256         [ +  + ]:        158 :   if (foutput == NULL)
    1257         [ -  + ]:         26 :     if (rename (fnew, fname) != 0)
    1258                 :            :       {
    1259                 :          0 :         error (0, errno, "Couldn't rename %s to %s", fnew, fname);
    1260                 :          0 :         goto cleanup;
    1261                 :            :       }
    1262                 :            : 
    1263                 :            :   /* We are finally done with the new file, don't unlink it now.  */
    1264                 :        158 :   free (fnew);
    1265                 :        158 :   fnew = NULL;
    1266                 :        158 :   res = 0;
    1267                 :            : 
    1268                 :        158 : cleanup:
    1269                 :        158 :   elf_end (elf);
    1270                 :        158 :   close (fd);
    1271                 :            : 
    1272                 :        158 :   elf_end (elfnew);
    1273                 :        158 :   close (fdnew);
    1274                 :            : 
    1275         [ -  + ]:        158 :   if (fnew != NULL)
    1276                 :            :     {
    1277                 :          0 :       unlink (fnew);
    1278                 :          0 :       free (fnew);
    1279                 :          0 :       fnew = NULL;
    1280                 :            :     }
    1281                 :            : 
    1282                 :        158 :   free (snamebuf);
    1283         [ +  + ]:        158 :   if (names != NULL)
    1284                 :            :     {
    1285                 :         74 :       dwelf_strtab_free (names);
    1286                 :         74 :       free (scnstrents);
    1287                 :         74 :       free (symstrents);
    1288                 :         74 :       free (namesbuf);
    1289         [ +  - ]:         74 :       if (scnnames != NULL)
    1290                 :            :         {
    1291         [ +  + ]:       1530 :           for (size_t n = 0; n < shnum; n++)
    1292                 :       1456 :             free (scnnames[n]);
    1293                 :         74 :           free (scnnames);
    1294                 :            :         }
    1295                 :            :     }
    1296                 :            : 
    1297                 :        158 :   free (sections);
    1298                 :        158 :   return res;
    1299                 :            : }
    1300                 :            : 
    1301                 :            : int
    1302                 :        142 : main (int argc, char **argv)
    1303                 :            : {
    1304                 :        142 :   const struct argp_option options[] =
    1305                 :            :     {
    1306                 :            :       { "output", 'o', "FILE", 0,
    1307                 :            :         N_("Place (de)compressed output into FILE"),
    1308                 :            :         0 },
    1309                 :            :       { "type", 't', "TYPE", 0,
    1310                 :            :         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)"),
    1311                 :            :         0 },
    1312                 :            :       { "name", 'n', "SECTION", 0,
    1313                 :            :         N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"),
    1314                 :            :         0 },
    1315                 :            :       { "verbose", 'v', NULL, 0,
    1316                 :            :         N_("Print a message for each section being (de)compressed"),
    1317                 :            :         0 },
    1318                 :            :       { "force", 'f', NULL, 0,
    1319                 :            :         N_("Force compression of section even if it would become larger or update/rewrite the file even if no section would be (de)compressed"),
    1320                 :            :         0 },
    1321                 :            :       { "permissive", 'p', NULL, 0,
    1322                 :            :         N_("Relax a few rules to handle slightly broken ELF files"),
    1323                 :            :         0 },
    1324                 :            :       { "quiet", 'q', NULL, 0,
    1325                 :            :         N_("Be silent when a section cannot be compressed"),
    1326                 :            :         0 },
    1327                 :            :       { NULL, 0, NULL, 0, NULL, 0 }
    1328                 :            :     };
    1329                 :            : 
    1330                 :        142 :   const struct argp argp =
    1331                 :            :     {
    1332                 :            :       .options = options,
    1333                 :            :       .parser = parse_opt,
    1334                 :            :       .args_doc = N_("FILE..."),
    1335                 :            :       .doc = N_("Compress or decompress sections in an ELF file.")
    1336                 :            :     };
    1337                 :            : 
    1338                 :        142 :   int remaining;
    1339         [ +  - ]:        142 :   if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
    1340                 :            :     return EXIT_FAILURE;
    1341                 :            : 
    1342                 :            :   /* Should already be handled by ARGP_KEY_NO_ARGS case above,
    1343                 :            :      just sanity check.  */
    1344         [ -  + ]:        142 :   if (remaining >= argc)
    1345                 :          0 :     error_exit (0, N_("No input file given"));
    1346                 :            : 
    1347                 :            :   /* Likewise for the ARGP_KEY_ARGS case above, an extra sanity check.  */
    1348   [ +  +  -  + ]:        142 :   if (foutput != NULL && remaining + 1 < argc)
    1349                 :          0 :     error_exit (0, N_("Only one input file allowed together with '-o'"));
    1350                 :            : 
    1351                 :        142 :   elf_version (EV_CURRENT);
    1352                 :            : 
    1353                 :            :   /* Process all the remaining files.  */
    1354                 :        142 :   int result = 0;
    1355                 :        158 :   do
    1356                 :        158 :     result |= process_file (argv[remaining]);
    1357         [ +  + ]:        158 :   while (++remaining < argc);
    1358                 :            : 
    1359                 :        142 :   free_patterns ();
    1360                 :        142 :   return result;
    1361                 :            : }

Generated by: LCOV version 1.14