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