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 : : }
|