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