Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.bin/ar/write.c
34677 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2007 Kai Wang
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/cdefs.h>
30
#include <sys/endian.h>
31
#include <sys/mman.h>
32
#include <sys/queue.h>
33
#include <sys/stat.h>
34
#include <archive.h>
35
#include <archive_entry.h>
36
#include <assert.h>
37
#include <errno.h>
38
#include <fcntl.h>
39
#include <gelf.h>
40
#include <libgen.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#include "ar.h"
47
48
#define _ARMAG_LEN 8 /* length of ar magic string */
49
#define _ARHDR_LEN 60 /* length of ar header */
50
#define _INIT_AS_CAP 128 /* initial archive string table size */
51
#define _INIT_SYMOFF_CAP (256*(sizeof(uint64_t))) /* initial so table size */
52
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
53
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
54
#define _TRUNCATE_LEN 15 /* number of bytes to keep for member name */
55
56
static void add_to_ar_str_table(struct bsdar *bsdar, const char *name);
57
static void add_to_ar_sym_table(struct bsdar *bsdar, const char *name);
58
static struct ar_obj *create_obj_from_file(struct bsdar *bsdar,
59
const char *name, time_t mtime);
60
static void create_symtab_entry(struct bsdar *bsdar, void *maddr,
61
size_t size);
62
static void free_obj(struct bsdar *bsdar, struct ar_obj *obj);
63
static void insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
64
struct ar_obj *pos);
65
static void prefault_buffer(const char *buf, size_t s);
66
static void read_objs(struct bsdar *bsdar, const char *archive,
67
int checkargv);
68
static void write_cleanup(struct bsdar *bsdar);
69
static void write_data(struct bsdar *bsdar, struct archive *a,
70
const void *buf, size_t s);
71
static void write_objs(struct bsdar *bsdar);
72
73
/*
74
* Create object from file, return created obj upon success, or NULL
75
* when an error occurs or the member is not newer than existing
76
* one while -u is specified.
77
*/
78
static struct ar_obj *
79
create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime)
80
{
81
struct ar_obj *obj;
82
struct stat sb;
83
const char *bname;
84
char *tmpname;
85
86
if (name == NULL)
87
return (NULL);
88
89
obj = malloc(sizeof(struct ar_obj));
90
if (obj == NULL)
91
bsdar_errc(bsdar, errno, "malloc failed");
92
if ((obj->fd = open(name, O_RDONLY, 0)) < 0) {
93
bsdar_warnc(bsdar, errno, "can't open file: %s", name);
94
free(obj);
95
return (NULL);
96
}
97
98
tmpname = strdup(name);
99
if (tmpname == NULL)
100
bsdar_errc(bsdar, errno, "strdup failed");
101
if ((bname = basename(tmpname)) == NULL)
102
bsdar_errc(bsdar, errno, "basename failed");
103
if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) {
104
if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL)
105
bsdar_errc(bsdar, errno, "malloc failed");
106
(void)strncpy(obj->name, bname, _TRUNCATE_LEN);
107
obj->name[_TRUNCATE_LEN] = '\0';
108
} else
109
if ((obj->name = strdup(bname)) == NULL)
110
bsdar_errc(bsdar, errno, "strdup failed");
111
free(tmpname);
112
113
if (fstat(obj->fd, &sb) < 0) {
114
bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name);
115
goto giveup;
116
}
117
if (!S_ISREG(sb.st_mode)) {
118
bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name);
119
goto giveup;
120
}
121
122
/*
123
* When option '-u' is specified and member is not newer than the
124
* existing one, the replacement will not happen. While if mtime == 0,
125
* which indicates that this is to "replace a none exist member",
126
* the replace will proceed regardless of '-u'.
127
*/
128
if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime)
129
goto giveup;
130
131
/*
132
* When option '-D' is specified, mtime and UID / GID from the file
133
* will be replaced with 0, and file mode with 644. This ensures that
134
* checksums will match for two archives containing the exact same
135
* files.
136
*/
137
if (bsdar->options & AR_D) {
138
obj->uid = 0;
139
obj->gid = 0;
140
obj->mtime = 0;
141
obj->md = S_IFREG | 0644;
142
} else {
143
obj->uid = sb.st_uid;
144
obj->gid = sb.st_gid;
145
obj->mtime = sb.st_mtime;
146
obj->md = sb.st_mode;
147
}
148
obj->size = sb.st_size;
149
obj->dev = sb.st_dev;
150
obj->ino = sb.st_ino;
151
152
if (obj->size == 0) {
153
obj->maddr = NULL;
154
return (obj);
155
}
156
157
if ((obj->maddr = mmap(NULL, obj->size, PROT_READ,
158
MAP_PRIVATE, obj->fd, (off_t)0)) == MAP_FAILED) {
159
bsdar_warnc(bsdar, errno, "can't mmap file: %s", obj->name);
160
goto giveup;
161
}
162
if (close(obj->fd) < 0)
163
bsdar_errc(bsdar, errno, "close failed: %s",
164
obj->name);
165
166
return (obj);
167
168
giveup:
169
if (close(obj->fd) < 0)
170
bsdar_errc(bsdar, errno, "close failed: %s",
171
obj->name);
172
free(obj->name);
173
free(obj);
174
return (NULL);
175
}
176
177
/*
178
* Free object itself and its associated allocations.
179
*/
180
static void
181
free_obj(struct bsdar *bsdar, struct ar_obj *obj)
182
{
183
if (obj->fd == -1)
184
free(obj->maddr);
185
else
186
if (obj->maddr != NULL && munmap(obj->maddr, obj->size))
187
bsdar_warnc(bsdar, errno,
188
"can't munmap file: %s", obj->name);
189
free(obj->name);
190
free(obj);
191
}
192
193
/*
194
* Insert obj to the tail, or before/after the pos obj.
195
*/
196
static void
197
insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos)
198
{
199
if (obj == NULL)
200
bsdar_errc(bsdar, 0, "try to insert a null obj");
201
202
if (pos == NULL || obj == pos)
203
/*
204
* If the object to move happens to be the position obj,
205
* or if there is not a pos obj, move it to tail.
206
*/
207
goto tail;
208
209
if (bsdar->options & AR_B) {
210
TAILQ_INSERT_BEFORE(pos, obj, objs);
211
return;
212
}
213
if (bsdar->options & AR_A) {
214
TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs);
215
return;
216
}
217
218
tail:
219
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
220
221
}
222
223
/*
224
* Read objects from archive into v_obj list. Note that checkargv is
225
* set when read_objs is used to read objects from the target of
226
* ADDLIB command (ar script mode), in this case argv array possibly
227
* specifies the members ADDLIB want.
228
*/
229
static void
230
read_objs(struct bsdar *bsdar, const char *archive, int checkargv)
231
{
232
struct archive *a;
233
struct archive_entry *entry;
234
struct ar_obj *obj;
235
const char *name;
236
const char *bname;
237
char *buff;
238
char **av;
239
size_t size;
240
int i, r, find;
241
242
if ((a = archive_read_new()) == NULL)
243
bsdar_errc(bsdar, 0, "archive_read_new failed");
244
archive_read_support_format_ar(a);
245
AC(archive_read_open_filename(a, archive, DEF_BLKSZ));
246
for (;;) {
247
r = archive_read_next_header(a, &entry);
248
if (r == ARCHIVE_FATAL)
249
bsdar_errc(bsdar, archive_errno(a), "%s",
250
archive_error_string(a));
251
if (r == ARCHIVE_EOF)
252
break;
253
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
254
bsdar_warnc(bsdar, archive_errno(a), "%s",
255
archive_error_string(a));
256
if (r == ARCHIVE_RETRY) {
257
bsdar_warnc(bsdar, 0, "Retrying...");
258
continue;
259
}
260
261
name = archive_entry_pathname(entry);
262
263
/*
264
* skip pseudo members.
265
*/
266
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
267
continue;
268
269
/*
270
* If checkargv is set, only read those members specified
271
* in argv.
272
*/
273
if (checkargv && bsdar->argc > 0) {
274
find = 0;
275
for(i = 0; i < bsdar->argc; i++) {
276
av = &bsdar->argv[i];
277
if (*av == NULL)
278
continue;
279
if ((bname = basename(*av)) == NULL)
280
bsdar_errc(bsdar, errno,
281
"basename failed");
282
if (strcmp(bname, name) != 0)
283
continue;
284
285
*av = NULL;
286
find = 1;
287
break;
288
}
289
if (!find)
290
continue;
291
}
292
293
size = archive_entry_size(entry);
294
295
if (size > 0) {
296
if ((buff = malloc(size)) == NULL)
297
bsdar_errc(bsdar, errno, "malloc failed");
298
if (archive_read_data(a, buff, size) != (ssize_t)size) {
299
bsdar_warnc(bsdar, archive_errno(a), "%s",
300
archive_error_string(a));
301
free(buff);
302
continue;
303
}
304
} else
305
buff = NULL;
306
307
obj = malloc(sizeof(struct ar_obj));
308
if (obj == NULL)
309
bsdar_errc(bsdar, errno, "malloc failed");
310
obj->maddr = buff;
311
if ((obj->name = strdup(name)) == NULL)
312
bsdar_errc(bsdar, errno, "strdup failed");
313
obj->size = size;
314
obj->uid = archive_entry_uid(entry);
315
obj->gid = archive_entry_gid(entry);
316
obj->md = archive_entry_mode(entry);
317
obj->mtime = archive_entry_mtime(entry);
318
obj->dev = 0;
319
obj->ino = 0;
320
321
/*
322
* Objects from archive have obj->fd set to -1,
323
* for the ease of cleaning up.
324
*/
325
obj->fd = -1;
326
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
327
}
328
AC(archive_read_close(a));
329
AC(archive_read_free(a));
330
}
331
332
/*
333
* Determine the constitution of resulting archive.
334
*/
335
int
336
ar_write_archive(struct bsdar *bsdar, int mode)
337
{
338
struct ar_obj *nobj, *obj, *obj_temp, *pos;
339
struct stat sb;
340
const char *bname;
341
char **av;
342
int exitcode, i;
343
344
TAILQ_INIT(&bsdar->v_obj);
345
exitcode = EXIT_SUCCESS;
346
nobj = NULL;
347
pos = NULL;
348
memset(&sb, 0, sizeof(sb));
349
350
assert(mode == 'A' || mode == 'd' || mode == 'm' || mode == 'q' ||
351
mode == 'r' || mode == 's');
352
353
/*
354
* Test if the specified archive exists, to figure out
355
* whether we are creating one here.
356
*/
357
if (stat(bsdar->filename, &sb) != 0) {
358
if (errno != ENOENT) {
359
bsdar_warnc(bsdar, 0, "stat %s failed",
360
bsdar->filename);
361
return (EXIT_FAILURE);
362
}
363
364
/* We do not create archive in mode 'd', 'm' and 's'. */
365
if (mode != 'r' && mode != 'q') {
366
bsdar_warnc(bsdar, 0, "%s: no such file",
367
bsdar->filename);
368
return (EXIT_FAILURE);
369
}
370
371
/* Issue a warning if -c is not specified when creating. */
372
if (!(bsdar->options & AR_C))
373
bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
374
goto new_archive;
375
}
376
377
/*
378
* First read members from existing archive.
379
*/
380
read_objs(bsdar, bsdar->filename, 0);
381
382
/*
383
* For mode 's', no member will be moved, deleted or replaced.
384
*/
385
if (mode == 's')
386
goto write_objs;
387
388
/*
389
* For mode 'q', we don't need to adjust existing members either.
390
* Also, -a, -b and -i are ignored in this mode. New members are
391
* always inserted at tail.
392
*/
393
if (mode == 'q')
394
goto new_archive;
395
396
/*
397
* Mode 'A' adds the contents of another archive to the tail of
398
* current archive. Note that mode 'A' is a special mode for the
399
* ADDLIB command of the ar script mode. Currently there is no
400
* access to this function from the ar command line mode.
401
*/
402
if (mode == 'A') {
403
/*
404
* Read objects from the target archive of ADDLIB command.
405
* If there are members specified in argv, read those members
406
* only, otherwise the entire archive will be read.
407
*/
408
read_objs(bsdar, bsdar->addlib, 1);
409
goto write_objs;
410
}
411
412
/*
413
* Try to find the position member specified by user.
414
*/
415
if (bsdar->options & AR_A || bsdar->options & AR_B) {
416
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
417
if (strcmp(obj->name, bsdar->posarg) == 0) {
418
pos = obj;
419
break;
420
}
421
}
422
423
/*
424
* If can't find `pos' specified by user,
425
* silently insert objects at tail.
426
*/
427
if (pos == NULL)
428
bsdar->options &= ~(AR_A | AR_B);
429
}
430
431
for (i = 0; i < bsdar->argc; i++) {
432
av = &bsdar->argv[i];
433
434
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
435
if ((bname = basename(*av)) == NULL)
436
bsdar_errc(bsdar, errno, "basename failed");
437
if (bsdar->options & AR_TR) {
438
if (strncmp(bname, obj->name, _TRUNCATE_LEN))
439
continue;
440
} else
441
if (strcmp(bname, obj->name) != 0)
442
continue;
443
444
if (mode == 'r') {
445
/*
446
* if the new member is not qualified
447
* to replace the old one, skip it.
448
*/
449
nobj = create_obj_from_file(bsdar, *av,
450
obj->mtime);
451
if (nobj == NULL) {
452
exitcode = EXIT_FAILURE;
453
goto skip_obj;
454
}
455
}
456
457
if (bsdar->options & AR_V)
458
(void)fprintf(stdout, "%c - %s\n", mode,
459
*av);
460
461
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
462
if (mode == 'd' || mode == 'r')
463
free_obj(bsdar, obj);
464
465
if (mode == 'm')
466
insert_obj(bsdar, obj, pos);
467
if (mode == 'r')
468
insert_obj(bsdar, nobj, pos);
469
470
skip_obj:
471
*av = NULL;
472
break;
473
}
474
475
}
476
477
new_archive:
478
/*
479
* When operating in mode 'r', directly add those user specified
480
* objects which do not exist in current archive. When operating
481
* in mode 'q', all objects specified in command line args are
482
* appended to the archive, without comparing with existing ones.
483
*/
484
for (i = 0; i < bsdar->argc; i++) {
485
av = &bsdar->argv[i];
486
if (*av != NULL && (mode == 'r' || mode == 'q')) {
487
nobj = create_obj_from_file(bsdar, *av, 0);
488
if (nobj == NULL) {
489
exitcode = EXIT_FAILURE;
490
*av = NULL;
491
continue;
492
}
493
insert_obj(bsdar, nobj, pos);
494
if (bsdar->options & AR_V && nobj != NULL)
495
(void)fprintf(stdout, "a - %s\n", *av);
496
*av = NULL;
497
}
498
}
499
500
write_objs:
501
write_objs(bsdar);
502
write_cleanup(bsdar);
503
504
return (exitcode);
505
}
506
507
/*
508
* Memory cleaning up.
509
*/
510
static void
511
write_cleanup(struct bsdar *bsdar)
512
{
513
struct ar_obj *obj, *obj_temp;
514
515
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
516
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
517
free_obj(bsdar, obj);
518
}
519
520
free(bsdar->as);
521
free(bsdar->s_so);
522
free(bsdar->s_sn);
523
bsdar->as = NULL;
524
bsdar->s_so = NULL;
525
bsdar->s_so_max = 0;
526
bsdar->s_sn = NULL;
527
}
528
529
/*
530
* Fault in the buffer prior to writing as a workaround for poor performance
531
* due to interaction with kernel fs deadlock avoidance code. See the comment
532
* above vn_io_fault_doio() in sys/kern/vfs_vnops.c for details of the issue.
533
*/
534
static void
535
prefault_buffer(const char *buf, size_t s)
536
{
537
volatile const char *p;
538
size_t page_size;
539
540
if (s == 0)
541
return;
542
page_size = sysconf(_SC_PAGESIZE);
543
for (p = buf; p < buf + s; p += page_size)
544
*p;
545
/*
546
* Ensure we touch the last page as well, in case the buffer is not
547
* page-aligned.
548
*/
549
*(volatile const char *)(buf + s - 1);
550
}
551
552
/*
553
* Wrapper for archive_write_data().
554
*/
555
static void
556
write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s)
557
{
558
ssize_t written;
559
560
prefault_buffer(buf, s);
561
while (s > 0) {
562
written = archive_write_data(a, buf, s);
563
if (written < 0)
564
bsdar_errc(bsdar, archive_errno(a), "%s",
565
archive_error_string(a));
566
buf = (const char *)buf + written;
567
s -= written;
568
}
569
}
570
571
/*
572
* Write the resulting archive members.
573
*/
574
static void
575
write_objs(struct bsdar *bsdar)
576
{
577
struct ar_obj *obj;
578
struct archive *a;
579
struct archive_entry *entry;
580
size_t s_sz; /* size of archive symbol table. */
581
size_t pm_sz; /* size of pseudo members */
582
size_t w_sz; /* size of words in symbol table */
583
size_t i;
584
uint64_t nr;
585
uint32_t nr32;
586
587
if (elf_version(EV_CURRENT) == EV_NONE)
588
bsdar_errc(bsdar, 0, "ELF library initialization failed: %s",
589
elf_errmsg(-1));
590
591
bsdar->rela_off = 0;
592
593
/* Create archive symbol table and archive string table, if need. */
594
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
595
if (!(bsdar->options & AR_SS) && obj->maddr != NULL)
596
create_symtab_entry(bsdar, obj->maddr, obj->size);
597
if (strlen(obj->name) > _MAXNAMELEN_SVR4)
598
add_to_ar_str_table(bsdar, obj->name);
599
bsdar->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
600
}
601
602
/*
603
* Pad the symbol name string table. It is treated specially because
604
* symbol name table should be padded by a '\0', not the common '\n'
605
* for other members. The size of sn table includes the pad bit.
606
*/
607
if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0)
608
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
609
610
/*
611
* Archive string table is padded by a "\n" as the normal members.
612
* The difference is that the size of archive string table counts
613
* in the pad bit, while normal members' size fields do not.
614
*/
615
if (bsdar->as != NULL && bsdar->as_sz % 2 != 0)
616
bsdar->as[bsdar->as_sz++] = '\n';
617
618
/*
619
* If there is a symbol table, calculate the size of pseudo members,
620
* convert previously stored relative offsets to absolute ones, and
621
* then make them Big Endian.
622
*
623
* absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
624
*/
625
w_sz = sizeof(uint32_t);
626
if (bsdar->s_cnt != 0) {
627
s_sz = (bsdar->s_cnt + 1) * sizeof(uint32_t) + bsdar->s_sn_sz;
628
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
629
if (bsdar->as != NULL)
630
pm_sz += _ARHDR_LEN + bsdar->as_sz;
631
/* Use the 64-bit word size format if necessary. */
632
if (bsdar->s_so_max > UINT32_MAX - pm_sz) {
633
w_sz = sizeof(uint64_t);
634
pm_sz -= s_sz;
635
s_sz = (bsdar->s_cnt + 1) * sizeof(uint64_t) +
636
bsdar->s_sn_sz;
637
pm_sz += s_sz;
638
/* Convert to big-endian. */
639
for (i = 0; i < bsdar->s_cnt; i++)
640
bsdar->s_so[i] =
641
htobe64(bsdar->s_so[i] + pm_sz);
642
} else {
643
/*
644
* Convert to big-endian and shuffle in-place to
645
* the front of the allocation. XXX UB
646
*/
647
for (i = 0; i < bsdar->s_cnt; i++)
648
((uint32_t *)(bsdar->s_so))[i] =
649
htobe32(bsdar->s_so[i] + pm_sz);
650
}
651
}
652
653
if ((a = archive_write_new()) == NULL)
654
bsdar_errc(bsdar, 0, "archive_write_new failed");
655
656
archive_write_set_format_ar_svr4(a);
657
658
AC(archive_write_open_filename(a, bsdar->filename));
659
660
/*
661
* write the archive symbol table, if there is one.
662
* If options -s is explicitly specified or we are invoked
663
* as ranlib, write the symbol table even if it is empty.
664
*/
665
if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) ||
666
bsdar->options & AR_S) {
667
entry = archive_entry_new();
668
if (entry == NULL)
669
bsdar_errc(bsdar, 0, "archive_entry_new failed");
670
if (w_sz == sizeof(uint64_t))
671
archive_entry_copy_pathname(entry, "/SYM64/");
672
else
673
archive_entry_copy_pathname(entry, "/");
674
if ((bsdar->options & AR_D) == 0)
675
archive_entry_set_mtime(entry, time(NULL), 0);
676
archive_entry_set_size(entry, (bsdar->s_cnt + 1) * w_sz +
677
bsdar->s_sn_sz);
678
AC(archive_write_header(a, entry));
679
if (w_sz == sizeof(uint64_t)) {
680
nr = htobe64(bsdar->s_cnt);
681
write_data(bsdar, a, &nr, sizeof(nr));
682
} else {
683
nr32 = htobe32((uint32_t)bsdar->s_cnt);
684
write_data(bsdar, a, &nr32, sizeof(nr32));
685
}
686
write_data(bsdar, a, bsdar->s_so, w_sz * bsdar->s_cnt);
687
write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
688
archive_entry_free(entry);
689
}
690
691
/* write the archive string table, if any. */
692
if (bsdar->as != NULL) {
693
entry = archive_entry_new();
694
if (entry == NULL)
695
bsdar_errc(bsdar, 0, "archive_entry_new failed");
696
archive_entry_copy_pathname(entry, "//");
697
archive_entry_set_size(entry, bsdar->as_sz);
698
AC(archive_write_header(a, entry));
699
write_data(bsdar, a, bsdar->as, bsdar->as_sz);
700
archive_entry_free(entry);
701
}
702
703
/* write normal members. */
704
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
705
entry = archive_entry_new();
706
if (entry == NULL)
707
bsdar_errc(bsdar, 0, "archive_entry_new failed");
708
archive_entry_copy_pathname(entry, obj->name);
709
archive_entry_set_uid(entry, obj->uid);
710
archive_entry_set_gid(entry, obj->gid);
711
archive_entry_set_mode(entry, obj->md);
712
archive_entry_set_size(entry, obj->size);
713
archive_entry_set_mtime(entry, obj->mtime, 0);
714
archive_entry_set_dev(entry, obj->dev);
715
archive_entry_set_ino(entry, obj->ino);
716
archive_entry_set_filetype(entry, AE_IFREG);
717
AC(archive_write_header(a, entry));
718
write_data(bsdar, a, obj->maddr, obj->size);
719
archive_entry_free(entry);
720
}
721
722
AC(archive_write_close(a));
723
AC(archive_write_free(a));
724
}
725
726
/*
727
* Extract global symbols from ELF binary members.
728
*/
729
static void
730
create_symtab_entry(struct bsdar *bsdar, void *maddr, size_t size)
731
{
732
Elf *e;
733
Elf_Scn *scn;
734
GElf_Shdr shdr;
735
GElf_Sym sym;
736
Elf_Data *data;
737
char *name;
738
size_t n, shstrndx;
739
int elferr, tabndx, len, i;
740
741
if ((e = elf_memory(maddr, size)) == NULL) {
742
bsdar_warnc(bsdar, 0, "elf_memory() failed: %s",
743
elf_errmsg(-1));
744
return;
745
}
746
if (elf_kind(e) != ELF_K_ELF) {
747
/* Silently ignore non-elf member. */
748
elf_end(e);
749
return;
750
}
751
if (elf_getshstrndx(e, &shstrndx) == 0) {
752
bsdar_warnc(bsdar, 0, "elf_getshstrndx failed: %s",
753
elf_errmsg(-1));
754
elf_end(e);
755
return;
756
}
757
758
tabndx = -1;
759
scn = NULL;
760
while ((scn = elf_nextscn(e, scn)) != NULL) {
761
if (gelf_getshdr(scn, &shdr) != &shdr) {
762
bsdar_warnc(bsdar, 0,
763
"elf_getshdr failed: %s", elf_errmsg(-1));
764
continue;
765
}
766
if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
767
bsdar_warnc(bsdar, 0,
768
"elf_strptr failed: %s", elf_errmsg(-1));
769
continue;
770
}
771
if (strcmp(name, ".strtab") == 0) {
772
tabndx = elf_ndxscn(scn);
773
break;
774
}
775
}
776
elferr = elf_errno();
777
if (elferr != 0)
778
bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
779
elf_errmsg(elferr));
780
if (tabndx == -1) {
781
bsdar_warnc(bsdar, 0, "can't find .strtab section");
782
elf_end(e);
783
return;
784
}
785
786
scn = NULL;
787
while ((scn = elf_nextscn(e, scn)) != NULL) {
788
if (gelf_getshdr(scn, &shdr) != &shdr) {
789
bsdar_warnc(bsdar, 0, "elf_getshdr failed: %s",
790
elf_errmsg(-1));
791
continue;
792
}
793
if (shdr.sh_type != SHT_SYMTAB)
794
continue;
795
796
data = NULL;
797
n = 0;
798
while (n < shdr.sh_size &&
799
(data = elf_getdata(scn, data)) != NULL) {
800
len = data->d_size / shdr.sh_entsize;
801
for (i = 0; i < len; i++) {
802
if (gelf_getsym(data, i, &sym) != &sym) {
803
bsdar_warnc(bsdar, 0,
804
"gelf_getsym failed: %s",
805
elf_errmsg(-1));
806
continue;
807
}
808
809
/* keep only global or weak symbols */
810
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
811
GELF_ST_BIND(sym.st_info) != STB_WEAK)
812
continue;
813
814
/* keep only defined symbols */
815
if (sym.st_shndx == SHN_UNDEF)
816
continue;
817
818
if ((name = elf_strptr(e, tabndx,
819
sym.st_name)) == NULL) {
820
bsdar_warnc(bsdar, 0,
821
"elf_strptr failed: %s",
822
elf_errmsg(-1));
823
continue;
824
}
825
826
add_to_ar_sym_table(bsdar, name);
827
}
828
}
829
}
830
elferr = elf_errno();
831
if (elferr != 0)
832
bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
833
elf_errmsg(elferr));
834
835
elf_end(e);
836
}
837
838
/*
839
* Append to the archive string table buffer.
840
*/
841
static void
842
add_to_ar_str_table(struct bsdar *bsdar, const char *name)
843
{
844
845
if (bsdar->as == NULL) {
846
bsdar->as_cap = _INIT_AS_CAP;
847
bsdar->as_sz = 0;
848
if ((bsdar->as = malloc(bsdar->as_cap)) == NULL)
849
bsdar_errc(bsdar, errno, "malloc failed");
850
}
851
852
/*
853
* The space required for holding one member name in as table includes:
854
* strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
855
*/
856
while (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) {
857
bsdar->as_cap *= 2;
858
bsdar->as = realloc(bsdar->as, bsdar->as_cap);
859
if (bsdar->as == NULL)
860
bsdar_errc(bsdar, errno, "realloc failed");
861
}
862
strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name));
863
bsdar->as_sz += strlen(name);
864
bsdar->as[bsdar->as_sz++] = '/';
865
bsdar->as[bsdar->as_sz++] = '\n';
866
}
867
868
/*
869
* Append to the archive symbol table buffer.
870
*/
871
static void
872
add_to_ar_sym_table(struct bsdar *bsdar, const char *name)
873
{
874
875
if (bsdar->s_so == NULL) {
876
if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) ==
877
NULL)
878
bsdar_errc(bsdar, errno, "malloc failed");
879
bsdar->s_so_cap = _INIT_SYMOFF_CAP;
880
bsdar->s_cnt = 0;
881
}
882
883
if (bsdar->s_sn == NULL) {
884
if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
885
bsdar_errc(bsdar, errno, "malloc failed");
886
bsdar->s_sn_cap = _INIT_SYMNAME_CAP;
887
bsdar->s_sn_sz = 0;
888
}
889
890
if (bsdar->s_cnt * sizeof(uint64_t) >= bsdar->s_so_cap) {
891
bsdar->s_so_cap *= 2;
892
bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap);
893
if (bsdar->s_so == NULL)
894
bsdar_errc(bsdar, errno, "realloc failed");
895
}
896
bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off;
897
if ((uint64_t)bsdar->rela_off > bsdar->s_so_max)
898
bsdar->s_so_max = (uint64_t)bsdar->rela_off;
899
bsdar->s_cnt++;
900
901
/*
902
* The space required for holding one symbol name in sn table includes:
903
* strlen(name) + (1 for '\n') + (possibly 1 for padding).
904
*/
905
while (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) {
906
bsdar->s_sn_cap *= 2;
907
bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap);
908
if (bsdar->s_sn == NULL)
909
bsdar_errc(bsdar, errno, "realloc failed");
910
}
911
strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name));
912
bsdar->s_sn_sz += strlen(name);
913
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
914
}
915
916