diff options
Diffstat (limited to 'src/strip.c')
-rw-r--r-- | src/strip.c | 975 |
1 files changed, 513 insertions, 462 deletions
diff --git a/src/strip.c b/src/strip.c index e5c4289c..0f83e61d 100644 --- a/src/strip.c +++ b/src/strip.c @@ -1,16 +1,28 @@ /* Discard section not used at runtime from object files. - Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008 Red Hat, Inc. + This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2000. - This program is Open Source software; you can redistribute it and/or - modify it under the terms of the Open Software License version 1.0 as - published by the Open Source Initiative. + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. - You should have received a copy of the Open Software License along - with this program; if not, you may obtain a copy of the Open Software - License version 1.0 from http://www.opensource.org/licenses/osl.php or - by writing the Open Source Initiative c/o Lawrence Rosen, Esq., - 3001 King Ranch Road, Ukiah, CA 95482. */ + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -45,6 +57,9 @@ static void print_version (FILE *stream, struct argp_state *state); void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; +/* Bug report address. */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + /* Values for the parameters which have no short form. */ #define OPT_REMOVE_COMMENT 0x100 @@ -54,19 +69,24 @@ void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; /* Definitions of arguments for argp functions. */ static const struct argp_option options[] = { - { NULL, 0, NULL, 0, N_("Output selection:") }, - { NULL, 'o', "FILE", 0, N_("Place stripped output into FILE") }, - { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE") }, - - { NULL, 0, NULL, 0, N_("Output options:") }, - { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols") }, + { NULL, 0, NULL, 0, N_("Output selection:"), 0 }, + { "output", 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 }, + { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE"), 0 }, + { NULL, 'F', "FILE", 0, N_("Embed name FILE instead of -f argument"), 0 }, + + { NULL, 0, NULL, 0, N_("Output options:"), 0 }, + { "strip-all", 's', NULL, OPTION_HIDDEN, NULL, 0 }, + { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols"), 0 }, + { NULL, 'd', NULL, OPTION_ALIAS, NULL, 0 }, + { NULL, 'S', NULL, OPTION_ALIAS, NULL, 0 }, { "preserve-dates", 'p', NULL, 0, - N_("Copy modified/access timestamps to the output") }, + N_("Copy modified/access timestamps to the output"), 0 }, { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0, - N_("Remove .comment section") }, + N_("Remove .comment section"), 0 }, + { "remove-section", 'R', "SECTION", OPTION_HIDDEN, NULL, 0 }, { "permissive", OPT_PERMISSIVE, NULL, 0, - N_("Relax a few rules to handle slightly broken ELF files") }, - { NULL, 0, NULL, 0, NULL } + N_("Relax a few rules to handle slightly broken ELF files"), 0 }, + { NULL, 0, NULL, 0, NULL, 0 } }; /* Short description of program. */ @@ -78,13 +98,10 @@ static const char args_doc[] = N_("[FILE...]"); /* Prototype for option handler. */ static error_t parse_opt (int key, char *arg, struct argp_state *state); -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, more_help + options, parse_opt, args_doc, doc, NULL, NULL, NULL }; @@ -101,7 +118,7 @@ static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, #define INTERNAL_ERROR(fname) \ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \ - fname, __LINE__, VERSION, __DATE__, elf_errmsg (-1)) + fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1)) /* Name of the output file. */ @@ -110,6 +127,9 @@ static const char *output_fname; /* Name of the debug output file. */ static const char *debug_fname; +/* Name to pretend the debug output file has. */ +static const char *debug_fname_embed; + /* If true output files shall have same date as the input file. */ static bool preserve_dates; @@ -141,13 +161,14 @@ main (int argc, char *argv[]) setlocale (LC_ALL, ""); /* Make sure the message catalog can be found. */ - bindtextdomain (PACKAGE, LOCALEDIR); + bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); /* Initialize the message catalog. */ - textdomain (PACKAGE); + textdomain (PACKAGE_TARNAME); /* Parse and process arguments. */ - argp_parse (&argp, argc, argv, 0, &remaining, NULL); + if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0) + return EXIT_FAILURE; /* Tell the library which version we are expecting. */ elf_version (EV_CURRENT); @@ -157,7 +178,7 @@ main (int argc, char *argv[]) result = process_file ("a.out"); else { - /* If we have seen the `-o' or '-f' option there must be exactly one + /* If we have seen the '-o' or '-f' option there must be exactly one input file. */ if ((output_fname != NULL || debug_fname != NULL) && remaining + 1 < argc) @@ -176,14 +197,14 @@ Only one input file allowed together with '-o' and '-f'")); /* Print the version information. */ static void -print_version (FILE *stream, struct argp_state *state) +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) { - fprintf (stream, "strip (%s) %s\n", PACKAGE_NAME, VERSION); + fprintf (stream, "strip (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf (stream, gettext ("\ Copyright (C) %s Red Hat, Inc.\n\ This is free software; see the source for copying conditions. There is NO\n\ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ -"), "2004"); +"), "2008"); fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); } @@ -195,10 +216,29 @@ parse_opt (int key, char *arg, struct argp_state *state) switch (key) { case 'f': + if (debug_fname != NULL) + { + error (0, 0, gettext ("-f option specified twice")); + return EINVAL; + } debug_fname = arg; break; + case 'F': + if (debug_fname_embed != NULL) + { + error (0, 0, gettext ("-F option specified twice")); + return EINVAL; + } + debug_fname_embed = arg; + break; + case 'o': + if (output_fname != NULL) + { + error (0, 0, gettext ("-o option specified twice")); + return EINVAL; + } output_fname = arg; break; @@ -210,7 +250,20 @@ parse_opt (int key, char *arg, struct argp_state *state) remove_comment = true; break; + case 'R': + if (!strcmp (arg, ".comment")) + remove_comment = true; + else + { + argp_error (state, + gettext ("-R option supports only .comment section")); + return EINVAL; + } + break; + case 'g': + case 'd': + case 'S': remove_debug = true; break; @@ -218,6 +271,9 @@ parse_opt (int key, char *arg, struct argp_state *state) permissive = true; break; + case 's': /* Ignored for compatibility. */ + break; + default: return ARGP_ERR_UNKNOWN; } @@ -225,27 +281,6 @@ parse_opt (int key, char *arg, struct argp_state *state) } -static char * -more_help (int key, const char *text, void *input) -{ - char *buf; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - if (asprintf (&buf, gettext ("Please report bugs to %s.\n"), - PACKAGE_BUGREPORT) < 0) - buf = NULL; - return buf; - - default: - break; - } - return (char *) text; -} - - static int process_file (const char *fname) { @@ -259,7 +294,7 @@ process_file (const char *fname) { if (stat64 (fname, &pre_st) != 0) { - error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + error (0, errno, gettext ("cannot stat input file '%s'"), fname); return 1; } @@ -270,10 +305,10 @@ process_file (const char *fname) } /* Open the file. */ - int fd = open (fname, O_RDWR); + int fd = open (fname, output_fname == NULL ? O_RDWR : O_RDONLY); if (fd == -1) { - error (0, errno, gettext ("while opening \"%s\""), fname); + error (0, errno, gettext ("while opening '%s'"), fname); return 1; } @@ -283,7 +318,7 @@ process_file (const char *fname) struct stat64 st; if (fstat64 (fd, &st) != 0) { - error (0, errno, gettext ("cannot stat input file \"%s\""), fname); + error (0, errno, gettext ("cannot stat input file '%s'"), fname); return 1; } /* Paranoid mode on. */ @@ -296,7 +331,8 @@ process_file (const char *fname) } /* Now get the ELF descriptor. */ - Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL); + Elf *elf = elf_begin (fd, output_fname == NULL ? ELF_C_RDWR : ELF_C_READ, + NULL); int result; switch (elf_kind (elf)) { @@ -308,9 +344,9 @@ process_file (const char *fname) case ELF_K_AR: /* It is not possible to strip the content of an archive direct the output to a specific file. */ - if (unlikely (output_fname != NULL)) + if (unlikely (output_fname != NULL || debug_fname != NULL)) { - error (0, 0, gettext ("%s: cannot use -o when stripping archive"), + error (0, 0, gettext ("%s: cannot use -o or -f when stripping archive"), fname); result = 1; } @@ -336,27 +372,6 @@ process_file (const char *fname) /* Maximum size of array allocated on stack. */ #define MAX_STACK_ALLOC (400 * 1024) - -static uint32_t -crc32_file (int fd, uint32_t *resp) -{ - unsigned char buffer[1024 * 8]; - uint32_t crc = 0; - ssize_t count; - - /* We have to rewind. */ - if (lseek (fd, 0, SEEK_SET) < 0) - return 1; - - while ((count = TEMP_FAILURE_RETRY (read (fd, buffer, sizeof (buffer)))) > 0) - crc = crc32 (crc, buffer, count); - - *resp = crc; - - return count != 0; -} - - static int handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, mode_t mode, struct timeval tvp[2]) @@ -365,19 +380,17 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, size_t fname_len = strlen (fname) + 1; char *fullname = alloca (prefix_len + 1 + fname_len); char *cp = fullname; - Elf *newelf; Elf *debugelf = NULL; char *tmp_debug_fname = NULL; int result = 0; - GElf_Ehdr ehdr_mem; - GElf_Ehdr *ehdr; + size_t shdridx = 0; size_t shstrndx; - size_t shnum; struct shdr_info { Elf_Scn *scn; GElf_Shdr shdr; Elf_Data *data; + Elf_Data *debug_data; const char *name; Elf32_Word idx; /* Index in new file. */ Elf32_Word old_sh_link; /* Original value of shdr.sh_link. */ @@ -398,7 +411,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, GElf_Ehdr debugehdr_mem; GElf_Ehdr *debugehdr; struct Ebl_Strtab *shst = NULL; - uint32_t debug_crc; + Elf_Data debuglink_crc_data; bool any_symtab_changes = false; Elf_Data *shstrtab_data = NULL; @@ -416,7 +429,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, fd = open (output_fname, O_RDWR | O_CREAT, mode); if (unlikely (fd == -1)) { - error (0, errno, gettext ("cannot open `%s'"), output_fname); + error (0, errno, gettext ("cannot open '%s'"), output_fname); return 1; } } @@ -451,14 +464,15 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debug_fd = mkstemp (tmp_debug_fname); if (unlikely (debug_fd == -1)) { - error (0, errno, gettext ("cannot open `%s'"), debug_fname); + error (0, errno, gettext ("cannot open '%s'"), debug_fname); result = 1; goto fail; } } /* Get the information from the old file. */ - ehdr = gelf_getehdr (elf, &ehdr_mem); + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); if (ehdr == NULL) INTERNAL_ERROR (fname); @@ -470,6 +484,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We now create a new ELF descriptor for the same file. We construct it almost exactly in the same way with some information dropped. */ + Elf *newelf; if (output_fname != NULL) newelf = elf_begin (fd, ELF_C_WRITE_MMAP, NULL); else @@ -479,7 +494,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, || (ehdr->e_type != ET_REL && unlikely (gelf_newphdr (newelf, ehdr->e_phnum) == 0))) { - error (0, 0, gettext ("cannot create new file `%s': %s"), + error (0, 0, gettext ("cannot create new file '%s': %s"), output_fname, elf_errmsg (-1)); goto fail; } @@ -489,9 +504,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - phdr = gelf_getphdr (elf, cnt, &phdr_mem); + GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); if (phdr == NULL || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0)) INTERNAL_ERROR (fname); @@ -505,7 +518,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, || (ehdr->e_type != ET_REL && unlikely (gelf_newphdr (debugelf, ehdr->e_phnum) == 0))) { - error (0, 0, gettext ("cannot create new file `%s': %s"), + error (0, 0, gettext ("cannot create new file '%s': %s"), debug_fname, elf_errmsg (-1)); goto fail_close; } @@ -515,9 +528,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) { GElf_Phdr phdr_mem; - GElf_Phdr *phdr; - - phdr = gelf_getphdr (elf, cnt, &phdr_mem); + GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); if (phdr == NULL || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0)) INTERNAL_ERROR (fname); @@ -525,6 +536,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } /* Number of sections. */ + size_t shnum; if (unlikely (elf_getshnum (elf, &shnum) < 0)) { error (0, 0, gettext ("cannot determine number of sections: %s"), @@ -566,7 +578,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].shdr.sh_name); if (shdr_info[cnt].name == NULL) { - error (0, 0, gettext ("illformed file `%s'"), fname); + error (0, 0, gettext ("illformed file '%s'"), fname); goto fail_close; } @@ -592,9 +604,6 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GROUP)) { - Elf32_Word *grpref; - size_t inner; - /* Cross-reference the sections contained in the section group. */ shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL); @@ -602,7 +611,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, INTERNAL_ERROR (fname); /* XXX Fix for unaligned access. */ - grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf; + size_t inner; for (inner = 1; inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word); ++inner) @@ -651,8 +661,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, */ for (cnt = 1; cnt < shnum; ++cnt) /* Check whether the section can be removed. */ - if (SECTION_STRIP_P (ebl, elf, ehdr, &shdr_info[cnt].shdr, - shdr_info[cnt].name, remove_comment, remove_debug)) + if (ebl_section_strip_p (ebl, ehdr, &shdr_info[cnt].shdr, + shdr_info[cnt].name, remove_comment, + remove_debug)) { /* For now assume this section will be removed. */ shdr_info[cnt].idx = 0; @@ -660,16 +671,14 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, idx = shdr_info[cnt].group_idx; while (idx != 0) { - /* If the references section group is a normal section - group and has one element remaining, or if it is an - empty COMDAT section group it is removed. */ - bool is_comdat; - /* The section group data is already loaded. */ assert (shdr_info[idx].data != NULL); - is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0] - & GRP_COMDAT) != 0; + /* If the references section group is a normal section + group and has one element remaining, or if it is an + empty COMDAT section group it is removed. */ + bool is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0] + & GRP_COMDAT) != 0; --shdr_info[idx].group_cnt; if ((!is_comdat && shdr_info[idx].group_cnt == 1) @@ -712,13 +721,11 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* The content of symbol tables we don't remove must not reference any section which we do remove. Otherwise we cannot remove the section. */ - if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM - || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) + if (debug_fname != NULL + && shdr_info[cnt].debug_data == NULL + && (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM + || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB)) { - Elf_Data *symdata; - Elf_Data *xndxdata; - size_t elsize; - /* Make sure the data is loaded. */ if (shdr_info[cnt].data == NULL) { @@ -727,7 +734,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (shdr_info[cnt].data == NULL) INTERNAL_ERROR (fname); } - symdata = shdr_info[cnt].data; + Elf_Data *symdata = shdr_info[cnt].data; /* If there is an extended section index table load it as well. */ @@ -742,11 +749,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (shdr_info[shdr_info[cnt].symtab_idx].data == NULL) INTERNAL_ERROR (fname); } - xndxdata = shdr_info[shdr_info[cnt].symtab_idx].data; + Elf_Data *xndxdata + = shdr_info[shdr_info[cnt].symtab_idx].data; /* Go through all symbols and make sure the section they reference is not removed. */ - elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version); + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); for (size_t inner = 0; inner < shdr_info[cnt].data->d_size / elsize; @@ -754,15 +763,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, { GElf_Sym sym_mem; Elf32_Word xndx; - GElf_Sym *sym; - size_t scnidx; - - sym = gelf_getsymshndx (symdata, xndxdata, inner, - &sym_mem, &xndx); + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + inner, &sym_mem, + &xndx); if (sym == NULL) INTERNAL_ERROR (fname); - scnidx = sym->st_shndx; + size_t scnidx = sym->st_shndx; if (scnidx == SHN_UNDEF || scnidx >= shnum || (scnidx >= SHN_LORESERVE && scnidx <= SHN_HIRESERVE @@ -775,24 +782,23 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, scnidx = xndx; if (shdr_info[scnidx].idx == 0) - { - /* Mark this section as used. */ - shdr_info[scnidx].idx = 1; - changes |= scnidx < cnt; - } + /* This symbol table has a real symbol in + a discarded section. So preserve the + original table in the debug file. */ + shdr_info[cnt].debug_data = symdata; } } /* Cross referencing happens: - for the cases the ELF specification says. That are - + SHT_DYNAMIC in sh_link to string table - + SHT_HASH in sh_link to symbol table - + SHT_REL and SHT_RELA in sh_link to symbol table - + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table - + SHT_GROUP in sh_link to symbol table - + SHT_SYMTAB_SHNDX in sh_link to symbol table - Other (OS or architecture-specific) sections might as - well use this field so we process it unconditionally. + + SHT_DYNAMIC in sh_link to string table + + SHT_HASH in sh_link to symbol table + + SHT_REL and SHT_RELA in sh_link to symbol table + + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table + + SHT_GROUP in sh_link to symbol table + + SHT_SYMTAB_SHNDX in sh_link to symbol table + Other (OS or architecture-specific) sections might as + well use this field so we process it unconditionally. - references inside section groups - specially marked references in sh_info if the SHF_INFO_LINK flag is set @@ -815,34 +821,63 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Mark the section as investigated. */ shdr_info[cnt].idx = 2; } + + if (debug_fname != NULL + && (shdr_info[cnt].idx == 0 || shdr_info[cnt].debug_data != NULL)) + { + /* This section is being preserved in the debug file. + Sections it refers to must be preserved there too. + + In this pass we mark sections to be preserved in both + files by setting the .debug_data pointer to the original + file's .data pointer. Below, we'll copy the section + contents. */ + + inline void check_preserved (size_t i) + { + if (i != 0 && shdr_info[i].idx != 0) + { + if (shdr_info[i].data == NULL) + shdr_info[i].data = elf_getdata (shdr_info[i].scn, NULL); + if (shdr_info[i].data == NULL) + INTERNAL_ERROR (fname); + + shdr_info[i].debug_data = shdr_info[i].data; + changes |= i < cnt; + } + } + + check_preserved (shdr_info[cnt].shdr.sh_link); + if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) + check_preserved (shdr_info[cnt].shdr.sh_info); + } } } while (changes); - /* Write out a copy of all the sections to the debug output file. - The ones that are not removed in the stripped file are SHT_NOBITS */ + /* Copy the removed sections to the debug output file. + The ones that are not removed in the stripped file are SHT_NOBITS. */ if (debug_fname != NULL) { for (cnt = 1; cnt < shnum; ++cnt) { - Elf_Data *debugdata; - GElf_Shdr debugshdr; - int discard_section; - scn = elf_newscn (debugelf); if (scn == NULL) error (EXIT_FAILURE, 0, gettext ("while generating output file: %s"), elf_errmsg (-1)); - discard_section = shdr_info[cnt].idx > 0 && cnt != ehdr->e_shstrndx; + bool discard_section = (shdr_info[cnt].idx > 0 + && shdr_info[cnt].debug_data == NULL + && shdr_info[cnt].shdr.sh_type != SHT_NOTE + && cnt != ehdr->e_shstrndx); /* Set the section header in the new file. */ - debugshdr = shdr_info[cnt].shdr; + GElf_Shdr debugshdr = shdr_info[cnt].shdr; if (discard_section) debugshdr.sh_type = SHT_NOBITS; - if (unlikely (gelf_update_shdr (scn, &debugshdr)) == 0) + if (unlikely (gelf_update_shdr (scn, &debugshdr) == 0)) /* There cannot be any overflows. */ INTERNAL_ERROR (fname); @@ -855,14 +890,22 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, } /* Set the data. This is done by copying from the old file. */ - debugdata = elf_newdata (scn); + Elf_Data *debugdata = elf_newdata (scn); if (debugdata == NULL) INTERNAL_ERROR (fname); - /* Copy the structure. */ + /* Copy the structure. This data may be modified in place + before we write out the file. */ *debugdata = *shdr_info[cnt].data; if (discard_section) debugdata->d_buf = NULL; + else if (shdr_info[cnt].debug_data != NULL) + { + /* Copy the original data before it gets modified. */ + shdr_info[cnt].debug_data = debugdata; + debugdata->d_buf = memcpy (xmalloc (debugdata->d_size), + debugdata->d_buf, debugdata->d_size); + } } /* Finish the ELF header. Fill in the fields not handled by @@ -879,46 +922,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debugehdr->e_flags = ehdr->e_flags; debugehdr->e_shstrndx = ehdr->e_shstrndx; - if (unlikely (gelf_update_ehdr (debugelf, debugehdr)) == 0) + if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0)) { error (0, 0, gettext ("%s: error while creating ELF header: %s"), debug_fname, elf_errmsg (-1)); result = 1; goto fail_close; } - - /* Finally write the file. */ - if (unlikely (elf_update (debugelf, ELF_C_WRITE)) == -1) - { - error (0, 0, gettext ("while writing `%s': %s"), - debug_fname, elf_errmsg (-1)); - result = 1; - goto fail_close; - } - - /* Create the real output file. First rename, then change the - mode. */ - if (rename (tmp_debug_fname, debug_fname) != 0 - || fchmod (debug_fd, mode) != 0) - { - error (0, errno, gettext ("while creating '%s'"), debug_fname); - result = 1; - goto fail_close; - } - - /* The temporary file does not exist anymore. */ - tmp_debug_fname = NULL; - - /* Compute the checksum which we will add to the executable. */ - if (crc32_file (debug_fd, &debug_crc) != 0) - { - error (0, errno, - gettext ("while computing checksum for debug information")); - unlink (debug_fname); - result = 1; - goto fail_close; - } - } /* Mark the section header string table as unused, we will create @@ -928,7 +938,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We need a string table for the section headers. */ shst = ebl_strtabinit (true); if (shst == NULL) - error (EXIT_FAILURE, errno, gettext ("while preparing output for `%s'"), + error (EXIT_FAILURE, errno, gettext ("while preparing output for '%s'"), output_fname ?: fname); /* Assign new section numbers. */ @@ -958,9 +968,6 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Create the reference to the file with the debug info. */ if (debug_fname != NULL) { - char *debug_basename; - off_t crc_offset; - /* Add the section header string table section name. */ shdr_info[cnt].se = ebl_strtabadd (shst, ".gnu_debuglink", 15); shdr_info[cnt].idx = idx++; @@ -991,8 +998,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, error (EXIT_FAILURE, 0, gettext ("cannot allocate section data: %s"), elf_errmsg (-1)); - debug_basename = basename (debug_fname); - crc_offset = strlen (debug_basename) + 1; + char *debug_basename = basename (debug_fname_embed ?: debug_fname); + off_t crc_offset = strlen (debug_basename) + 1; /* Align to 4 byte boundary */ crc_offset = ((crc_offset - 1) & ~3) + 4; @@ -1002,21 +1009,20 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].data->d_buf = xcalloc (1, shdr_info[cnt].data->d_size); strcpy (shdr_info[cnt].data->d_buf, debug_basename); - /* Store the crc value in the correct byteorder */ - if ((__BYTE_ORDER == __LITTLE_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2MSB) - || (__BYTE_ORDER == __BIG_ENDIAN - && ehdr->e_ident[EI_DATA] == ELFDATA2LSB)) - debug_crc = bswap_32 (debug_crc); - memcpy ((char *)shdr_info[cnt].data->d_buf + crc_offset, - (char *) &debug_crc, 4); + + /* Cache this Elf_Data describing the CRC32 word in the section. + We'll fill this in when we have written the debug file. */ + debuglink_crc_data = *shdr_info[cnt].data; + debuglink_crc_data.d_buf = ((char *) debuglink_crc_data.d_buf + + crc_offset); + debuglink_crc_data.d_size = 4; /* One more section done. */ ++cnt; } /* Index of the section header table in the shdr_info array. */ - size_t shdridx = cnt; + shdridx = cnt; /* Add the section header string table section name. */ shdr_info[cnt].se = ebl_strtabadd (shst, ".shstrtab", 10); @@ -1112,7 +1118,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* We know the size. */ shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size; - /* We have to adjust symtol tables. The st_shndx member might + /* We have to adjust symbol tables. The st_shndx member might have to be updated. */ if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) @@ -1240,7 +1246,8 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, shdr_info[cnt].shdr.sh_info = destidx - 1; } } - else + else if (debug_fname == NULL + || shdr_info[cnt].debug_data == NULL) /* This is a section symbol for a section which has been removed. */ assert (GELF_ST_TYPE (sym->st_info) == STT_SECTION); @@ -1282,302 +1289,339 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Adjust symbol references if symbol tables changed. */ if (any_symtab_changes) - { - /* Find all relocation sections which use this - symbol table. */ - for (cnt = 1; cnt <= shdridx; ++cnt) + /* Find all relocation sections which use this symbol table. */ + for (cnt = 1; cnt <= shdridx; ++cnt) + { + /* Update section headers when the data size has changed. + We also update the SHT_NOBITS section in the debug + file so that the section headers match in sh_size. */ + inline void update_section_size (const Elf_Data *newdata) { - if (shdr_info[cnt].idx == 0) - /* Ignore sections which are discarded. */ - continue; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + shdr->sh_size = newdata->d_size; + (void) gelf_update_shdr (scn, shdr); + if (debugelf != NULL) + { + /* libelf will use d_size to set sh_size. */ + Elf_Data *debugdata = elf_getdata (elf_getscn (debugelf, + cnt), NULL); + debugdata->d_size = newdata->d_size; + } + } - if (shdr_info[cnt].shdr.sh_type == SHT_REL - || shdr_info[cnt].shdr.sh_type == SHT_RELA) + if (shdr_info[cnt].idx == 0 && debug_fname == NULL) + /* Ignore sections which are discarded. When we are saving a + relocation section in a separate debug file, we must fix up + the symbol table references. */ + continue; + + const Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + const Elf32_Word *const newsymidx = shdr_info[symtabidx].newsymidx; + switch (shdr_info[cnt].shdr.sh_type) + { + inline bool no_symtab_updates (void) { /* If the symbol table hasn't changed, do not do anything. */ - if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx == NULL) - continue; + if (shdr_info[symtabidx].newsymidx == NULL) + return true; - Elf32_Word *newsymidx - = shdr_info[shdr_info[cnt].old_sh_link].newsymidx; - Elf_Data *d = elf_getdata (elf_getscn (newelf, + /* If the symbol table is not discarded, but additionally + duplicated in the separate debug file and this section + is discarded, don't adjust anything. */ + return (shdr_info[cnt].idx == 0 + && shdr_info[symtabidx].debug_data != NULL); + } + + case SHT_REL: + case SHT_RELA: + if (no_symtab_updates ()) + break; + + Elf_Data *d = elf_getdata (shdr_info[cnt].idx == 0 + ? elf_getscn (debugelf, cnt) + : elf_getscn (newelf, shdr_info[cnt].idx), - NULL); - assert (d != NULL); - size_t nrels = (shdr_info[cnt].shdr.sh_size - / shdr_info[cnt].shdr.sh_entsize); + NULL); + assert (d != NULL); + size_t nrels = (shdr_info[cnt].shdr.sh_size + / shdr_info[cnt].shdr.sh_entsize); - if (shdr_info[cnt].shdr.sh_type == SHT_REL) - for (size_t relidx = 0; relidx < nrels; ++relidx) - { - GElf_Rel rel_mem; - if (gelf_getrel (d, relidx, &rel_mem) == NULL) - INTERNAL_ERROR (fname); + if (shdr_info[cnt].shdr.sh_type == SHT_REL) + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rel rel_mem; + if (gelf_getrel (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); - size_t symidx = GELF_R_SYM (rel_mem.r_info); - if (newsymidx[symidx] != symidx) - { - rel_mem.r_info - = GELF_R_INFO (newsymidx[symidx], - GELF_R_TYPE (rel_mem.r_info)); + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); - if (gelf_update_rel (d, relidx, &rel_mem) == 0) - INTERNAL_ERROR (fname); - } - } - else - for (size_t relidx = 0; relidx < nrels; ++relidx) - { - GElf_Rela rel_mem; - if (gelf_getrela (d, relidx, &rel_mem) == NULL) - INTERNAL_ERROR (fname); + if (gelf_update_rel (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + else + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + GElf_Rela rel_mem; + if (gelf_getrela (d, relidx, &rel_mem) == NULL) + INTERNAL_ERROR (fname); - size_t symidx = GELF_R_SYM (rel_mem.r_info); - if (newsymidx[symidx] != symidx) - { - rel_mem.r_info - = GELF_R_INFO (newsymidx[symidx], - GELF_R_TYPE (rel_mem.r_info)); + size_t symidx = GELF_R_SYM (rel_mem.r_info); + if (newsymidx[symidx] != symidx) + { + rel_mem.r_info + = GELF_R_INFO (newsymidx[symidx], + GELF_R_TYPE (rel_mem.r_info)); - if (gelf_update_rela (d, relidx, &rel_mem) == 0) - INTERNAL_ERROR (fname); - } - } - } - else if (shdr_info[cnt].shdr.sh_type == SHT_HASH) - { - /* We have to recompute the hash table. */ - Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + if (gelf_update_rela (d, relidx, &rel_mem) == 0) + INTERNAL_ERROR (fname); + } + } + break; - /* We do not have to do anything if the symbol table was - not changed. */ - if (shdr_info[symtabidx].newsymidx == NULL) - continue; + case SHT_HASH: + if (no_symtab_updates ()) + break; - /* The symbol version section in the new file. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); + /* We have to recompute the hash table. */ - /* The symbol table data. */ - Elf_Data *symd = elf_getdata (elf_getscn (newelf, - shdr_info[symtabidx].idx), - NULL); - assert (symd != NULL); + assert (shdr_info[cnt].idx > 0); - /* The hash table data. */ - Elf_Data *hashd = elf_getdata (scn, NULL); - assert (hashd != NULL); + /* The hash section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); - if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) - { - /* Sane arches first. */ - Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; + /* The symbol table data. */ + Elf_Data *symd = elf_getdata (elf_getscn (newelf, + shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); - size_t strshndx = shdr_info[symtabidx].old_sh_link; - size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, - ehdr->e_version); + /* The hash table data. */ + Elf_Data *hashd = elf_getdata (scn, NULL); + assert (hashd != NULL); - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word)) + { + /* Sane arches first. */ + Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; - /* Adjust the nchain value. The symbol table size - changed. We keep the same size for the bucket array. */ - bucket[1] = symd->d_size / elsize; - Elf32_Word nbucket = bucket[0]; - bucket += 2; - Elf32_Word *chain = bucket + nbucket; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = hashd->d_size - = (2 + symd->d_size / elsize + nbucket) - * sizeof (Elf32_Word); - (void) gelf_update_shdr (scn, shdr); - - /* Clear the arrays. */ - memset (bucket, '\0', - (symd->d_size / elsize + nbucket) - * sizeof (Elf32_Word)); - - for (size_t inner = shdr_info[symtabidx].shdr.sh_info; - inner < symd->d_size / elsize; ++inner) - { - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); - assert (sym != NULL); + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); - const char *name = elf_strptr (elf, strshndx, - sym->st_name); - assert (name != NULL); - size_t hidx = elf_hash (name) % nbucket; + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf32_Word nbucket = bucket[0]; + bucket += 2; + Elf32_Word *chain = bucket + nbucket; + + /* New size of the section. */ + hashd->d_size = ((2 + symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word)); + update_section_size (hashd); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf32_Word)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); - if (bucket[hidx] == 0) - bucket[hidx] = inner; - else - { - hidx = bucket[hidx]; + const char *name = elf_strptr (elf, strshndx, + sym->st_name); + assert (name != NULL); + size_t hidx = elf_hash (name) % nbucket; - while (chain[hidx] != 0) - hidx = chain[hidx]; + if (bucket[hidx] == 0) + bucket[hidx] = inner; + else + { + hidx = bucket[hidx]; - chain[hidx] = inner; - } - } - } - else - { - /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */ - assert (shdr_info[cnt].shdr.sh_entsize - == sizeof (Elf64_Xword)); + while (chain[hidx] != 0) + hidx = chain[hidx]; - Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; + chain[hidx] = inner; + } + } + } + else + { + /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */ + assert (shdr_info[cnt].shdr.sh_entsize + == sizeof (Elf64_Xword)); - size_t strshndx = shdr_info[symtabidx].old_sh_link; - size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, - ehdr->e_version); + Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf; - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + size_t strshndx = shdr_info[symtabidx].old_sh_link; + size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, + ehdr->e_version); - /* Adjust the nchain value. The symbol table size - changed. We keep the same size for the bucket array. */ - bucket[1] = symd->d_size / elsize; - Elf64_Xword nbucket = bucket[0]; - bucket += 2; - Elf64_Xword *chain = bucket + nbucket; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = hashd->d_size - = (2 + symd->d_size / elsize + nbucket) - * sizeof (Elf64_Xword); - (void) gelf_update_shdr (scn, shdr); - - /* Clear the arrays. */ - memset (bucket, '\0', - (symd->d_size / elsize + nbucket) - * sizeof (Elf64_Xword)); - - for (size_t inner = shdr_info[symtabidx].shdr.sh_info; - inner < symd->d_size / elsize; ++inner) - { - GElf_Sym sym_mem; - GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); - assert (sym != NULL); + /* Adjust the nchain value. The symbol table size + changed. We keep the same size for the bucket array. */ + bucket[1] = symd->d_size / elsize; + Elf64_Xword nbucket = bucket[0]; + bucket += 2; + Elf64_Xword *chain = bucket + nbucket; + + /* New size of the section. */ + hashd->d_size = ((2 + symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword)); + update_section_size (hashd); + + /* Clear the arrays. */ + memset (bucket, '\0', + (symd->d_size / elsize + nbucket) + * sizeof (Elf64_Xword)); + + for (size_t inner = shdr_info[symtabidx].shdr.sh_info; + inner < symd->d_size / elsize; ++inner) + { + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem); + assert (sym != NULL); - const char *name = elf_strptr (elf, strshndx, - sym->st_name); - assert (name != NULL); - size_t hidx = elf_hash (name) % nbucket; + const char *name = elf_strptr (elf, strshndx, + sym->st_name); + assert (name != NULL); + size_t hidx = elf_hash (name) % nbucket; - if (bucket[hidx] == 0) - bucket[hidx] = inner; - else - { - hidx = bucket[hidx]; + if (bucket[hidx] == 0) + bucket[hidx] = inner; + else + { + hidx = bucket[hidx]; - while (chain[hidx] != 0) - hidx = chain[hidx]; + while (chain[hidx] != 0) + hidx = chain[hidx]; - chain[hidx] = inner; - } - } - } + chain[hidx] = inner; + } + } + } + break; - /* Convert back to the file byte order. */ - if (gelf_xlatetof (newelf, hashd, hashd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); - } - else if (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym) - { - /* If the symbol table changed we have to adjust the - entries. */ - Elf32_Word symtabidx = shdr_info[cnt].old_sh_link; + case SHT_GNU_versym: + /* If the symbol table changed we have to adjust the entries. */ + if (no_symtab_updates ()) + break; - /* We do not have to do anything if the symbol table was - not changed. */ - if (shdr_info[symtabidx].newsymidx == NULL) - continue; + assert (shdr_info[cnt].idx > 0); + + /* The symbol version section in the new file. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + + /* The symbol table data. */ + symd = elf_getdata (elf_getscn (newelf, shdr_info[symtabidx].idx), + NULL); + assert (symd != NULL); + + /* The version symbol data. */ + Elf_Data *verd = elf_getdata (scn, NULL); + assert (verd != NULL); + + /* The symbol version array. */ + GElf_Half *verstab = (GElf_Half *) verd->d_buf; + + /* Walk through the list and */ + size_t elsize = gelf_fsize (elf, verd->d_type, 1, + ehdr->e_version); + for (size_t inner = 1; inner < verd->d_size / elsize; ++inner) + if (newsymidx[inner] != 0) + /* Overwriting the same array works since the + reordering can only move entries to lower indices + in the array. */ + verstab[newsymidx[inner]] = verstab[inner]; + + /* New size of the section. */ + verd->d_size = gelf_fsize (newelf, verd->d_type, + symd->d_size + / gelf_fsize (elf, symd->d_type, 1, + ehdr->e_version), + ehdr->e_version); + update_section_size (verd); + break; + + case SHT_GROUP: + if (no_symtab_updates ()) + break; - /* The symbol version section in the new file. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); + /* Yes, the symbol table changed. + Update the section header of the section group. */ + scn = elf_getscn (newelf, shdr_info[cnt].idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr != NULL); - /* The symbol table data. */ - Elf_Data *symd = elf_getdata (elf_getscn (newelf, - shdr_info[symtabidx].idx), - NULL); - assert (symd != NULL); + shdr->sh_info = newsymidx[shdr->sh_info]; - /* The version symbol data. */ - Elf_Data *verd = elf_getdata (scn, NULL); - assert (verd != NULL); + (void) gelf_update_shdr (scn, shdr); + break; + } + } - /* Convert to the correct byte order. */ - if (gelf_xlatetom (newelf, verd, verd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); + /* Now that we have done all adjustments to the data, + we can actually write out the debug file. */ + if (debug_fname != NULL) + { + uint32_t debug_crc; + Elf_Data debug_crc_data = + { + .d_type = ELF_T_WORD, + .d_buf = &debug_crc, + .d_size = sizeof (debug_crc), + .d_version = EV_CURRENT + }; - /* The symbol version array. */ - GElf_Half *verstab = (GElf_Half *) verd->d_buf; - - /* New indices of the symbols. */ - Elf32_Word *newsymidx = shdr_info[symtabidx].newsymidx; - - /* Walk through the list and */ - size_t elsize = gelf_fsize (elf, verd->d_type, 1, - ehdr->e_version); - for (size_t inner = 1; inner < verd->d_size / elsize; ++inner) - if (newsymidx[inner] != 0) - /* Overwriting the same array works since the - reordering can only move entries to lower indices - in the array. */ - verstab[newsymidx[inner]] = verstab[inner]; - - /* New size of the section. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - shdr->sh_size = verd->d_size - = gelf_fsize (newelf, verd->d_type, - symd->d_size / gelf_fsize (elf, symd->d_type, 1, - ehdr->e_version), - ehdr->e_version); - (void) gelf_update_shdr (scn, shdr); - - /* Convert back to the file byte order. */ - if (gelf_xlatetof (newelf, verd, verd, - BYTE_ORDER == LITTLE_ENDIAN - ? ELFDATA2LSB : ELFDATA2MSB) == NULL) - INTERNAL_ERROR (fname); - } - else if (shdr_info[cnt].shdr.sh_type == SHT_GROUP) - { - /* Check whether the associated symbol table changed. */ - if (shdr_info[shdr_info[cnt].old_sh_link].newsymidx != NULL) - { - /* Yes the symbol table changed. Update the section - header of the section group. */ - scn = elf_getscn (newelf, shdr_info[cnt].idx); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - assert (shdr != NULL); + /* Finally write the file. */ + if (unlikely (elf_update (debugelf, ELF_C_WRITE) == -1)) + { + error (0, 0, gettext ("while writing '%s': %s"), + debug_fname, elf_errmsg (-1)); + result = 1; + goto fail_close; + } - size_t stabidx = shdr_info[cnt].old_sh_link; - shdr->sh_info = shdr_info[stabidx].newsymidx[shdr->sh_info]; + /* Create the real output file. First rename, then change the + mode. */ + if (rename (tmp_debug_fname, debug_fname) != 0 + || fchmod (debug_fd, mode) != 0) + { + error (0, errno, gettext ("while creating '%s'"), debug_fname); + result = 1; + goto fail_close; + } - (void) gelf_update_shdr (scn, shdr); - } - } + /* The temporary file does not exist anymore. */ + tmp_debug_fname = NULL; + + /* Compute the checksum which we will add to the executable. */ + if (crc32_file (debug_fd, &debug_crc) != 0) + { + error (0, errno, + gettext ("while computing checksum for debug information")); + unlink (debug_fname); + result = 1; + goto fail_close; } + + /* Store it in the debuglink section data. */ + if (unlikely (gelf_xlatetof (newelf, &debuglink_crc_data, + &debug_crc_data, ehdr->e_ident[EI_DATA]) + != &debuglink_crc_data)) + INTERNAL_ERROR (fname); } /* Finally finish the ELF header. Fill in the fields not handled by @@ -1641,7 +1685,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* Finally write the file. */ if (elf_update (newelf, ELF_C_WRITE) == -1) { - error (0, 0, gettext ("while writing `%s': %s"), + error (0, 0, gettext ("while writing '%s': %s"), fname, elf_errmsg (-1)); result = 1; } @@ -1653,7 +1697,11 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, table indices. */ if (any_symtab_changes) for (cnt = 1; cnt <= shdridx; ++cnt) - free (shdr_info[cnt].newsymidx); + { + free (shdr_info[cnt].newsymidx); + if (shdr_info[cnt].debug_data != NULL) + free (shdr_info[cnt].debug_data->d_buf); + } /* Free the memory. */ if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC) @@ -1669,14 +1717,14 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, /* That was it. Close the descriptors. */ if (elf_end (newelf) != 0) { - error (0, 0, gettext ("error while finishing `%s': %s"), fname, + error (0, 0, gettext ("error while finishing '%s': %s"), fname, elf_errmsg (-1)); result = 1; } if (debugelf != NULL && elf_end (debugelf) != 0) { - error (0, 0, gettext ("error while finishing `%s': %s"), debug_fname, + error (0, 0, gettext ("error while finishing '%s': %s"), debug_fname, elf_errmsg (-1)); result = 1; } @@ -1700,7 +1748,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (futimes (fd, tvp) != 0) { error (0, errno, gettext ("\ -cannot set access and modification date of \"%s\""), +cannot set access and modification date of '%s'"), output_fname ?: fname); result = 1; } @@ -1757,13 +1805,16 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname, if (unlikely (futimes (fd, tvp) != 0)) { error (0, errno, gettext ("\ -cannot set access and modification date of \"%s\""), fname); +cannot set access and modification date of '%s'"), fname); result = 1; } } if (unlikely (close (fd) != 0)) - error (EXIT_FAILURE, errno, gettext ("while closing `%s'"), fname); + error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname); return result; } + + +#include "debugpred.h" |