2 * Copyright (c) 2007-2009 Kai Wang
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
29 #include <sys/param.h>
38 #include <archive_entry.h>
39 #endif /* ! LIBELF_AR */
43 ELFTC_VCSID("$Id: archive.c 3102 2014-10-29 21:09:01Z jkoshy $");
45 #define _ARMAG_LEN 8 /* length of ar magic string */
46 #define _ARHDR_LEN 60 /* length of ar header */
47 #define _INIT_AS_CAP 128 /* initial archive string table size */
48 #define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
49 #define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
50 #define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
53 static void ac_read_objs(struct elfcopy *ecp, int ifd);
54 static void ac_write_cleanup(struct elfcopy *ecp);
55 static void ac_write_data(struct archive *a, const void *buf, size_t s);
56 static void ac_write_objs(struct elfcopy *ecp, int ofd);
57 #endif /* ! LIBELF_AR */
58 static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name);
59 static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name);
60 static void extract_arsym(struct elfcopy *ecp);
61 static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj);
62 static void sync_ar(struct elfcopy *ecp);
66 process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj)
72 /* Output to a temporary file. */
73 create_tempfile(&tempfile, &fd);
74 if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL)
75 errx(EXIT_FAILURE, "elf_begin() failed: %s",
77 elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT);
84 /* Extract archive symbols. */
85 if (lseek(fd, 0, SEEK_SET) < 0)
86 err(EXIT_FAILURE, "lseek failed for '%s'", tempfile);
87 if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
88 errx(EXIT_FAILURE, "elf_begin() failed: %s",
93 if (fstat(fd, &sb) == -1)
94 err(EXIT_FAILURE, "fstat %s failed", tempfile);
95 if (lseek(fd, 0, SEEK_SET) < 0)
96 err(EXIT_FAILURE, "lseek %s failed", tempfile);
97 obj->size = sb.st_size;
98 if ((obj->maddr = malloc(obj->size)) == NULL)
99 err(EXIT_FAILURE, "memory allocation failed for '%s'",
101 if ((size_t) read(fd, obj->maddr, obj->size) != obj->size)
102 err(EXIT_FAILURE, "read failed for '%s'", tempfile);
103 if (unlink(tempfile))
104 err(EXIT_FAILURE, "unlink %s failed", tempfile);
107 if (strlen(obj->name) > _MAXNAMELEN_SVR4)
108 add_to_ar_str_table(ecp, obj->name);
109 ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
110 STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs);
114 * Append to the archive string table buffer.
117 add_to_ar_str_table(struct elfcopy *ecp, const char *name)
120 if (ecp->as == NULL) {
121 ecp->as_cap = _INIT_AS_CAP;
123 if ((ecp->as = malloc(ecp->as_cap)) == NULL)
124 err(EXIT_FAILURE, "malloc failed");
128 * The space required for holding one member name in as table includes:
129 * strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
131 while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) {
133 ecp->as = realloc(ecp->as, ecp->as_cap);
135 err(EXIT_FAILURE, "realloc failed");
137 strncpy(&ecp->as[ecp->as_sz], name, strlen(name));
138 ecp->as_sz += strlen(name);
139 ecp->as[ecp->as_sz++] = '/';
140 ecp->as[ecp->as_sz++] = '\n';
144 * Append to the archive symbol table buffer.
147 add_to_ar_sym_table(struct elfcopy *ecp, const char *name)
150 if (ecp->s_so == NULL) {
151 if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL)
152 err(EXIT_FAILURE, "malloc failed");
153 ecp->s_so_cap = _INIT_SYMOFF_CAP;
157 if (ecp->s_sn == NULL) {
158 if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
159 err(EXIT_FAILURE, "malloc failed");
160 ecp->s_sn_cap = _INIT_SYMNAME_CAP;
164 if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) {
166 ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap);
167 if (ecp->s_so == NULL)
168 err(EXIT_FAILURE, "realloc failed");
170 ecp->s_so[ecp->s_cnt] = ecp->rela_off;
174 * The space required for holding one symbol name in sn table includes:
175 * strlen(name) + (1 for '\n') + (possibly 1 for padding).
177 while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) {
179 ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap);
180 if (ecp->s_sn == NULL)
181 err(EXIT_FAILURE, "realloc failed");
183 strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name));
184 ecp->s_sn_sz += strlen(name);
185 ecp->s_sn[ecp->s_sn_sz++] = '\0';
189 sync_ar(struct elfcopy *ecp)
191 size_t s_sz; /* size of archive symbol table. */
192 size_t pm_sz; /* size of pseudo members */
196 * Pad the symbol name string table. It is treated specially because
197 * symbol name table should be padded by a '\0', not the common '\n'
198 * for other members. The size of sn table includes the pad bit.
200 if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0)
201 ecp->s_sn[ecp->s_sn_sz++] = '\0';
204 * Archive string table is padded by a "\n" as the normal members.
205 * The difference is that the size of archive string table counts
206 * in the pad bit, while normal members' size fileds do not.
208 if (ecp->as != NULL && ecp->as_sz % 2 != 0)
209 ecp->as[ecp->as_sz++] = '\n';
212 * If there is a symbol table, calculate the size of pseudo members,
213 * convert previously stored relative offsets to absolute ones, and
214 * then make them Big Endian.
216 * absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
219 if (ecp->s_cnt != 0) {
220 s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz;
221 pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
223 pm_sz += _ARHDR_LEN + ecp->as_sz;
224 for (i = 0; (size_t)i < ecp->s_cnt; i++)
225 *(ecp->s_so + i) = htobe32(*(ecp->s_so + i) +
231 * Extract global symbols from archive members.
234 extract_arsym(struct elfcopy *ecp)
242 int elferr, tabndx, len, i;
244 if (elf_kind(ecp->eout) != ELF_K_ELF) {
245 warnx("internal: cannot extract symbols from non-elf object");
248 if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) {
249 warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
255 while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
256 if (gelf_getshdr(scn, &shdr) != &shdr) {
257 warnx("elf_getshdr failed: %s", elf_errmsg(-1));
260 if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) ==
262 warnx("elf_strptr failed: %s", elf_errmsg(-1));
265 if (strcmp(name, ".strtab") == 0) {
266 tabndx = elf_ndxscn(scn);
270 elferr = elf_errno();
272 warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
274 /* Ignore members without symbol table. */
279 while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
280 if (gelf_getshdr(scn, &shdr) != &shdr) {
281 warnx("elf_getshdr failed: %s", elf_errmsg(-1));
284 if (shdr.sh_type != SHT_SYMTAB)
289 while (n < shdr.sh_size &&
290 (data = elf_getdata(scn, data)) != NULL) {
291 len = data->d_size / shdr.sh_entsize;
292 for (i = 0; i < len; i++) {
293 if (gelf_getsym(data, i, &sym) != &sym) {
294 warnx("gelf_getsym failed: %s",
299 /* keep only global or weak symbols */
300 if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
301 GELF_ST_BIND(sym.st_info) != STB_WEAK)
304 /* keep only defined symbols */
305 if (sym.st_shndx == SHN_UNDEF)
308 if ((name = elf_strptr(ecp->eout, tabndx,
309 sym.st_name)) == NULL) {
310 warnx("elf_strptr failed: %s",
315 add_to_ar_sym_table(ecp, name);
319 elferr = elf_errno();
321 warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
327 * Convenient wrapper for general libarchive error handling.
329 #define AC(CALL) do { \
331 errx(EXIT_FAILURE, "%s", archive_error_string(a)); \
334 /* Earlier versions of libarchive had some functions that returned 'void'. */
335 #if ARCHIVE_VERSION_NUMBER >= 2000000
336 #define ACV(CALL) AC(CALL)
338 #define ACV(CALL) do { \
344 ac_detect_ar(int ifd)
347 struct archive_entry *entry;
351 if ((a = archive_read_new()) == NULL)
353 archive_read_support_format_ar(a);
354 if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK)
355 r = archive_read_next_header(a, &entry);
356 archive_read_close(a);
357 archive_read_free(a);
359 return (r == ARCHIVE_OK);
363 ac_create_ar(struct elfcopy *ecp, int ifd, int ofd)
366 ac_read_objs(ecp, ifd);
368 ac_write_objs(ecp, ofd);
369 ac_write_cleanup(ecp);
373 ac_read_objs(struct elfcopy *ecp, int ifd)
376 struct archive_entry *entry;
384 if (lseek(ifd, 0, SEEK_SET) == -1)
385 err(EXIT_FAILURE, "lseek failed");
386 if ((a = archive_read_new()) == NULL)
387 errx(EXIT_FAILURE, "%s", archive_error_string(a));
388 archive_read_support_format_ar(a);
389 AC(archive_read_open_fd(a, ifd, 10240));
391 r = archive_read_next_header(a, &entry);
392 if (r == ARCHIVE_FATAL)
393 errx(EXIT_FAILURE, "%s", archive_error_string(a));
394 if (r == ARCHIVE_EOF)
396 if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
397 warnx("%s", archive_error_string(a));
398 if (r == ARCHIVE_RETRY)
401 name = archive_entry_pathname(entry);
403 /* skip pseudo members. */
404 if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
407 size = archive_entry_size(entry);
410 if ((buff = malloc(size)) == NULL)
411 err(EXIT_FAILURE, "malloc failed");
412 if (archive_read_data(a, buff, size) != (ssize_t)size) {
413 warnx("%s", archive_error_string(a));
417 if ((obj = malloc(sizeof(*obj))) == NULL)
418 err(EXIT_FAILURE, "malloc failed");
419 if ((obj->name = strdup(name)) == NULL)
420 err(EXIT_FAILURE, "strdup failed");
422 obj->uid = archive_entry_uid(entry);
423 obj->gid = archive_entry_gid(entry);
424 obj->md = archive_entry_mode(entry);
425 obj->mtime = archive_entry_mtime(entry);
426 if ((ecp->ein = elf_memory(buff, size)) == NULL)
427 errx(EXIT_FAILURE, "elf_memory() failed: %s",
429 if (elf_kind(ecp->ein) != ELF_K_ELF)
431 "file format not recognized");
432 process_ar_obj(ecp, obj);
435 AC(archive_read_close(a));
436 ACV(archive_read_free(a));
440 ac_write_objs(struct elfcopy *ecp, int ofd)
443 struct archive_entry *entry;
447 if ((a = archive_write_new()) == NULL)
448 errx(EXIT_FAILURE, "%s", archive_error_string(a));
449 archive_write_set_format_ar_svr4(a);
450 AC(archive_write_open_fd(a, ofd));
452 /* Write the archive symbol table, even if it's empty. */
453 entry = archive_entry_new();
454 archive_entry_copy_pathname(entry, "/");
455 archive_entry_set_mtime(entry, time(NULL), 0);
456 archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) +
458 AC(archive_write_header(a, entry));
459 nr = htobe32(ecp->s_cnt);
460 ac_write_data(a, &nr, sizeof(uint32_t));
461 ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt);
462 ac_write_data(a, ecp->s_sn, ecp->s_sn_sz);
463 archive_entry_free(entry);
465 /* Write the archive string table, if exist. */
466 if (ecp->as != NULL) {
467 entry = archive_entry_new();
468 archive_entry_copy_pathname(entry, "//");
469 archive_entry_set_size(entry, ecp->as_sz);
470 AC(archive_write_header(a, entry));
471 ac_write_data(a, ecp->as, ecp->as_sz);
472 archive_entry_free(entry);
475 /* Write normal members. */
476 STAILQ_FOREACH(obj, &ecp->v_arobj, objs) {
477 entry = archive_entry_new();
478 archive_entry_copy_pathname(entry, obj->name);
479 archive_entry_set_uid(entry, obj->uid);
480 archive_entry_set_gid(entry, obj->gid);
481 archive_entry_set_mode(entry, obj->md);
482 archive_entry_set_size(entry, obj->size);
483 archive_entry_set_mtime(entry, obj->mtime, 0);
484 archive_entry_set_filetype(entry, AE_IFREG);
485 AC(archive_write_header(a, entry));
486 ac_write_data(a, obj->maddr, obj->size);
487 archive_entry_free(entry);
490 AC(archive_write_close(a));
491 ACV(archive_write_free(a));
495 ac_write_cleanup(struct elfcopy *ecp)
497 struct ar_obj *obj, *obj_temp;
499 STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) {
500 STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs);
501 if (obj->maddr != NULL)
516 * Wrapper for archive_write_data().
519 ac_write_data(struct archive *a, const void *buf, size_t s)
521 if (archive_write_data(a, buf, s) != (ssize_t)s)
522 errx(EXIT_FAILURE, "%s", archive_error_string(a));
525 #endif /* ! LIBELF_AR */