xref: /freebsd/contrib/elftoolchain/ar/write.c (revision ae500c1f)
13fe401a5SEd Maste /*-
23fe401a5SEd Maste  * Copyright (c) 2007 Kai Wang
33fe401a5SEd Maste  * All rights reserved.
43fe401a5SEd Maste  *
53fe401a5SEd Maste  * Redistribution and use in source and binary forms, with or without
63fe401a5SEd Maste  * modification, are permitted provided that the following conditions
73fe401a5SEd Maste  * are met:
83fe401a5SEd Maste  * 1. Redistributions of source code must retain the above copyright
93fe401a5SEd Maste  *    notice, this list of conditions and the following disclaimer
103fe401a5SEd Maste  *    in this position and unchanged.
113fe401a5SEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
123fe401a5SEd Maste  *    notice, this list of conditions and the following disclaimer in the
133fe401a5SEd Maste  *    documentation and/or other materials provided with the distribution.
143fe401a5SEd Maste  *
153fe401a5SEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
163fe401a5SEd Maste  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
173fe401a5SEd Maste  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
183fe401a5SEd Maste  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
193fe401a5SEd Maste  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
203fe401a5SEd Maste  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
213fe401a5SEd Maste  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
223fe401a5SEd Maste  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
233fe401a5SEd Maste  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
243fe401a5SEd Maste  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
253fe401a5SEd Maste  */
263fe401a5SEd Maste 
273fe401a5SEd Maste #include <sys/queue.h>
283fe401a5SEd Maste #include <sys/stat.h>
293fe401a5SEd Maste 
303fe401a5SEd Maste #include <archive.h>
313fe401a5SEd Maste #include <archive_entry.h>
323fe401a5SEd Maste #include <assert.h>
333fe401a5SEd Maste #include <errno.h>
343fe401a5SEd Maste #include <fcntl.h>
353fe401a5SEd Maste #include <gelf.h>
363fe401a5SEd Maste #include <libgen.h>
373fe401a5SEd Maste #include <stdio.h>
383fe401a5SEd Maste #include <stdlib.h>
393fe401a5SEd Maste #include <string.h>
403fe401a5SEd Maste 
413fe401a5SEd Maste #include "ar.h"
423fe401a5SEd Maste 
43*ae500c1fSEd Maste ELFTC_VCSID("$Id: write.c 3629 2018-09-30 19:26:28Z jkoshy $");
443fe401a5SEd Maste 
453fe401a5SEd Maste #define _ARMAG_LEN 8		/* length of the magic string */
463fe401a5SEd Maste #define _ARHDR_LEN 60		/* length of the archive header */
473fe401a5SEd Maste #define _INIT_AS_CAP 128	/* initial archive string table size */
483fe401a5SEd Maste #define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
493fe401a5SEd Maste #define _INIT_SYMNAME_CAP 1024			  /* initial sn table size */
503fe401a5SEd Maste #define _MAXNAMELEN_SVR4 15	/* max member name length in svr4 variant */
513fe401a5SEd Maste #define _MAXNAMELEN_BSD  16	/* max member name length in bsd variant */
523fe401a5SEd Maste #define _TRUNCATE_LEN 15	/* number of bytes to keep for member name */
533fe401a5SEd Maste 
543fe401a5SEd Maste static void	add_to_ar_str_table(struct bsdar *bsdar, const char *name);
553fe401a5SEd Maste static void	add_to_ar_sym_table(struct bsdar *bsdar, const char *name);
563fe401a5SEd Maste static struct ar_obj	*create_obj_from_file(struct bsdar *bsdar,
573fe401a5SEd Maste 		    const char *name, time_t mtime);
583fe401a5SEd Maste static void	create_symtab_entry(struct bsdar *bsdar, Elf *e);
593fe401a5SEd Maste static void	free_obj(struct ar_obj *obj);
603fe401a5SEd Maste static void	insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
613fe401a5SEd Maste 		    struct ar_obj *pos);
623fe401a5SEd Maste static void	read_objs(struct bsdar *bsdar, const char *archive,
633fe401a5SEd Maste 		    int checkargv);
643fe401a5SEd Maste static void	write_cleanup(struct bsdar *bsdar);
653fe401a5SEd Maste static void	write_data(struct bsdar *bsdar, struct archive *a,
663fe401a5SEd Maste 		    const void *buf, size_t s);
673fe401a5SEd Maste static void	write_objs(struct bsdar *bsdar);
683fe401a5SEd Maste 
693fe401a5SEd Maste /*
703fe401a5SEd Maste  * Create an object from a file, and return the created object
713fe401a5SEd Maste  * descriptor.  Return NULL if either an error occurs, or if the '-u'
723fe401a5SEd Maste  * option was specified and the member is not newer than the existing
733fe401a5SEd Maste  * one in the archive.
743fe401a5SEd Maste  */
753fe401a5SEd Maste static struct ar_obj *
create_obj_from_file(struct bsdar * bsdar,const char * name,time_t mtime)763fe401a5SEd Maste create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime)
773fe401a5SEd Maste {
783fe401a5SEd Maste 	struct ar_obj		*obj;
793fe401a5SEd Maste 	struct stat		 sb;
803fe401a5SEd Maste 	const char		*bname;
813fe401a5SEd Maste 	char			*tmpname;
823fe401a5SEd Maste 	int			fd;
833fe401a5SEd Maste 
843fe401a5SEd Maste 	if (name == NULL)
853fe401a5SEd Maste 		return (NULL);
863fe401a5SEd Maste 
873fe401a5SEd Maste 	obj = malloc(sizeof(struct ar_obj));
883fe401a5SEd Maste 	if (obj == NULL)
893fe401a5SEd Maste 		bsdar_errc(bsdar, errno, "malloc failed");
903fe401a5SEd Maste 
913fe401a5SEd Maste 	obj->elf = NULL;
923fe401a5SEd Maste 
933fe401a5SEd Maste 	if ((fd = open(name, O_RDONLY, 0)) < 0) {
943fe401a5SEd Maste 		bsdar_warnc(bsdar, errno, "can't open file: %s", name);
953fe401a5SEd Maste 		free(obj);
963fe401a5SEd Maste 		return (NULL);
973fe401a5SEd Maste 	}
983fe401a5SEd Maste 
993fe401a5SEd Maste 	tmpname = strdup(name);
1003fe401a5SEd Maste 	if ((bname = basename(tmpname)) == NULL)
1013fe401a5SEd Maste 		bsdar_errc(bsdar, errno, "basename failed");
1023fe401a5SEd Maste 	if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) {
1033fe401a5SEd Maste 		if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL)
1043fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "malloc failed");
1053fe401a5SEd Maste 		(void)strncpy(obj->name, bname, _TRUNCATE_LEN);
1063fe401a5SEd Maste 		obj->name[_TRUNCATE_LEN] = '\0';
1073fe401a5SEd Maste 	} else
1083fe401a5SEd Maste 		if ((obj->name = strdup(bname)) == NULL)
1093fe401a5SEd Maste 		    bsdar_errc(bsdar, errno, "strdup failed");
1103fe401a5SEd Maste 	free(tmpname);
1113fe401a5SEd Maste 
1123fe401a5SEd Maste 	if (fstat(fd, &sb) < 0) {
1133fe401a5SEd Maste 		bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name);
1143fe401a5SEd Maste 		goto giveup;
1153fe401a5SEd Maste 	}
1163fe401a5SEd Maste 	if (!S_ISREG(sb.st_mode)) {
1173fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name);
1183fe401a5SEd Maste 		goto giveup;
1193fe401a5SEd Maste 	}
1203fe401a5SEd Maste 
1213fe401a5SEd Maste 	if (sb.st_dev == bsdar->ar_dev && sb.st_ino == bsdar->ar_ino) {
1223fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "cannot add archive \"%s\" to itself",
1233fe401a5SEd Maste 		    obj->name);
1243fe401a5SEd Maste 		goto giveup;
1253fe401a5SEd Maste 	}
1263fe401a5SEd Maste 
1273fe401a5SEd Maste 	/*
1283fe401a5SEd Maste 	 * If the '-u' option is specified and member is not newer
1293fe401a5SEd Maste 	 * than the existing one, we should not replace the member.
1303fe401a5SEd Maste 	 * However, if mtime == 0, i.e., if nonexistent members are to
1313fe401a5SEd Maste 	 * be forcibly replaced, then the '-u' option is to be ignored.
1323fe401a5SEd Maste 	 */
1333fe401a5SEd Maste 	if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime)
1343fe401a5SEd Maste 		goto giveup;
1353fe401a5SEd Maste 
1363fe401a5SEd Maste 	/*
1373fe401a5SEd Maste 	 * When the '-D' option is specified, the mtime and UID/GID of
1383fe401a5SEd Maste 	 * the member will be set to 0, and the file mode will be set
1393fe401a5SEd Maste 	 * to 644. This ensures that checksums will match for two
1403fe401a5SEd Maste 	 * archives containing identical content.
1413fe401a5SEd Maste 	 */
1423fe401a5SEd Maste 	if (bsdar->options & AR_D) {
1433fe401a5SEd Maste 		obj->uid = 0;
1443fe401a5SEd Maste 		obj->gid = 0;
1453fe401a5SEd Maste 		obj->mtime = 0;
1463fe401a5SEd Maste 		obj->md = S_IFREG | 0644;
1473fe401a5SEd Maste 	} else {
1483fe401a5SEd Maste 		obj->uid = sb.st_uid;
1493fe401a5SEd Maste 		obj->gid = sb.st_gid;
1503fe401a5SEd Maste 		obj->mtime = sb.st_mtime;
1513fe401a5SEd Maste 		obj->md = sb.st_mode;
1523fe401a5SEd Maste 	}
1533fe401a5SEd Maste 	obj->size = sb.st_size;
1543fe401a5SEd Maste 	obj->dev = sb.st_dev;
1553fe401a5SEd Maste 	obj->ino = sb.st_ino;
1563fe401a5SEd Maste 
1573fe401a5SEd Maste 	if (obj->size == 0) {
1583fe401a5SEd Maste 		return (obj);
1593fe401a5SEd Maste 	}
1603fe401a5SEd Maste 
1613fe401a5SEd Maste 	if ((obj->elf = elf_open(fd)) == NULL) {
1623fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "file initialization failed for %s: %s",
1633fe401a5SEd Maste 		    obj->name, elf_errmsg(-1));
1643fe401a5SEd Maste 		goto giveup;
1653fe401a5SEd Maste 	}
1663fe401a5SEd Maste 
1673fe401a5SEd Maste 	/*
1683fe401a5SEd Maste 	 * Read the object fully into memory and close its file
1693fe401a5SEd Maste 	 * descriptor.
1703fe401a5SEd Maste 	 */
1713fe401a5SEd Maste 	if (elf_cntl(obj->elf, ELF_C_FDREAD) < 0) {
1723fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "%s could not be read in: %s",
1733fe401a5SEd Maste 		    obj->name, elf_errmsg(-1));
1743fe401a5SEd Maste 		goto giveup;
1753fe401a5SEd Maste 	}
1763fe401a5SEd Maste 
1773fe401a5SEd Maste 	if (close(fd) < 0)
1783fe401a5SEd Maste 		bsdar_errc(bsdar, errno, "close failed: %s",
1793fe401a5SEd Maste 		    obj->name);
1803fe401a5SEd Maste 
1813fe401a5SEd Maste 	return (obj);
1823fe401a5SEd Maste 
1833fe401a5SEd Maste giveup:
1843fe401a5SEd Maste 	if (obj->elf)
1853fe401a5SEd Maste 		elf_end(obj->elf);
1863fe401a5SEd Maste 
1873fe401a5SEd Maste 	if (close(fd) < 0)
1883fe401a5SEd Maste 		bsdar_errc(bsdar, errno, "close failed: %s",
1893fe401a5SEd Maste 		    obj->name);
1903fe401a5SEd Maste 	free(obj->name);
1913fe401a5SEd Maste 	free(obj);
1923fe401a5SEd Maste 	return (NULL);
1933fe401a5SEd Maste }
1943fe401a5SEd Maste 
1953fe401a5SEd Maste /*
1963fe401a5SEd Maste  * Free an object and its associated allocations.
1973fe401a5SEd Maste  */
1983fe401a5SEd Maste static void
free_obj(struct ar_obj * obj)1993fe401a5SEd Maste free_obj(struct ar_obj *obj)
2003fe401a5SEd Maste {
2013fe401a5SEd Maste 	if (obj->elf)
2023fe401a5SEd Maste 		elf_end(obj->elf);
2033fe401a5SEd Maste 
2043fe401a5SEd Maste 	free(obj->name);
2053fe401a5SEd Maste 	free(obj);
2063fe401a5SEd Maste }
2073fe401a5SEd Maste 
2083fe401a5SEd Maste /*
2093fe401a5SEd Maste  * Insert an object into a list, either before/after the 'pos' obj or
2103fe401a5SEd Maste  * at the end of the list.
2113fe401a5SEd Maste  */
2123fe401a5SEd Maste static void
insert_obj(struct bsdar * bsdar,struct ar_obj * obj,struct ar_obj * pos)2133fe401a5SEd Maste insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos)
2143fe401a5SEd Maste {
2153fe401a5SEd Maste 	if (obj == NULL)
2163fe401a5SEd Maste 		bsdar_errc(bsdar, 0, "try to insert a null obj");
2173fe401a5SEd Maste 
2183fe401a5SEd Maste 	if (pos == NULL || obj == pos)
2193fe401a5SEd Maste 		/*
2203fe401a5SEd Maste 		 * If the object to move happens to be the position
2213fe401a5SEd Maste 		 * obj, or if there is no position obj, move the
2223fe401a5SEd Maste 		 * object to the end.
2233fe401a5SEd Maste 		 */
2243fe401a5SEd Maste 		goto tail;
2253fe401a5SEd Maste 
2263fe401a5SEd Maste 	if (bsdar->options & AR_B) {
2273fe401a5SEd Maste 		TAILQ_INSERT_BEFORE(pos, obj, objs);
2283fe401a5SEd Maste 		return;
2293fe401a5SEd Maste 	}
2303fe401a5SEd Maste 	if (bsdar->options & AR_A) {
2313fe401a5SEd Maste 		TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs);
2323fe401a5SEd Maste 		return;
2333fe401a5SEd Maste 	}
2343fe401a5SEd Maste 
2353fe401a5SEd Maste tail:
2363fe401a5SEd Maste 	TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
2373fe401a5SEd Maste 
2383fe401a5SEd Maste }
2393fe401a5SEd Maste 
2403fe401a5SEd Maste /*
2413fe401a5SEd Maste  * Read objects from archive into the 'v_obj' list. Note that
2423fe401a5SEd Maste  * 'checkargv' is set when read_objs() is used to read objects from
2433fe401a5SEd Maste  * the target of 'ADDLIB' command in ar script mode; in this case the
2443fe401a5SEd Maste  * 'argv' array specifies the members that 'ADDLIB' is to operate on.
2453fe401a5SEd Maste  */
2463fe401a5SEd Maste static void
read_objs(struct bsdar * bsdar,const char * archive,int checkargv)2473fe401a5SEd Maste read_objs(struct bsdar *bsdar, const char *archive, int checkargv)
2483fe401a5SEd Maste {
2493fe401a5SEd Maste 	struct archive		 *a;
2503fe401a5SEd Maste 	struct archive_entry	 *entry;
2513fe401a5SEd Maste 	struct ar_obj		 *obj;
2523fe401a5SEd Maste 	const char		 *name;
2533fe401a5SEd Maste 	const char		 *bname;
2543fe401a5SEd Maste 	char			 *buff;
2553fe401a5SEd Maste 	char			**av;
2563fe401a5SEd Maste 	size_t			  size;
2573fe401a5SEd Maste 	int			  i, r, find;
2583fe401a5SEd Maste 
2593fe401a5SEd Maste 	if ((a = archive_read_new()) == NULL)
2603fe401a5SEd Maste 		bsdar_errc(bsdar, 0, "archive_read_new failed");
2613fe401a5SEd Maste 	archive_read_support_format_ar(a);
2623fe401a5SEd Maste 	AC(archive_read_open_filename(a, archive, DEF_BLKSZ));
2633fe401a5SEd Maste 	for (;;) {
2643fe401a5SEd Maste 		r = archive_read_next_header(a, &entry);
2653fe401a5SEd Maste 		if (r == ARCHIVE_FATAL)
2663fe401a5SEd Maste 			bsdar_errc(bsdar, 0, "%s", archive_error_string(a));
2673fe401a5SEd Maste 		if (r == ARCHIVE_EOF)
2683fe401a5SEd Maste 			break;
2693fe401a5SEd Maste 		if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
2703fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
2713fe401a5SEd Maste 		if (r == ARCHIVE_RETRY) {
2723fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "Retrying...");
2733fe401a5SEd Maste 			continue;
2743fe401a5SEd Maste 		}
2753fe401a5SEd Maste 
2763fe401a5SEd Maste 		name = archive_entry_pathname(entry);
2773fe401a5SEd Maste 
2783fe401a5SEd Maste 		/*
2793fe401a5SEd Maste 		 * Skip pseudo members.
2803fe401a5SEd Maste 		 */
2813fe401a5SEd Maste 		if (bsdar_is_pseudomember(bsdar, name))
2823fe401a5SEd Maste 			continue;
2833fe401a5SEd Maste 
2843fe401a5SEd Maste 		/*
2853fe401a5SEd Maste 		 * If 'checkargv' is set, only read those members
2863fe401a5SEd Maste 		 * specified in argv.
2873fe401a5SEd Maste 		 */
2883fe401a5SEd Maste 		if (checkargv && bsdar->argc > 0) {
2893fe401a5SEd Maste 			find = 0;
2903fe401a5SEd Maste 			for(i = 0; i < bsdar->argc; i++) {
2913fe401a5SEd Maste 				av = &bsdar->argv[i];
2923fe401a5SEd Maste 				if (*av == NULL)
2933fe401a5SEd Maste 					continue;
2943fe401a5SEd Maste 				if ((bname = basename(*av)) == NULL)
2953fe401a5SEd Maste 					bsdar_errc(bsdar, errno,
2963fe401a5SEd Maste 					    "basename failed");
2973fe401a5SEd Maste 				if (strcmp(bname, name) != 0)
2983fe401a5SEd Maste 					continue;
2993fe401a5SEd Maste 
3003fe401a5SEd Maste 				*av = NULL;
3013fe401a5SEd Maste 				find = 1;
3023fe401a5SEd Maste 				break;
3033fe401a5SEd Maste 			}
3043fe401a5SEd Maste 			if (!find)
3053fe401a5SEd Maste 				continue;
3063fe401a5SEd Maste 		}
3073fe401a5SEd Maste 
3083fe401a5SEd Maste 		size = archive_entry_size(entry);
3093fe401a5SEd Maste 
3103fe401a5SEd Maste 		if (size > 0) {
3113fe401a5SEd Maste 			if ((buff = malloc(size)) == NULL)
3123fe401a5SEd Maste 				bsdar_errc(bsdar, errno, "malloc failed");
3133fe401a5SEd Maste 			if (archive_read_data(a, buff, size) != (ssize_t)size) {
3143fe401a5SEd Maste 				bsdar_warnc(bsdar, 0, "%s",
3153fe401a5SEd Maste 				    archive_error_string(a));
3163fe401a5SEd Maste 				free(buff);
3173fe401a5SEd Maste 				continue;
3183fe401a5SEd Maste 			}
3193fe401a5SEd Maste 		} else
3203fe401a5SEd Maste 			buff = NULL;
3213fe401a5SEd Maste 
3223fe401a5SEd Maste 		obj = malloc(sizeof(struct ar_obj));
3233fe401a5SEd Maste 		if (obj == NULL)
3243fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "malloc failed");
3253fe401a5SEd Maste 		obj->elf = NULL;
3263fe401a5SEd Maste 		if (buff) {
3273fe401a5SEd Maste 			obj->elf = elf_openmemory(buff, size);
3283fe401a5SEd Maste 			if (obj->elf == NULL) {
3293fe401a5SEd Maste 				bsdar_warnc(bsdar, 0, "elf_openmemory() "
3303fe401a5SEd Maste 				    "failed for %s: %s", name,
3313fe401a5SEd Maste 				    elf_errmsg(-1));
3323fe401a5SEd Maste 				free(buff);
3333fe401a5SEd Maste 				free(obj);
3343fe401a5SEd Maste 				continue;
3353fe401a5SEd Maste 			}
3363fe401a5SEd Maste 		}
3373fe401a5SEd Maste 		if ((obj->name = strdup(name)) == NULL)
3383fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "strdup failed");
3393fe401a5SEd Maste 		obj->size = size;
3403fe401a5SEd Maste 		obj->uid = archive_entry_uid(entry);
3413fe401a5SEd Maste 		obj->gid = archive_entry_gid(entry);
3423fe401a5SEd Maste 		obj->md = archive_entry_mode(entry);
3433fe401a5SEd Maste 		obj->mtime = archive_entry_mtime(entry);
3443fe401a5SEd Maste 		obj->dev = 0;
3453fe401a5SEd Maste 		obj->ino = 0;
3463fe401a5SEd Maste 
3473fe401a5SEd Maste 		TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
3483fe401a5SEd Maste 	}
3493fe401a5SEd Maste 	AC(archive_read_close(a));
3503fe401a5SEd Maste 	ACV(archive_read_free(a));
3513fe401a5SEd Maste }
3523fe401a5SEd Maste 
3533fe401a5SEd Maste /*
3543fe401a5SEd Maste  * Write an archive.
355*ae500c1fSEd Maste  *
356*ae500c1fSEd Maste  * Returns EXIT_SUCCESS if the write succeeded or EXIT_FAILURE otherwise.
3573fe401a5SEd Maste  */
358*ae500c1fSEd Maste int
ar_write_archive(struct bsdar * bsdar,int mode)3593fe401a5SEd Maste ar_write_archive(struct bsdar *bsdar, int mode)
3603fe401a5SEd Maste {
3613fe401a5SEd Maste 	struct ar_obj		 *nobj, *obj, *obj_temp, *pos;
3623fe401a5SEd Maste 	struct stat		  sb;
3633fe401a5SEd Maste 	const char		 *bname;
3643fe401a5SEd Maste 	char			**av;
365*ae500c1fSEd Maste 	int			  exitcode, i;
3663fe401a5SEd Maste 
3673fe401a5SEd Maste 	TAILQ_INIT(&bsdar->v_obj);
368*ae500c1fSEd Maste 	exitcode = EXIT_SUCCESS;
3693fe401a5SEd Maste 	nobj = NULL;
3703fe401a5SEd Maste 	pos = NULL;
3713fe401a5SEd Maste 	memset(&sb, 0, sizeof(sb));
3723fe401a5SEd Maste 
3733fe401a5SEd Maste 	assert(mode == 'A' || mode == 'd' || mode == 'm' || mode == 'q' ||
3743fe401a5SEd Maste 	    mode == 'r' || mode == 's');
3753fe401a5SEd Maste 
3763fe401a5SEd Maste 	/*
3773fe401a5SEd Maste 	 * Test if the specified archive exists, to determine
3783fe401a5SEd Maste 	 * whether we are creating a new archive.
3793fe401a5SEd Maste 	 */
3803fe401a5SEd Maste 	if (stat(bsdar->filename, &sb) != 0) {
3813fe401a5SEd Maste 		if (errno != ENOENT) {
382*ae500c1fSEd Maste 			bsdar_warnc(bsdar, errno, "stat %s failed",
3833fe401a5SEd Maste 			    bsdar->filename);
384*ae500c1fSEd Maste 			return (EXIT_FAILURE);
3853fe401a5SEd Maste 		}
3863fe401a5SEd Maste 
3873fe401a5SEd Maste 		/* We do not create archive in mode 'd', 'm' and 's'.  */
3883fe401a5SEd Maste 		if (mode != 'r' && mode != 'q') {
3893fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "%s: no such file",
3903fe401a5SEd Maste 			    bsdar->filename);
391*ae500c1fSEd Maste 			return (EXIT_FAILURE);
3923fe401a5SEd Maste 		}
3933fe401a5SEd Maste 
3943fe401a5SEd Maste 		/* Issue a message if the '-c' option was not specified. */
3953fe401a5SEd Maste 		if (!(bsdar->options & AR_C))
3963fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
3973fe401a5SEd Maste 		goto new_archive;
3983fe401a5SEd Maste 	}
3993fe401a5SEd Maste 
4003fe401a5SEd Maste 	bsdar->ar_dev = sb.st_dev;
4013fe401a5SEd Maste 	bsdar->ar_ino = sb.st_ino;
4023fe401a5SEd Maste 
4033fe401a5SEd Maste 	/*
4043fe401a5SEd Maste 	 * First read members from the existing archive.
4053fe401a5SEd Maste 	 */
4063fe401a5SEd Maste 	read_objs(bsdar, bsdar->filename, 0);
4073fe401a5SEd Maste 
4083fe401a5SEd Maste 	/*
4093fe401a5SEd Maste 	 * For mode 's', no member will be moved, deleted or replaced.
4103fe401a5SEd Maste 	 */
4113fe401a5SEd Maste 	if (mode == 's')
4123fe401a5SEd Maste 		goto write_objs;
4133fe401a5SEd Maste 
4143fe401a5SEd Maste 	/*
4153fe401a5SEd Maste 	 * For mode 'q', we don't need to adjust existing members either.
4163fe401a5SEd Maste 	 * Also, -a, -b and -i are ignored in this mode. New members are
4173fe401a5SEd Maste 	 * always inserted at tail.
4183fe401a5SEd Maste 	 */
4193fe401a5SEd Maste 	if (mode == 'q')
4203fe401a5SEd Maste 		goto new_archive;
4213fe401a5SEd Maste 
4223fe401a5SEd Maste 	/*
4233fe401a5SEd Maste 	 * Mode 'A' adds the contents of another archive to the tail
4243fe401a5SEd Maste 	 * of current archive. Note that mode 'A' is a special mode
4253fe401a5SEd Maste 	 * for the 'ADDLIB' command in ar's script mode. Currently
4263fe401a5SEd Maste 	 * there is no option that invokes this function from ar's
4273fe401a5SEd Maste 	 * command line.
4283fe401a5SEd Maste 	 */
4293fe401a5SEd Maste 	if (mode == 'A') {
4303fe401a5SEd Maste 		/*
4313fe401a5SEd Maste 		 * Read objects from the target archive of the
4323fe401a5SEd Maste 		 * 'ADDLIB' command.  If there are members specified in
4333fe401a5SEd Maste 		 * 'argv', read those members only, otherwise the
4343fe401a5SEd Maste 		 * entire archive will be read.
4353fe401a5SEd Maste 		 */
4363fe401a5SEd Maste 		read_objs(bsdar, bsdar->addlib, 1);
4373fe401a5SEd Maste 		goto write_objs;
4383fe401a5SEd Maste 	}
4393fe401a5SEd Maste 
4403fe401a5SEd Maste 	/*
4413fe401a5SEd Maste 	 * Try to find the position member specified by user.
4423fe401a5SEd Maste 	 */
4433fe401a5SEd Maste 	if (bsdar->options & AR_A || bsdar->options & AR_B) {
4443fe401a5SEd Maste 		TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
4453fe401a5SEd Maste 			if (strcmp(obj->name, bsdar->posarg) == 0) {
4463fe401a5SEd Maste 				pos = obj;
4473fe401a5SEd Maste 				break;
4483fe401a5SEd Maste 			}
4493fe401a5SEd Maste 		}
4503fe401a5SEd Maste 
4513fe401a5SEd Maste 		/*
4523fe401a5SEd Maste 		 * If we cannot find the position specified by the
4533fe401a5SEd Maste 		 * user, silently insert objects at the tail of the
4543fe401a5SEd Maste 		 * list.
4553fe401a5SEd Maste 		 */
4563fe401a5SEd Maste 		if (pos == NULL)
4573fe401a5SEd Maste 			bsdar->options &= ~(AR_A | AR_B);
4583fe401a5SEd Maste 	}
4593fe401a5SEd Maste 
4603fe401a5SEd Maste 	for (i = 0; i < bsdar->argc; i++) {
4613fe401a5SEd Maste 		av = &bsdar->argv[i];
4623fe401a5SEd Maste 
4633fe401a5SEd Maste 		TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
4643fe401a5SEd Maste 			if ((bname = basename(*av)) == NULL)
4653fe401a5SEd Maste 				bsdar_errc(bsdar, errno, "basename failed");
4663fe401a5SEd Maste 			if (bsdar->options & AR_TR) {
4673fe401a5SEd Maste 				if (strncmp(bname, obj->name, _TRUNCATE_LEN))
4683fe401a5SEd Maste 					continue;
4693fe401a5SEd Maste 			} else
4703fe401a5SEd Maste 				if (strcmp(bname, obj->name) != 0)
4713fe401a5SEd Maste 					continue;
4723fe401a5SEd Maste 
4733fe401a5SEd Maste 			if (mode == 'r') {
4743fe401a5SEd Maste 				/*
4753fe401a5SEd Maste 				 * If the new member should not
4763fe401a5SEd Maste 				 * replace the old one, skip it.
4773fe401a5SEd Maste 				 */
4783fe401a5SEd Maste 				nobj = create_obj_from_file(bsdar, *av,
4793fe401a5SEd Maste 				    obj->mtime);
480*ae500c1fSEd Maste 				if (nobj == NULL) {
481*ae500c1fSEd Maste 					exitcode = EXIT_FAILURE;
4823fe401a5SEd Maste 					goto skip_obj;
4833fe401a5SEd Maste 				}
484*ae500c1fSEd Maste 			}
4853fe401a5SEd Maste 
4863fe401a5SEd Maste 			if (bsdar->options & AR_V)
4873fe401a5SEd Maste 				(void)fprintf(bsdar->output, "%c - %s\n",
4883fe401a5SEd Maste 				    mode, *av);
4893fe401a5SEd Maste 
4903fe401a5SEd Maste 			TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
4913fe401a5SEd Maste 			if (mode == 'd' || mode == 'r')
4923fe401a5SEd Maste 				free_obj(obj);
4933fe401a5SEd Maste 
4943fe401a5SEd Maste 			if (mode == 'm')
4953fe401a5SEd Maste 				insert_obj(bsdar, obj, pos);
4963fe401a5SEd Maste 			if (mode == 'r')
4973fe401a5SEd Maste 				insert_obj(bsdar, nobj, pos);
4983fe401a5SEd Maste 
4993fe401a5SEd Maste 		skip_obj:
5003fe401a5SEd Maste 			*av = NULL;
5013fe401a5SEd Maste 			break;
5023fe401a5SEd Maste 		}
5033fe401a5SEd Maste 
5043fe401a5SEd Maste 	}
5053fe401a5SEd Maste 
5063fe401a5SEd Maste new_archive:
5073fe401a5SEd Maste 	/*
5083fe401a5SEd Maste 	 * When operating in mode 'r', directly add the specified
5093fe401a5SEd Maste 	 * objects which do not exist in current archive. When
5103fe401a5SEd Maste 	 * operating in mode 'q', all objects specified by the command
5113fe401a5SEd Maste 	 * line args are appended to the archive, without checking
5123fe401a5SEd Maste 	 * existing members in the archive.
5133fe401a5SEd Maste 	 */
5143fe401a5SEd Maste 	for (i = 0; i < bsdar->argc; i++) {
5153fe401a5SEd Maste 		av = &bsdar->argv[i];
5163fe401a5SEd Maste 		if (*av != NULL && (mode == 'r' || mode == 'q')) {
5173fe401a5SEd Maste 			nobj = create_obj_from_file(bsdar, *av, 0);
518*ae500c1fSEd Maste 			if (nobj == NULL) {
519*ae500c1fSEd Maste 				exitcode = EXIT_FAILURE;
520*ae500c1fSEd Maste 				*av = NULL;
521*ae500c1fSEd Maste 				continue;
522*ae500c1fSEd Maste 			}
5233fe401a5SEd Maste 			insert_obj(bsdar, nobj, pos);
524*ae500c1fSEd Maste 			if (bsdar->options & AR_V)
5253fe401a5SEd Maste 				(void)fprintf(bsdar->output, "a - %s\n", *av);
5263fe401a5SEd Maste 			*av = NULL;
5273fe401a5SEd Maste 		}
5283fe401a5SEd Maste 	}
5293fe401a5SEd Maste 
5303fe401a5SEd Maste write_objs:
5313fe401a5SEd Maste 	write_objs(bsdar);
5323fe401a5SEd Maste 	write_cleanup(bsdar);
533*ae500c1fSEd Maste 
534*ae500c1fSEd Maste 	return (exitcode);
5353fe401a5SEd Maste }
5363fe401a5SEd Maste 
5373fe401a5SEd Maste /*
5383fe401a5SEd Maste  * Release memory.
5393fe401a5SEd Maste  */
5403fe401a5SEd Maste static void
write_cleanup(struct bsdar * bsdar)5413fe401a5SEd Maste write_cleanup(struct bsdar *bsdar)
5423fe401a5SEd Maste {
5433fe401a5SEd Maste 	struct ar_obj		*obj, *obj_temp;
5443fe401a5SEd Maste 
5453fe401a5SEd Maste 	TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
5463fe401a5SEd Maste 		TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
5473fe401a5SEd Maste 		free_obj(obj);
5483fe401a5SEd Maste 	}
5493fe401a5SEd Maste 
5503fe401a5SEd Maste 	free(bsdar->as);
5513fe401a5SEd Maste 	free(bsdar->s_so);
5523fe401a5SEd Maste 	free(bsdar->s_sn);
5533fe401a5SEd Maste 	bsdar->as = NULL;
5543fe401a5SEd Maste 	bsdar->s_so = NULL;
5553fe401a5SEd Maste 	bsdar->s_sn = NULL;
5563fe401a5SEd Maste }
5573fe401a5SEd Maste 
5583fe401a5SEd Maste /*
5593fe401a5SEd Maste  * Wrapper for archive_write_data().
5603fe401a5SEd Maste  */
5613fe401a5SEd Maste static void
write_data(struct bsdar * bsdar,struct archive * a,const void * buf,size_t s)5623fe401a5SEd Maste write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s)
5633fe401a5SEd Maste {
5643fe401a5SEd Maste 	if (archive_write_data(a, buf, s) != (ssize_t)s)
5653fe401a5SEd Maste 		bsdar_errc(bsdar, 0, "%s", archive_error_string(a));
5663fe401a5SEd Maste }
5673fe401a5SEd Maste 
5683fe401a5SEd Maste /*
5693fe401a5SEd Maste  * Compute the size of the symbol table for an archive.
5703fe401a5SEd Maste  */
5713fe401a5SEd Maste static size_t
bsdar_symtab_size(struct bsdar * bsdar)5723fe401a5SEd Maste bsdar_symtab_size(struct bsdar *bsdar)
5733fe401a5SEd Maste {
5743fe401a5SEd Maste 	size_t sz;
5753fe401a5SEd Maste 
5763fe401a5SEd Maste 	if (bsdar->options & AR_BSD) {
5773fe401a5SEd Maste 		/*
5783fe401a5SEd Maste 		 * A BSD style symbol table has two parts.
5793fe401a5SEd Maste 		 * Each part is preceded by its size in bytes,
5803fe401a5SEd Maste 		 * encoded as a C 'long'.  In the first part,
5813fe401a5SEd Maste 		 * there are 's_cnt' entries, each entry being
5823fe401a5SEd Maste 		 * 2 'long's in size.  The second part
5833fe401a5SEd Maste 		 * contains a string table.
5843fe401a5SEd Maste 		 */
5853fe401a5SEd Maste 		sz = 2 * sizeof(long) + (bsdar->s_cnt * 2 * sizeof(long)) +
5863fe401a5SEd Maste 		    bsdar->s_sn_sz;
5873fe401a5SEd Maste 	} else {
5883fe401a5SEd Maste 		/*
5893fe401a5SEd Maste 		 * An SVR4 style symbol table comprises of a 32 bit
5903fe401a5SEd Maste 		 * number holding the number of entries, followed by
5913fe401a5SEd Maste 		 * that many 32-bit offsets, followed by a string
5923fe401a5SEd Maste 		 * table.
5933fe401a5SEd Maste 		 */
5943fe401a5SEd Maste 		sz = sizeof(uint32_t) + bsdar->s_cnt * sizeof(uint32_t) +
5953fe401a5SEd Maste 		    bsdar->s_sn_sz;
5963fe401a5SEd Maste 	}
5973fe401a5SEd Maste 
5983fe401a5SEd Maste 	return (sz);
5993fe401a5SEd Maste }
6003fe401a5SEd Maste 
6013fe401a5SEd Maste static void
write_svr4_symtab_entry(struct bsdar * bsdar,struct archive * a)6023fe401a5SEd Maste write_svr4_symtab_entry(struct bsdar *bsdar, struct archive *a)
6033fe401a5SEd Maste {
6043fe401a5SEd Maste 	int		nr;
6053fe401a5SEd Maste 	uint32_t	i;
6063fe401a5SEd Maste 
6073fe401a5SEd Maste 	/* Translate offsets to big-endian form. */
6083fe401a5SEd Maste 	for (i = 0; i < bsdar->s_cnt; i++)
6093fe401a5SEd Maste 		bsdar->s_so[i] = htobe32(bsdar->s_so[i]);
6103fe401a5SEd Maste 
6113fe401a5SEd Maste 	nr = htobe32(bsdar->s_cnt);
6123fe401a5SEd Maste 	write_data(bsdar, a, &nr, sizeof(uint32_t));
6133fe401a5SEd Maste 	write_data(bsdar, a, bsdar->s_so, sizeof(uint32_t) *
6143fe401a5SEd Maste 	    bsdar->s_cnt);
6153fe401a5SEd Maste 	write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
6163fe401a5SEd Maste }
6173fe401a5SEd Maste 
6183fe401a5SEd Maste static void
write_bsd_symtab_entry(struct bsdar * bsdar,struct archive * a)6193fe401a5SEd Maste write_bsd_symtab_entry(struct bsdar *bsdar, struct archive *a)
6203fe401a5SEd Maste {
6213fe401a5SEd Maste 	long br_sz, br_off, br_strx;
6223fe401a5SEd Maste 	char *s;
6233fe401a5SEd Maste 	uint32_t i;
6243fe401a5SEd Maste 
6253fe401a5SEd Maste 	/*
6263fe401a5SEd Maste 	 * Write out the size in the byte of the array of 'ranlib'
6273fe401a5SEd Maste 	 * descriptors to follow.
6283fe401a5SEd Maste 	 */
6293fe401a5SEd Maste 
6303fe401a5SEd Maste 	br_sz = (long) (bsdar->s_cnt * 2 * sizeof(long));
6313fe401a5SEd Maste 	write_data(bsdar, a, &br_sz, sizeof(long));
6323fe401a5SEd Maste 
6333fe401a5SEd Maste 	/*
6343fe401a5SEd Maste 	 * Write out the array of 'ranlib' descriptors.  Each
6353fe401a5SEd Maste 	 * descriptor comprises of (a) an offset into the following
6363fe401a5SEd Maste 	 * string table and (b) a file offset to the relevant member.
6373fe401a5SEd Maste 	 */
6383fe401a5SEd Maste 	for (i = 0, s = bsdar->s_sn; i < bsdar->s_cnt; i++) {
6393fe401a5SEd Maste 		br_strx = (long) (s - bsdar->s_sn);
6403fe401a5SEd Maste 		br_off = (long) bsdar->s_so[i];
6413fe401a5SEd Maste 		write_data(bsdar, a, &br_strx, sizeof(long));
6423fe401a5SEd Maste 		write_data(bsdar, a, &br_off, sizeof(long));
6433fe401a5SEd Maste 
6443fe401a5SEd Maste 		/* Find the start of the next symbol in the string table. */
6453fe401a5SEd Maste 		while (*s++ != '\0')
6463fe401a5SEd Maste 			;
6473fe401a5SEd Maste 	}
6483fe401a5SEd Maste 
6493fe401a5SEd Maste 	/*
6503fe401a5SEd Maste 	 * Write out the size of the string table as a 'long',
6513fe401a5SEd Maste 	 * followed by the string table itself.
6523fe401a5SEd Maste 	 */
6533fe401a5SEd Maste 	br_sz = (long) bsdar->s_sn_sz;
6543fe401a5SEd Maste 	write_data(bsdar, a, &br_sz, sizeof(long));
6553fe401a5SEd Maste 	write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
6563fe401a5SEd Maste }
6573fe401a5SEd Maste 
6583fe401a5SEd Maste 
6593fe401a5SEd Maste /*
6603fe401a5SEd Maste  * Write the resulting archive members.
6613fe401a5SEd Maste  */
6623fe401a5SEd Maste static void
write_objs(struct bsdar * bsdar)6633fe401a5SEd Maste write_objs(struct bsdar *bsdar)
6643fe401a5SEd Maste {
6653fe401a5SEd Maste 	struct ar_obj		*obj;
6663fe401a5SEd Maste 	struct archive		*a;
6673fe401a5SEd Maste 	struct archive_entry	*entry;
6683fe401a5SEd Maste 	size_t s_sz;		/* size of archive symbol table. */
6693fe401a5SEd Maste 	size_t pm_sz;		/* size of pseudo members */
6703fe401a5SEd Maste 	size_t namelen;		/* size of member name. */
6713fe401a5SEd Maste 	size_t obj_sz;		/* size of object + extended header. */
6723fe401a5SEd Maste 	int			 i;
6733fe401a5SEd Maste 	char			*buf;
6743fe401a5SEd Maste 	const char		*entry_name;
6753fe401a5SEd Maste 
6763fe401a5SEd Maste 	bsdar->rela_off = 0;
6773fe401a5SEd Maste 
6783fe401a5SEd Maste 	/*
6793fe401a5SEd Maste 	 * Create the archive symbol table and the archive string
6803fe401a5SEd Maste 	 * table, if needed.
6813fe401a5SEd Maste 	 */
6823fe401a5SEd Maste 	TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
6833fe401a5SEd Maste 		if (!(bsdar->options & AR_SS) && obj->elf != NULL)
6843fe401a5SEd Maste 			create_symtab_entry(bsdar, obj->elf);
6853fe401a5SEd Maste 
6863fe401a5SEd Maste 		obj_sz = 0;
6873fe401a5SEd Maste 		namelen = strlen(obj->name);
6883fe401a5SEd Maste 		if (bsdar->options & AR_BSD) {
6893fe401a5SEd Maste 			/* Account for the space used by the file name. */
6903fe401a5SEd Maste 			if (namelen > _MAXNAMELEN_BSD ||
6913fe401a5SEd Maste 			    strchr(obj->name, ' '))
6923fe401a5SEd Maste 				obj_sz += namelen;
6933fe401a5SEd Maste 		} else if (namelen > _MAXNAMELEN_SVR4)
6943fe401a5SEd Maste 			add_to_ar_str_table(bsdar, obj->name);
6953fe401a5SEd Maste 
6963fe401a5SEd Maste 		obj_sz += obj->size; /* add the actual object size  */
6973fe401a5SEd Maste 
6983fe401a5SEd Maste 		/* Roundup the final size and add the header length. */
6993fe401a5SEd Maste 		bsdar->rela_off += _ARHDR_LEN + obj_sz + (obj_sz & 1);
7003fe401a5SEd Maste 	}
7013fe401a5SEd Maste 
7023fe401a5SEd Maste 	/*
7033fe401a5SEd Maste 	 * Pad the symbol name string table. It is treated specially
7043fe401a5SEd Maste 	 * because symbol name table should be padded by a '\0', and
7053fe401a5SEd Maste 	 * not '\n' as for normal members. The size of the 'sn' table
7063fe401a5SEd Maste 	 * includes the pad byte.
7073fe401a5SEd Maste 	 */
7083fe401a5SEd Maste 	if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0)
7093fe401a5SEd Maste 		bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
7103fe401a5SEd Maste 
7113fe401a5SEd Maste 	/*
7123fe401a5SEd Maste 	 * The archive string table is padded by a "\n" like a normal
7133fe401a5SEd Maste 	 * member.  The difference is that the size of archive string
7143fe401a5SEd Maste 	 * table includes the pad byte, while normal members' size
7153fe401a5SEd Maste 	 * fields do not.
7163fe401a5SEd Maste 	 */
7173fe401a5SEd Maste 	if (bsdar->as != NULL && bsdar->as_sz % 2 != 0)
7183fe401a5SEd Maste 		bsdar->as[bsdar->as_sz++] = '\n';
7193fe401a5SEd Maste 
7203fe401a5SEd Maste 	/*
7213fe401a5SEd Maste 	 * If there is a symbol table, calculate the size of pseudo
7223fe401a5SEd Maste 	 * members, and convert previously stored relative offsets to
7233fe401a5SEd Maste 	 * absolute ones.
7243fe401a5SEd Maste 	 *
7253fe401a5SEd Maste 	 * absolute_offset = relative_offset + size_of_pseudo_members)
7263fe401a5SEd Maste 	 */
7273fe401a5SEd Maste 
7283fe401a5SEd Maste 	s_sz = bsdar_symtab_size(bsdar);
7293fe401a5SEd Maste 	if (bsdar->s_cnt != 0) {
7303fe401a5SEd Maste 		pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
7313fe401a5SEd Maste 		if (bsdar->as != NULL) /* SVR4 archives only */
7323fe401a5SEd Maste 			pm_sz += _ARHDR_LEN + bsdar->as_sz;
7333fe401a5SEd Maste 		for (i = 0; (size_t) i < bsdar->s_cnt; i++)
7343fe401a5SEd Maste 			bsdar->s_so[i] = bsdar->s_so[i] + pm_sz;
7353fe401a5SEd Maste 	}
7363fe401a5SEd Maste 
7373fe401a5SEd Maste 	if ((a = archive_write_new()) == NULL)
7383fe401a5SEd Maste 		bsdar_errc(bsdar, 0, "archive_write_new failed");
7393fe401a5SEd Maste 
7403fe401a5SEd Maste 	if (bsdar->options & AR_BSD)
7413fe401a5SEd Maste 		archive_write_set_format_ar_bsd(a);
7423fe401a5SEd Maste 	else
7433fe401a5SEd Maste 		archive_write_set_format_ar_svr4(a);
7443fe401a5SEd Maste 
7453fe401a5SEd Maste 	AC(archive_write_open_filename(a, bsdar->filename));
7463fe401a5SEd Maste 
7473fe401a5SEd Maste 	/*
7483fe401a5SEd Maste 	 * Write the archive symbol table, if there is one.  If
7493fe401a5SEd Maste 	 * options '-s' was explicitly specified or if we were invoked
7503fe401a5SEd Maste 	 * as 'ranlib', write the symbol table even if it is empty.
7513fe401a5SEd Maste 	 */
7523fe401a5SEd Maste 	if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) ||
7533fe401a5SEd Maste 	    bsdar->options & AR_S) {
7543fe401a5SEd Maste 		if (bsdar->options & AR_BSD)
7553fe401a5SEd Maste 			entry_name = AR_SYMTAB_NAME_BSD;
7563fe401a5SEd Maste 		else
7573fe401a5SEd Maste 			entry_name = AR_SYMTAB_NAME_SVR4;
7583fe401a5SEd Maste 
7593fe401a5SEd Maste 		entry = archive_entry_new();
7603fe401a5SEd Maste 		archive_entry_copy_pathname(entry, entry_name);
7613fe401a5SEd Maste 		if ((bsdar->options & AR_D) == 0)
7623fe401a5SEd Maste 			archive_entry_set_mtime(entry, time(NULL), 0);
7633fe401a5SEd Maste 		archive_entry_set_size(entry, s_sz);
7643fe401a5SEd Maste 		AC(archive_write_header(a, entry));
7653fe401a5SEd Maste 		if (bsdar->options & AR_BSD)
7663fe401a5SEd Maste 			write_bsd_symtab_entry(bsdar, a);
7673fe401a5SEd Maste 		else
7683fe401a5SEd Maste 			write_svr4_symtab_entry(bsdar, a);
7693fe401a5SEd Maste 		archive_entry_free(entry);
7703fe401a5SEd Maste 	}
7713fe401a5SEd Maste 
7723fe401a5SEd Maste 	/* Write the archive string table, if any. */
7733fe401a5SEd Maste 	if (bsdar->as != NULL) {
7743fe401a5SEd Maste 		entry = archive_entry_new();
7753fe401a5SEd Maste 		archive_entry_copy_pathname(entry, AR_STRINGTAB_NAME_SVR4);
7763fe401a5SEd Maste 		archive_entry_set_size(entry, bsdar->as_sz);
7773fe401a5SEd Maste 		AC(archive_write_header(a, entry));
7783fe401a5SEd Maste 		write_data(bsdar, a, bsdar->as, bsdar->as_sz);
7793fe401a5SEd Maste 		archive_entry_free(entry);
7803fe401a5SEd Maste 	}
7813fe401a5SEd Maste 
7823fe401a5SEd Maste 	/* Write normal members. */
7833fe401a5SEd Maste 	TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
7843fe401a5SEd Maste 		if ((buf = elf_rawfile(obj->elf, NULL)) == NULL) {
7853fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "elf_rawfile() failed: %s",
7863fe401a5SEd Maste 			    elf_errmsg(-1));
7873fe401a5SEd Maste 			continue;
7883fe401a5SEd Maste 		}
7893fe401a5SEd Maste 
7903fe401a5SEd Maste 		entry = archive_entry_new();
7913fe401a5SEd Maste 		archive_entry_copy_pathname(entry, obj->name);
7923fe401a5SEd Maste 		archive_entry_set_uid(entry, obj->uid);
7933fe401a5SEd Maste 		archive_entry_set_gid(entry, obj->gid);
7943fe401a5SEd Maste 		archive_entry_set_mode(entry, obj->md);
7953fe401a5SEd Maste 		archive_entry_set_size(entry, obj->size);
7963fe401a5SEd Maste 		archive_entry_set_mtime(entry, obj->mtime, 0);
7973fe401a5SEd Maste 		archive_entry_set_dev(entry, obj->dev);
7983fe401a5SEd Maste 		archive_entry_set_ino(entry, obj->ino);
7993fe401a5SEd Maste 		archive_entry_set_filetype(entry, AE_IFREG);
8003fe401a5SEd Maste 		AC(archive_write_header(a, entry));
8013fe401a5SEd Maste 		write_data(bsdar, a, buf, obj->size);
8023fe401a5SEd Maste 		archive_entry_free(entry);
8033fe401a5SEd Maste 	}
8043fe401a5SEd Maste 
8053fe401a5SEd Maste 	AC(archive_write_close(a));
8063fe401a5SEd Maste 	ACV(archive_write_free(a));
8073fe401a5SEd Maste }
8083fe401a5SEd Maste 
8093fe401a5SEd Maste /*
8103fe401a5SEd Maste  * Extract global symbols from ELF binary members.
8113fe401a5SEd Maste  */
8123fe401a5SEd Maste static void
create_symtab_entry(struct bsdar * bsdar,Elf * e)8133fe401a5SEd Maste create_symtab_entry(struct bsdar *bsdar, Elf *e)
8143fe401a5SEd Maste {
8153fe401a5SEd Maste 	Elf_Scn		*scn;
8163fe401a5SEd Maste 	GElf_Shdr	 shdr;
8173fe401a5SEd Maste 	GElf_Sym	 sym;
8183fe401a5SEd Maste 	Elf_Data	*data;
8193fe401a5SEd Maste 	char		*name;
8203fe401a5SEd Maste 	size_t		 n, shstrndx;
8213fe401a5SEd Maste 	int		 elferr, tabndx, len, i;
8223fe401a5SEd Maste 
8233fe401a5SEd Maste 	if (elf_kind(e) != ELF_K_ELF) {
8243fe401a5SEd Maste 		/* Silently a ignore non-ELF member. */
8253fe401a5SEd Maste 		return;
8263fe401a5SEd Maste 	}
8273fe401a5SEd Maste 	if (elf_getshstrndx(e, &shstrndx) == 0) {
8283fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "elf_getshstrndx failed: %s",
8293fe401a5SEd Maste 		     elf_errmsg(-1));
8303fe401a5SEd Maste 		return;
8313fe401a5SEd Maste 	}
8323fe401a5SEd Maste 
8333fe401a5SEd Maste 	tabndx = -1;
8343fe401a5SEd Maste 	scn = NULL;
8353fe401a5SEd Maste 	while ((scn = elf_nextscn(e, scn)) != NULL) {
8363fe401a5SEd Maste 		if (gelf_getshdr(scn, &shdr) != &shdr) {
8373fe401a5SEd Maste 			bsdar_warnc(bsdar, 0,
8383fe401a5SEd Maste 			    "elf_getshdr failed: %s", elf_errmsg(-1));
8393fe401a5SEd Maste 			continue;
8403fe401a5SEd Maste 		}
8413fe401a5SEd Maste 		if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
8423fe401a5SEd Maste 			bsdar_warnc(bsdar, 0,
8433fe401a5SEd Maste 			    "elf_strptr failed: %s", elf_errmsg(-1));
8443fe401a5SEd Maste 			continue;
8453fe401a5SEd Maste 		}
8463fe401a5SEd Maste 		if (strcmp(name, ".strtab") == 0) {
8473fe401a5SEd Maste 			tabndx = elf_ndxscn(scn);
8483fe401a5SEd Maste 			break;
8493fe401a5SEd Maste 		}
8503fe401a5SEd Maste 	}
8513fe401a5SEd Maste 	elferr = elf_errno();
8523fe401a5SEd Maste 	if (elferr != 0)
8533fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
8543fe401a5SEd Maste 		     elf_errmsg(elferr));
8553fe401a5SEd Maste 	if (tabndx == -1) {
8563fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "can't find .strtab section");
8573fe401a5SEd Maste 		return;
8583fe401a5SEd Maste 	}
8593fe401a5SEd Maste 
8603fe401a5SEd Maste 	scn = NULL;
8613fe401a5SEd Maste 	while ((scn = elf_nextscn(e, scn)) != NULL) {
8623fe401a5SEd Maste 		if (gelf_getshdr(scn, &shdr) != &shdr) {
8633fe401a5SEd Maste 			bsdar_warnc(bsdar, 0, "elf_getshdr failed: %s",
8643fe401a5SEd Maste 			    elf_errmsg(-1));
8653fe401a5SEd Maste 			continue;
8663fe401a5SEd Maste 		}
8673fe401a5SEd Maste 		if (shdr.sh_type != SHT_SYMTAB)
8683fe401a5SEd Maste 			continue;
8693fe401a5SEd Maste 
8703fe401a5SEd Maste 		data = NULL;
8713fe401a5SEd Maste 		n = 0;
8723fe401a5SEd Maste 		while (n < shdr.sh_size &&
8733fe401a5SEd Maste 		    (data = elf_getdata(scn, data)) != NULL) {
8743fe401a5SEd Maste 			len = data->d_size / shdr.sh_entsize;
8753fe401a5SEd Maste 			for (i = 0; i < len; i++) {
8763fe401a5SEd Maste 				if (gelf_getsym(data, i, &sym) != &sym) {
8773fe401a5SEd Maste 					bsdar_warnc(bsdar, 0,
8783fe401a5SEd Maste 					    "gelf_getsym failed: %s",
8793fe401a5SEd Maste 					     elf_errmsg(-1));
8803fe401a5SEd Maste 					continue;
8813fe401a5SEd Maste 				}
8823fe401a5SEd Maste 
8833fe401a5SEd Maste 				/* Keep only global and weak symbols. */
8843fe401a5SEd Maste 				if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
8853fe401a5SEd Maste 				    GELF_ST_BIND(sym.st_info) != STB_WEAK)
8863fe401a5SEd Maste 					continue;
8873fe401a5SEd Maste 
8883fe401a5SEd Maste 				/* Keep only defined symbols. */
8893fe401a5SEd Maste 				if (sym.st_shndx == SHN_UNDEF)
8903fe401a5SEd Maste 					continue;
8913fe401a5SEd Maste 
8923fe401a5SEd Maste 				if ((name = elf_strptr(e, tabndx,
8933fe401a5SEd Maste 				    sym.st_name)) == NULL) {
8943fe401a5SEd Maste 					bsdar_warnc(bsdar, 0,
8953fe401a5SEd Maste 					    "elf_strptr failed: %s",
8963fe401a5SEd Maste 					     elf_errmsg(-1));
8973fe401a5SEd Maste 					continue;
8983fe401a5SEd Maste 				}
8993fe401a5SEd Maste 
9003fe401a5SEd Maste 				add_to_ar_sym_table(bsdar, name);
9013fe401a5SEd Maste 			}
9023fe401a5SEd Maste 		}
9033fe401a5SEd Maste 	}
9043fe401a5SEd Maste 	elferr = elf_errno();
9053fe401a5SEd Maste 	if (elferr != 0)
9063fe401a5SEd Maste 		bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
9073fe401a5SEd Maste 		     elf_errmsg(elferr));
9083fe401a5SEd Maste }
9093fe401a5SEd Maste 
9103fe401a5SEd Maste /*
9113fe401a5SEd Maste  * Append to the archive string table buffer.
9123fe401a5SEd Maste  */
9133fe401a5SEd Maste static void
add_to_ar_str_table(struct bsdar * bsdar,const char * name)9143fe401a5SEd Maste add_to_ar_str_table(struct bsdar *bsdar, const char *name)
9153fe401a5SEd Maste {
9163fe401a5SEd Maste 
9173fe401a5SEd Maste 	if (bsdar->as == NULL) {
9183fe401a5SEd Maste 		bsdar->as_cap = _INIT_AS_CAP;
9193fe401a5SEd Maste 		bsdar->as_sz = 0;
9203fe401a5SEd Maste 		if ((bsdar->as = malloc(bsdar->as_cap)) == NULL)
9213fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "malloc failed");
9223fe401a5SEd Maste 	}
9233fe401a5SEd Maste 
9243fe401a5SEd Maste 	/*
9253fe401a5SEd Maste 	 * The space required for holding one member name in the 'as'
9263fe401a5SEd Maste 	 * table includes: strlen(name) + (1 for '/') + (1 for '\n') +
9273fe401a5SEd Maste 	 * (possibly 1 for padding).
9283fe401a5SEd Maste 	 */
9293fe401a5SEd Maste 	while (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) {
9303fe401a5SEd Maste 		bsdar->as_cap *= 2;
9313fe401a5SEd Maste 		bsdar->as = realloc(bsdar->as, bsdar->as_cap);
9323fe401a5SEd Maste 		if (bsdar->as == NULL)
9333fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "realloc failed");
9343fe401a5SEd Maste 	}
9353fe401a5SEd Maste 	strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name));
9363fe401a5SEd Maste 	bsdar->as_sz += strlen(name);
9373fe401a5SEd Maste 	bsdar->as[bsdar->as_sz++] = '/';
9383fe401a5SEd Maste 	bsdar->as[bsdar->as_sz++] = '\n';
9393fe401a5SEd Maste }
9403fe401a5SEd Maste 
9413fe401a5SEd Maste /*
9423fe401a5SEd Maste  * Append to the archive symbol table buffer.
9433fe401a5SEd Maste  */
9443fe401a5SEd Maste static void
add_to_ar_sym_table(struct bsdar * bsdar,const char * name)9453fe401a5SEd Maste add_to_ar_sym_table(struct bsdar *bsdar, const char *name)
9463fe401a5SEd Maste {
9473fe401a5SEd Maste 
9483fe401a5SEd Maste 	if (bsdar->s_so == NULL) {
9493fe401a5SEd Maste 		if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) ==
9503fe401a5SEd Maste 		    NULL)
9513fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "malloc failed");
9523fe401a5SEd Maste 		bsdar->s_so_cap = _INIT_SYMOFF_CAP;
9533fe401a5SEd Maste 		bsdar->s_cnt = 0;
9543fe401a5SEd Maste 	}
9553fe401a5SEd Maste 
9563fe401a5SEd Maste 	if (bsdar->s_sn == NULL) {
9573fe401a5SEd Maste 		if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
9583fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "malloc failed");
9593fe401a5SEd Maste 		bsdar->s_sn_cap = _INIT_SYMNAME_CAP;
9603fe401a5SEd Maste 		bsdar->s_sn_sz = 0;
9613fe401a5SEd Maste 	}
9623fe401a5SEd Maste 
9633fe401a5SEd Maste 	if (bsdar->s_cnt * sizeof(uint32_t) >= bsdar->s_so_cap) {
9643fe401a5SEd Maste 		bsdar->s_so_cap *= 2;
9653fe401a5SEd Maste 		bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap);
9663fe401a5SEd Maste 		if (bsdar->s_so == NULL)
9673fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "realloc failed");
9683fe401a5SEd Maste 	}
9693fe401a5SEd Maste 	bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off;
9703fe401a5SEd Maste 	bsdar->s_cnt++;
9713fe401a5SEd Maste 
9723fe401a5SEd Maste 	/*
9733fe401a5SEd Maste 	 * The space required for holding one symbol name in the 'sn'
9743fe401a5SEd Maste 	 * table includes: strlen(name) + (1 for '\n') + (possibly 1
9753fe401a5SEd Maste 	 * for padding).
9763fe401a5SEd Maste 	 */
9773fe401a5SEd Maste 	while (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) {
9783fe401a5SEd Maste 		bsdar->s_sn_cap *= 2;
9793fe401a5SEd Maste 		bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap);
9803fe401a5SEd Maste 		if (bsdar->s_sn == NULL)
9813fe401a5SEd Maste 			bsdar_errc(bsdar, errno, "realloc failed");
9823fe401a5SEd Maste 	}
9833fe401a5SEd Maste 	strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name));
9843fe401a5SEd Maste 	bsdar->s_sn_sz += strlen(name);
9853fe401a5SEd Maste 	bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
9863fe401a5SEd Maste }
987