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