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