Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame] | 1 | /* Merge string sections. |
| 2 | Copyright (C) 2015, 2016 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 | |
| 20 | #include <assert.h> |
| 21 | #include <errno.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <sys/types.h> |
| 25 | #include <sys/stat.h> |
| 26 | #include <fcntl.h> |
| 27 | #include <error.h> |
| 28 | #include <stdio.h> |
| 29 | #include <inttypes.h> |
| 30 | #include <unistd.h> |
| 31 | |
| 32 | #include <system.h> |
| 33 | #include <gelf.h> |
| 34 | #include ELFUTILS_HEADER(dwelf) |
| 35 | #include "elf-knowledge.h" |
| 36 | |
| 37 | /* The original ELF file. */ |
| 38 | static int fd = -1; |
| 39 | static Elf *elf = NULL; |
| 40 | static bool replace; |
| 41 | |
| 42 | /* The new ELF file. */ |
| 43 | static char *fnew = NULL; |
| 44 | static int fdnew = -1; |
| 45 | static Elf *elfnew = NULL; |
| 46 | |
| 47 | /* The merged string table. */ |
| 48 | static Dwelf_Strtab *strings = NULL; |
| 49 | |
| 50 | /* Section name strents. */ |
| 51 | static Dwelf_Strent **scnstrents = NULL; |
| 52 | |
| 53 | /* Symbol name strends. */ |
| 54 | static Dwelf_Strent **symstrents = NULL; |
| 55 | |
| 56 | /* New ELF file buffers. */ |
| 57 | static Elf_Data newstrtabdata = { .d_buf = NULL }; |
| 58 | static size_t newshnums = 0; |
| 59 | static void **newscnbufs = NULL; |
| 60 | |
| 61 | /* Release all files and resources allocated. */ |
| 62 | static void |
| 63 | release (void) |
| 64 | { |
| 65 | /* The new string table. */ |
| 66 | if (strings != NULL) |
| 67 | dwelf_strtab_free (strings); |
| 68 | |
| 69 | free (scnstrents); |
| 70 | free (symstrents); |
| 71 | free (newstrtabdata.d_buf); |
| 72 | |
| 73 | /* Any new data buffers allocated. */ |
| 74 | for (size_t i = 0; i < newshnums; i++) |
| 75 | free (newscnbufs[i]); |
| 76 | free (newscnbufs); |
| 77 | |
| 78 | /* The new ELF file. */ |
| 79 | if (fdnew != -1) |
| 80 | { |
| 81 | unlink (fnew); |
| 82 | elf_end (elfnew); |
| 83 | close (fdnew); |
| 84 | } |
| 85 | // Don't release, we might need it in the error message. |
| 86 | // if (replace) |
| 87 | // free (fnew); |
| 88 | |
| 89 | /* The original ELF file. */ |
| 90 | elf_end (elf); |
| 91 | close (fd); |
| 92 | } |
| 93 | |
| 94 | /* The various ways we can fail... Cleanup and show some message to |
| 95 | the user. The file name may be NULL. */ |
| 96 | static void __attribute__ ((noreturn)) |
| 97 | fail (const char *msg, const char *fname) |
| 98 | { |
| 99 | release (); |
| 100 | if (fname != NULL) |
| 101 | error (1, 0, "%s: %s", fname, msg); |
| 102 | else |
| 103 | error (1, 0, "%s", msg); |
| 104 | abort(); |
| 105 | } |
| 106 | |
| 107 | static void __attribute__ ((noreturn)) |
| 108 | fail_errno (const char *msg, const char *fname) |
| 109 | { |
| 110 | release (); |
| 111 | if (fname != NULL) |
| 112 | error (1, errno, "%s: %s", fname, msg); |
| 113 | else |
| 114 | error (1, errno, "%s", msg); |
| 115 | abort(); |
| 116 | } |
| 117 | |
| 118 | static void __attribute__ ((noreturn)) |
| 119 | fail_idx (const char *msg, const char *fname, size_t idx) |
| 120 | { |
| 121 | release (); |
| 122 | if (fname != NULL) |
| 123 | error (1, 0, "%s: %s %zd", fname, msg, idx); |
| 124 | else |
| 125 | error (1, 0, "%s %zd", msg, idx); |
| 126 | abort(); |
| 127 | } |
| 128 | |
| 129 | static void __attribute__ ((noreturn)) |
| 130 | fail_elf (const char *msg, const char *fname) |
| 131 | { |
| 132 | release (); |
| 133 | if (fname != NULL) |
| 134 | error (1, 0, "%s: %s: %s", fname, msg, elf_errmsg (-1)); |
| 135 | else |
| 136 | error (1, 0, "%s: %s", msg, elf_errmsg (-1)); |
| 137 | abort(); |
| 138 | } |
| 139 | |
| 140 | static void __attribute__ ((noreturn)) |
| 141 | fail_elf_idx (const char *msg, const char *fname, size_t idx) |
| 142 | { |
| 143 | release (); |
| 144 | if (fname != NULL) |
| 145 | error (1, 0, "%s: %s %zd: %s", fname, msg, idx, elf_errmsg (-1)); |
| 146 | else |
| 147 | error (1, 0, "%s %zd: %s", msg, idx, elf_errmsg (-1)); |
| 148 | abort(); |
| 149 | } |
| 150 | |
| 151 | int |
| 152 | main (int argc, char **argv) |
| 153 | { |
| 154 | elf_version (EV_CURRENT); |
| 155 | |
| 156 | /* Basic command line handling. Need to replace the input file? */ |
| 157 | if ((argc != 2 && argc != 4) |
| 158 | || (argc == 4 && strcmp (argv[1], "-o") != 0)) |
| 159 | fail ("Usage argument: [-o <outputfile>] <inputfile>", NULL); |
| 160 | replace = argc == 2; |
| 161 | |
| 162 | /* Get the ELF file. */ |
| 163 | const char *fname; |
| 164 | if (replace) |
| 165 | fname = argv[1]; |
| 166 | else |
| 167 | fname = argv[3]; |
| 168 | fd = open (fname, O_RDONLY); |
| 169 | if (fd < 0) |
| 170 | fail_errno ("couldn't open", fname); |
| 171 | |
| 172 | elf = elf_begin (fd, ELF_C_READ, NULL); |
| 173 | if (elf == NULL) |
| 174 | fail_elf ("couldn't open ELF file for reading", fname); |
| 175 | |
| 176 | GElf_Ehdr ehdr; |
| 177 | if (gelf_getehdr (elf, &ehdr) == NULL) |
| 178 | fail_elf ("Couldn't get ehdr", fname); |
| 179 | |
| 180 | /* Get the section header string table. */ |
| 181 | size_t shdrstrndx; |
| 182 | if (elf_getshdrstrndx (elf, &shdrstrndx) != 0) |
| 183 | fail_elf ("couldn't get section header string table index", fname); |
| 184 | |
| 185 | Elf_Scn *shdrstrscn = elf_getscn (elf, shdrstrndx); |
| 186 | GElf_Shdr shdrstrshdr_mem; |
| 187 | GElf_Shdr *shdrstrshdr = gelf_getshdr (shdrstrscn, &shdrstrshdr_mem); |
| 188 | if (shdrstrshdr == NULL) |
| 189 | fail_elf ("couldn't get section header string table section", fname); |
| 190 | |
| 191 | if ((shdrstrshdr->sh_flags & SHF_ALLOC) != 0) |
| 192 | fail ("section header string table is an allocated section", fname); |
| 193 | |
| 194 | /* Get the symtab section. */ |
| 195 | size_t symtabndx = 0; |
| 196 | Elf_Scn *symtabscn = NULL; |
| 197 | GElf_Shdr symtabshdr_mem; |
| 198 | GElf_Shdr *symtabshdr = NULL; |
| 199 | while ((symtabscn = elf_nextscn (elf, symtabscn)) != NULL) |
| 200 | { |
| 201 | symtabshdr = gelf_getshdr (symtabscn, &symtabshdr_mem); |
| 202 | if (symtabshdr == NULL) |
| 203 | fail_elf ("couldn't get shdr", fname); |
| 204 | |
| 205 | if (symtabshdr->sh_type == SHT_SYMTAB) |
| 206 | { |
| 207 | /* Just pick the first, we don't expect more than one. */ |
| 208 | symtabndx = elf_ndxscn (symtabscn); |
| 209 | break; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | if (symtabshdr == NULL) |
| 214 | fail ("No symtab found", fname); |
| 215 | |
| 216 | if ((symtabshdr->sh_flags & SHF_ALLOC) != 0) |
| 217 | fail ("symtab is an allocated section", fname); |
| 218 | |
| 219 | /* Get the strtab of the symtab. */ |
| 220 | size_t strtabndx = symtabshdr->sh_link; |
| 221 | Elf_Scn *strtabscn = elf_getscn (elf, strtabndx); |
| 222 | GElf_Shdr strtabshdr_mem; |
| 223 | GElf_Shdr *strtabshdr = gelf_getshdr (strtabscn, &strtabshdr_mem); |
| 224 | if (strtabshdr == NULL) |
| 225 | fail_elf ("Couldn't get strtab section", fname); |
| 226 | |
| 227 | if (shdrstrndx == strtabndx) |
| 228 | { |
| 229 | error (0, 0, "%s: Nothing to do, shstrtab == strtab", fname); |
| 230 | release (); |
| 231 | return 0; |
| 232 | } |
| 233 | |
| 234 | if ((strtabshdr->sh_flags & SHF_ALLOC) != 0) |
| 235 | fail ("strtab is an allocated section", fname); |
| 236 | |
| 237 | size_t phnum; |
| 238 | if (elf_getphdrnum (elf, &phnum) != 0) |
| 239 | fail_elf ("Couldn't get number of phdrs", fname); |
| 240 | |
| 241 | /* If there are phdrs we want to maintain the layout of the |
| 242 | allocated sections in the file. */ |
| 243 | bool layout = phnum != 0; |
| 244 | |
| 245 | /* Create a new merged strings table that starts with the empty string. */ |
| 246 | strings = dwelf_strtab_init (true); |
| 247 | if (strings == NULL) |
| 248 | fail ("No memory to create merged string table", NULL); |
| 249 | |
| 250 | /* Add the strings from all the sections. */ |
| 251 | size_t shdrnum; |
| 252 | if (elf_getshdrnum (elf, &shdrnum) != 0) |
| 253 | fail_elf ("Couldn't get number of sections", fname); |
| 254 | scnstrents = malloc (shdrnum * sizeof (Dwelf_Strent *)); |
| 255 | if (scnstrents == NULL) |
| 256 | fail ("couldn't allocate memory for section strings", NULL); |
| 257 | |
| 258 | /* While going through all sections keep track of last allocated |
| 259 | offset if needed to keep the layout. We'll put any unallocated |
| 260 | sections behind those (strtab is unallocated and will change |
| 261 | size). */ |
| 262 | GElf_Off last_offset = 0; |
| 263 | if (layout) |
| 264 | last_offset = (ehdr.e_phoff |
| 265 | + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT)); |
| 266 | Elf_Scn *scn = NULL; |
| 267 | while ((scn = elf_nextscn (elf, scn)) != NULL) |
| 268 | { |
| 269 | size_t scnnum = elf_ndxscn (scn); |
| 270 | GElf_Shdr shdr_mem; |
| 271 | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
| 272 | if (shdr == NULL) |
| 273 | fail_elf_idx ("couldn't get shdr", fname, scnnum); |
| 274 | /* Don't add the .shstrtab section itself, we'll not use it. */ |
| 275 | if (shdr->sh_name != 0 && scnnum != shdrstrndx) |
| 276 | { |
| 277 | const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name); |
| 278 | if (sname == NULL) |
| 279 | fail_elf_idx ("couldn't get section name", fname, scnnum); |
| 280 | if ((scnstrents[scnnum] = dwelf_strtab_add (strings, sname)) == NULL) |
| 281 | fail ("No memory to add to merged string table", NULL); |
| 282 | } |
| 283 | |
| 284 | if (layout) |
| 285 | if ((shdr->sh_flags & SHF_ALLOC) != 0) |
| 286 | { |
| 287 | GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS |
| 288 | ? shdr->sh_size : 0); |
| 289 | if (last_offset < off) |
| 290 | last_offset = off; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | /* Add the strings from all the symbols. */ |
| 295 | size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT); |
| 296 | Elf_Data *symd = elf_getdata (symtabscn, NULL); |
| 297 | if (symd == NULL) |
| 298 | fail_elf ("couldn't get symtab data", fname); |
| 299 | size_t symsnum = symd->d_size / elsize; |
| 300 | symstrents = malloc (symsnum * sizeof (Dwelf_Strent *)); |
| 301 | if (symstrents == NULL) |
| 302 | fail_errno ("Couldn't allocate memory for symbol strings", NULL); |
| 303 | for (size_t i = 0; i < symsnum; i++) |
| 304 | { |
| 305 | GElf_Sym sym_mem; |
| 306 | GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem); |
| 307 | if (sym == NULL) |
| 308 | fail_elf_idx ("Couldn't get symbol", fname, i); |
| 309 | if (sym->st_name != 0) |
| 310 | { |
| 311 | const char *sname = elf_strptr (elf, strtabndx, sym->st_name); |
| 312 | if (sname == NULL) |
| 313 | fail_elf_idx ("Couldn't get symbol name", fname, i); |
| 314 | if ((symstrents[i] = dwelf_strtab_add (strings, sname)) == NULL) |
| 315 | fail_idx ("No memory to add to merged string table symbol", |
| 316 | fname, i); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /* We got all strings, build the new string table and store it as |
| 321 | new strtab. */ |
| 322 | dwelf_strtab_finalize (strings, &newstrtabdata); |
| 323 | |
| 324 | /* We share at least the empty string so the result is at least 1 |
| 325 | byte smaller. */ |
| 326 | if (newstrtabdata.d_size >= shdrstrshdr->sh_size + strtabshdr->sh_size) |
| 327 | fail ("Impossible, merged string table is larger", fname); |
| 328 | |
| 329 | /* section index mapping and sanity checking. */ |
| 330 | size_t newsecndx (size_t secndx, const char *what, size_t widx, |
| 331 | const char *member, size_t midx) |
| 332 | { |
| 333 | if (unlikely (secndx == 0 || secndx == shdrstrndx || secndx >= shdrnum)) |
| 334 | { |
| 335 | /* Don't use fail... too specialized messages. Call release |
| 336 | outselves and then error. Ignores midx if widx is |
| 337 | zero. */ |
| 338 | release (); |
| 339 | if (widx == 0) |
| 340 | error (1, 0, "%s: bad section index %zd in %s for %s", |
| 341 | fname, secndx, what, member); |
| 342 | else if (midx == 0) |
| 343 | error (1, 0, "%s: bad section index %zd in %s %zd for %s", |
| 344 | fname, secndx, what, widx, member); |
| 345 | else |
| 346 | error (1, 0, "%s: bad section index %zd in %s %zd for %s %zd", |
| 347 | fname, secndx, what, widx, member, midx); |
| 348 | } |
| 349 | |
| 350 | return secndx < shdrstrndx ? secndx : secndx - 1; |
| 351 | } |
| 352 | |
| 353 | struct stat st; |
| 354 | if (fstat (fd, &st) != 0) |
| 355 | fail_errno("Couldn't fstat", fname); |
| 356 | |
| 357 | /* Create a new (temporary) ELF file for the result. */ |
| 358 | if (replace) |
| 359 | { |
| 360 | size_t fname_len = strlen (fname); |
| 361 | fnew = malloc (fname_len + sizeof (".XXXXXX")); |
| 362 | if (fnew == NULL) |
| 363 | fail_errno ("couldn't allocate memory for new file name", NULL); |
| 364 | strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX"); |
| 365 | |
| 366 | fdnew = mkstemp (fnew); |
| 367 | } |
| 368 | else |
| 369 | { |
| 370 | fnew = argv[2]; |
| 371 | fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS); |
| 372 | } |
| 373 | |
| 374 | if (fdnew < 0) |
| 375 | fail_errno ("couldn't create output file", fnew); |
| 376 | |
| 377 | elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL); |
| 378 | if (elfnew == NULL) |
| 379 | fail_elf ("couldn't open new ELF for writing", fnew); |
| 380 | |
| 381 | /* Create the new ELF header and copy over all the data. */ |
| 382 | if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0) |
| 383 | fail_elf ("Couldn't create new ehdr", fnew); |
| 384 | GElf_Ehdr newehdr; |
| 385 | if (gelf_getehdr (elfnew, &newehdr) == NULL) |
| 386 | fail_elf ("Couldn't get ehdr", fnew); |
| 387 | |
| 388 | newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA]; |
| 389 | newehdr.e_type = ehdr.e_type; |
| 390 | newehdr.e_machine = ehdr.e_machine; |
| 391 | newehdr.e_version = ehdr.e_version; |
| 392 | newehdr.e_entry = ehdr.e_entry; |
| 393 | newehdr.e_flags = ehdr.e_flags; |
| 394 | |
| 395 | /* The new file uses the new strtab as shstrtab. */ |
| 396 | size_t newstrtabndx = newsecndx (strtabndx, "ehdr", 0, "e_shstrndx", 0); |
| 397 | if (newstrtabndx < SHN_LORESERVE) |
| 398 | newehdr.e_shstrndx = newstrtabndx; |
| 399 | else |
| 400 | { |
| 401 | Elf_Scn *zscn = elf_getscn (elfnew, 0); |
| 402 | GElf_Shdr zshdr_mem; |
| 403 | GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem); |
| 404 | if (zshdr == NULL) |
| 405 | fail_elf ("Couldn't get section zero", fnew); |
| 406 | zshdr->sh_link = strtabndx; |
| 407 | if (gelf_update_shdr (zscn, zshdr) == 0) |
| 408 | fail_elf ("Couldn't update section zero", fnew); |
| 409 | newehdr.e_shstrndx = SHN_XINDEX; |
| 410 | } |
| 411 | |
| 412 | if (gelf_update_ehdr (elfnew, &newehdr) == 0) |
| 413 | fail ("Couldn't update ehdr", fnew); |
| 414 | |
| 415 | /* Copy the program headers if any. */ |
| 416 | if (phnum != 0) |
| 417 | { |
| 418 | if (gelf_newphdr (elfnew, phnum) == 0) |
| 419 | fail_elf ("Couldn't create phdrs", fnew); |
| 420 | |
| 421 | for (size_t cnt = 0; cnt < phnum; ++cnt) |
| 422 | { |
| 423 | GElf_Phdr phdr_mem; |
| 424 | GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem); |
| 425 | if (phdr == NULL) |
| 426 | fail_elf_idx ("Couldn't get phdr", fname, cnt); |
| 427 | if (gelf_update_phdr (elfnew, cnt, phdr) == 0) |
| 428 | fail_elf_idx ("Couldn't create phdr", fnew, cnt); |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | newshnums = shdrnum - 1; |
| 433 | newscnbufs = calloc (sizeof (void *), newshnums); |
| 434 | if (newscnbufs == NULL) |
| 435 | fail_errno ("Couldn't allocate memory for new section buffers", NULL); |
| 436 | |
| 437 | /* Copy the sections, except the shstrtab, fill the strtab with the |
| 438 | combined strings and adjust section references. */ |
| 439 | while ((scn = elf_nextscn (elf, scn)) != NULL) |
| 440 | { |
| 441 | size_t ndx = elf_ndxscn (scn); |
| 442 | |
| 443 | GElf_Shdr shdr_mem; |
| 444 | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
| 445 | if (shdr == NULL) |
| 446 | fail_elf_idx ("Couldn't get shdr", fname, ndx); |
| 447 | |
| 448 | /* Section zero is always created. Skip the shtrtab. */ |
| 449 | if (ndx == 0 || ndx == shdrstrndx) |
| 450 | continue; |
| 451 | |
| 452 | Elf_Scn *newscn = elf_newscn (elfnew); |
| 453 | if (newscn == NULL) |
| 454 | fail_elf_idx ("couldn't create new section", fnew, ndx); |
| 455 | |
| 456 | GElf_Shdr newshdr; |
| 457 | newshdr.sh_name = (shdr->sh_name != 0 |
| 458 | ? dwelf_strent_off (scnstrents[ndx]) : 0); |
| 459 | newshdr.sh_type = shdr->sh_type; |
| 460 | newshdr.sh_flags = shdr->sh_flags; |
| 461 | newshdr.sh_addr = shdr->sh_addr; |
| 462 | newshdr.sh_size = shdr->sh_size; |
| 463 | if (shdr->sh_link != 0) |
| 464 | newshdr.sh_link = newsecndx (shdr->sh_link, "shdr", ndx, "sh_link", 0); |
| 465 | else |
| 466 | newshdr.sh_link = 0; |
| 467 | if (SH_INFO_LINK_P (shdr) && shdr->sh_info != 0) |
| 468 | newshdr.sh_info = newsecndx (shdr->sh_info, "shdr", ndx, "sh_info", 0); |
| 469 | else |
| 470 | newshdr.sh_info = shdr->sh_info; |
| 471 | newshdr.sh_entsize = shdr->sh_entsize; |
| 472 | |
| 473 | /* Some sections need a new data buffer because they need to |
| 474 | manipulate the original data. Allocate and check here, so we |
| 475 | have a list of all data buffers we might need to release when |
| 476 | done. */ |
| 477 | void new_data_buf (Elf_Data *d) |
| 478 | { |
| 479 | size_t s = d->d_size; |
| 480 | if (s == 0) |
| 481 | fail_idx ("Expected data in section", fname, ndx); |
| 482 | void *b = malloc (d->d_size); |
| 483 | if (b == NULL) |
| 484 | fail_idx ("Couldn't allocated buffer for section", NULL, ndx); |
| 485 | newscnbufs[newsecndx (ndx, "section", ndx, "d_buf", 0)] = d->d_buf = b; |
| 486 | } |
| 487 | |
| 488 | Elf_Data *newdata = elf_newdata (newscn); |
| 489 | if (newdata == NULL) |
| 490 | fail_elf_idx ("Couldn't create new data for section", fnew, ndx); |
| 491 | if (ndx == strtabndx) |
| 492 | *newdata = newstrtabdata; |
| 493 | else |
| 494 | { |
| 495 | /* The symtab, dynsym, group and symtab_shndx sections |
| 496 | contain section indexes. Symbol tables (symtab and |
| 497 | dynsym) contain indexes to strings. Update both if |
| 498 | necessary. */ |
| 499 | Elf_Data *data = elf_getdata (scn, NULL); |
| 500 | if (data == NULL) |
| 501 | fail_elf_idx ("Couldn't get data from section", fname, ndx); |
| 502 | *newdata = *data; |
| 503 | switch (shdr->sh_type) |
| 504 | { |
| 505 | case SHT_SYMTAB: |
| 506 | case SHT_DYNSYM: |
| 507 | { |
| 508 | /* We need to update the section numbers of the |
| 509 | symbols and if this symbol table uses the strtab |
| 510 | section also the name indexes. */ |
| 511 | const bool update_name = shdr->sh_link == strtabndx; |
| 512 | if (update_name && ndx != symtabndx) |
| 513 | fail ("Only one symbol table using strtab expected", fname); |
| 514 | new_data_buf (newdata); |
| 515 | size_t syms = (data->d_size |
| 516 | / gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT)); |
| 517 | for (size_t i = 0; i < syms; i++) |
| 518 | { |
| 519 | GElf_Sym sym; |
| 520 | if (gelf_getsym (data, i, &sym) == NULL) |
| 521 | fail_elf_idx ("Couldn't get symbol", fname, i); |
| 522 | |
| 523 | if (GELF_ST_TYPE (sym.st_info) == STT_SECTION |
| 524 | && sym.st_shndx == shdrstrndx) |
| 525 | fprintf (stderr, "WARNING:" |
| 526 | " symbol table [%zd] contains section symbol %zd" |
| 527 | " for old shdrstrndx %zd\n", ndx, i, shdrstrndx); |
| 528 | else if (sym.st_shndx != SHN_UNDEF |
| 529 | && sym.st_shndx < SHN_LORESERVE) |
| 530 | sym.st_shndx = newsecndx (sym.st_shndx, "section", ndx, |
| 531 | "symbol", i); |
| 532 | if (update_name && sym.st_name != 0) |
| 533 | sym.st_name = dwelf_strent_off (symstrents[i]); |
| 534 | |
| 535 | /* We explicitly don't update the SHNDX table at |
| 536 | the same time, we do that below. */ |
| 537 | if (gelf_update_sym (newdata, i, &sym) == 0) |
| 538 | fail_elf_idx ("Couldn't update symbol", fnew, i); |
| 539 | } |
| 540 | } |
| 541 | break; |
| 542 | |
| 543 | case SHT_GROUP: |
| 544 | { |
| 545 | new_data_buf (newdata); |
| 546 | /* A section group contains Elf32_Words. The first |
| 547 | word is a falg value, the rest of the words are |
| 548 | indexes of the sections belonging to the group. */ |
| 549 | Elf32_Word *group = (Elf32_Word *) data->d_buf; |
| 550 | Elf32_Word *newgroup = (Elf32_Word *) newdata->d_buf; |
| 551 | size_t words = data->d_size / sizeof (Elf32_Word); |
| 552 | if (words == 0) |
| 553 | fail_idx ("Not enough data in group section", fname, ndx); |
| 554 | newgroup[0] = group[0]; |
| 555 | for (size_t i = 1; i < words; i++) |
| 556 | newgroup[i] = newsecndx (group[i], "section", ndx, |
| 557 | "group", i); |
| 558 | } |
| 559 | break; |
| 560 | |
| 561 | case SHT_SYMTAB_SHNDX: |
| 562 | { |
| 563 | new_data_buf (newdata); |
| 564 | /* A SHNDX just contains an array of section indexes |
| 565 | for the corresponding symbol table. The entry is |
| 566 | SHN_UNDEF unless the corresponding symbol is |
| 567 | SHN_XINDEX. */ |
| 568 | Elf32_Word *shndx = (Elf32_Word *) data->d_buf; |
| 569 | Elf32_Word *newshndx = (Elf32_Word *) newdata->d_buf; |
| 570 | size_t words = data->d_size / sizeof (Elf32_Word); |
| 571 | for (size_t i = 0; i < words; i++) |
| 572 | if (shndx[i] == SHN_UNDEF) |
| 573 | newshndx[i] = SHN_UNDEF; |
| 574 | else |
| 575 | newshndx[i] = newsecndx (shndx[i], "section", ndx, |
| 576 | "shndx", i); |
| 577 | } |
| 578 | break; |
| 579 | |
| 580 | case SHT_DYNAMIC: |
| 581 | FALLTHROUGH; |
| 582 | /* There are string indexes in here, but |
| 583 | they (should) point to a allocated string table, |
| 584 | which we don't alter. */ |
| 585 | default: |
| 586 | /* Nothing to do. Section data doesn't contain section |
| 587 | or strtab indexes. */ |
| 588 | break; |
| 589 | } |
| 590 | } |
| 591 | |
| 592 | /* When we are responsible for the layout explicitly set |
| 593 | sh_addralign, sh_size and sh_offset. Otherwise libelf will |
| 594 | calculate those from the Elf_Data. */ |
| 595 | if (layout) |
| 596 | { |
| 597 | /* We have just one Elf_Data. */ |
| 598 | newshdr.sh_size = newdata->d_size; |
| 599 | newshdr.sh_addralign = newdata->d_align; |
| 600 | |
| 601 | /* Keep the offset of allocated sections so they are at the |
| 602 | same place in the file. Add unallocated ones after the |
| 603 | allocated ones. */ |
| 604 | if ((shdr->sh_flags & SHF_ALLOC) != 0) |
| 605 | newshdr.sh_offset = shdr->sh_offset; |
| 606 | else |
| 607 | { |
| 608 | /* Zero means one. No alignment constraints. */ |
| 609 | size_t addralign = newshdr.sh_addralign ?: 1; |
| 610 | last_offset = (last_offset + addralign - 1) & ~(addralign - 1); |
| 611 | newshdr.sh_offset = last_offset; |
| 612 | if (newshdr.sh_type != SHT_NOBITS) |
| 613 | last_offset += newshdr.sh_size; |
| 614 | } |
| 615 | } |
| 616 | else |
| 617 | { |
| 618 | newshdr.sh_addralign = 0; |
| 619 | newshdr.sh_size = 0; |
| 620 | newshdr.sh_offset = 0; |
| 621 | } |
| 622 | |
| 623 | if (gelf_update_shdr (newscn, &newshdr) == 0) |
| 624 | fail_elf_idx ("Couldn't update section header", fnew, ndx); |
| 625 | } |
| 626 | |
| 627 | /* If we have phdrs we want elf_update to layout the SHF_ALLOC |
| 628 | sections precisely as in the original file. In that case we are |
| 629 | also responsible for setting phoff and shoff */ |
| 630 | if (layout) |
| 631 | { |
| 632 | /* Position the shdrs after the last (unallocated) section. */ |
| 633 | if (gelf_getehdr (elfnew, &newehdr) == NULL) |
| 634 | fail_elf ("Couldn't get ehdr", fnew); |
| 635 | const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT); |
| 636 | newehdr.e_shoff = ((last_offset + offsize - 1) |
| 637 | & ~((GElf_Off) (offsize - 1))); |
| 638 | |
| 639 | /* The phdrs go in the same place as in the original file. |
| 640 | Normally right after the ELF header. */ |
| 641 | newehdr.e_phoff = ehdr.e_phoff; |
| 642 | |
| 643 | if (gelf_update_ehdr (elfnew, &newehdr) == 0) |
| 644 | fail_elf ("Couldn't update ehdr", fnew); |
| 645 | |
| 646 | elf_flagelf (elfnew, ELF_C_SET, ELF_F_LAYOUT); |
| 647 | } |
| 648 | |
| 649 | if (elf_update (elfnew, ELF_C_WRITE) == -1) |
| 650 | fail_elf ("Couldn't write ELF", fnew); |
| 651 | |
| 652 | elf_end (elfnew); |
| 653 | elfnew = NULL; |
| 654 | |
| 655 | /* Try to match mode and owner.group of the original file. */ |
| 656 | if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0) |
| 657 | error (0, errno, "Couldn't fchmod %s", fnew); |
| 658 | if (fchown (fdnew, st.st_uid, st.st_gid) != 0) |
| 659 | error (0, errno, "Couldn't fchown %s", fnew); |
| 660 | |
| 661 | /* Finally replace the old file with the new merged strings file. */ |
| 662 | if (replace) |
| 663 | if (rename (fnew, fname) != 0) |
| 664 | fail_errno ("rename", fnew); |
| 665 | |
| 666 | /* We are finally done with the new file, don't unlink it now. */ |
| 667 | close (fdnew); |
| 668 | if (replace) |
| 669 | free (fnew); |
| 670 | fnew = NULL; |
| 671 | fdnew = -1; |
| 672 | |
| 673 | release (); |
| 674 | return 0; |
| 675 | } |