1 /*- 2 * Copyright (c) 2007-2009 Kai Wang 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/stat.h> 29 #include <err.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #ifndef LIBELF_AR 35 #include <archive.h> 36 #include <archive_entry.h> 37 #endif /* ! LIBELF_AR */ 38 39 #include "elfcopy.h" 40 41 ELFTC_VCSID("$Id: archive.c 3490 2016-08-31 00:12:22Z emaste $"); 42 43 #define _ARMAG_LEN 8 /* length of ar magic string */ 44 #define _ARHDR_LEN 60 /* length of ar header */ 45 #define _INIT_AS_CAP 128 /* initial archive string table size */ 46 #define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */ 47 #define _INIT_SYMNAME_CAP 1024 /* initial sn table size */ 48 #define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */ 49 50 #ifndef LIBELF_AR 51 static void ac_read_objs(struct elfcopy *ecp, int ifd); 52 static void ac_write_cleanup(struct elfcopy *ecp); 53 static void ac_write_data(struct archive *a, const void *buf, size_t s); 54 static void ac_write_objs(struct elfcopy *ecp, int ofd); 55 #endif /* ! LIBELF_AR */ 56 static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name); 57 static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name); 58 static void extract_arsym(struct elfcopy *ecp); 59 static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj); 60 static void sync_ar(struct elfcopy *ecp); 61 62 63 static void 64 process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj) 65 { 66 struct stat sb; 67 char *tempfile; 68 int fd; 69 70 /* Output to a temporary file. */ 71 create_tempfile(&tempfile, &fd); 72 if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) 73 errx(EXIT_FAILURE, "elf_begin() failed: %s", 74 elf_errmsg(-1)); 75 elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT); 76 create_elf(ecp); 77 elf_end(ecp->ein); 78 elf_end(ecp->eout); 79 free(obj->buf); 80 obj->buf = NULL; 81 82 /* Extract archive symbols. */ 83 if (lseek(fd, 0, SEEK_SET) < 0) 84 err(EXIT_FAILURE, "lseek failed for '%s'", tempfile); 85 if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL) 86 errx(EXIT_FAILURE, "elf_begin() failed: %s", 87 elf_errmsg(-1)); 88 extract_arsym(ecp); 89 elf_end(ecp->eout); 90 91 if (fstat(fd, &sb) == -1) 92 err(EXIT_FAILURE, "fstat %s failed", tempfile); 93 if (lseek(fd, 0, SEEK_SET) < 0) 94 err(EXIT_FAILURE, "lseek %s failed", tempfile); 95 obj->size = sb.st_size; 96 if ((obj->maddr = malloc(obj->size)) == NULL) 97 err(EXIT_FAILURE, "memory allocation failed for '%s'", 98 tempfile); 99 if ((size_t) read(fd, obj->maddr, obj->size) != obj->size) 100 err(EXIT_FAILURE, "read failed for '%s'", tempfile); 101 if (unlink(tempfile)) 102 err(EXIT_FAILURE, "unlink %s failed", tempfile); 103 free(tempfile); 104 close(fd); 105 if (strlen(obj->name) > _MAXNAMELEN_SVR4) 106 add_to_ar_str_table(ecp, obj->name); 107 ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2; 108 STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs); 109 } 110 111 /* 112 * Append to the archive string table buffer. 113 */ 114 static void 115 add_to_ar_str_table(struct elfcopy *ecp, const char *name) 116 { 117 118 if (ecp->as == NULL) { 119 ecp->as_cap = _INIT_AS_CAP; 120 ecp->as_sz = 0; 121 if ((ecp->as = malloc(ecp->as_cap)) == NULL) 122 err(EXIT_FAILURE, "malloc failed"); 123 } 124 125 /* 126 * The space required for holding one member name in as table includes: 127 * strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding). 128 */ 129 while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) { 130 ecp->as_cap *= 2; 131 ecp->as = realloc(ecp->as, ecp->as_cap); 132 if (ecp->as == NULL) 133 err(EXIT_FAILURE, "realloc failed"); 134 } 135 strncpy(&ecp->as[ecp->as_sz], name, strlen(name)); 136 ecp->as_sz += strlen(name); 137 ecp->as[ecp->as_sz++] = '/'; 138 ecp->as[ecp->as_sz++] = '\n'; 139 } 140 141 /* 142 * Append to the archive symbol table buffer. 143 */ 144 static void 145 add_to_ar_sym_table(struct elfcopy *ecp, const char *name) 146 { 147 148 if (ecp->s_so == NULL) { 149 if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL) 150 err(EXIT_FAILURE, "malloc failed"); 151 ecp->s_so_cap = _INIT_SYMOFF_CAP; 152 ecp->s_cnt = 0; 153 } 154 155 if (ecp->s_sn == NULL) { 156 if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL) 157 err(EXIT_FAILURE, "malloc failed"); 158 ecp->s_sn_cap = _INIT_SYMNAME_CAP; 159 ecp->s_sn_sz = 0; 160 } 161 162 if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) { 163 ecp->s_so_cap *= 2; 164 ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap); 165 if (ecp->s_so == NULL) 166 err(EXIT_FAILURE, "realloc failed"); 167 } 168 ecp->s_so[ecp->s_cnt] = ecp->rela_off; 169 ecp->s_cnt++; 170 171 /* 172 * The space required for holding one symbol name in sn table includes: 173 * strlen(name) + (1 for '\n') + (possibly 1 for padding). 174 */ 175 while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) { 176 ecp->s_sn_cap *= 2; 177 ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap); 178 if (ecp->s_sn == NULL) 179 err(EXIT_FAILURE, "realloc failed"); 180 } 181 strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name)); 182 ecp->s_sn_sz += strlen(name); 183 ecp->s_sn[ecp->s_sn_sz++] = '\0'; 184 } 185 186 static void 187 sync_ar(struct elfcopy *ecp) 188 { 189 size_t s_sz; /* size of archive symbol table. */ 190 size_t pm_sz; /* size of pseudo members */ 191 int i; 192 193 /* 194 * Pad the symbol name string table. It is treated specially because 195 * symbol name table should be padded by a '\0', not the common '\n' 196 * for other members. The size of sn table includes the pad bit. 197 */ 198 if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0) 199 ecp->s_sn[ecp->s_sn_sz++] = '\0'; 200 201 /* 202 * Archive string table is padded by a "\n" as the normal members. 203 * The difference is that the size of archive string table counts 204 * in the pad bit, while normal members' size fileds do not. 205 */ 206 if (ecp->as != NULL && ecp->as_sz % 2 != 0) 207 ecp->as[ecp->as_sz++] = '\n'; 208 209 /* 210 * If there is a symbol table, calculate the size of pseudo members, 211 * convert previously stored relative offsets to absolute ones, and 212 * then make them Big Endian. 213 * 214 * absolute_offset = htobe32(relative_offset + size_of_pseudo_members) 215 */ 216 217 if (ecp->s_cnt != 0) { 218 s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz; 219 pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz); 220 if (ecp->as != NULL) 221 pm_sz += _ARHDR_LEN + ecp->as_sz; 222 for (i = 0; (size_t)i < ecp->s_cnt; i++) 223 *(ecp->s_so + i) = htobe32(*(ecp->s_so + i) + 224 pm_sz); 225 } 226 } 227 228 /* 229 * Extract global symbols from archive members. 230 */ 231 static void 232 extract_arsym(struct elfcopy *ecp) 233 { 234 Elf_Scn *scn; 235 GElf_Shdr shdr; 236 GElf_Sym sym; 237 Elf_Data *data; 238 char *name; 239 size_t n, shstrndx; 240 int elferr, tabndx, len, i; 241 242 if (elf_kind(ecp->eout) != ELF_K_ELF) { 243 warnx("internal: cannot extract symbols from non-elf object"); 244 return; 245 } 246 if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) { 247 warnx("elf_getshstrndx failed: %s", elf_errmsg(-1)); 248 return; 249 } 250 251 tabndx = -1; 252 scn = NULL; 253 while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) { 254 if (gelf_getshdr(scn, &shdr) != &shdr) { 255 warnx("elf_getshdr failed: %s", elf_errmsg(-1)); 256 continue; 257 } 258 if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) == 259 NULL) { 260 warnx("elf_strptr failed: %s", elf_errmsg(-1)); 261 continue; 262 } 263 if (strcmp(name, ".strtab") == 0) { 264 tabndx = elf_ndxscn(scn); 265 break; 266 } 267 } 268 elferr = elf_errno(); 269 if (elferr != 0) 270 warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); 271 272 /* Ignore members without symbol table. */ 273 if (tabndx == -1) 274 return; 275 276 scn = NULL; 277 while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) { 278 if (gelf_getshdr(scn, &shdr) != &shdr) { 279 warnx("elf_getshdr failed: %s", elf_errmsg(-1)); 280 continue; 281 } 282 if (shdr.sh_type != SHT_SYMTAB) 283 continue; 284 285 data = NULL; 286 n = 0; 287 while (n < shdr.sh_size && 288 (data = elf_getdata(scn, data)) != NULL) { 289 len = data->d_size / shdr.sh_entsize; 290 for (i = 0; i < len; i++) { 291 if (gelf_getsym(data, i, &sym) != &sym) { 292 warnx("gelf_getsym failed: %s", 293 elf_errmsg(-1)); 294 continue; 295 } 296 297 /* keep only global or weak symbols */ 298 if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL && 299 GELF_ST_BIND(sym.st_info) != STB_WEAK) 300 continue; 301 302 /* keep only defined symbols */ 303 if (sym.st_shndx == SHN_UNDEF) 304 continue; 305 306 if ((name = elf_strptr(ecp->eout, tabndx, 307 sym.st_name)) == NULL) { 308 warnx("elf_strptr failed: %s", 309 elf_errmsg(-1)); 310 continue; 311 } 312 313 add_to_ar_sym_table(ecp, name); 314 } 315 } 316 } 317 elferr = elf_errno(); 318 if (elferr != 0) 319 warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); 320 } 321 322 #ifndef LIBELF_AR 323 324 /* 325 * Convenient wrapper for general libarchive error handling. 326 */ 327 #define AC(CALL) do { \ 328 if ((CALL)) \ 329 errx(EXIT_FAILURE, "%s", archive_error_string(a)); \ 330 } while (0) 331 332 /* Earlier versions of libarchive had some functions that returned 'void'. */ 333 #if ARCHIVE_VERSION_NUMBER >= 2000000 334 #define ACV(CALL) AC(CALL) 335 #else 336 #define ACV(CALL) do { \ 337 (CALL); \ 338 } while (0) 339 #endif 340 341 int 342 ac_detect_ar(int ifd) 343 { 344 struct archive *a; 345 struct archive_entry *entry; 346 int r; 347 348 r = -1; 349 if ((a = archive_read_new()) == NULL) 350 return (0); 351 archive_read_support_format_ar(a); 352 if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK) 353 r = archive_read_next_header(a, &entry); 354 archive_read_close(a); 355 archive_read_free(a); 356 357 return (r == ARCHIVE_OK); 358 } 359 360 void 361 ac_create_ar(struct elfcopy *ecp, int ifd, int ofd) 362 { 363 364 ac_read_objs(ecp, ifd); 365 sync_ar(ecp); 366 ac_write_objs(ecp, ofd); 367 ac_write_cleanup(ecp); 368 } 369 370 static void 371 ac_read_objs(struct elfcopy *ecp, int ifd) 372 { 373 struct archive *a; 374 struct archive_entry *entry; 375 struct ar_obj *obj; 376 const char *name; 377 char *buff; 378 size_t size; 379 int r; 380 381 ecp->rela_off = 0; 382 if (lseek(ifd, 0, SEEK_SET) == -1) 383 err(EXIT_FAILURE, "lseek failed"); 384 if ((a = archive_read_new()) == NULL) 385 errx(EXIT_FAILURE, "archive_read_new failed"); 386 archive_read_support_format_ar(a); 387 AC(archive_read_open_fd(a, ifd, 10240)); 388 for(;;) { 389 r = archive_read_next_header(a, &entry); 390 if (r == ARCHIVE_FATAL) 391 errx(EXIT_FAILURE, "%s", archive_error_string(a)); 392 if (r == ARCHIVE_EOF) 393 break; 394 if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY) 395 warnx("%s", archive_error_string(a)); 396 if (r == ARCHIVE_RETRY) 397 continue; 398 399 name = archive_entry_pathname(entry); 400 401 /* skip pseudo members. */ 402 if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0) 403 continue; 404 405 size = archive_entry_size(entry); 406 407 if (size > 0) { 408 if ((buff = malloc(size)) == NULL) 409 err(EXIT_FAILURE, "malloc failed"); 410 if (archive_read_data(a, buff, size) != (ssize_t)size) { 411 warnx("%s", archive_error_string(a)); 412 free(buff); 413 continue; 414 } 415 if ((obj = malloc(sizeof(*obj))) == NULL) 416 err(EXIT_FAILURE, "malloc failed"); 417 if ((obj->name = strdup(name)) == NULL) 418 err(EXIT_FAILURE, "strdup failed"); 419 obj->buf = buff; 420 obj->uid = archive_entry_uid(entry); 421 obj->gid = archive_entry_gid(entry); 422 obj->md = archive_entry_mode(entry); 423 obj->mtime = archive_entry_mtime(entry); 424 if ((ecp->ein = elf_memory(buff, size)) == NULL) 425 errx(EXIT_FAILURE, "elf_memory() failed: %s", 426 elf_errmsg(-1)); 427 if (elf_kind(ecp->ein) != ELF_K_ELF) 428 errx(EXIT_FAILURE, 429 "file format not recognized"); 430 process_ar_obj(ecp, obj); 431 } 432 } 433 AC(archive_read_close(a)); 434 ACV(archive_read_free(a)); 435 } 436 437 static void 438 ac_write_objs(struct elfcopy *ecp, int ofd) 439 { 440 struct archive *a; 441 struct archive_entry *entry; 442 struct ar_obj *obj; 443 time_t timestamp; 444 int nr; 445 446 if ((a = archive_write_new()) == NULL) 447 errx(EXIT_FAILURE, "archive_write_new failed"); 448 archive_write_set_format_ar_svr4(a); 449 AC(archive_write_open_fd(a, ofd)); 450 451 /* Write the archive symbol table, even if it's empty. */ 452 entry = archive_entry_new(); 453 archive_entry_copy_pathname(entry, "/"); 454 if (elftc_timestamp(×tamp) != 0) 455 err(EXIT_FAILURE, "elftc_timestamp"); 456 archive_entry_set_mtime(entry, timestamp, 0); 457 archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) + 458 ecp->s_sn_sz); 459 AC(archive_write_header(a, entry)); 460 nr = htobe32(ecp->s_cnt); 461 ac_write_data(a, &nr, sizeof(uint32_t)); 462 ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt); 463 ac_write_data(a, ecp->s_sn, ecp->s_sn_sz); 464 archive_entry_free(entry); 465 466 /* Write the archive string table, if exist. */ 467 if (ecp->as != NULL) { 468 entry = archive_entry_new(); 469 archive_entry_copy_pathname(entry, "//"); 470 archive_entry_set_size(entry, ecp->as_sz); 471 AC(archive_write_header(a, entry)); 472 ac_write_data(a, ecp->as, ecp->as_sz); 473 archive_entry_free(entry); 474 } 475 476 /* Write normal members. */ 477 STAILQ_FOREACH(obj, &ecp->v_arobj, objs) { 478 entry = archive_entry_new(); 479 archive_entry_copy_pathname(entry, obj->name); 480 archive_entry_set_uid(entry, obj->uid); 481 archive_entry_set_gid(entry, obj->gid); 482 archive_entry_set_mode(entry, obj->md); 483 archive_entry_set_size(entry, obj->size); 484 archive_entry_set_mtime(entry, obj->mtime, 0); 485 archive_entry_set_filetype(entry, AE_IFREG); 486 AC(archive_write_header(a, entry)); 487 ac_write_data(a, obj->maddr, obj->size); 488 archive_entry_free(entry); 489 } 490 491 AC(archive_write_close(a)); 492 ACV(archive_write_free(a)); 493 } 494 495 static void 496 ac_write_cleanup(struct elfcopy *ecp) 497 { 498 struct ar_obj *obj, *obj_temp; 499 500 STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) { 501 STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs); 502 if (obj->maddr != NULL) 503 free(obj->maddr); 504 free(obj->name); 505 free(obj); 506 } 507 508 free(ecp->as); 509 free(ecp->s_so); 510 free(ecp->s_sn); 511 ecp->as = NULL; 512 ecp->s_so = NULL; 513 ecp->s_sn = NULL; 514 } 515 516 /* 517 * Wrapper for archive_write_data(). 518 */ 519 static void 520 ac_write_data(struct archive *a, const void *buf, size_t s) 521 { 522 if (archive_write_data(a, buf, s) != (ssize_t)s) 523 errx(EXIT_FAILURE, "%s", archive_error_string(a)); 524 } 525 526 #endif /* ! LIBELF_AR */ 527